BeautifulSoup 美味的汤

学习一时爽,一直学习一直爽!

   Hello,大家好,我是Connor,一个从无到有的技术小白。上一次我们说到了 Xpath 的使用方法。Xpath 我觉得还是比较绕该怎么办呢???有没有更加简单易懂的方法呢?答案是肯定的,当然有更加简单易懂的方法了,那就是 BeautifulSoup 美味的汤。这个方法对于正则和 Xpath 来说更加的简单方便,更加易懂,能够节省我们大量的分析时间。

1.BeautifulSoup 的简介

  BeautifulSoup是一个HTML数据提取库。几乎没有什么数据可以难住BeautifulSoup。只要是你可以获取的到的数据,那么你都可以通过BeautifulSoup简单快捷的进行数据提取。是一款非常适合新手入门使用的数据提取库。当然,作为一个HTML数据提取库,requests 都有了官方中文文档,那么 BeautifulSoup 当然也不能少啊,你可以访问 BeautifulSoup 的官方中文文档:点我走起 >>>


2.BeautifulSoup 的安装

  既然我又来说到安装了,那就证明这个库和我们平常想的库不太一样,它具体的安装方法为:

  1. pip install beautifulsoup4

  注意,是beautifulsoup4,并不是beautifulsoup,虽然我们beautifulsoup的叫,但人家实际叫beautifulsoup4,一定要记清楚哈。


3.BeautifulSoup 的基础使用

  安装完了,下面我们就正式开始使用,老规矩,我们先来一段html文档,然后逐一举例,来看BeautifulSoup如何使用:

  首先我们来随意编写一段html代码:

  1. html = """
  2. <html>
  3. <head>
  4. <title>Hello,Wrold</title>
  5. </head>
  6. <body>
  7. <div class="book">
  8. <span><!--这里是注释的部分--></span>
  9. <a href="https://www.baidu.com">百度一下,你就知道</a>
  10. <img src="https://abc.jpg" />
  11. <p class="abc">这是一个示例</p>
  12. </div>
  13. </body>
  14. </html>"""

3.1 简单的使用

  在进行内容提取之前,我们需要将获取的html内容转换成BeautifulSoup对象:

  1. In [1]: from bs4 import BeautifulSoup
  2. In [2]: soup = BeautifulSoup(html, "html5lib")
  3. In [3]: type(soup)
  4. Out[3]: bs4.BeautifulSoup

  可以看的到,我们生成的对象是一个 bs4.BeautfulSoup 对象,我们所有的内容提取都基于这个对象。切记进行内容提取之前先生成 bs4.BeautifulSoup 对象。

3.2 解析器的使用

  在上面的语句中,大家可以看到我们使用了一个 html5lib 这是一个解析器,在构造BeautifulSoup 对象的时候,需要用到解析器。BeautifulSoup 支持python自带的解析器和少数第三方解析器。详细对比如下:

解析器 使用方法 优势 劣势
Python标准库 BeautifulSoup(html,"html.parser") Python的内置标准库。 执行速度适中。 文档容错能力强。 Python 3.2.2前的版本文档容错能力差
lxml HTML 解析器 BeautifulSoup(html, "lxml") 速度快文档容错能力强 需要安装C语言库
lxml XML 解析器 BeautifulSoup(html, ["lxml","xml"]) BeautifulSoup(html, "xml") 速度快 唯一支持XML的解析器 需要安装C语言库
html5lib BeautifulSoup(markup,"html5lib") 最好的容错性 以浏览器的方式解析文档生成HTML5格式的文档 速度慢但不依赖外部扩展

  一般来说,对于速度或性能要求不太高的话,还是建议大家使用 html5lib 来进行解析的,但是当规模达到一定程度的时候,解析速度就会影响到整体项目的快慢了,所以如果你对性能有要求的话,还是推荐使用 lxml 来进行解析的。具体的视情况而定吧。


3.3 节点对象

  BeautifulSoup将复杂的HTML文档转换成了一个树状的结构,每个节点都是一个Python对象,所有的对象都可以归纳为四类:TagNavigableStringBeautifulSoup,Commnet

3.3.1 Tag 对象

  Tag 就是我们平时所说的标签,Tag下拥有许多属性和方法,和前端类似,例如 a 标签一定会有它的href属性,某些属性是某些标签所独有的。下面我们来看一下如何提取一个 Tag 对象:

  1. In [1]: soup = BeautifulSoup(html)
  2. In [2]: tag = soup.p
  3. In [3]: type(tag)
  4. Out[3]: bs4.element.Tag

  可以看的到,我们生成了一个 Tag 对象,我们再来看看 Tag 对象有哪些属性:

  • name属性:

  每一个tag标签都有name属性

  1. In [4]: tag.name
  2. Out[4]: 'p'
  • Attributes

  在html中,某个 Tag 可能有多个属性, Tag 属性使用和字典一样的方法取值:

  1. In [5]: tag["class"]
  2. Out[5]: ['abc']

如果某个 Tag 属性有多个值,那么返回的是一个列表:

  1. In [6]: soup = BeautifulSoup('<p class="body strikeout"></p>')
  2. In [7]: soup.p['class']
  3. Out[7]: ['body', 'strikeout']
  • get_text()

  通过 get_text() 方法我们可以获取某个 Tag 下所有的文本内容:

  1. In [8]: soup.a.get_text()
  2. Out[8]: '百度一下,你就知道'

3.3.2 NavigableString 对象

  NavigableString 的意思是可以遍历的字符串,一般被标签包裹在自种的文本就是NavigableString 格式:

  1. In [9]: soup.p.string
  2. Out[9]: '这是一个示例'
  3. In [10]: type(soup.p.string)
  4. Out[10]: bs4.element.NavigableString

3.3.3 BeautifulSoup 对象

BeautifulSoup 对象就是通过解析网页所得到的对象,我们的 soup 即是 BeautifulSoup 对象:

  1. In [1]: from bs4 import BeautifulSoup
  2. In [2]: soup = BeautifulSoup(html, "html5lib")
  3. In [3]: type(soup)
  4. Out[3]: bs4.BeautifulSoup

3.3.4 Comment 对象

Comment 对象是网页中的注释及特殊字符串,当你提取网页中的注释的时候,它会自动帮你生成Comment 对象:

  1. In [4]: comment = soup.body.span.string
  2. In [5]: type(comment)
  3. Out[5]: bs4.element.Comment

了解了 BeautifulSoup 的基础使用之后,我们来看一下 BeautifulSoup 的进阶用法:

4 BeautifulSoup 的高级用法

4.1 Tag与遍历文档树

  Tag 对象可以说 BeautifulSoup 中最为重要的对象,通过 BeautifulSoup 来提取数据基本都围绕着这个对象来进行操作。

  首先,一个节点中是可以包含多个子节点和多个字符串的。例如html节点中包含着headbody节点。所以BeautifulSoup就可以将一个HTML的网页用这样一层层嵌套的节点来进行表示。

  使用我们的例子,你可以这样做:

4.1.1 contents 和 children

  通过 contents 可以获取某个节点的所有子节点,包括里面的 NavigbleString 对象,获取的子节点是列表格式:

  1. In [5]: soup.head.contents
  2. Out[5]: [<title>Hello,Wrold</title>]

  通过 children 也可以获取某个节点的所有子节点,但是返回的是一个迭代器,这种方式使用起来比列表更加的省内存:

  1. In [6]: tags = soup.head.children
  2. In [7]: print(tags)
  3. <list_iterator object at 0x000002E5B44E6860>
  4. In [8]: for tag in tags:
  5. ...: print(tag)
  6. ...:
  7. <title>Hello,Wrold</title>

4.1.2 descendants

  上面的contentschildren获取的是某个节点的直接子节点,而无法获得子孙节点。通过descendants可以获得所有子孙节点,返回的结果跟children一样,需要迭代或者转类型使用。

  1. In [15]: tags = soup.body.descendants
  2. In [16]: for tag in tags:
  3. ...: print(tag)
  4. ...:
  5. <div class="book">
  6. <span><!--这里是注释的部分--></span>
  7. <a href="https://www.baidu.com">百度一下,你就知道</a>
  8. <img src="https://abc.jpg"/>
  9. <p class="abc">这是一个示例</p>
  10. </div>
  11. <span><!--这里是注释的部分--></span>
  12. 这里是注释的部分
  13. <a href="https://www.baidu.com">百度一下,你就知道</a>
  14. 百度一下,你就知道
  15. <img src="https://abc.jpg"/>
  16. <p class="abc">这是一个示例</p>
  17. 这是一个示例

  通过上图我们可以看得出通过 descendants 首先找出了 body 标签的第一个子节点,然后将子节点中的字符串提取出来。提取出子节点的字符串之后再提取子节点的子节点,再将其内容提取出来。直到该节点不再拥有子节点。这么说可能有些抽象,我们来直接看图:


  是不是一目了然了???鬼知道我为了做这个图到底经历了什么…


4.1.3 string 和 strings

  我们常常会遇到需要获取某个节点中的文本值的情况,如果这个节点中只有一个字符串,那么使用string可以正常将其取出。

  1. In [17]: soup.body.a.string
  2. Out[18]: '百度一下,你就知道'

  但是如果一个节点下有多个节点中包含有字符串的时候,这时使用 string 方法就无法准确的取出字符串了,它无法确定你要取出的是哪个字符串,这时你需要使用 strings

  1. In [19]: strings = soup.body.strings
  2. In [20]: strings
  3. Out[20]: <generator object _all_strings at 0x000002E5B44EA1A8>
  4. In [21]: for string in strings:
  5. ...: print(string)
  6. ...:
  7. 百度一下,你就知道
  8. 这是一个示例

  使用 strings 也会给你返回一个可迭代对象。当然,你会发现里面有很多的’\n’,’\t’啊等这样的转义字符,上面的程序中没有是因为为了美观我手动去掉了。如果你想要获取的内容中没有转义字符的话,你可以使用 stripped_strings 来去掉内容中的空白:

  1. In [22]: strings = soup.body.stripped_strings
  2. In [23]: strings
  3. Out[23]: <generator object stripped_strings at 0x000002E5B39DF3B8>
  4. In [24]: for string in strings:
  5. ...: print(string)
  6. ...:
  7. 百度一下,你就知道
  8. 这是一个示例

4.1.4 父节点 parent 和 parents

  有的时候我们也需要去获取某个节点的父节点,就是当前节点的上一层节点:

  1. In [25]: soup.a.parent
  2. Out[25]:
  3. <div class="book">
  4. <span><!--这里是注释的部分--></span>
  5. <a href="https://www.baidu.com">百度一下,你就知道</a>
  6. <img src="https://abc.jpg"/>
  7. <p class="abc">这是一个示例</p>
  8. </div>

  如果使用 parents 的话将会递归获取该节点的所有父辈元素:

  1. In [26]: soup.a.parents
  2. Out[26]: <generator object parents at 0x000002E5B38C3150>

  同样这种方式获取的父辈元素也是一个可迭代对象,需要处理后才能使用


4.1.5 兄弟节点

  兄弟节点就是指当前节点同级节点。

  • next_sibling 和 previous_sibling

    兄弟节点选取的方法与当前节点的位置有关,next_sibling获取的是当前节点的下一个兄弟节点,previous_sibling获取的是当前节点的上一个兄弟节点。

    所以,兄弟节点中排第一个的节点是没有previous_sibling的,最后一个节点是没有next_sibling的。

    1. In [27]: soup.head.next_sibling
    2. Out[27]: '\n'
    3. In [28]: soup.head.previos_sibling
    4. In [29]: soup.body.previous_sibling
    5. Out[29]: '\n'
  • next_siblings 和 previous_siblings

    相对应的,next_siblings获取的是下方所有的兄弟节点,previous_siblings获取的上方所有的兄弟节点。

    1. In [30]: [i.name for i in soup.head.next_siblings]
    2. Out[30]: [None, 'body', None]
    3. In [31]: [i.name for i in soup.body.next_siblings]
    4. Out[31]: [None]
    5. In [32]: [i.name for i in soup.body.previous_siblings]
    6. Out[32]: [None, 'head', None]

4.2. find_all()

  在前面我们讲了通过标签的属性来进行标签的访问的方法,大多都只适用于简单的一些场景,所以 BeautifulSoup 还提供了搜索整个文档树的方法,即 find_all()。该方法基本适用于任何节点:

4.2.1 通过 name 搜索

最简单的使用方式就是使用 name 属性进行搜索,你可以这样做:

  1. In [33]: soup.find_all('a')
  2. Out[33]: [<a href="https://www.baidu.com">百度一下,你就知道</a>]

  通过 find_all() 方法获取的内容是一个列表对象。如果你给的条件是一个列表,则会匹配列表里的全部标签,例如:

  1. In [34]: soup.find_all(['a','p'])
  2. Out[34]: [<a href="https://www.baidu.com">百度一下,你就知道</a>, <p class="abc">这是一个示例</p>]

  通过上面的例子我们可以看得出,我们可以看得到, BeautifulSoup 对象匹配出了所有的 a标签和 p 标签。


4.2.2 通过属性搜索

  除了通过 name 属性来进行匹配之外,我们还可以通过属性进行匹配。这个时候我们需要向 find_all() 方法传递一个字典参数:

  1. In [35]: soup.find_all(attrs={'class':'book'})
  2. Out[35]:
  3. [<div class="book">
  4. <span><!--这里是注释的部分--></span>
  5. <a href="https://www.baidu.com">百度一下,你就知道</a>
  6. <img src="https://abc.jpg"/>
  7. <p class="abc">这是一个示例</p>
  8. </div>]

  如果一个标签有多个参数,为了查找的准确性,你也可以向attrs传递多个参数。


4.2.3 通过文本搜索

  在find_all()方法中,还可以根据文本内容来进行搜索。

  1. In [36]: soup.find_all("a", text="百度一下,你就知道")
  2. Out[36]: [<a href="https://www.baidu.com">百度一下,你就知道</a>]

可见找到的都是字符串对象,如果想要找到包含某个文本的tag,加上tag名即可。


4.2.4 限制查找范围为子节点

  find_all() 方法会默认的去所有的子孙节点中搜索,而如果将 recursive 参数设置为False,则可以将搜索范围限制在直接子节点中:

  1. In [37]: soup.find_all("a",recursive=False)
  2. Out[37]: []
  3. In [38]: soup.find_all("a",recursive=True)
  4. Out[38]: [<a href="https://www.baidu.com">百度一下,你就知道</a>]

4.2.5 通过正则表达式来筛选结果

  在BeautifulSoup中,也是可以与re模块进行相互配合的,将re.compile编译的对象传入find_all()方法,即可通过正则来进行搜索。

  1. In [39]: import re
  2. In [40]: soup.find_all(re.compile("b"))
  3. Out[40]:
  4. [<body>
  5. <div class="book">
  6. <span><!--这里是注释的部分--></span>
  7. <a href="https://www.baidu.com">百度一下,你就知道</a>
  8. <img src="https://abc.jpg"/>
  9. <p class="abc">这是一个示例</p>
  10. </div>
  11. </body>]

  可以看到,通过正则表达式,我们找到了所有以 b 开头的标签。正则怎么用我们会在之后的文章中详细的说的,大家不用着急。当然,正则除了能用在标签上,也可以用在属性上:

  1. In [57]: soup.find_all(attrs={"class":re.compile("a")})
  2. Out[57]: [<p class="abc">这是一个示例</p>]

4.3 CSS选择器

  在BeautifulSoup中,同样也支持使用CSS选择器来进行搜索。使用select(),在其中传入字符串参数,就可以使用CSS选择器的语法来找到tag:

  1. In [58]: soup.select("title")
  2. Out[58]: [<title>Hello,Wrold</title>]
  3. In [60]: soup.select(".book")
  4. Out[60]:
  5. [<div class="book">
  6. <span><!--这里是注释的部分--></span>
  7. <a href="https://www.baidu.com">百度一下,你就知道</a>
  8. <img src="https://abc.jpg"/>
  9. <p class="abc">这是一个示例</p>
  10. </div>]

下期预告:

   Xpath 和 BueatifulSoup 都很好用,但是有时候遇到复杂的选择也很麻烦,有没有能像Jquery一样快速通过css来选择的工具啊???当然有了!!!那就是我们的PyQuery,一个和JQuery就像亲兄弟一样的库,敬请期待下期—PyQuery,一个类似JQuery的库。

  好了,这就是今天最美味的汤了,不知道你喝了以后有什么感受,我是Connor,一个从无到有的技术小白,希望你能和我一同进步,一同成长!我们下期再见!

学习一时爽,一直学习一直爽!


系列文章连接:

Python 爬虫十六式 - 第一式:HTTP协议 >>>

Python 爬虫十六式 - 第二式:urllib 与 urllib3 >>>

Python 爬虫十六式 - 第三式:Requests的用法 >>>

Python 爬虫十六式 - 第四式: 使用Xpath提取网页内容 >>>

Python 爬虫十六式 - 第六式:JQuery的假兄弟-pyquery >>>

Python 爬虫十六式 - 第七式:正则的艺术 >>>

Python 爬虫十六式 - 第五式:BeautifulSoup-美味的汤的更多相关文章

  1. Python 爬虫十六式 - 第六式:JQuery的假兄弟-pyquery

    PyQuery:一个类似jquery的python库 学习一时爽,一直学习一直爽   Hello,大家好,我是 Connor,一个从无到有的技术小白.上一次我们说到了 BeautifulSoup 美味 ...

  2. Python爬虫十六式 - 第四式: 使用Xpath提取网页内容

    Xpath:简单易用的网页内容提取工具 学习一时爽,一直学习一直爽 !   Hello,大家好,我是Connor,一个从无到有的技术小白.上一次我们说到了 requests 的使用方法.到上节课为止, ...

  3. Python 爬虫十六式 - 第七式:正则的艺术

    RE:用匹配来演绎编程的艺术 学习一时爽,一直学习一直爽   Hello,大家好,我是 Connor,一个从无到有的技术小白.上一次我们说到了 pyquery 今天我们将迎来我们数据匹配部分的最后一位 ...

  4. Python爬虫十六式 - 第三式:Requests的用法

    Requests: 让 HTTP 服务人类 学习一时爽,一直学习一直爽   Hello,大家好,我是Connor,一个从无到有的技术小白.今天我们继续来说我们的 Python 爬虫,上一次我们说到了 ...

  5. Python 爬虫十六式 - 第二式:urllib 与 urllib3

    Python请求标准库 urllib 与 urllib3 学习一时爽,一直学习一直爽!   大家好,我是 Connor,一个从无到有的技术小白.上一次我们说到了什么是HTTP协议,那么这一次我们就要动 ...

  6. Python 爬虫十六式 - 第一式:HTTP协议

    HTTP:伟大而又无闻的协议 学习一时爽,一直学习一直爽!   Hello,大家好啊,我是Connor,一个从无到有的技术小白.有的人一说什么是HTTP协议就犯愁,写东西的时候也没想过什么是HTTP协 ...

  7. Python爬虫(十六)_JSON模块与JsonPath

    本篇将介绍使用,更多内容请参考:Python学习指南 数据提取之JSON与JsonPATH JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,它是的人们很容易 ...

  8. Python爬虫利器六之PyQuery的用法

    前言 你是否觉得 XPath 的用法多少有点晦涩难记呢? 你是否觉得 BeautifulSoup 的语法多少有些悭吝难懂呢? 你是否甚至还在苦苦研究正则表达式却因为少些了一个点而抓狂呢? 你是否已经有 ...

  9. Python爬虫实战六之抓取爱问知识人问题并保存至数据库

    大家好,本次为大家带来的是抓取爱问知识人的问题并将问题和答案保存到数据库的方法,涉及的内容包括: Urllib的用法及异常处理 Beautiful Soup的简单应用 MySQLdb的基础用法 正则表 ...

随机推荐

  1. mysql——触发器——前期整理笔记00

    一.触发器 触发器是由事件来出发某个动作.这些事件包括insert语句.update语句和delete语句. 当数据库系统执行这些事件时,就会激活触发器执行相应得动作. 触发器是有insert.upd ...

  2. ElasticSearch 7.3.0 查询、修改、删除 文档操作

    PUT chuyuan/_doc/ { "name":"xiaolin", , "sex":"F", "lov ...

  3. setter 和 getter 高级 以及内存管理初级

    setter 和 getter 的演变,紧接setter 和 getter 初级 1.@property 和  @synthesize 这两个关键字的出现,就是为了剔除代码中的setter方法和get ...

  4. luogu P4383 [九省联考2018]林克卡特树lct

    传送门 题目操作有点奇怪,不过可以发现这就是把树先变成\(k+1\)个连通块,然后每个连通块选一条路径(本题中一个点也是一条路径),然后依次接起来.所以实际上要求的是选出\(k+1\)条点不相交的路径 ...

  5. 修改jar包package目录结构操作方法

    开发中会遇到用第三方的jar包,有时候会出现不同的jar包,包名一致的情况,这就会引发运行时异常,找不到相应的jar包. 这种问题时常困扰我们很长时间.下面提出一种解决办法,例如gson.jar. 1 ...

  6. 关于KMeans和range的使用

    #!/usr/bin/python#-*-coding:utf-8-*-import numpy as npfrom sklearn.cluster import KMeansfrom scipy.s ...

  7. python socket使用

    自学python,先在菜鸟教程网自学,然后买了本书看.又从同事那里淘到了某个培训学校python教学视频,查缺补漏.视频是用python3.0讲的,讲解的很不错,中间有让写作业,这个我很喜欢.这几天看 ...

  8. dedecms织梦调用二级和三级分类标签

    dedecms调用二级.三级以及调用栏目所有子栏目 <!--频道分类具体内容开始--> <div class="channel_sort"> {dede:c ...

  9. yaourt

    https://blog.csdn.net/relcodego/article/details/50531379 https://blog.csdn.net/lsvtogergo/article/de ...

  10. [uboot] (第二章)uboot流程——uboot-spl编译流程(转)

    版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明.本文链接:https://blog.csdn.net/ooonebook/article/det ...