学习一时爽,一直学习一直爽!html
Hello,你们好,我是Connor,一个从无到有的技术小白。上一次咱们说到了 Xpath
的使用方法。Xpath
我以为仍是比较绕该怎么办呢???有没有更加简单易懂的方法呢?答案是确定的,固然有更加简单易懂的方法了,那就是 BeautifulSoup
美味的汤。这个方法对于正则和 Xpath
来讲更加的简单方便,更加易懂,可以节省咱们大量的分析时间。前端
BeautifulSoup是一个HTML数据提取库。几乎没有什么数据能够难住BeautifulSoup。只要是你能够获取的到的数据,那么你均可以经过BeautifulSoup简单快捷的进行数据提取。是一款很是适合新手入门使用的数据提取库。固然,做为一个HTML数据提取库,requests
都有了官方中文文档,那么 BeautifulSoup
固然也不能少啊,你能够访问 BeautifulSoup
的官方中文文档:点我走起 >>>html5
既然我又来讲到安装了,那就证实这个库和咱们日常想的库不太同样,它具体的安装方法为:python
pip install beautifulsoup4
复制代码
注意,是beautifulsoup4,并非beautifulsoup,虽然咱们beautifulsoup的叫,但人家实际叫beautifulsoup4,必定要记清楚哈。ios
安装完了,下面咱们就正式开始使用,老规矩,咱们先来一段html文档,而后逐一举例,来看BeautifulSoup如何使用:正则表达式
首先咱们来随意编写一段html代码:浏览器
html = """
<html>
<head>
<title>Hello,Wrold</title>
</head>
<body>
<div class="book">
<span><!--这里是注释的部分--></span>
<a href="https://www.baidu.com">百度一下,你就知道</a>
<img src="https://abc.jpg" />
<p class="abc">这是一个示例</p>
</div>
</body>
</html>"""
复制代码
在进行内容提取以前,咱们须要将获取的html内容转换成BeautifulSoup对象:工具
In [1]: from bs4 import BeautifulSoup
In [2]: soup = BeautifulSoup(html, "html5lib")
In [3]: type(soup)
Out[3]: bs4.BeautifulSoup
复制代码
能够看的到,咱们生成的对象是一个 bs4.BeautfulSoup
对象,咱们全部的内容提取都基于这个对象。切记进行内容提取以前先生成 bs4.BeautifulSoup
对象。post
在上面的语句中,你们能够看到咱们使用了一个 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
来进行解析的。具体的视状况而定吧。
BeautifulSoup将复杂的HTML文档转换成了一个树状的结构,每一个节点都是一个Python对象,全部的对象均可以概括为四类:Tag
,NavigableString
,BeautifulSoup
,Commnet
。
Tag
就是咱们平时所说的标签,Tag下拥有许多属性和方法,和前端相似,例如 a 标签必定会有它的href属性,某些属性是某些标签所独有的。下面咱们来看一下如何提取一个 Tag
对象:
In [1]: soup = BeautifulSoup(html)
In [2]: tag = soup.p
In [3]: type(tag)
Out[3]: bs4.element.Tag
复制代码
能够看的到,咱们生成了一个 Tag
对象,咱们再来看看 Tag
对象有哪些属性:
每个tag标签都有name属性
In [4]: tag.name
Out[4]: 'p'
复制代码
在html中,某个 Tag
可能有多个属性, Tag
属性使用和字典同样的方法取值:
In [5]: tag["class"]
Out[5]: ['abc']
复制代码
若是某个 Tag
属性有多个值,那么返回的是一个列表:
In [6]: soup = BeautifulSoup('<p class="body strikeout"></p>')
In [7]: soup.p['class']
Out[7]: ['body', 'strikeout']
复制代码
经过 get_text()
方法咱们能够获取某个 Tag
下全部的文本内容:
In [8]: soup.a.get_text()
Out[8]: '百度一下,你就知道'
复制代码
NavigableString
的意思是能够遍历的字符串,通常被标签包裹在自种的文本就是NavigableString
格式:
In [9]: soup.p.string
Out[9]: '这是一个示例'
In [10]: type(soup.p.string)
Out[10]: bs4.element.NavigableString
复制代码
BeautifulSoup
对象就是经过解析网页所获得的对象,咱们的 soup
便是 BeautifulSoup
对象:
In [1]: from bs4 import BeautifulSoup
In [2]: soup = BeautifulSoup(html, "html5lib")
In [3]: type(soup)
Out[3]: bs4.BeautifulSoup
复制代码
Comment
对象是网页中的注释及特殊字符串,当你提取网页中的注释的时候,它会自动帮你生成Comment
对象:
In [4]: comment = soup.body.span.string
In [5]: type(comment)
Out[5]: bs4.element.Comment
复制代码
了解了 BeautifulSoup
的基础使用以后,咱们来看一下 BeautifulSoup
的进阶用法:
Tag
对象能够说 BeautifulSoup
中最为重要的对象,经过 BeautifulSoup
来提取数据基本都围绕着这个对象来进行操做。
首先,一个节点中是能够包含多个子节点和多个字符串的。例如html
节点中包含着head
和body
节点。因此BeautifulSoup
就能够将一个HTML的网页用这样一层层嵌套的节点来进行表示。
使用咱们的例子,你能够这样作:
经过 contents
能够获取某个节点的全部子节点,包括里面的 NavigbleString
对象,获取的子节点是列表格式:
In [5]: soup.head.contents
Out[5]: [<title>Hello,Wrold</title>]
复制代码
经过 children
也能够获取某个节点的全部子节点,可是返回的是一个迭代器,这种方式使用起来比列表更加的省内存:
In [6]: tags = soup.head.children
In [7]: print(tags)
<list_iterator object at 0x000002E5B44E6860>
In [8]: for tag in tags:
...: print(tag)
...:
<title>Hello,Wrold</title>
复制代码
上面的contents
和children
获取的是某个节点的直接子节点,而没法得到子孙节点。经过descendants
能够得到全部子孙节点,返回的结果跟children
同样,须要迭代或者转类型使用。
In [15]: tags = soup.body.descendants
In [16]: for tag in tags:
...: print(tag)
...:
<div class="book">
<span><!--这里是注释的部分--></span>
<a href="https://www.baidu.com">百度一下,你就知道</a>
<img src="https://abc.jpg"/>
<p class="abc">这是一个示例</p>
</div>
<span><!--这里是注释的部分--></span>
这里是注释的部分
<a href="https://www.baidu.com">百度一下,你就知道</a>
百度一下,你就知道
<img src="https://abc.jpg"/>
<p class="abc">这是一个示例</p>
这是一个示例
复制代码
经过上图咱们能够看得出经过 descendants
首先找出了 body
标签的第一个子节点,而后将子节点中的字符串提取出来。提取出子节点的字符串以后再提取子节点的子节点,再将其内容提取出来。直到该节点再也不拥有子节点。这么说可能有些抽象,咱们来直接看图:
咱们经常会遇到须要获取某个节点中的文本值的状况,若是这个节点中只有一个字符串,那么使用string
能够正常将其取出。
In [17]: soup.body.a.string
Out[18]: '百度一下,你就知道'
复制代码
可是若是一个节点下有多个节点中包含有字符串的时候,这时使用 string
方法就没法准确的取出字符串了,它没法肯定你要取出的是哪一个字符串,这时你须要使用 strings
:
In [19]: strings = soup.body.strings
In [20]: strings
Out[20]: <generator object _all_strings at 0x000002E5B44EA1A8>
In [21]: for string in strings:
...: print(string)
...:
百度一下,你就知道
这是一个示例
复制代码
使用 strings
也会给你返回一个可迭代对象。固然,你会发现里面有不少的'\n','\t'啊等这样的转义字符,上面的程序中没有是由于为了美观我手动去掉了。若是你想要获取的内容中没有转义字符的话,你可使用 stripped_strings
来去掉内容中的空白:
In [22]: strings = soup.body.stripped_strings
In [23]: strings
Out[23]: <generator object stripped_strings at 0x000002E5B39DF3B8>
In [24]: for string in strings:
...: print(string)
...:
百度一下,你就知道
这是一个示例
复制代码
有的时候咱们也须要去获取某个节点的父节点,就是当前节点的上一层节点:
In [25]: soup.a.parent
Out[25]:
<div class="book">
<span><!--这里是注释的部分--></span>
<a href="https://www.baidu.com">百度一下,你就知道</a>
<img src="https://abc.jpg"/>
<p class="abc">这是一个示例</p>
</div>
复制代码
若是使用 parents
的话将会递归获取该节点的全部父辈元素:
In [26]: soup.a.parents
Out[26]: <generator object parents at 0x000002E5B38C3150>
复制代码
一样这种方式获取的父辈元素也是一个可迭代对象,须要处理后才能使用
兄弟节点就是指当前节点同级节点。
next_sibling 和 previous_sibling
兄弟节点选取的方法与当前节点的位置有关,next_sibling
获取的是当前节点的下一个兄弟节点,previous_sibling
获取的是当前节点的上一个兄弟节点。
因此,兄弟节点中排第一个的节点是没有previous_sibling
的,最后一个节点是没有next_sibling
的。
In [27]: soup.head.next_sibling
Out[27]: '\n'
In [28]: soup.head.previos_sibling
In [29]: soup.body.previous_sibling
Out[29]: '\n'
复制代码
next_siblings 和 previous_siblings
相对应的,next_siblings
获取的是下方全部的兄弟节点,previous_siblings
获取的上方全部的兄弟节点。
In [30]: [i.name for i in soup.head.next_siblings]
Out[30]: [None, 'body', None]
In [31]: [i.name for i in soup.body.next_siblings]
Out[31]: [None]
In [32]: [i.name for i in soup.body.previous_siblings]
Out[32]: [None, 'head', None]
复制代码
在前面咱们讲了经过标签的属性来进行标签的访问的方法,大多都只适用于简单的一些场景,因此 BeautifulSoup
还提供了搜索整个文档树的方法,即 find_all()
。该方法基本适用于任何节点:
最简单的使用方式就是使用 name 属性进行搜索,你能够这样作:
In [33]: soup.find_all('a')
Out[33]: [<a href="https://www.baidu.com">百度一下,你就知道</a>]
复制代码
经过 find_all()
方法获取的内容是一个列表对象。若是你给的条件是一个列表,则会匹配列表里的所有标签,例如:
In [34]: soup.find_all(['a','p'])
Out[34]: [<a href="https://www.baidu.com">百度一下,你就知道</a>, <p class="abc">这是一个示例</p>]
复制代码
经过上面的例子咱们能够看得出,咱们能够看获得, BeautifulSoup
对象匹配出了全部的 a标签和 p 标签。
除了经过 name 属性来进行匹配以外,咱们还能够经过属性进行匹配。这个时候咱们须要向 find_all()
方法传递一个字典参数:
In [35]: soup.find_all(attrs={'class':'book'})
Out[35]:
[<div class="book">
<span><!--这里是注释的部分--></span>
<a href="https://www.baidu.com">百度一下,你就知道</a>
<img src="https://abc.jpg"/>
<p class="abc">这是一个示例</p>
</div>]
复制代码
若是一个标签有多个参数,为了查找的准确性,你也能够向attrs传递多个参数。
在find_all()
方法中,还能够根据文本内容来进行搜索。
In [36]: soup.find_all("a", text="百度一下,你就知道")
Out[36]: [<a href="https://www.baidu.com">百度一下,你就知道</a>]
复制代码
可见找到的都是字符串对象,若是想要找到包含某个文本的tag
,加上tag
名便可。
find_all()
方法会默认的去全部的子孙节点中搜索,而若是将 recursive
参数设置为False,则能够将搜索范围限制在直接子节点中:
In [37]: soup.find_all("a",recursive=False)
Out[37]: []
In [38]: soup.find_all("a",recursive=True)
Out[38]: [<a href="https://www.baidu.com">百度一下,你就知道</a>]
复制代码
在BeautifulSoup
中,也是能够与re
模块进行相互配合的,将re.compile编译的对象传入find_all()
方法,便可经过正则来进行搜索。
In [39]: import re
In [40]: soup.find_all(re.compile("b"))
Out[40]:
[<body>
<div class="book">
<span><!--这里是注释的部分--></span>
<a href="https://www.baidu.com">百度一下,你就知道</a>
<img src="https://abc.jpg"/>
<p class="abc">这是一个示例</p>
</div>
</body>]
复制代码
能够看到,经过正则表达式,咱们找到了全部以 b 开头的标签。正则怎么用咱们会在以后的文章中详细的说的,你们不用着急。固然,正则除了能用在标签上,也能够用在属性上:
In [57]: soup.find_all(attrs={"class":re.compile("a")})
Out[57]: [<p class="abc">这是一个示例</p>]
复制代码
在BeautifulSoup
中,一样也支持使用CSS选择器来进行搜索。使用select()
,在其中传入字符串参数,就可使用CSS选择器的语法来找到tag:
In [58]: soup.select("title")
Out[58]: [<title>Hello,Wrold</title>]
In [60]: soup.select(".book")
Out[60]:
[<div class="book">
<span><!--这里是注释的部分--></span>
<a href="https://www.baidu.com">百度一下,你就知道</a>
<img src="https://abc.jpg"/>
<p class="abc">这是一个示例</p>
</div>]
复制代码
Xpath 和 BueatifulSoup 都很好用,可是有时候遇到复杂的选择也很麻烦,有没有能像 JQuery同样快速经过 CSS 来选择的工具啊???固然有了!!!那就是咱们的 PyQuery,一个和 JQuery 像兄弟同样的库,敬请期待下期——PyQuery,一个相似JQuery的库。
好了,这就是今天最美味的汤了,不知道你喝了之后有什么感觉,我是Connor,一个从无到有的技术小白,但愿你能和我一同进步,一同成长!咱们下期再见!
学习一时爽,一直学习一直爽!
Python 爬虫十六式 - 第一式:HTTP协议 >>>
Python 爬虫十六式 - 第二式:urllib 与 urllib3 >>>
Python 爬虫十六式 - 第三式:Requests的用法 >>>
Python 爬虫十六式 - 第四式: 使用Xpath提取网页内容 >>>
Python 爬虫十六式 - 第六式:JQuery的假兄弟-pyquery >>>
Python 爬虫十六式 - 第七式:正则的艺术 >>>
Python 爬虫十六式 - 第八式:实例解析-全书网 >>>