[关闭]
@tianxingjian 2020-02-14T14:41:45.000000Z 字数 18196 阅读 936

网络爬虫之页面解析

前言

With the rapid development of the Internet,越来越多的信息充斥着各大网络平台。正如《死亡笔记》中L·Lawliet这一角色所提到的大数定律,在众多繁杂的数据中必然存在着某种规律,偶然中必然包含着某种必然的发生。不管是我们提到的大数定律,还是最近火热的大数据亦或其他领域都离不开大量而又干净数据的支持,为此,网络爬虫能够满足我们的需求,即在互联网上按照我们的意愿来爬取我们任何想要得到的信息,以便我们分析出其中的必然规律,进而做出正确的决策。同样,在我们平时上网的过程中,无时无刻可见爬虫的影子,比如我们广为熟知的“度娘”就是其中一个大型而又名副其实的“蜘蛛王”(SPIDER KING)。而要想写出一个强大的爬虫程序,则离不开熟练的对各种网络页面的解析,这篇文章将给读者介绍如何在Python中使用各大解析工具。

内容扼要

常用的解析方式主要有正则、Beautiful Soup、XPath、pyquery,本文主要是讲解后三种工具的使用,而对正则表达式的使用不做讲解,对正则有兴趣了解的读者可以跳转:正则表达式

Beautiful Soup

Beautiful Soup是Python爬虫中针对HTML、XML的其中一个解析工具,熟练的使用之可以很方便的提取页面中我们想要的数据。此外,在Beautiful Soup中,为我们提供了以下四种解析器:

在以上四种解析库中,lxml解析具有解析速度快兼容错能力强的merits,所以本文主要使用的是lxml解析器,下面我们主要拿百度首页的html来具体讲解下Beautiful Soup的使用:

  1. from bs4 import BeautifulSoup
  2. import requests
  3. if __name__ == "__main__":
  4. response = requests.get("https://www.baidu.com")
  5. encoding = response.apparent_encoding
  6. response.encoding = encoding
  7. print(BeautifulSoup(response.text, "lxml"))

代码解读:

打印后的结果如下所示:

  1. <!DOCTYPE html>
  2. <!--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&amp;tpl=mn&amp;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>');
  3. </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()方法来对其进行标准的缩进操作,为了方便讲解,博主对结果进行适当的删除,只留下有价值的内容,源码及输出如下:

  1. bd_soup = BeautifulSoup(response.text, "lxml")
  2. print(bd_soup.prettify())
  1. <html>
  2. <head>
  3. <title>
  4. 百度一下,你就知道
  5. </title>
  6. </head>
  7. <body link="#0000cc">
  8. <div id="wrapper">
  9. <div id="head">
  10. <div class="head_wrapper">
  11. <div class="s_form">
  12. <div class="s_form_wrapper">
  13. <div id="lg">
  14. <img height="129" hidefocus="true" src="//www.baidu.com/img/bd_logo1.png" width="270"/>
  15. </div>
  16. </div>
  17. </div>
  18. <div id="u1">
  19. <a class="mnav" href="http://news.baidu.com" name="tj_trnews">
  20. 新闻
  21. </a>
  22. <a class="mnav" href="https://www.hao123.com" name="tj_trhao123">
  23. hao123
  24. </a>
  25. <a class="mnav" href="http://map.baidu.com" name="tj_trmap">
  26. 地图
  27. </a>
  28. <a class="mnav" href="http://v.baidu.com" name="tj_trvideo">
  29. 视频
  30. </a>
  31. <a class="mnav" href="http://tieba.baidu.com" name="tj_trtieba">
  32. 贴吧
  33. </a>
  34. <a class="bri" href="//www.baidu.com/more/" name="tj_briicon" style="display: block;">
  35. 更多产品
  36. </a>
  37. </div>
  38. </div>
  39. </div>
  40. <div id="ftCon">
  41. <div id="ftConw">
  42. <p id="lh">
  43. <a href="http://home.baidu.com">
  44. 关于百度
  45. </a>
  46. <a href="http://ir.baidu.com">
  47. About Baidu
  48. </a>
  49. </p>
  50. <p id="cp">
  51. ©2017 Baidu
  52. <a href="http://www.baidu.com/duty/">
  53. 使用百度前必读
  54. </a>
  55. <a class="cp-feedback" href="http://jianyi.baidu.com/">
  56. 意见反馈
  57. </a>
  58. 京ICP证030173号
  59. <img src="//www.baidu.com/img/gs.gif"/>
  60. </p>
  61. </div>
  62. </div>
  63. </div>
  64. </body>
  65. </html>

节点选择

在Beautiful Soup中,我们可以很方便的选择想要得到的节点,只需要在bd_soup对象中使用.的方式即可,使用如下:

  1. bd_title_bj = bd_soup.title
  2. bd_title_bj_name = bd_soup.title.name
  3. bd_title_name = bd_soup.title.string
  4. bd_title_parent_bj_name = bd_soup.title.parent.name
  5. bd_image_bj = bd_soup.img
  6. bd_image_bj_dic = bd_soup.img.attrs
  7. bd_image_all = bd_soup.find_all("img")
  8. bd_image_idlg = bd_soup.find("div", id="lg")

代码解读:

上述代码中解析的结果对应打印如下:

  1. <title>百度一下,你就知道</title>
  2. title
  3. 百度一下,你就知道
  4. head
  5. <img height="129" hidefocus="true" src="//www.baidu.com/img/bd_logo1.png" width="270"/>
  6. {'hidefocus': 'true', 'src': '//www.baidu.com/img/bd_logo1.png', 'width': '270', 'height': '129'}
  7. [<img height="129" hidefocus="true" src="//www.baidu.com/img/bd_logo1.png" width="270"/>, <img src="//www.baidu.com/img/gs.gif"/>]
  8. <div id="lg"> <img height="129" hidefocus="true" src="//www.baidu.com/img/bd_logo1.png" width="270"/> </div>

数据提取

在上一小节节点选择我们讲到了部分数据提取的方法,然而,Beautiful Soup的强大之处还不止步于此。接下来我们继续揭开其神秘的面纱。

.get_text()

获取对象中所有的内容:

  1. all_content = bd_soup.get_text()
  1. 百度一下,你就知道 新闻 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>');
  2. 更多产品 关于百度 About Baidu ©2017 Baidu 使用百度前必读  意见反馈 ICP030173 

.strings,.stripped_strings

  1. print(type(bd_soup.strings))
  2. # <class 'generator'>

.strings用于提取bd_soup对象中所有的内容,而从上面的输出结果我们可以看出.strings的类型是一个生成器,对此可以使用循环来提取出其中的内容。但是我们在使用.strings的过程中会发现提取出来的内容有很多的空格以及换行,对此我们可以使用.stripped_strings方法来解决该问题,用法如下:

  1. for each in bd_soup.stripped_strings:
  2. print(each)

输出结果:

  1. 百度一下,你就知道
  2. 新闻
  3. hao123
  4. 地图
  5. 视频
  6. 贴吧
  7. 登录
  8. 更多产品
  9. 关于百度
  10. About Baidu
  11. ©2017 Baidu
  12. 使用百度前必读
  13. 意见反馈
  14. ICP030173

.parent,.children,.parents

.parent可以选择该节点的父节点,.children可以选择该节点的孩子节点,.parents选择该节点所有的上层节店,返回的是生成器,各用法如下:

  1. bd_div_bj = bd_soup.find("div", id="u1")
  2. print(type(bd_div_bj.parent))
  3. print("*" * 50)
  4. for child in bd_div_bj.children:
  5. print(child)
  6. print("*" * 50)
  7. for parent in bd_div_bj.parents:
  8. print(parent.name)

结果输出:

  1. <class 'bs4.element.Tag'>
  2. **************************************************
  3. <a class="mnav" href="http://news.baidu.com" name="tj_trnews">新闻</a>
  4. <a class="mnav" href="https://www.hao123.com" name="tj_trhao123">hao123</a>
  5. <a class="mnav" href="http://map.baidu.com" name="tj_trmap">地图</a>
  6. <a class="mnav" href="http://v.baidu.com" name="tj_trvideo">视频</a>
  7. <a class="mnav" href="http://tieba.baidu.com" name="tj_trtieba">贴吧</a>
  8. **************************************************
  9. div
  10. div
  11. div
  12. body
  13. html

Beautiful Soup小结

Beautiful Soup主要的用法就是以上一些,还有其他一些操作在实际开发过程中使用的不多,这里不做过多的讲解了,所以整体来讲Beautiful Soup的使用还是比较简单的,其他一些操作可见官方文档:https://www.crummy.com/software/BeautifulSoup/bs4/doc/index.zh.html#contents-children

XPath

XPath全称是XML Path Language,它既可以用来解析XML,也可以用来解析HTML。在上一部分已经讲解了Beautiful Soup的一些常见的骚操作,在这里,我们继续来看看XPath的使用,瞧一瞧XPath的功能到底有多么的强大以致于受到了不少开发者的青睐。同Beautiful Soup一样,在XPath中提供了非常简洁的节点选择的方法,Beautiful Soup主要是通过.的方式来进行子节点或者子孙节点的选择,而在XPath中则主要通过/的方式来选择节点。除此之外,在XPath中还提供了大量的内置函数来处理各个数据之间的匹配关系。

首先,我们先来看看XPath常见的节点匹配规则:

表达式 解释说明
/ 在当前节点中选取直接子节点
// 在当前节点中选取子孙节点
. 选取当前节点
.. 选取当前节点的父节点
@ 指定属性(id、class......)

下面我们继续拿上面的百度首页的HTML来讲解下XPath的使用。

节点选择

要想正常使用Xpath,我们首先需要正确导入对应的模块,在此我们一般使用的是lxml,操作示例如下:

  1. from lxml import etree
  2. import requests
  3. import html
  4. if __name__ == "__main__":
  5. response = requests.get("https://www.baidu.com")
  6. encoding = response.apparent_encoding
  7. response.encoding = encoding
  8. print(response.text)
  9. bd_bj = etree.HTML(response.text)
  10. bd_html = etree.tostring(bd_bj).decode("utf-8")
  11. print(html.unescape(bd_html))

1~9行代码如Beautiful Soup一致,下面对之后的代码进行解释:

打印出的结果如Beautiful Soup使用时一致,这里就不再显示了,不知道的读者可回翻。既然我们已经得到了Xpath可解析的对象(bd_bj),下面我们就需要针对这个对象来选择节点了,在上面我们也已经提到了,XPath主要是通过/的方式来提取节点,请看下面Xpath中节点选择的一些常见操作:

  1. all_bj = bd_bj.xpath("//*") # 选取所有节点
  2. img_bj = bd_bj.xpath("//img") # 选取指定名称的节点
  3. p_a_zj_bj = bd_bj.xpath("//p/a") # 选取直接节点
  4. p_a_all_bj = bd_bj.xpath("//p//a") # 选取所有节点
  5. head_bj = bd_bj.xpath("//title/..") # 选取父节点

结果如下:

  1. [<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>, ......]
  2. [<Element img at 0x14d6a6e4748>, <Element img at 0x14d6a6e4ec8>]
  3. [<Element a at 0x14d6a6e4d88>, <Element a at 0x14d6a6e4dc8>, <Element a at 0x14d6a6e4e48>, <Element a at 0x14d6a6e4e88>]
  4. [<Element a at 0x14d6a6e4d88>, <Element a at 0x14d6a6e4dc8>, <Element a at 0x14d6a6e4e48>, <Element a at 0x14d6a6e4e88>]
  5. [<Element head at 0x14d6a6e4408>]

数据提取

在了解如何选择指定的节点之后,我们就需要提取节点中所包含的数据了,具体提取请看下面的示例:

  1. img_href_ls = bd_bj.xpath("//img/@src")
  2. img_href = bd_bj.xpath("//div[@id='lg']/img[@hidefocus='true']/@src")
  3. a_content_ls = bd_bj.xpath("//a//text()")
  4. a_news_content = bd_bj.xpath("//a[@class='mnav' and @name='tj_trnews']/text()")

输出结果:

  1. ['//www.baidu.com/img/bd_logo1.png', '//www.baidu.com/img/gs.gif']
  2. ['//www.baidu.com/img/bd_logo1.png']
  3. ['新闻', 'hao123', '地图', '视频', '贴吧', '登录', '更多产品', '关于百度', 'About Baidu', '使用百度前必读', '意见反馈']
  4. ['新闻']

提醒:读者在阅读的过程中注意将代码和输出的结果仔细对应起来,只要理解其中的意思也就不难记忆了。

XPath小结

耐心看完了XPath的使用方法之后,聪明的读者应该不难发现,其实Beautiful Soup和XPath的本质和思路上基本相同,只要我们在阅读XPath用法的同时在脑袋中不断的思考,相信聪明的你阅读至此已经能够基本掌握了XPath用法。

pyquery

对于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文件路径即可,读者在实际使用的过程中根据自己的习惯来编码即可,下面我们来看下这三种方式的表达:

  1. import requests
  2. from pyquery import PyQuery as pq
  3. bd_html = requests.get("https://www.baidu.com").text
  4. bd_url = "https://www.baidu.com"
  5. bd_path = "./bd.html"
  6. # 使用html参数进行传递
  7. def way1(html):
  8. return pq(html)
  9. # 使用url参数进行传递
  10. def way2(url):
  11. return pq(url=url)
  12. def way3(path):
  13. return pq(filename=path)
  14. print(type(way1(html=bd_html)))
  15. print(type(way2(url=bd_url)))
  16. print(type(way3(path=bd_path)))
  17. # <class 'pyquery.pyquery.PyQuery'>
  18. # <class 'pyquery.pyquery.PyQuery'>
  19. # <class 'pyquery.pyquery.PyQuery'>

从上面三种获得解析对象方法的代码中我们可以明显看见都可以得到一样的解析对象,接下来我们只要利用这个对象来对页面进行解析从而提取出我们想要得到的有效信息即可,在qyquery中一般使用的是CSS选择器来选取。下面我们仍然使用百度首页来讲解pyquery的使用,在这里我们假设解析对象为bd_bj

  1. response = requests.get("https://www.baidu.com")
  2. response.encoding = "utf-8"
  3. bd_bj = pq(response.text)
  4. bd_title = bd_bj("title")
  5. bd_img_ls = bd_bj("img")
  6. bd_img_ls2 = bd_bj.find("img")
  7. bd_mnav = bd_bj(".mnav")
  8. bd_img = bd_bj("#u1 a")
  9. bd_a_video = bd_bj("#u1 .mnav")
  10. # <title>百度一下,你就知道</title>
  11. # <img hidefocus="true" src="//www.baidu.com/img/bd_logo1.png" width="270" height="129"/> <img src="//www.baidu.com/img/gs.gif"/>
  12. # ......
  13. # 输出结果较长,读者可自行运行

正如上面代码所示,pyquery在进行节点提取的时候通常有三种方式,一种是直接提取出节点名即可提取出整个节点,当然这种方式你也可以使用find方法,这种提取节点的方式是不加任何属性限定的,所以提取出的节点往往会含有多个,所以我们可以使用循环.items()来进行操作;一种是提取出含有特定class属性的节点,这种形式采用的是.+class属性值;还有一种是提取含有特定id属性的节点,这种形式采用的是#+id属性值。熟悉CSS的读者应该不难理解以上提取节点的方法,正是在CSS中提取节点然后对其进行样式操作的方法。上述三种方式您也可以像提取bd_a_video一样混合使用

数据提取

在实际解析网页的过程中,三种解析方式基本上大同小异,为了读者认识pyquery的数据提取的操作以及博主日后的查阅,在这里简单的介绍下

  1. img_src1 = bd_bj("img").attr("src") # //www.baidu.com/img/bd_logo1.png
  2. img_src2 = bd_bj("img").attr.src # //www.baidu.com/img/bd_logo1.png
  3. for each in bd_bj.find("img").items():
  4. print(each.attr("src"))
  5. print(bd_bj("title").text()) # 百度一下,你就知道

如上一二行代码所示,提取节点属性我们可以有两种方式,这里拿src属性来进行说明,一种是.attr("src"),另外一种是.attr.src,读者根据自己的习惯来操作即可,这里需要注意的是:在节点提取小结中我们说了在不限制属性的情况下是提取出所有满足条件的节点,所以在这种情况下提取出的属性是第一个节点属性。要想提取所有的节点的属性,我们可以如四五行代码那样使用.items()然后进行遍历,最后和之前一样提取各个节点属性即可。qyquery提取节点中文本内容如第七行代码那样直接使用.text()即可。

pyquery小结

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标签内,如下所示:

下面我们具体来分别使用以上三种工具来解析该站所有职位数据。

案例源码

  1. import requests
  2. from bs4 import BeautifulSoup
  3. from lxml import etree
  4. from pyquery import PyQuery as pq
  5. import itertools
  6. import pandas as pd
  7. class TencentPosition():
  8. """
  9. 功能: 定义初始变量
  10. 参数:
  11. start: 起始数据
  12. """
  13. def __init__(self, start):
  14. self.url = "https://hr.tencent.com/position.php?&start={}#a".format(start)
  15. self.headers = {
  16. "Host": "hr.tencent.com",
  17. "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",
  18. }
  19. self.file_path = "./TencentPosition.csv"
  20. """
  21. 功能: 请求目标页面
  22. 参数:
  23. url: 目标链接
  24. headers: 请求头
  25. 返回:
  26. html,页面源码
  27. """
  28. def get_page(self, url, headers):
  29. res = requests.get(url, headers=headers)
  30. try:
  31. if res.status_code == 200:
  32. return res.text
  33. else:
  34. return self.get_page(url, headers=headers)
  35. except RequestException as e:
  36. return self.get_page(url, headers=headers)
  37. """
  38. 功能: Beautiful Soup解析页面
  39. 参数:
  40. html: 请求页面源码
  41. """
  42. def soup_analysis(self, html):
  43. soup = BeautifulSoup(html, "lxml")
  44. tr_list = soup.find("table", class_="tablelist").find_all("tr")
  45. for tr in tr_list[1:-1]:
  46. position_info = [td_data for td_data in tr.stripped_strings]
  47. self.settle_data(position_info=position_info)
  48. """
  49. 功能: xpath解析页面
  50. 参数:
  51. html: 请求页面源码
  52. """
  53. def xpath_analysis(self, html):
  54. result = etree.HTML(html)
  55. tr_list = result.xpath("//table[@class='tablelist']//tr")
  56. for tr in tr_list[1:-1]:
  57. position_info = tr.xpath("./td//text()")
  58. self.settle_data(position_info=position_info)
  59. """
  60. 功能: pyquery解析页面
  61. 参数:
  62. html: 请求页面源码
  63. """
  64. def pyquery_analysis(self, html):
  65. result = pq(html)
  66. tr_list = result.find(".tablelist").find("tr")
  67. for tr in itertools.islice(tr_list.items(), 1, 11):
  68. position_info = [td.text() for td in tr.find("td").items()]
  69. self.settle_data(position_info=position_info)
  70. """
  71. 功能: 职位数据整合
  72. 参数:
  73. position_info: 字段数据列表
  74. """
  75. def settle_data(self, position_info):
  76. position_data = {
  77. "职位名称": position_info[0].replace("\xa0", " "), # replace替换\xa0字符防止转码error
  78. "职位类别": position_info[1],
  79. "招聘人数": position_info[2],
  80. "工作地点": position_info[3],
  81. "发布时间": position_info[-1],
  82. }
  83. print(position_data)
  84. self.save_data(self.file_path, position_data)
  85. """
  86. 功能: 数据保存
  87. 参数:
  88. file_path: 文件保存路径
  89. position_data: 职位数据
  90. """
  91. def save_data(self, file_path, position_data):
  92. df = pd.DataFrame([position_data])
  93. try:
  94. df.to_csv(file_path, header=False, index=False, mode="a+", encoding="gbk") # 数据转码并换行存储
  95. except:
  96. pass
  97. if __name__ == "__main__":
  98. for page, index in enumerate(range(287)):
  99. print("正在爬取第{}页的职位数据:".format(page+1))
  100. tp = TencentPosition(start=(index*10))
  101. tp_html = tp.get_page(url=tp.url, headers=tp.headers)
  102. tp.pyquery_analysis(html=tp_html)
  103. print("\n")

部分结果如下:

总结

在本篇文章中,首先我们分别介绍了Beautiful Soup、XPath、pyquery的常见操作,之后通过使用该三种解析工具来爬取腾讯招聘网中所有的职位招聘数据,从而进一步让读者有一个更加深刻的认识。该案例中,由于本篇文章重点在于网站页面的解析方法,所以未使用多线程、多进程,爬取所有的数据爬取的时间在两分钟左右,在之后的文章中有时间的话会再次介绍多线程多进程的使用,案例中的解析方式都已介绍过,所以读者阅读源码即可。

注意:本文章中所有的内容皆为在实际开发中常见的一些操作,并非所有,想要进一步提升等级的读者务必请阅读官方文档。

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注