Beautiful Soup 是一个可以从HTML或XML文件中提取数据的Python库.它能够通过你喜欢的转换器实现惯用的文档导航,查找,修改文档的方式.Beautiful Soup会帮你节省数小时甚至数天的工作时间.
这篇文档介绍了BeautifulSoup4中所有主要特性,并且有小例子.让我来向你展示它适合做什么,如何工作,怎样使用,如何达到你想要的效果,和处理异常情况.
文档中出现的例子在Python2.7和Python3.2中的执行结果相同
你可能在寻找 Beautiful Soup3 的文档,Beautiful Soup 3 目前已经停止开发,我们推荐在现在的项目中使用Beautiful Soup 4, 移植到BS4。
下面的一段HTML代码将作为例子被多次用到.这是 爱丽丝梦游仙境的 的一段内容(以后内容中简称为 爱丽丝 的文档):
html_doc = """The Dormouse's story The Dormouse's story
Once upon a time there were three little sisters; and their names were
Elsie,
Lacie and
Tillie;
and they lived at the bottom of a well....
"""
使用BeautifulSoup解析这段代码,能够得到一个 BeautifulSoup
的对象,并能按照标准的缩进格式的结构输出:
from bs4 import BeautifulSoup
soup = BeautifulSoup(html_doc, 'html.parser')
print(soup.prettify())
#
#
#
# The Dormouse's story
#
#
#
#
#
# The Dormouse's story
#
#
#
# Once upon a time there were three little sisters; and their names were
#
# Elsie
#
# ,
#
# Lacie
#
# and
#
# Tillie
#
# ; and they lived at the bottom of a well.
#
#
# ...
#
#
#
几个简单的浏览结构化数据的方法:
soup.title
#The Dormouse's story
soup.title.name
# u'title'
soup.title.string
# u'The Dormouse's story'
soup.title.parent.name
# u'head'
soup.p
#The Dormouse's story
soup.p['class']
# u'title'
soup.a
# Elsie
soup.find_all('a')
# [Elsie,
# Lacie,
# Tillie]
soup.find(id="link3")
# Tillie
从文档中找到所有标签的链接:
for link in soup.find_all('a'):
print(link.get('href'))
# http://example.com/elsie
# http://example.com/lacie
# http://example.com/tillie
从文档中获取所有文字内容:
print(soup.get_text())
# The Dormouse's story
#
# The Dormouse's story
#
# Once upon a time there were three little sisters; and their names were
# Elsie,
# Lacie and
# Tillie;
# and they lived at the bottom of a well.
#
# ...
这是你想要的吗?别着急,还有更好用的
如果你用的是新版的Debain或ubuntu,那么可以通过系统的软件包管理来安装:
$ apt-get install Python-bs4
Beautiful Soup 4 通过PyPi发布,所以如果你无法使用系统包管理安装,那么也可以通过 easy_install
或 pip
来安装.包的名字是 beautifulsoup4
,这个包兼容Python2和Python3.
$ easy_install beautifulsoup4
$ pip install beautifulsoup4
(在PyPi中还有一个名字是 BeautifulSoup
的包,但那可能不是你想要的,那是 Beautiful Soup3 的发布版本,因为很多项目还在使用BS3, 所以 BeautifulSoup
包依然有效.但是如果你在编写新项目,那么你应该安装的 beautifulsoup4
)
如果你没有安装 easy_install
或 pip
,那你也可以 下载BS4的源码 ,然后通过setup.py来安装.
$ Python setup.py install
如果上述安装方法都行不通,Beautiful Soup的发布协议允许你将BS4的代码打包在你的项目中,这样无须安装即可使用.
作者在Python2.7和Python3.2的版本下开发Beautiful Soup, 理论上Beautiful Soup应该在所有当前的Python版本中正常工作
Beautiful Soup支持Python标准库中的HTML解析器,还支持一些第三方的解析器,其中一个是 lxml .根据操作系统不同,可以选择下列方法来安装lxml:
$ apt-get install Python-lxml
$ easy_install lxml
$ pip install lxml
另一个可供选择的解析器是纯Python实现的 html5lib , html5lib的解析方式与浏览器相同,可以选择下列方法来安装html5lib:
$ apt-get install Python-html5lib
$ easy_install html5lib
$ pip install html5lib
下表列出了主要的解析器,以及它们的优缺点:
解析器 | 使用方法 | 优势 | 劣势 |
---|---|---|---|
Python标准库 | BeautifulSoup(markup,"html.parser") |
Python的内置标准库 执行速度适中 文档容错能力强 |
Python 2.7.3 or 3.2.2)前 的版本中文档容错能力差 |
lxml HTML 解析器 | BeautifulSoup(markup,"lxml") |
速度快 文档容错能力强 |
需要安装C语言库 |
lxml XML 解析器 |
|
速度快 唯一支持XML的解析器 |
需要安装C语言库 |
html5lib | BeautifulSoup(markup,"html5lib") |
最好的容错性 以浏览器的方式解析文档 生成HTML5格式的文档 |
速度慢 不依赖外部扩展 |
推荐使用lxml作为解析器,因为效率更高. 在Python2.7.3之前的版本和Python3中3.2.2之前的版本,必须安装lxml或html5lib, 因为那些Python版本的标准库中内置的HTML解析方法不够稳定.
提示: 如果一段HTML或XML文档格式不正确的话,那么在不同的解析器中返回的结果可能是不一样的,查看 解析器之间的区别 了解更多细节
将一段文档传入BeautifulSoup 的构造方法,就能得到一个文档的对象, 可以传入一段字符串或一个文件句柄.
from bs4 import BeautifulSoup
soup = BeautifulSoup(open("index.html"))
soup = BeautifulSoup("data")
首先,文档被转换成Unicode,并且HTML的实例都被转换成Unicode编码
BeautifulSoup("Sacré bleu!")
Sacré bleu!
然后,Beautiful Soup选择最合适的解析器来解析这段文档,如果手动指定解析器那么Beautiful Soup会选择指定的解析器来解析文档.(参考 解析成XML ).
Beautiful Soup将复杂HTML文档转换成一个复杂的树形结构,每个节点都是Python对象,所有对象可以归纳为4种: Tag
, NavigableString
, BeautifulSoup
, Comment
.
Tag
对象与XML或HTML原生文档中的tag相同:
soup = BeautifulSoup('Extremely bold')
tag = soup.b
type(tag)
#
Tag有很多方法和属性,在 遍历文档树 和 搜索文档树 中有详细解释.现在介绍一下tag中最重要的属性: name和attributes
每个tag都有自己的名字,通过 .name
来获取:
tag.name
# u'b'
如果改变了tag的name,那将影响所有通过当前Beautiful Soup对象生成的HTML文档:
tag.name = "blockquote"
tag
#Extremely bold
一个tag可能有很多个属性. tag class="boldest">
有一个 “class” 的属性,值为 “boldest” . tag的属性的操作方法与字典相同:
tag['class']
# u'boldest'
也可以直接”点”取属性, 比如: .attrs
:
tag.attrs
# {u'class': u'boldest'}
tag的属性可以被添加,删除或修改. 再说一次, tag的属性操作方法与字典一样
tag['class'] = 'verybold'
tag['id'] = 1
tag
#Extremely bold
del tag['class']
del tag['id']
tag
#Extremely bold
tag['class']
# KeyError: 'class'
print(tag.get('class'))
# None
HTML 4定义了一系列可以包含多个值的属性.在HTML5中移除了一些,却增加更多.最常见的多值的属性是 class (一个tag可以有多个CSS的class). 还有一些属性 rel
, rev
, accept-charset
, headers
, accesskey
. 在Beautiful Soup中多值属性的返回类型是list:
css_soup = BeautifulSoup('')
css_soup.p['class']
# ["body", "strikeout"]
css_soup = BeautifulSoup('')
css_soup.p['class']
# ["body"]
如果某个属性看起来好像有多个值,但在任何版本的HTML定义中都没有被定义为多值属性,那么Beautiful Soup会将这个属性作为字符串返回
id_soup = BeautifulSoup('')
id_soup.p['id']
# 'my id'
将tag转换成字符串时,多值属性会合并为一个值
rel_soup = BeautifulSoup('Back to the homepage
')
rel_soup.a['rel']
# ['index']
rel_soup.a['rel'] = ['index', 'contents']
print(rel_soup.p)
#Back to the homepage
如果转换的文档是XML格式,那么tag中不包含多值属性
xml_soup = BeautifulSoup('', 'xml')
xml_soup.p['class']
# u'body strikeout'
BeautifulSoup
对象表示的是一个文档的全部内容.大部分时候,可以把它当作 Tag
对象,它支持 遍历文档树 和 搜索文档树 中描述的大部分的方法.
因为 BeautifulSoup
对象并不是真正的HTML或XML的tag,所以它没有name和attribute属性.但有时查看它的 .name
属性是很方便的,所以 BeautifulSoup
对象包含了一个值为 “[document]” 的特殊属性 .name
soup.name
# u'[document]'
还拿”爱丽丝梦游仙境”的文档来做例子:
html_doc = """The Dormouse's story
The Dormouse's story
Once upon a time there were three little sisters; and their names were
Elsie,
Lacie and
Tillie;
and they lived at the bottom of a well....
"""
from bs4 import BeautifulSoup
soup = BeautifulSoup(html_doc, 'html.parser')
通过这段例子来演示怎样从文档的一段内容找到另一段内容
一个Tag可能包含多个字符串或其它的Tag,这些都是这个Tag的子节点.Beautiful Soup提供了许多操作和遍历子节点的属性.
注意: Beautiful Soup中字符串节点不支持这些属性,因为字符串没有子节点
操作文档树最简单的方法就是告诉它你想获取的tag的name.如果想获取
标签,只要用soup.head
:
soup.head
#The Dormouse's story
soup.title
#The Dormouse's story
这是个获取tag的小窍门,可以在文档树的tag中多次调用这个方法.下面的代码可以获取
标签中的第一个标签:soup.body.b
# The Dormouse's story
通过点取属性的方式只能获得当前名字的第一个tag:
soup.a
# Elsie
如果想要得到所有的标签,或是通过名字得到比一个tag更多的内容的时候,就需要用到 Searching the tree 中描述的方法,比如: find_all()
soup.find_all('a')
# [Elsie,
# Lacie,
# Tillie]
tag的 .contents
属性可以将tag的子节点以列表的方式输出:
head_tag = soup.head
head_tag
#The Dormouse's story
head_tag.contents
[<title>The Dormouse's story]
title_tag = head_tag.contents[0]
title_tag
#The Dormouse's story
title_tag.contents
# [u'The Dormouse's story']
BeautifulSoup
对象本身一定会包含子节点,也就是说标签也是 BeautifulSoup
对象的子节点:
len(soup.contents)
# 1
soup.contents[0].name
# u'html'
字符串没有 .contents
属性,因为字符串没有子节点:
text = title_tag.contents[0]
text.contents
# AttributeError: 'NavigableString' object has no attribute 'contents'
通过tag的 .children
生成器,可以对tag的子节点进行循环:
for child in title_tag.children:
print(child)
# The Dormouse's story
.contents
和 .children
属性仅包含tag的直接子节点.例如,
head_tag.contents
# [The Dormouse's story ]
但是
.descendants
属性可以对所有tag的子孙节点进行递归循环 [5] :
for child in head_tag.descendants:
print(child)
#The Dormouse's story
# The Dormouse's story
上面的例子中,
标签只有一个子节点,但是有2个子孙节点:节点和的子节点,BeautifulSoup
有一个直接子节点(节点),却有很多子孙节点:
len(list(soup.children))
# 1
len(list(soup.descendants))
# 25
如果tag只有一个 NavigableString
类型子节点,那么这个tag可以使用 .string
得到子节点:
title_tag.string
# u'The Dormouse's story'
如果一个tag仅有一个子节点,那么这个tag也可以使用 .string
方法,输出结果与当前唯一子节点的 .string
结果相同:
head_tag.contents
# [The Dormouse's story ]
head_tag.string
# u'The Dormouse's story'
如果tag包含了多个子节点,tag就无法确定 .string
方法应该调用哪个子节点的内容, .string
的输出结果是 None
:
print(soup.html.string)
# None
继续分析文档树,每个tag或字符串都有父节点:被包含在某个tag中
通过 .parent
属性来获取某个元素的父节点.在例子“爱丽丝”的文档中,
title_tag = soup.title
title_tag
#The Dormouse's story
title_tag.parent
#The Dormouse's story
文档title的字符串也有父节点:
title_tag.string.parent
#The Dormouse's story
文档的顶层节点比如的父节点是 BeautifulSoup
对象:
html_tag = soup.html
type(html_tag.parent)
#
BeautifulSoup
对象的 .parent
是None:
print(soup.parent)
# None
通过元素的 .parents
属性可以递归得到元素的所有父辈节点,下面的例子使用了 .parents
方法遍历了标签到根节点的所有节点.
link = soup.a
link
# Elsie
for parent in link.parents:
if parent is None:
print(parent)
else:
print(parent.name)
# p
# body
# html
# [document]
# None
看一段简单的例子:
sibling_soup = BeautifulSoup("text1text2 ")
print(sibling_soup.prettify())
#
#
#
#
# text1
#
#
# text2
#
#
#
#
因为标签和
在文档树中,使用 .next_sibling
和 .previous_sibling
属性来查询兄弟节点:
sibling_soup.b.next_sibling
#text2
sibling_soup.c.previous_sibling
# text1
标签有 .next_sibling
属性,但是没有 .previous_sibling
属性,因为标签在同级节点中是第一个.同理,.previous_sibling
属性,却没有 .next_sibling
属性:
print(sibling_soup.b.previous_sibling)
# None
print(sibling_soup.c.next_sibling)
# None
例子中的字符串“text1”和“text2”不是兄弟节点,因为它们的父节点不同:
sibling_soup.b.string
# u'text1'
print(sibling_soup.b.string.next_sibling)
# None
实际文档中的tag的 .next_sibling
和 .previous_sibling
属性通常是字符串或空白. 看看“爱丽丝”文档:
<a href="http://example.com/elsie" class="sister" id="link1">Elsiea>
<a href="http://example.com/lacie" class="sister" id="link2">Laciea>
<a href="http://example.com/tillie" class="sister" id="link3">Tilliea>
如果以为第一个标签的 .next_sibling
结果是第二个标签,那就错了,真实结果是第一个标签和第二个标签之间的顿号和换行符:
link = soup.a
link
# Elsie
link.next_sibling
# u',\n'
link.next_sibling.next_sibling
# Lacie
通过 .next_siblings
和 .previous_siblings
属性可以对当前节点的兄弟节点迭代输出:
for sibling in soup.a.next_siblings:
print(repr(sibling))
# u',\n'
# Lacie
# u' and\n'
# Tillie
# u'; and they lived at the bottom of a well.'
# None
for sibling in soup.find(id="link3").previous_siblings:
print(repr(sibling))
# ' and\n'
# Lacie
# u',\n'
# Elsie
# u'Once upon a time there were three little sisters; and their names were\n'
# None
介绍 find_all()
方法前,先介绍一下过滤器的类型 [3] ,这些过滤器贯穿整个搜索的API.过滤器可以被用在tag的name中,节点的属性中,字符串中或他们的混合中.
最简单的过滤器是字符串.在搜索方法中传入一个字符串参数,Beautiful Soup会查找与字符串完整匹配的内容,下面的例子用于查找文档中所有的标签:
soup.find_all('b')
# [The Dormouse's story]
如果传入字节码参数,Beautiful Soup会当作UTF-8编码,可以传入一段Unicode 编码来避免Beautiful Soup解析编码出错
如果传入正则表达式作为参数,Beautiful Soup会通过正则表达式的 match()
来匹配内容.下面例子中找出所有以b开头的标签,这表示
import re
for tag in soup.find_all(re.compile("^b")):
print(tag.name)
# body
# b
下面代码找出所有名字中包含”t”的标签:
for tag in soup.find_all(re.compile("t")):
print(tag.name)
# html
# title
如果传入列表参数,Beautiful Soup会将与列表中任一元素匹配的内容返回.下面代码找到文档中所有标签和标签:
soup.find_all(["a", "b"])
# [The Dormouse's story,
# Elsie,
# Lacie,
# Tillie]
find_all( name , attrs , recursive , string , **kwargs )
find_all()
方法搜索当前tag的所有tag子节点,并判断是否符合过滤器的条件.这里有几个例子:
soup.find_all("title")
# [The Dormouse's story ]
soup.find_all("p", "title")
# [The Dormouse's story
]
soup.find_all("a")
# [Elsie,
# Lacie,
# Tillie]
soup.find_all(id="link2")
# [Lacie]
import re
soup.find(string=re.compile("sisters"))
# u'Once upon a time there were three little sisters; and their names were\n'
有几个方法很相似,还有几个方法是新的,参数中的 string
和 id
是什么含义? 为什么 find_all("p", "title")
返回的是CSS Class为”title”的
标签? 我们来仔细看一下 find_all()
的参数
name
参数可以查找所有名字为 name
的tag,字符串对象会被自动忽略掉.
简单的用法如下:
soup.find_all("title")
# [The Dormouse's story ]
重申: 搜索 name
参数的值可以使任一类型的 过滤器 ,字符窜,正则表达式,列表,方法或是 True
.
如果一个指定名字的参数不是搜索内置的参数名,搜索时会把该参数当作指定名字tag的属性来搜索,如果包含一个名字为 id
的参数,Beautiful Soup会搜索每个tag的”id”属性.
soup.find_all(id='link2')
# [Lacie]
如果传入 href
参数,Beautiful Soup会搜索每个tag的”href”属性:
soup.find_all(href=re.compile("elsie"))
# [Elsie]
搜索指定名字的属性时可以使用的参数值包括 字符串 , 正则表达式 , 列表, True .
下面的例子在文档树中查找所有包含 id
属性的tag,无论 id
的值是什么:
soup.find_all(id=True)
# [Elsie,
# Lacie,
# Tillie]
使用多个指定名字的参数可以同时过滤tag的多个属性:
soup.find_all(href=re.compile("elsie"), id='link1')
# [three]
有些tag属性在搜索不能使用,比如HTML5中的 data-* 属性:
data_soup = BeautifulSoup('foo!')
data_soup.find_all(data-foo="value")
# SyntaxError: keyword can't be an expression
但是可以通过 find_all()
方法的 attrs
参数定义一个字典参数来搜索包含特殊属性的tag:
data_soup.find_all(attrs={"data-foo": "value"})
# [foo!]
按照CSS类名搜索tag的功能非常实用,但标识CSS类名的关键字 class
在Python中是保留字,使用 class
做参数会导致语法错误.从Beautiful Soup的4.1.1版本开始,可以通过 class_
参数搜索有指定CSS类名的tag:
soup.find_all("a", class_="sister")
# [Elsie,
# Lacie,
# Tillie]
class_
参数同样接受不同类型的 过滤器
,字符串,正则表达式,方法或 True
:
soup.find_all(class_=re.compile("itl"))
# [The Dormouse's story
]
def has_six_characters(css_class):
return css_class is not None and len(css_class) == 6
soup.find_all(class_=has_six_characters)
# [Elsie,
# Lacie,
# Tillie]
tag的 class
属性是 多值属性 .按照CSS类名搜索tag时,可以分别搜索tag中的每个CSS类名:
css_soup = BeautifulSoup('')
css_soup.find_all("p", class_="strikeout")
# []
css_soup.find_all("p", class_="body")
# []
搜索 class
属性时也可以通过CSS值完全匹配:
css_soup.find_all("p", class_="body strikeout")
# []
完全匹配 class
的值时,如果CSS类名的顺序与实际不符,将搜索不到结果:
soup.find_all("a", attrs={"class": "sister"})
# [Elsie,
# Lacie,
# Tillie]
find_all()
一样调用tagfind_all()
几乎是Beautiful Soup中最常用的搜索方法,所以我们定义了它的简写方法. BeautifulSoup
对象和 tag
对象可以被当作一个方法来使用,这个方法的执行结果与调用这个对象的 find_all()
方法相同,下面两行代码是等价的:
soup.find_all("a")
soup("a")
这两行代码也是等价的:
soup.title.find_all(string=True)
soup.title(string=True)
Beautiful Soup支持大部分的CSS选择器 http://www.w3.org/TR/CSS2/selector.html [6] , 在 Tag
或 BeautifulSoup
对象的 .select()
方法中传入字符串参数, 即可使用CSS选择器的语法找到tag:
soup.select("title")
# [The Dormouse's story ]
soup.select("p:nth-of-type(3)")
# [...
]
通过tag标签逐层查找:
soup.select("body a")
# [Elsie,
# Lacie,
# Tillie]
soup.select("html head title")
# [The Dormouse's story ]
找到某个tag标签下的直接子标签 [6] :
soup.select("head > title")
# [The Dormouse's story ]
soup.select("p > a")
# [Elsie,
# Lacie,
# Tillie]
soup.select("p > a:nth-of-type(2)")
# [Lacie]
soup.select("p > #link1")
# [Elsie]
soup.select("body > a")
# []
找到兄弟节点标签:
soup.select("#link1 ~ .sister")
# [Lacie,
# Tillie]
soup.select("#link1 + .sister")
# [Lacie]
通过CSS的类名查找:
soup.select(".sister")
# [Elsie,
# Lacie,
# Tillie]
soup.select("[class~=sister]")
# [Elsie,
# Lacie,
# Tillie]
通过tag的id查找:
soup.select("#link1")
# [Elsie]
soup.select("a#link2")
# [Lacie]
同时用多种CSS选择器查询元素:
soup.select("#link1,#link2")
# [Elsie,
# Lacie]
通过是否存在某个属性来查找:
soup.select('a[href]')
# [Elsie,
# Lacie,
# Tillie]
通过属性的值来查找:
soup.select('a[href="http://example.com/elsie"]')
# [Elsie]
soup.select('a[href^="http://example.com/"]')
# [Elsie,
# Lacie,
# Tillie]
soup.select('a[href$="tillie"]')
# [Tillie]
soup.select('a[href*=".com/el"]')
# [Elsie]
通过语言设置来查找:
multilingual_markup = """
Hello
Howdy, y'all
Pip-pip, old fruit
Bonjour mes amis
"""
multilingual_soup = BeautifulSoup(multilingual_markup)
multilingual_soup.select('p[lang|=en]')
# [Hello
,
#Howdy, y'all
,
#Pip-pip, old fruit
]
返回查找到的元素的第一个
soup.select_one(".sister")
# Elsie
对于熟悉CSS选择器语法的人来说这是个非常方便的方法.Beautiful Soup也支持CSS选择器API, 如果你仅仅需要CSS选择器的功能,那么直接使用 lxml
也可以, 而且速度更快,支持更多的CSS选择器语法,但Beautiful Soup整合了CSS选择器的语法和自身方便使用API.
prettify()
方法将Beautiful Soup的文档树格式化后以Unicode编码输出,每个XML/HTML标签都独占一行
markup = 'I linked to example.com'
soup = BeautifulSoup(markup)
soup.prettify()
# '\n \n \n \n \n...'
print(soup.prettify())
#
#
#
#
#
# I linked to
#
# example.com
#
#
#
#
BeautifulSoup
对象和它的tag节点都可以调用 prettify()
方法:
print(soup.a.prettify())
#
# I linked to
#
# example.com
#
#
如果只想得到结果字符串,不重视格式,那么可以对一个 BeautifulSoup
对象或 Tag
对象使用Python的 unicode()
或 str()
方法:
str(soup)
# 'I linked to example.com'
unicode(soup.a)
# u'I linked to example.com'
str()
方法返回UTF-8编码的字符串,可以指定 编码 的设置.
还可以调用 encode()
方法获得字节码或调用 decode()
方法获得Unicode.
Beautiful Soup输出是会将HTML中的特殊字符转换成Unicode,比如“&lquot;”:
soup = BeautifulSoup("“Dammit!” he said.")
unicode(soup)
# u'\u201cDammit!\u201d he said.'
如果将文档转换成字符串,Unicode编码会被编码成UTF-8.这样就无法正确显示HTML特殊字符了:
str(soup)
# '\xe2\x80\x9cDammit!\xe2\x80\x9d he said.'
如果只想得到tag中包含的文本内容,那么可以调用 get_text()
方法,这个方法获取到tag中包含的所有文版内容包括子孙tag中的内容,并将结果作为Unicode字符串返回:
markup = '\nI linked to example.com\n'
soup = BeautifulSoup(markup)
soup.get_text()
u'\nI linked to example.com\n'
soup.i.get_text()
u'example.com'
可以通过参数指定tag的文本内容的分隔符:
# soup.get_text("|")
u'\nI linked to |example.com|\n'
还可以去除获得文本内容的前后空白:
# soup.get_text("|", strip=True)
u'I linked to|example.com'
或者使用 .stripped_strings 生成器,获得文本列表后手动处理列表:
[text for text in soup.stripped_strings]
# [u'I linked to', u'example.com']
如果仅是想要解析HTML文档,只要用文档创建 BeautifulSoup
对象就可以了.Beautiful Soup会自动选择一个解析器来解析文档.但是还可以通过参数指定使用那种解析器来解析当前文档.
BeautifulSoup
第一个参数应该是要被解析的文档字符串或是文件句柄,第二个参数用来标识怎样解析文档.如果第二个参数为空,那么Beautiful Soup根据当前系统安装的库自动选择解析器,解析器的优先数序: lxml, html5lib, Python标准库.在下面两种条件下解析器优先顺序会变化:
要解析的文档是什么类型: 目前支持, “html”, “xml”, 和 “html5”
指定使用哪种解析器: 目前支持, “lxml”, “html5lib”, 和 “html.parser”
安装解析器 章节介绍了可以使用哪种解析器,以及如何安装.
如果指定的解析器没有安装,Beautiful Soup会自动选择其它方案.目前只有 lxml 解析器支持XML文档的解析,在没有安装lxml库的情况下,创建 beautifulsoup
对象时无论是否指定使用lxml,都无法得到解析后的对象
Beautiful Soup为不同的解析器提供了相同的接口,但解析器本身时有区别的.同一篇文档被不同的解析器解析后可能会生成不同结构的树型文档.区别最大的是HTML解析器和XML解析器,看下面片段被解析成HTML结构:
BeautifulSoup("")
#
因为空标签不符合HTML标准,所以解析器把它解析成
同样的文档使用XML解析如下(解析XML需要安装lxml库).注意,空标签依然被保留,并且文档前添加了XML头,而不是被包含在标签内:
BeautifulSoup("", "xml")
#
#
HTML解析器之间也有区别,如果被解析的HTML文档是标准格式,那么解析器之间没有任何差别,只是解析速度不同,结果都会返回正确的文档树.
但是如果被解析文档不是标准格式,那么不同的解析器返回结果可能不同.下面例子中,使用lxml解析错误格式的文档,结果
标签被直接忽略掉了:BeautifulSoup("", "lxml")
#
使用html5lib库解析相同文档会得到不同的结果:
BeautifulSoup("", "html5lib")
#
html5lib库没有忽略掉
标签,而是自动补全了标签,还给文档树添加了标签.使用pyhton内置库解析结果如下:
BeautifulSoup("", "html.parser")
#
与lxml [7] 库类似的,Python内置库忽略掉了
标签,与html5lib库不同的是标准库没有尝试创建符合标准的文档格式或将文档片段包含在标签内,与lxml不同的是标准库甚至连标签都没有尝试去添加. ”是错误格式,所以以上解析方式都能算作”正确”,html5lib库使用的是HTML5的部分标准,所以最接近”正确”.不过所有解析器的结构都能够被认为是”正常”的.不同的解析器可能影响代码执行结果,如果在分发给别人的代码中使用了 BeautifulSoup
,那么最好注明使用了哪种解析器,以减少不必要的麻烦.
猜你可能喜欢