---
title: goquery爬虫Boss直聘信息
tags: go,goquery
author: Clown95
---
# 背景
Hello小伙伴们,在之前的文章中,我们对go的爬虫库goquery进行了简单的介绍,今天我们就来进行一个爬虫BOSS直聘Golang招聘信息的实战项目。
![](https://imgconvert.csdnimg.cn/aHR0cDovL3d3MS5zaW5haW1nLmNuL2xhcmdlLzVmZTc5MTA2bHkxZzI1Z3ZkM2FuOWoyMTVvMHY4anhxLmpwZw?x-oss-process=image/format,png)
# 需求
在写代码之前,我们先了解下我们需要爬取什么内容。
![](https://imgconvert.csdnimg.cn/aHR0cDovL3d3MS5zaW5haW1nLmNuL2xhcmdlLzVmZTc5MTA2bHkxZzI1anh1NjE0dmoyMTQyMDVzd2c5LmpwZw?x-oss-process=image/format,png)
`招聘要求`我们鼠标悬停在列表上,会出现一个悬浮窗里面有`职位描述`
![](https://imgconvert.csdnimg.cn/aHR0cDovL3d3MS5zaW5haW1nLmNuL2xhcmdlLzVmZTc5MTA2bHkxZzI1anpzbmNtYmoyMTVrMG9hdGVqLmpwZw?x-oss-process=image/format,png)
根据网站给出的信息我们可以爬虫到以下内容:
- 工作岗位
- 薪资范围
- 公司地址
- 工作经验
- 学历要求
- 公司名称
- 公司信息
- 招聘要求
# 源文件说明
我们先看一个列表的HTML代码,了解我们需要爬取的内容在哪个标签。
```html
<li>
<div class="job-primary">
<div class="info-primary">
<h3 class="name">
<a href="/job_detail/6177f4eb544e15f81n172dm8GVI~.html" data-jid="6177f4eb544e15f81n172dm8GVI~" data-itemid="1" data-lid="1rzMXyp7wgw.search" data-jobid="19124190" data-index="0" ka="search_list_1" target="_blank">
<div class="job-title">Golang</div>
<span class="red">15k-30k</span>
<div class="info-detail"></div>
</a>
</h3>
<p>北京 朝阳区 四惠<em class="vline"></em>3-5年<em class="vline"></em>本科</p>
</div>
<div class="info-company">
<div class="company-text">
<h3 class="name"><a href="/gongsi/a40445e10ec47e2b1Xx83tm-.html" ka="search_list_company_1_custompage" target="_blank">元征科技</a></h3>
<p>互联网<em class="vline"></em>已上市<em class="vline"></em>1000-9999人</p>
</div>
</div>
<div class="info-publis">
<h3 class="name"><img src="https://img.bosszhipin.com/beijin/mcs/useravatar/20180828/41c31311d7c965b71e51ad58a3be8eca6a4d2e0010b6eada6c207131ca360c65_s.jpg?x-oss-process=image/resize,w_40,limit_0" />罗女士<em class="vline"></em>HRBP</h3>
<p></p>
</div>
<a href="javascript:;" data-url="/gchat/addRelation.json?jobId=6177f4eb544e15f81n172dm8GVI~&lid=1rzMXyp7wgw.search" redirect-url="/geek/new/index/chat?id=9e2e5060ec68738a1X1729y4EVQ~" target="_blank" class="btn btn-startchat">立即沟通
</a>
</div>
</li>
```
通过上面的代码我们可以看到:
- 工作岗位是在div class="info-primary"下面的div class="job-title"
- 薪资范围是在div class="info-primary"下面的span class="red"
- 公司地址+工作经验 +学历要求是在div class="info-primary"下面的p标签
- 公司名称是在div class="info-company"下面的h3 标签class="name"
- 公司信息是在div class="info-company"下面的p标签
- 招聘要求是Json接口有两种方法一是可以直接获取a标签中的json,而是获取a标签中的data-jid属性和data-lid属性拼接json
# 代码编写
1. 浏览器的User-age设置
大部分网站都具备一定的反爬虫能力,最简单的就是判断访问网页的客户端是否是浏览器,所以我们现在需要模拟下浏览器的`User-Agent`
```go
//常见浏览器的User-Agent
var UserAgentList = []string{"Mozilla/5.0 (compatible, MSIE 10.0, Windows NT, DigExt)",
"Mozilla/4.0 (compatible, MSIE 7.0, Windows NT 5.1, 360SE)",
"Mozilla/4.0 (compatible, MSIE 8.0, Windows NT 6.0, Trident/4.0)",
"Mozilla/5.0 (compatible, MSIE 9.0, Windows NT 6.1, Trident/5.0,",
"Opera/9.80 (Windows NT 6.1, U, en) Presto/2.8.131 Version/11.11",
"Mozilla/4.0 (compatible, MSIE 7.0, Windows NT 5.1, TencentTraveler 4.0)",
"Mozilla/5.0 (Windows, U, Windows NT 6.1, en-us) AppleWebKit/534.50 (KHTML, like Gecko) Version/5.1 Safari/534.50",
"Mozilla/5.0 (Macintosh, Intel Mac OS X 10_7_0) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.56 Safari/535.11",
"Mozilla/5.0 (Macintosh, U, Intel Mac OS X 10_6_8, en-us) AppleWebKit/534.50 (KHTML, like Gecko) Version/5.1 Safari/534.50",
"Mozilla/5.0 (Linux, U, Android 3.0, en-us, Xoom Build/HRI39) AppleWebKit/534.13 (KHTML, like Gecko) Version/4.0 Safari/534.13",
"Mozilla/5.0 (iPad, U, CPU OS 4_3_3 like Mac OS X, en-us) AppleWebKit/533.17.9 (KHTML, like Gecko) Version/5.0.2 Mobile/8J2 Safari/6533.18.5",
"Mozilla/4.0 (compatible, MSIE 7.0, Windows NT 5.1, Trident/4.0, SE 2.X MetaSr 1.0, SE 2.X MetaSr 1.0, .NET CLR 2.0.50727, SE 2.X MetaSr 1.0)",
"Mozilla/5.0 (iPhone, U, CPU iPhone OS 4_3_3 like Mac OS X, en-us) AppleWebKit/533.17.9 (KHTML, like Gecko) Version/5.0.2 Mobile/8J2 Safari/6533.18.5",
"MQQBrowser/26 Mozilla/5.0 (Linux, U, Android 2.3.7, zh-cn, MB200 Build/GRJ22, CyanogenMod-7) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1"}
//从上面列表中随机获取一个User-Agent
func GetRandomUserAgent() string {
r := rand.New(rand.NewSource(time.Now().UnixNano()))
return UserAgentList[r.Intn(len(UserAgentList))]
}
```
2. 获取网页
我们爬虫的第一步就是先要获取目标页面的源代码。
```go
func GetHtml(bossurl string) *http.Response {
proxyurl := "http://122.136.212.132:53281" //代理IP,需要自己更换
proxy, _ := url.Parse(proxyurl) // 解析代理IP
netTransport := &http.Transport{ //要管理代理、TLS配置、keep-alive、压缩和其他设置,可以创建一个Transport
Proxy: http.ProxyURL(proxy),
MaxIdleConnsPerHost: 10,
ResponseHeaderTimeout: time.Second*2, //超时设置
}
client := &http.Client{ //要管理HTTP客户端的头域、重定向策略和其他设置,创建一个Client
Timeout: time.Second * 2,
Transport: netTransport,
}
req, err := http.NewRequest("GET", bossurl, nil) //NewRequest使用指定的方法、网址和可选的主题创建并返回一个新的*Request。
if err != nil {
log.Println(err)
}
req.Header.Add("User-Agent", models.GetRandomUserAgent()) //模拟浏览器User-Agent
resp, err := client.Do(req) //Do方法发送请求,返回HTTP回复
if err != nil {
log.Println(err)
}
return resp //返回网页响应
}
```
可以看到代码中,通过`http.Transpor`添加了代理IP,并且设置了超时时间。接着通过`http.Client`创建了一个客户端设置代理IP。为什么需要添加代理IP呢?这是为了防止我们本机的IP,因为访问目标网站过多而被拉黑。简单的来说也是防反爬虫的一种必备方法,我们的代码中只使用了一个代理仅作为演示。在实际开发中,你应该拥有一个代理池能够验证代理的有效性,并随机更换代理。
3. 解析网页内容
网页内容的解析是我们程序的核心,我们使用GoQuery遍历的匹配我们需要的信息。因为使用方都一样,在这边我们侧重说下`招聘需求`的解析。
```go
func ParseHtml(resp *http.Response){
defer resp.Body.Close()
dom, err := goquery.NewDocumentFromReader(resp.Body)
if err != nil {
log.Fatalln(err)
}
dom.Find(".job-primary").Each(func(i int, selection *goquery.Selection) {
time.Sleep(3 * time.Second) //防止访问次数过于频繁
fmt.Println("公司名称:", selection.Find(".info-company .name").Text()) //公司名称
fmt.Println("公司简介:", selection.Find(".info-company .company-text p ").Text()) //公司简单信息
fmt.Println("招聘岗位:", selection.Find(".info-primary .job-title").Text()) //招聘岗位
fmt.Println("简单信息:", selection.Find(".info-primary p").Text()) // 公司地址+ 工作经验+ 学历要求
fmt.Println("薪资范围:", selection.Find(".info-primary .red").Text()) // 薪资
data_lid, _ := selection.Find(".info-primary a").Attr("data-lid")
data_jid, _ := selection.Find(".info-primary a").Attr("data-jid")
jobJson := fmt.Sprintf("https://www.zhipin.com/wapi/zpgeek/view/job/card.json?jid=%s&lid=%s", data_jid, data_lid)
fmt.Println("招聘要求:", GetJobInfo(jobJson))
fmt.Println()
})
}
```
这段代码可以看出,我们的程序是首先获取到json的两个参数信息`data_jid`和`data_lid`,然后拼接成一个完成的json接口。`GetJobInfo()`函数还未给出,我们稍后在看。
我们随便打开一个josn地址复制内容到`json.cn`解析下,可以看到主要的信息还是一个html。
![](https://imgconvert.csdnimg.cn/aHR0cDovL3d3MS5zaW5haW1nLmNuL2xhcmdlLzVmZTc5MTA2bHkxZzI2bHJhdHEyZ2oyMW1tMTF3ZTFkLmpwZw?x-oss-process=image/format,png)
因此我们先定义一个结构体用来解析json
```go
type JsInfo struct {
Code int `json:"code"`
Message string `json:"message"`
ZpData struct {
HTML string `json:"html"`
} `json:"zpData"`
}
```
然后我们在封装一个`GetJobInfo()`函数进行Json内容的截取。因为json的主要内容是个html信息,所以我们仍然使用GoQery进行爬取。
```go
func GetJobInfo(url string) (string) {
respjson, _ := http.Get(url)
resp_byte, _ := ioutil.ReadAll(respjson.Body)
respHtml := string(resp_byte)
var job models.JsInfo
json.Unmarshal([]byte(respHtml), &job)
dom, err := goquery.NewDocumentFromReader(strings.NewReader(job.ZpData.HTML))
if err != nil {
fmt.Println(err)
}
var s string
dom.Find(".detail-bottom-text").Each(func(i int, selection *goquery.Selection) {
s = selection.Text()
})
return s
}
```
4. 主函数
主函数的内容比较简单,因为BOSS直聘的翻页跳转是通过ur实现的,我们通过for循环遍历10页的内容,并且添加sleep函数,防止访问过于频繁从而导致我们的IP被拉黑。
```go
func main() {
for page := 1; page < 10; page++ {
bossurl := fmt.Sprintf("https://www.zhipin.com/c101010100-p100116/?page=%d&ka=page-%d", page, page)
time.Sleep(3*time.Second) // 增加延时防止网站拉黑IP
resp :=GetHtml(bossurl)
ParseHtml(resp)
}
}
```
# 总结
我们的爬虫小实战到此就完成了,为了防止IP被拉黑,我没有使用并发,有兴趣的小伙伴可以改善下我们的代码。我必须要再次强调,我们在爬虫的时候一定要设置代理IP,因为大部分网站都设置了反爬虫机制,如果我们IP在同一时间内访问次数过多,我们真实的IP会被网站运营商短暂或者永久性拉黑,这会影响到其他用户对网站的正常使用。
最后我们我们看下运行结果。
![](https://imgconvert.csdnimg.cn/aHR0cDovL3d3MS5zaW5haW1nLmNuL2xhcmdlLzVmZTc5MTA2bHkxZzI2bWZ0bnpmc2oyMTFpMTV5YWxxLmpwZw?x-oss-process=image/format,png)
有疑问加站长微信联系(非本文作者)