goquery爬虫Boss直聘信息


title: goquery爬虫Boss直聘信息
tags: go,goquery
author: Clown95


背景

Hello小伙伴们,在之前的文章中,我们对go的爬虫库goquery进行了简单的介绍,今天我们就来进行一个爬虫BOSS直聘Golang招聘信息的实战项目。

需求

在写代码之前,我们先了解下我们需要爬取什么内容。

招聘要求我们鼠标悬停在列表上,会出现一个悬浮窗里面有职位描述

根据网站给出的信息我们可以爬虫到以下内容:

  • 工作岗位
  • 薪资范围
  • 公司地址
  • 工作经验
  • 学历要求
  • 公司名称
  • 公司信息
  • 招聘要求

源文件说明

我们先看一个列表的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

//常见浏览器的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))]
}
  1. 获取网页

我们爬虫的第一步就是先要获取目标页面的源代码。


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,因为访问目标网站过多而被拉黑。简单的来说也是防反爬虫的一种必备方法,我们的代码中只使用了一个代理仅作为演示。在实际开发中,你应该拥有一个代理池能够验证代理的有效性,并随机更换代理。

  1. 解析网页内容

网页内容的解析是我们程序的核心,我们使用GoQuery遍历的匹配我们需要的信息。因为使用方都一样,在这边我们侧重说下招聘需求的解析。

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_jiddata_lid,然后拼接成一个完成的json接口。GetJobInfo()函数还未给出,我们稍后在看。

我们随便打开一个josn地址复制内容到json.cn解析下,可以看到主要的信息还是一个html。

因此我们先定义一个结构体用来解析json

type JsInfo struct {
	Code    int    `json:"code"`
	Message string `json:"message"`
	ZpData  struct {
		HTML string `json:"html"`
	} `json:"zpData"`
}

然后我们在封装一个GetJobInfo()函数进行Json内容的截取。因为json的主要内容是个html信息,所以我们仍然使用GoQery进行爬取。

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
}
  1. 主函数

主函数的内容比较简单,因为BOSS直聘的翻页跳转是通过ur实现的,我们通过for循环遍历10页的内容,并且添加sleep函数,防止访问过于频繁从而导致我们的IP被拉黑。

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会被网站运营商短暂或者永久性拉黑,这会影响到其他用户对网站的正常使用。

最后我们我们看下运行结果。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值