在之前的学习笔记中,介绍了Xpath语法规则和lxml库的基本使用,同时也列举出了一部分示例代码。为了更加深入地学习和运用好这两大工具,下面以爬取Tencent招聘网站职位信息为实例介绍在实践中基于Xpath和lxml库编写爬虫的基本流程和方法。
Tencent招聘网址:https://hr.tencent.com/
进入网站后,我们来浏览一下社会招聘一栏,并输入搜索关键字为’python’,之后方可进入如下页面:
此后,在爬取前,我们先任意选择一个职位进入职位详情页,分析一下我们所要爬取的信息有哪些。在如下详情页中我们可以看到欲获取的信息有职位名称、工作地点、职位类别、招聘人数、工作职责和要求。
这么一来,爬取需求就已经明确,那么接下来就是分析爬取过程。回顾一下,我们是如何进入职位详情页——在社会招聘先输入关键字后进入职位列表页,然后点击一个职位,浏览器便打开了一个新的url地址,而这也就是职位详情页。因此总体过程是:发送请求获取职位列表页,然后从列表中检索每一个职位详情页的url,再发送新的请求进入详情页,此后便可开展信息提取工作。
第一部分——获取职位详情页url
通过上述分析知,职位详情页url蕴含在职位列表中,所以我们先从职位列表页分析。我们要爬取的是所有职位信息,而这些职位是分布在不同的组(页)中,因此我们先依次点击几页看看这些不同页的url都有什么关联。
经实践后得到如下URL
第2页:https://hr.tencent.com/position.php?lid=&tid=&keywords=python&start=10#a
第3页:https://hr.tencent.com/position.php?lid=&tid=&keywords=python&start=20#a
第4页:https://hr.tencent.com/position.php?lid=&tid=&keywords=python&start=30#a
…
这里我们可以发现其URL是有一定规律的,它们的大体构成都相同,只是参数start的值不同。经分析后我们可以知道其实start代表的是当前显示职位列表在全部职位中的起始序号,这个与每页显示的职位个数有关,比如每页显示10条,第2页起始为10,那么第3页起始就是10+10=20,第4页就是20+10=30……如此一来,我们就基本上找到了URL的构成法,那么是不是都这样的呢?以第1页为例,按照我们的分析,第1页URL中应该对应的是10-10=0,我们从其他页点击第1页来验证一下,结果发现确实是0,也印证了我们推测的正确性。考虑到后面锚点’#a’对请求无关紧要,因此职位列表URL结构为:’https://hr.tencent.com/position.php?lid=&tid=&keywords=python&start={}’
在此之后我们便可以结合之前学习的Xpath与lxml来获取相应的URL信息。这里我们要注意一点的是在获取href属性时,我们检查元素发现其前面没有域名,同上分析详情页URL我们可以得知其构成法为’域名+href’,故在获取href后需要做适当修改。
## 获取职位详情页URL import requests from lxml import etree # 设置请求头 HEADERS = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.92 Safari/537.36' } # 设置域名 BASE_DOMAIN = 'https://hr.tencent.com/' # 定义获取详情页URL函数 def get_detail_page(url): response = requests.get(url, headers=HEADERS) html = etree.HTML(response.content.decode('utf-8')) urls = html.xpath('//table[@class="tablelist"]//tr[@class="even" or @class="odd"]//a/@href') detail_page = list(map(lambda url: BASE_DOMAIN + url, urls)) return detail_page
第二部分——职位详细解析
在获取了每一个职位详情页url后,便可再次发送请求以获取数据。经分析发现数据都存放在了一个table标签里,我们便可先获取这个标签,然后在使用Xpath来解析其他数据。这里解析方法和之前一样,也相对比较简单,这里不再叙述。
## 解析详情页面数据 def parse_page(url): response = requests.get(url, headers=HEADERS) html = etree.HTML(response.content.decode('utf-8')) infotable = html.xpath('//table[contains(@class,"tablelist")]')[0] info = { 'title': infotable.xpath('.//td[@id="sharetitle"]//text()')[0], 'address': infotable.xpath('.//tr[2]/td[1]//text()')[1], 'category': infotable.xpath('.//tr[2]/td[2]//text()')[1], 'number': infotable.xpath('.//tr[2]/td[3]//text()')[1], 'responsibility': ''.join(infotable.xpath('.//tr[3]//ul//text()')), 'requirement': ''.join(infotable.xpath('.//tr[4]//ul//text()')) } return info
第三部分——爬虫集成
其实这一部分并不是一个独立的部分,只是运用上述两部分的方法集成为了一个Spider函数供封装使用。
## 爬虫集成 def Spider(): BASE_URL = 'https://hr.tencent.com/position.php?lid=&tid=&keywords=python&start={}' jobs = [] for index in range(0, 53): #职位列表页数 url = BASE_URL.format(index * 10)
#获取详情页URL details_urls = get_detail_page(url) for page_url in details_urls:
#对单独的详情页进行解析 info = parse_page(page_url) jobs.append(info) print(info) #为方便显示结果,直接在次进行输出
这么一来,我们便可以在主程序中直接调用Spider()函数实现Tencent招聘职位信息的获取,部分结果如下:
以上便是利用Xpath和lxml爬取数据的一个小小实践,主要是为了加深对上述两个工具的理解和运用,其实,这里面还有很多可以完善的地方,如数据的永久化存储以及多进程爬虫等,而这些都还需要后续的多多学习与练习。