With the rapid development of the Internet,愈来愈多的信息充斥着各大网络平台。正如《死亡笔记》中L·Lawliet这一角色所提到的大数定律,在众多繁杂的数据中必然存在着某种规律,偶然中必然包含着某种必然的发生。不论是咱们提到的大数定律,仍是最近火热的大数据亦或其余领域都离不开大量而又干净数据的支持,为此,网络爬虫可以知足咱们的需求,即在互联网上按照咱们的意愿来爬取咱们任何想要获得的信息,以便咱们分析出其中的必然规律,进而作出正确的决策。一样,在咱们平时上网的过程当中,无时无刻可见爬虫的影子,好比咱们广为熟知的“度娘”就是其中一个大型而又名副其实的“蜘蛛王”(SPIDER KING)。而要想写出一个强大的爬虫程序,则离不开熟练的对各类网络页面的解析,这篇文章将给读者介绍如何在Python中使用各大解析工具。javascript
经常使用的解析方式主要有正则、Beautiful Soup、XPath、pyquery,本文主要是讲解后三种工具的使用,而对正则表达式的使用不作讲解,对正则有兴趣了解的读者能够跳转:正则表达式php
Beautiful Soup是Python爬虫中针对HTML、XML的其中一个解析工具,熟练的使用之能够很方便的提取页面中咱们想要的数据。此外,在Beautiful Soup中,为咱们提供了如下四种解析器:css
soup = BeautifulSoup(content, "html.parser")
soup = BeautifulSoup(content, "lxml")
soup = BeautifulSoup(content, "xml")
soup = BeautifulSoup(content, "html5lib")
在以上四种解析库中,lxml解析具备解析速度快兼容错能力强的merits,因此本文主要使用的是lxml解析器,下面咱们主要拿百度首页的html来具体讲解下Beautiful Soup的使用:html
from bs4 import BeautifulSoup
import requests
if __name__ == "__main__":
response = requests.get("https://www.baidu.com")
encoding = response.apparent_encoding
response.encoding = encoding
print(BeautifulSoup(response.text, "lxml"))
代码解读:html5
response = requests.get("https://www.baidu.com")
,requests请求百度连接encoding = response.apparent_encoding
,获取页面编码格式response.encoding = encoding
,修改请求编码为页面对应的编码格式,以免乱码print(BeautifulSoup(response.text, "lxml"))
,使用lxml解析器来对百度首页html进行解析并打印结果打印后的结果以下所示:java
<!DOCTYPE html>
<!--STATUS OK--><html> <head><meta content="text/html;charset=utf-8" http-equiv="content-type"/><meta content="IE=Edge" http-equiv="X-UA-Compatible"/><meta content="always" name="referrer"/><link href="https://ss1.bdstatic.com/5eN1bjq8AAUYm2zgoY3K/r/www/cache/bdorz/baidu.min.css" rel="stylesheet" type="text/css"/><title>百度一下,你就知道</title></head> <body link="#0000cc"> <div id="wrapper"> <div id="head"> <div class="head_wrapper"> <div class="s_form"> <div class="s_form_wrapper"> <div id="lg"> <img height="129" hidefocus="true" src="//www.baidu.com/img/bd_logo1.png" width="270"/> </div> <form action="//www.baidu.com/s" class="fm" id="form" name="f"> <input name="bdorz_come" type="hidden" value="1"/> <input name="ie" type="hidden" value="utf-8"/> <input name="f" type="hidden" value="8"/> <input name="rsv_bp" type="hidden" value="1"/> <input name="rsv_idx" type="hidden" value="1"/> <input name="tn" type="hidden" value="baidu"/><span class="bg s_ipt_wr"><input autocomplete="off" autofocus="autofocus" class="s_ipt" id="kw" maxlength="255" name="wd" value=""/></span><span class="bg s_btn_wr"><input autofocus="" class="bg s_btn" id="su" type="submit" value="百度一下"/></span> </form> </div> </div> <div id="u1"> <a class="mnav" href="http://news.baidu.com" name="tj_trnews">新闻</a> <a class="mnav" href="https://www.hao123.com" name="tj_trhao123">hao123</a> <a class="mnav" href="http://map.baidu.com" name="tj_trmap">地图</a> <a class="mnav" href="http://v.baidu.com" name="tj_trvideo">视频</a> <a class="mnav" href="http://tieba.baidu.com" name="tj_trtieba">贴吧</a> <noscript> <a class="lb" href="http://www.baidu.com/bdorz/login.gif?login&tpl=mn&u=http%3A%2F%2Fwww.baidu.com%2f%3fbdorz_come%3d1" name="tj_login">登陆</a> </noscript> <script>document.write('<a href="http://www.baidu.com/bdorz/login.gif?login&tpl=mn&u='+ encodeURIComponent(window.location.href+ (window.location.search === "" ? "?" : "&")+ "bdorz_come=1")+ '" name="tj_login" class="lb">登陆</a>');
</script> <a class="bri" href="//www.baidu.com/more/" name="tj_briicon" style="display: block;">更多产品</a> </div> </div> </div> <div id="ftCon"> <div id="ftConw"> <p id="lh"> <a href="http://home.baidu.com">关于百度</a> <a href="http://ir.baidu.com">About Baidu</a> </p> <p id="cp">©2017 Baidu <a href="http://www.baidu.com/duty/">使用百度前必读</a> <a class="cp-feedback" href="http://jianyi.baidu.com/">意见反馈</a> 京ICP证030173号 <img src="//www.baidu.com/img/gs.gif"/> </p> </div> </div> </div> </body> </html>
从上述代码中,咱们能够看见打印出的内容有点过于杂乱无章,为了使得解析后的页面清洗直观,咱们可使用prettify()
方法来对其进行标准的缩进操做,为了方便讲解,博主对结果进行适当的删除,只留下有价值的内容,源码及输出以下:python
bd_soup = BeautifulSoup(response.text, "lxml")
print(bd_soup.prettify())
<html>
<head>
<title>
百度一下,你就知道
</title>
</head>
<body link="#0000cc">
<div id="wrapper">
<div id="head">
<div class="head_wrapper">
<div class="s_form">
<div class="s_form_wrapper">
<div id="lg">
<img height="129" hidefocus="true" src="//www.baidu.com/img/bd_logo1.png" width="270"/>
</div>
</div>
</div>
<div id="u1">
<a class="mnav" href="http://news.baidu.com" name="tj_trnews">
新闻
</a>
<a class="mnav" href="https://www.hao123.com" name="tj_trhao123">
hao123
</a>
<a class="mnav" href="http://map.baidu.com" name="tj_trmap">
地图
</a>
<a class="mnav" href="http://v.baidu.com" name="tj_trvideo">
视频
</a>
<a class="mnav" href="http://tieba.baidu.com" name="tj_trtieba">
贴吧
</a>
<a class="bri" href="//www.baidu.com/more/" name="tj_briicon" style="display: block;">
更多产品
</a>
</div>
</div>
</div>
<div id="ftCon">
<div id="ftConw">
<p id="lh">
<a href="http://home.baidu.com">
关于百度
</a>
<a href="http://ir.baidu.com">
About Baidu
</a>
</p>
<p id="cp">
©2017 Baidu
<a href="http://www.baidu.com/duty/">
使用百度前必读
</a>
<a class="cp-feedback" href="http://jianyi.baidu.com/">
意见反馈
</a>
京ICP证030173号
<img src="//www.baidu.com/img/gs.gif"/>
</p>
</div>
</div>
</div>
</body>
</html>
在Beautiful Soup中,咱们能够很方便的选择想要获得的节点,只须要在bd_soup
对象中使用.
的方式便可,使用以下:jquery
bd_title_bj = bd_soup.title
bd_title_bj_name = bd_soup.title.name
bd_title_name = bd_soup.title.string
bd_title_parent_bj_name = bd_soup.title.parent.name
bd_image_bj = bd_soup.img
bd_image_bj_dic = bd_soup.img.attrs
bd_image_all = bd_soup.find_all("img")
bd_image_idlg = bd_soup.find("div", id="lg")
代码解读:git
bd_soup.title
,正如前面所说,Beautiful Soup能够很简单的解析对应的页面,只须要使用bd_soup.
的方式进行选择节点便可,该行代码正是得到百度首页html的title
节点内容bd_soup.title.name
,使用.name
的形式便可获取节点的名称.string
的形式便可得到节点当中的内容,这句代码就是获取百度首页的title节点的内容,即浏览器导航条中所显示的百度一下,你就知道
bd_soup.title.parent.name
,使用.parent
能够该节点的父节点,通俗地讲就是该节点所对应的上一层节点,而后使用.name
获取父节点名称bd_soup.img
,如bd_soup.title
同样,该代码获取的是img
节点,只不过须要注意的是:在上面html中咱们能够看见总共有两个img
节点,而若是使用.img
的话默认是获取html中的第一个img
节点,而不是全部bd_soup.img.attrs
,获取img
节点中全部的属性及属性内容,该代码输出的结果是一个键值对的字典格式,因此以后咱们只须要经过字典的操做来获取属性所对应的内容便可。好比bd_soup.img.attrs.get("src")
和bd_soup.img.attrs["src"]
的方式来获取img
节点所对应的src
属性的内容,即图片连接bd_soup.find_all("img")
,在上述中的.img
操做默认只能获取第一个img
节点,而要想获取html中全部的img
节点,咱们须要使用.find_all("img")
方法,所返回的是一个列表格式,列表内容为全部的选择的节点bd_soup.find("div", id="lg")
,在实际运用中,咱们每每会选择指定的节点,这个时候咱们可使用.find()
方法,里面可传入所需查找节点的属性,这里须要注意的是:在传入class
属性的时候其中的写法是.find("div", class_="XXX")
的方式。因此该行代码表示的是获取id
属性为lg
的div
节点,此外,在上面的.find_all()
一样可使用该方法来获取指定属性所对应的全部节点上述代码中解析的结果对应打印以下:github
<title>百度一下,你就知道</title>
title
百度一下,你就知道
head
<img height="129" hidefocus="true" src="//www.baidu.com/img/bd_logo1.png" width="270"/>
{'hidefocus': 'true', 'src': '//www.baidu.com/img/bd_logo1.png', 'width': '270', 'height': '129'}
[<img height="129" hidefocus="true" src="//www.baidu.com/img/bd_logo1.png" width="270"/>, <img src="//www.baidu.com/img/gs.gif"/>]
<div id="lg"> <img height="129" hidefocus="true" src="//www.baidu.com/img/bd_logo1.png" width="270"/> </div>
在上一小节节点选择咱们讲到了部分数据提取的方法,然而,Beautiful Soup的强大之处还不止步于此。接下来咱们继续揭开其神秘的面纱。
获取对象中全部的内容:
all_content = bd_soup.get_text()
百度一下,你就知道 新闻 hao123 地图 视频 贴吧 登陆 document.write('<a href="http://www.baidu.com/bdorz/login.gif?login&tpl=mn&u='+ encodeURIComponent(window.location.href+ (window.location.search === "" ? "?" : "&")+ "bdorz_come=1")+ '" name="tj_login" class="lb">登陆</a>');
更多产品 关于百度 About Baidu ©2017 Baidu 使用百度前必读 意见反馈 京ICP证030173号
print(type(bd_soup.strings))
# <class 'generator'>
.strings
用于提取bd_soup
对象中全部的内容,而从上面的输出结果咱们能够看出.strings
的类型是一个生成器,对此可使用循环来提取出其中的内容。可是咱们在使用.strings
的过程当中会发现提取出来的内容有不少的空格以及换行,对此咱们可使用.stripped_strings
方法来解决该问题,用法以下:
for each in bd_soup.stripped_strings:
print(each)
输出结果:
百度一下,你就知道
新闻
hao123
地图
视频
贴吧
登陆
更多产品
关于百度
About Baidu
©2017 Baidu
使用百度前必读
意见反馈
京ICP证030173号
.parent
能够选择该节点的父节点,.children
能够选择该节点的孩子节点,.parents
选择该节点全部的上层节店,返回的是生成器,各用法以下:
bd_div_bj = bd_soup.find("div", id="u1")
print(type(bd_div_bj.parent))
print("*" * 50)
for child in bd_div_bj.children:
print(child)
print("*" * 50)
for parent in bd_div_bj.parents:
print(parent.name)
结果输出:
<class 'bs4.element.Tag'>
**************************************************
<a class="mnav" href="http://news.baidu.com" name="tj_trnews">新闻</a>
<a class="mnav" href="https://www.hao123.com" name="tj_trhao123">hao123</a>
<a class="mnav" href="http://map.baidu.com" name="tj_trmap">地图</a>
<a class="mnav" href="http://v.baidu.com" name="tj_trvideo">视频</a>
<a class="mnav" href="http://tieba.baidu.com" name="tj_trtieba">贴吧</a>
**************************************************
div
div
div
body
html
Beautiful Soup主要的用法就是以上一些,还有其余一些操做在实际开发过程当中使用的很少,这里不作过多的讲解了,因此总体来说Beautiful Soup的使用仍是比较简单的,其余一些操做可见官方文档:https://www.crummy.com/software/BeautifulSoup/bs4/doc/index.zh.html#contents-children
XPath全称是XML Path Language,它既能够用来解析XML,也能够用来解析HTML。在上一部分已经讲解了Beautiful Soup的一些常见的骚操做,在这里,咱们继续来看看XPath的使用,瞧一瞧XPath的功能到底有多么的强大以至于受到了很多开发者的青睐。同Beautiful Soup同样,在XPath中提供了很是简洁的节点选择的方法,Beautiful Soup主要是经过.
的方式来进行子节点或者子孙节点的选择,而在XPath中则主要经过/
的方式来选择节点。除此以外,在XPath中还提供了大量的内置函数来处理各个数据之间的匹配关系。
首先,咱们先来看看XPath常见的节点匹配规则:
表达式 | 解释说明 |
---|---|
/ |
在当前节点中选取直接子节点 |
// |
在当前节点中选取子孙节点 |
. |
选取当前节点 |
.. |
选取当前节点的父节点 |
@ |
指定属性(id、class……) |
下面咱们继续拿上面的百度首页的HTML来说解下XPath的使用。
要想正常使用Xpath,咱们首先须要正确导入对应的模块,在此咱们通常使用的是lxml
,操做示例以下:
from lxml import etree
import requests
import html
if __name__ == "__main__":
response = requests.get("https://www.baidu.com")
encoding = response.apparent_encoding
response.encoding = encoding
print(response.text)
bd_bj = etree.HTML(response.text)
bd_html = etree.tostring(bd_bj).decode("utf-8")
print(html.unescape(bd_html))
1~9行代码如Beautiful Soup一致,下面对以后的代码进行解释:
etree.HTML(response.text)
,使用etree
模块中的HTML
类来对百度html(response.text)
进行初始化以构造XPath解析对象,返回的类型为
etree.tostring(bd_html_elem).decode("utf-8")
,将上述的对象转化为字符串类型且编码为utf-8
html.unescape(bd_html)
,使用HTML5标准定义的规则将bd_html
转换成对应的unicode字符。打印出的结果如Beautiful Soup使用时一致,这里就再也不显示了,不知道的读者可回翻。既然咱们已经获得了Xpath可解析的对象(bd_bj)
,下面咱们就须要针对这个对象来选择节点了,在上面咱们也已经提到了,XPath主要是经过/
的方式来提取节点,请看下面Xpath中节点选择的一些常见操做:
all_bj = bd_bj.xpath("//*") # 选取全部节点
img_bj = bd_bj.xpath("//img") # 选取指定名称的节点
p_a_zj_bj = bd_bj.xpath("//p/a") # 选取直接节点
p_a_all_bj = bd_bj.xpath("//p//a") # 选取全部节点
head_bj = bd_bj.xpath("//title/..") # 选取父节点
结果以下:
[<Element html at 0x14d6a6d1c88>, <Element head at 0x14d6a6e4408>, <Element meta at 0x14d6a6e4448>, <Element meta at 0x14d6a6e4488>, <Element meta at 0x14d6a6e44c8>, <Element link at 0x14d6a6e4548>, <Element title at 0x14d6a6e4588>, <Element body at 0x14d6a6e45c8>, <Element div at 0x14d6a6e4608>, <Element div at 0x14d6a6e4508>, <Element div at 0x14d6a6e4648>, <Element div at 0x14d6a6e4688>, ......]
[<Element img at 0x14d6a6e4748>, <Element img at 0x14d6a6e4ec8>]
[<Element a at 0x14d6a6e4d88>, <Element a at 0x14d6a6e4dc8>, <Element a at 0x14d6a6e4e48>, <Element a at 0x14d6a6e4e88>]
[<Element a at 0x14d6a6e4d88>, <Element a at 0x14d6a6e4dc8>, <Element a at 0x14d6a6e4e48>, <Element a at 0x14d6a6e4e88>]
[<Element head at 0x14d6a6e4408>]
all_bj = bd_bj.xpath("//*")
,使用//
能够选择当前节点(html)
下的全部子孙节点,且以一个列表的形式来返回,列表元素经过bd_bj
同样是element
对象,下面的返回类型一致img_bj = bd_bj.xpath("//img")
,选取当前节点下指定名称的节点,这里建议与Beautiful Soup的使用相比较可加强记忆,Beautiful Soup是经过.find_all("img")
的形式p_a_zj_bj = bd_bj.xpath("//p/a")
,选取当前节点下的全部p
节点下的直接子a
节点,这里须要注意的是”直接“,若是a
不是p
节点的直接子节点则选取失败p_a_all_bj = bd_bj.xpath("//p//a")
,选取当前节点下的全部p
节点下的全部子孙a
节点,这里须要注意的是”全部“,注意与上一个操做进行区分head_bj = bd_bj.xpath("//title/..")
,选取当前节点下的title
节点的父节点,即head
节点在了解如何选择指定的节点以后,咱们就须要提取节点中所包含的数据了,具体提取请看下面的示例:
img_href_ls = bd_bj.xpath("//img/@src")
img_href = bd_bj.xpath("//div[@id='lg']/img[@hidefocus='true']/@src")
a_content_ls = bd_bj.xpath("//a//text()")
a_news_content = bd_bj.xpath("//a[@class='mnav' and @name='tj_trnews']/text()")
输出结果:
['//www.baidu.com/img/bd_logo1.png', '//www.baidu.com/img/gs.gif']
['//www.baidu.com/img/bd_logo1.png']
['新闻', 'hao123', '地图', '视频', '贴吧', '登陆', '更多产品', '关于百度', 'About Baidu', '使用百度前必读', '意见反馈']
['新闻']
img_href_ls = bd_bj.xpath("//img/@src")
,该代码先选取了当前节点下的全部img
节点,而后将全部img
节点的src
属性值选取出来,返回的是一个列表形式img_href = bd_bj.xpath("//div[@id='lg']/img[@hidefocus='true']/@src")
,该代码首先选取了当前节点下全部id
属性值为lg
的div
,而后继续选取div
节点下的直接子img
节点(hidefoucus=true
),最后选取其中的src
属性值a_content_ls = bd_bj.xpath("//a//text()")
,选取当前节点全部的a
节点的所遇文本内容a_news_content = bd_bj.xpath("//a[@class='mnav' and @name='tj_trnews']/text()")
,多属性选择,在xpath中能够指定知足多个属性的节点,只须要and
便可提醒:读者在阅读的过程当中注意将代码和输出的结果仔细对应起来,只要理解其中的意思也就不难记忆了。
耐心看完了XPath的使用方法以后,聪明的读者应该不难发现,其实Beautiful Soup和XPath的本质和思路上基本相同,只要咱们在阅读XPath用法的同时在脑壳中不断的思考,相信聪明的你阅读至此已经可以基本掌握了XPath用法。
对于pyquery,官方的解释以下:
pyquery allows you to make jquery queries on xml documents. The API is as much as possible the similar to jquery. pyquery uses lxml for fast xml and html manipulation.
This is not (or at least not yet) a library to produce or interact with javascript code. I just liked the jquery API and I missed it in python so I told myself “Hey let’s make jquery in python”. This is the result.
It can be used for many purposes, one idea that I might try in the future is to use it for templating with pure http templates that you modify using pyquery. I can also be used for web scrapping or for theming applications with Deliverance.
The project is being actively developped on a git repository on Github. I have the policy of giving push access to anyone who wants it and then to review what he does. So if you want to contribute just email me.
Please report bugs on the github issue tracker.
在网页解析过程当中,除了强大的Beautiful Soup和XPath以外,还有qyquery的存在,qyquery一样受到了很多“蜘蛛”的欢迎,下面咱们来介绍下qyquery的使用。
与Beautiful Soup和XPath明显不一样的是,在qyquery中,通常存在着三种解析方式,一种是requests
请求连接以后把html进行传递,一种是将url
直接进行传递,还有一种是直接传递本地html
文件路径便可,读者在实际使用的过程当中根据本身的习惯来编码便可,下面咱们来看下这三种方式的表达:
import requests
from pyquery import PyQuery as pq
bd_html = requests.get("https://www.baidu.com").text
bd_url = "https://www.baidu.com"
bd_path = "./bd.html"
# 使用html参数进行传递
def way1(html):
return pq(html)
# 使用url参数进行传递
def way2(url):
return pq(url=url)
def way3(path):
return pq(filename=path)
print(type(way1(html=bd_html)))
print(type(way2(url=bd_url)))
print(type(way3(path=bd_path)))
# <class 'pyquery.pyquery.PyQuery'>
# <class 'pyquery.pyquery.PyQuery'>
# <class 'pyquery.pyquery.PyQuery'>
从上面三种得到解析对象方法的代码中咱们能够明显看见均可以获得同样的解析对象,接下来咱们只要利用这个对象来对页面进行解析从而提取出咱们想要获得的有效信息便可,在qyquery中通常使用的是CSS选择器来选取。下面咱们仍然使用百度首页来说解pyquery的使用,在这里咱们假设解析对象为bd_bj
。
response = requests.get("https://www.baidu.com")
response.encoding = "utf-8"
bd_bj = pq(response.text)
bd_title = bd_bj("title")
bd_img_ls = bd_bj("img")
bd_img_ls2 = bd_bj.find("img")
bd_mnav = bd_bj(".mnav")
bd_img = bd_bj("#u1 a")
bd_a_video = bd_bj("#u1 .mnav")
# <title>百度一下,你就知道</title>
# <img hidefocus="true" src="//www.baidu.com/img/bd_logo1.png" width="270" height="129"/> <img src="//www.baidu.com/img/gs.gif"/>
# ......
# 输出结果较长,读者可自行运行
正如上面代码所示,pyquery在进行节点提取的时候一般有三种方式,一种是直接提取出节点名便可提取出整个节点,固然这种方式你也可使用find
方法,这种提取节点的方式是不加任何属性限定的,因此提取出的节点每每会含有多个,因此咱们可使用循环.items()
来进行操做;一种是提取出含有特定class
属性的节点,这种形式采用的是.+class属性值
;还有一种是提取含有特定id
属性的节点,这种形式采用的是#+id属性值
。熟悉CSS
的读者应该不难理解以上提取节点的方法,正是在CSS
中提取节点而后对其进行样式操做的方法。上述三种方式您也能够像提取bd_a_video
同样混合使用
在实际解析网页的过程当中,三种解析方式基本上大同小异,为了读者认识pyquery
的数据提取的操做以及博主往后的查阅,在这里简单的介绍下
img_src1 = bd_bj("img").attr("src") # //www.baidu.com/img/bd_logo1.png
img_src2 = bd_bj("img").attr.src # //www.baidu.com/img/bd_logo1.png
for each in bd_bj.find("img").items():
print(each.attr("src"))
print(bd_bj("title").text()) # 百度一下,你就知道
如上一二行代码所示,提取节点属性咱们能够有两种方式,这里拿src
属性来进行说明,一种是.attr("src")
,另一种是.attr.src
,读者根据本身的习惯来操做便可,这里须要注意的是:在节点提取小结中咱们说了在不限制属性的状况下是提取出全部知足条件的节点,因此在这种状况下提取出的属性是第一个节点属性。要想提取全部的节点的属性,咱们能够如四五行代码那样使用.items()
而后进行遍历,最后和以前同样提取各个节点属性便可。qyquery
提取节点中文本内容如第七行代码那样直接使用.text()
便可。
pyquery解析如Beautiful Soup和XPath思想一致,因此这了只是简单的介绍了下,想要进一步了解的读者可查阅官方文档在加之熟练操做便可。
经过上述对Beautiful Soup、XPath以及pyquery的介绍,认真阅读过的读者想必已经有了必定的基础,下面咱们经过一个简单的实战案例来强化一下三种解析方式的操做。这次解析的网站为腾讯招聘网,网址url:https://hr.tencent.com/,其社会招聘网首页以下所示:
这次咱们的任务就是分别利用上述三种解析工具来接下该网站下的社会招聘中的全部数据。
经过该网站的社会招聘的首页,咱们能够发现以下三条主要信息:
既然咱们解析的是该网站下全部职位数据,再者咱们停留在第一页也没有发现其余有价值的信息,不如进入第二页看看,这时咱们能够发现网站的url连接有了一个比较明显的变化,即原连接在用户端提交了一个start
参数,此时连接为https://hr.tencent.com/position.php?&start=10#a,陆续打开后面的页面咱们不难发现其规律:每一页提交的start
参数以10位公差进行逐步递增。以后,咱们使用谷歌开发者工具来审查该网页,咱们能够发现全站皆为静态页面,这位咱们解析省下了很多麻烦,咱们须要的数据就静态的放置在table
标签内,以下所示:
下面咱们具体来分别使用以上三种工具来解析该站全部职位数据。
import requests
from bs4 import BeautifulSoup
from lxml import etree
from pyquery import PyQuery as pq
import itertools
import pandas as pd
class TencentPosition():
"""
功能: 定义初始变量
参数:
start: 起始数据
"""
def __init__(self, start):
self.url = "https://hr.tencent.com/position.php?&start={}#a".format(start)
self.headers = {
"Host": "hr.tencent.com",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36",
}
self.file_path = "./TencentPosition.csv"
"""
功能: 请求目标页面
参数:
url: 目标连接
headers: 请求头
返回:
html,页面源码
"""
def get_page(self, url, headers):
res = requests.get(url, headers=headers)
try:
if res.status_code == 200:
return res.text
else:
return self.get_page(url, headers=headers)
except RequestException as e:
return self.get_page(url, headers=headers)
"""
功能: Beautiful Soup解析页面
参数:
html: 请求页面源码
"""
def soup_analysis(self, html):
soup = BeautifulSoup(html, "lxml")
tr_list = soup.find("table", class_="tablelist").find_all("tr")
for tr in tr_list[1:-1]:
position_info = [td_data for td_data in tr.stripped_strings]
self.settle_data(position_info=position_info)
"""
功能: xpath解析页面
参数:
html: 请求页面源码
"""
def xpath_analysis(self, html):
result = etree.HTML(html)
tr_list = result.xpath("//table[@class='tablelist']//tr")
for tr in tr_list[1:-1]:
position_info = tr.xpath("./td//text()")
self.settle_data(position_info=position_info)
"""
功能: pyquery解析页面
参数:
html: 请求页面源码
"""
def pyquery_analysis(self, html):
result = pq(html)
tr_list = result.find(".tablelist").find("tr")
for tr in itertools.islice(tr_list.items(), 1, 11):
position_info = [td.text() for td in tr.find("td").items()]
self.settle_data(position_info=position_info)
"""
功能: 职位数据整合
参数:
position_info: 字段数据列表
"""
def settle_data(self, position_info):
position_data = {
"职位名称": position_info[0].replace("\xa0", " "), # replace替换\xa0字符防止转码error
"职位类别": position_info[1],
"招聘人数": position_info[2],
"工做地点": position_info[3],
"发布时间": position_info[-1],
}
print(position_data)
self.save_data(self.file_path, position_data)
"""
功能: 数据保存
参数:
file_path: 文件保存路径
position_data: 职位数据
"""
def save_data(self, file_path, position_data):
df = pd.DataFrame([position_data])
try:
df.to_csv(file_path, header=False, index=False, mode="a+", encoding="gbk") # 数据转码并换行存储
except:
pass
if __name__ == "__main__":
for page, index in enumerate(range(287)):
print("正在爬取第{}页的职位数据:".format(page+1))
tp = TencentPosition(start=(index*10))
tp_html = tp.get_page(url=tp.url, headers=tp.headers)
tp.pyquery_analysis(html=tp_html)
print("\n")
部分结果以下:
在本篇文章中,首先咱们分别介绍了Beautiful Soup、XPath、pyquery的常见操做,以后经过使用该三种解析工具来爬取腾讯招聘网中全部的职位招聘数据,从而进一步让读者有一个更加深入的认识。该案例中,因为本篇文章重点在于网站页面的解析方法,因此未使用多线程、多进程,爬取全部的数据爬取的时间在两分钟左右,在以后的文章中有时间的话会再次介绍多线程多进程的使用,案例中的解析方式都已介绍过,因此读者阅读源码便可。
注意:本文章中全部的内容皆为在实际开发中常见的一些操做,并不是全部,想要进一步提高等级的读者务必请阅读官方文档。