系列文章的第3篇介绍了网络请求库神器 Requests ,请求把数据返回来以后就要提取目标数据,不一样的网站返回的内容一般有多种不一样的格式,一种是 json 格式,这类数据对开发者来讲最友好。另外一种 XML 格式的,还有一种最多见格式的是 HTML 文档,今天就来说讲如何从 HTML 中提取出感兴趣的数据javascript
本身写个 HTML 解析器来解析吗?仍是用正则表达式?这些都不是最好的办法,好在,Python 社区在这方便早就有了很成熟的方案,BeautifulSoup 就是这一类问题的克星,它专一于 HTML 文档操做,名字来源于 Lewis Carroll 的一首同名诗歌。html
BeautifulSoup 是一个用于解析 HTML 文档的 Python 库,经过 BeautifulSoup,你只须要用不多的代码就能够提取出 HTML 中任何感兴趣的内容,此外,它还有必定的 HTML 容错能力,对于一个格式不完整的HTML 文档,它也能够正确处理。java
pip install beautifulsoup4复制代码
BeautifulSoup3 被官方放弃维护,你要下载最新的版本 BeautifulSoup4。python
学习 BeautifulSoup4 前有必要先对 HTML 文档有一个基本认识,以下代码,HTML 是一个树形组织结构。正则表达式
<html>
<head>
<title>hello, world</title>
</head>
<body>
<h1>BeautifulSoup</h1>
<p>如何使用BeautifulSoup</p>
<body>
</html>复制代码
构建一个 BeautifulSoup 对象须要两个参数,第一个参数是将要解析的 HTML 文本字符串,第二个参数告诉 BeautifulSoup 使用哪一个解析器来解析 HTML。json
解析器负责把 HTML 解析成相关的对象,而 BeautifulSoup 负责操做数据(增删改查)。”html.parser” 是Python内置的解析器,”lxml” 则是一个基于c语言开发的解析器,它的执行速度更快,不过它须要额外安装网络
经过 BeautifulSoup 对象就能够定位到 HTML 中的任何一个标签节点。学习
from bs4 import BeautifulSoup
text = """
<html>
<head>
<title >hello, world</title>
</head>
<body>
<h1>BeautifulSoup</h1>
<p class="bold">如何使用BeautifulSoup</p>
<p class="big" id="key1"> 第二个p标签</p>
<a href="http://foofish.net">python</a>
</body>
</html>
"""
soup = BeautifulSoup(text, "html.parser")
# title 标签
>>> soup.title
<title>hello, world</title>
# p 标签
>>> soup.p
<p class="bold">\u5982\u4f55\u4f7f\u7528BeautifulSoup</p>
# p 标签的内容
>>> soup.p.string
u'\u5982\u4f55\u4f7f\u7528BeautifulSoup'复制代码
BeatifulSoup 将 HTML 抽象成为 4 类主要的数据类型,分别是Tag , NavigableString , BeautifulSoup,Comment 。每一个标签节点就是一个Tag对象,NavigableString 对象通常是包裹在Tag对象中的字符串,BeautifulSoup 对象表明整个 HTML 文档。例如:网站
>>> type(soup)
<class 'bs4.BeautifulSoup'>
>>> type(soup.h1)
<class 'bs4.element.Tag'>
>>> type(soup.p.string)
<class 'bs4.element.NavigableString'>复制代码
每一个 Tag 都有一个名字,它对应 HTML 的标签名称。spa
>>> soup.h1.name
u'h1'
>>> soup.p.name
u'p'复制代码
标签还能够有属性,属性的访问方式和字典是相似的,它返回一个列表对象
>>> soup.p['class']
[u'bold']复制代码
获取标签中的内容,直接使用 .stirng 便可获取,它是一个 NavigableString 对象,你能够显式地将它转换为 unicode 字符串。
>>> soup.p.string
u'\u5982\u4f55\u4f7f\u7528BeautifulSoup'
>>> type(soup.p.string)
<class 'bs4.element.NavigableString'>
>>> unicode_str = unicode(soup.p.string)
>>> unicode_str
u'\u5982\u4f55\u4f7f\u7528BeautifulSoup'复制代码
基本概念介绍完,如今能够正式进入主题了,如何从 HTML 中找到咱们关心的数据?BeautifulSoup 提供了两种方式,一种是遍历,另外一种是搜索,一般二者结合来完成查找任务。
遍历文档树,顾名思义,就是是从根节点 html 标签开始遍历,直到找到目标元素为止,遍历的一个缺陷是,若是你要找的内容在文档的末尾,那么它要遍历整个文档才能找到它,速度上就慢了。所以还须要配合第二种方法。
经过遍历文档树的方式获取标签节点能够直接经过 .标签名
的方式获取,例如:
获取 body 标签:
>>> soup.body
<body>\n<h1>BeautifulSoup</h1>\n<p class="bold">\u5982\u4f55\u4f7f\u7528BeautifulSoup</p>\n</body>复制代码
获取 p 标签
>>> soup.body.p
<p class="bold">\u5982\u4f55\u4f7f\u7528BeautifulSoup</p>复制代码
获取 p 标签的内容
>>> soup.body.p.string
\u5982\u4f55\u4f7f\u7528BeautifulSoup复制代码
前面说了,内容也是一个节点,这里就能够用 .string
的方式获得。遍历文档树的另外一个缺点是只能获取到与之匹配的第一个子节点,例如,若是有两个相邻的 p 标签时,第二个标签就无法经过 .p
的方式获取,这是须要借用 next_sibling 属性获取相邻且在后面的节点。此外,还有不少不怎么经常使用的属性,好比:.contents 获取全部子节点,.parent 获取父节点,更多的参考请查看官方文档。
搜索文档树是经过指定标签名来搜索元素,另外还能够经过指定标签的属性值来精肯定位某个节点元素,最经常使用的两个方法就是 find 和 find_all。这两个方法在 BeatifulSoup 和 Tag 对象上均可以被调用。
find_all( name , attrs , recursive , text , **kwargs )复制代码
find_all 的返回值是一个 Tag 组成的列表,方法调用很是灵活,全部的参数都是可选的。
第一个参数 name 是标签节点的名字。
# 找到全部标签名为title的节点
>>> soup.find_all("title")
[<title>hello, world</title>]
>>> soup.find_all("p")
[<p class="bold">\xc8\xe7\xba\xce\xca\xb9\xd3\xc3BeautifulSoup</p>,
<p class="big"> \xb5\xda\xb6\xfe\xb8\xf6p\xb1\xea\xc7\xa9</p>]复制代码
第二个参数是标签的class属性值
# 找到全部class属性为big的p标签
>>> soup.find_all("p", "big")
[<p class="big"> \xb5\xda\xb6\xfe\xb8\xf6p\xb1\xea\xc7\xa9</p>]复制代码
等效于
>>> soup.find_all("p", class_="big")
[<p class="big"> \xb5\xda\xb6\xfe\xb8\xf6p\xb1\xea\xc7\xa9</p>]复制代码
由于 class 是 Python 关键字,因此这里指定为 class_。
kwargs 是标签的属性名值对,例如:查找有href属性值为 "foofish.net" 的标签
>>> soup.find_all(href="http://foofish.net")
[<a href="http://foofish.net">python</a>]复制代码
固然,它还支持正则表达式
>>> import re
>>> soup.find_all(href=re.compile("^http"))
[<a href="http://foofish.net">python</a>]复制代码
属性除了能够是具体的值、正则表达式以外,它还能够是一个布尔值(True/Flase),表示有属性或者没有该属性。
>>> soup.find_all(id="key1")
[<p class="big" id="key1"> \xb5\xda\xb6\xfe\xb8\xf6p\xb1\xea\xc7\xa9</p>]
>>> soup.find_all(id=True)
[<p class="big" id="key1"> \xb5\xda\xb6\xfe\xb8\xf6p\xb1\xea\xc7\xa9</p>]复制代码
遍历和搜索相结合查找,先定位到 body 标签,缩小搜索范围,再从 body 中找 a 标签。
>>> body_tag = soup.body
>>> body_tag.find_all("a")
[<a href="http://foofish.net">python</a>]复制代码
find 方法跟 find_all 相似,惟一不一样的地方是,它返回的单个 Tag 对象而非列表,若是没找到匹配的节点则返回 None。若是匹配多个 Tag,只返回第0个。
>>> body_tag.find("a")
<a href="http://foofish.net">python</a>
>>> body_tag.find("p")
<p class="bold">\xc8\xe7\xba\xce\xca\xb9\xd3\xc3BeautifulSoup</p>复制代码
获取标签里面内容,除了可使用 .string 以外,还可使用 get_text 方法,不一样的地方在于前者返回的一个 NavigableString 对象,后者返回的是 unicode 类型的字符串。
>>> p1 = body_tag.find('p').get_text()
>>> type(p1)
<type 'unicode'>
>>> p1
u'\xc8\xe7\xba\xce\xca\xb9\xd3\xc3BeautifulSoup'
>>> p2 = body_tag.find("p").string
>>> type(p2)
<class 'bs4.element.NavigableString'> >>> p2 u'\xc8\xe7\xba\xce\xca\xb9\xd3\xc3BeautifulSoup' >>>复制代码
实际场景中咱们通常使用 get_text 方法获取标签中的内容。
BeatifulSoup 是一个用于操做 HTML 文档的 Python 库,初始化 BeatifulSoup 时,须要指定 HTML 文档字符串和具体的解析器。BeatifulSoup 有3类经常使用的数据类型,分别是 Tag、NavigableString、和 BeautifulSoup。查找 HTML元素有两种方式,分别是遍历文档树和搜索文档树,一般快速获取数据须要两者结合。最后是一个实践项目:用 Requests 和 BeatifulSoup 把廖雪峰的教程转换成 PDF 电子书。