.. BeautifulSoup文档 documentation master file, created by
delong wang on Fri Nov 29 13:49:30 2013.
You can adapt this file completely to your liking, but it should at least
contain the root `toctree` directive.
Beautiful Soup 4.4.0 文档
==========================
`Beautiful Soup 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. ...
#
# 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.
#
# ...
# The Dormouse's story Back to the homepage Back to the homepage 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. ... The Dormouse's story 标签",等等.Beautiful Soup提供了重现解析器初始化过程的方法.
.next_element 和 .previous_element
...................................
``.next_element`` 属性指向解析过程中下一个被解析的对象(字符串或tag),结果可能与 ``.next_sibling`` 相同,但通常是不一样的.
这是“爱丽丝”文档中最后一个标签,它的 ``.next_sibling`` 结果是一个字符串,因为当前的解析过程 [2]_ 因为当前的解析过程因为遇到了标签而中断了:
::
last_a_tag = soup.find("a", id="link3")
last_a_tag
# Tillie
last_a_tag.next_sibling
# '; and they lived at the bottom of a well.'
但这个标签的 ``.next_element`` 属性结果是在标签被解析之后的解析内容,不是标签后的句子部分,应该是字符串"Tillie":
::
last_a_tag.next_element
# u'Tillie'
这是因为在原始文档中,字符串“Tillie” 在分号前出现,解析器先进入标签,然后是字符串“Tillie”,然后关闭标签,然后是分号和剩余部分.分号与标签在同一层级,但是字符串“Tillie”会被先解析.
``.previous_element`` 属性刚好与 ``.next_element`` 相反,它指向当前被解析的对象的前一个解析对象:
::
last_a_tag.previous_element
# u' and\n'
last_a_tag.previous_element.next_element
# Tillie
.next_elements 和 .previous_elements
.....................................
通过 ``.next_elements`` 和 ``.previous_elements`` 的迭代器就可以向前或向后访问文档的解析内容,就好像文档正在被解析一样:
::
for element in last_a_tag.next_elements:
print(repr(element))
# u'Tillie'
# u';\nand they lived at the bottom of a well.'
# u'\n\n'
# ... 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.find_all(has_class_but_no_id)
# [ The Dormouse's story Once upon a time there were... ... 标签没有标签,因为标签还定义了"id",没有返回和,因为和中没有定义"class"属性.
通过一个方法来过滤一类标签属性的时候, 这个方法的参数是要被过滤的属性的值, 而不是这个标签.
下面的例子是找出 ``href`` 属性不符合指定正则的 ``a`` 标签.
::
def not_lacie(href):
return href and not re.compile("lacie").search(href)
soup.find_all(href=not_lacie)
# [Elsie,
# Tillie]
标签过滤方法可以使用复杂方法. 下面的例子可以过滤出前后都有文字的标签.
::
from bs4 import NavigableString
def surrounded_by_strings(tag):
return (isinstance(tag.next_element, NavigableString)
and isinstance(tag.previous_element, NavigableString))
for tag in soup.find_all(surrounded_by_strings):
print tag.name
# p
# a
# a
# a
# p
现在来了解一下搜索方法的细节
find_all()
-----------
find_all( `name`_ , `attrs`_ , `recursive`_ , `string`_ , `**kwargs`_ )
``find_all()`` 方法搜索当前tag的所有tag子节点,并判断是否符合过滤器的条件.这里有几个例子:
::
soup.find_all("title")
# [ The Dormouse's story 标签? 我们来仔细看一下 ``find_all()`` 的参数
name 参数
..........
``name`` 参数可以查找所有名字为 ``name`` 的tag,字符串对象会被自动忽略掉.
简单的用法如下:
::
soup.find_all("title")
# [ 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. 标签,是目标叶子节点的间接父辈节点,所以也可以被找到.包含class值为"title"的 标签不是不是目标叶子节点的父辈节点,所以通过 ``find_parents()`` 方法搜索不到.
``find_parent()`` 和 ``find_parents()`` 方法会让人联想到 `.parent`_ 和 `.parents`_ 属性.它们之间的联系非常紧密.搜索父辈节点的方法实际上就是对 ``.parents`` 属性的迭代搜索.
find_next_siblings() 合 find_next_sibling()
-------------------------------------------
find_next_siblings( `name`_ , `attrs`_ , `recursive`_ , `string`_ , `**kwargs`_ )
find_next_sibling( `name`_ , `attrs`_ , `recursive`_ , `string`_ , `**kwargs`_ )
这2个方法通过 `.next_siblings`_ 属性对当tag的所有后面解析 [5]_ 的兄弟tag节点进行迭代, ``find_next_siblings()`` 方法返回所有符合条件的后面的兄弟节点, ``find_next_sibling()`` 只返回符合条件的后面的第一个tag节点.
::
first_link = soup.a
first_link
# Elsie
first_link.find_next_siblings("a")
# [Lacie,
# Tillie]
first_story_paragraph = soup.find("p", "story")
first_story_paragraph.find_next_sibling("p")
# ... The Dormouse's story ... 标签也被显示出来,尽管它与我们开始查找位置的标签不属于同一部分.例子中,搜索的重点是要匹配过滤器的条件,并且在文档中出现的顺序而不是开始查找的元素的位置.
find_all_previous() 和 find_previous()
---------------------------------------
find_all_previous( `name`_ , `attrs`_ , `recursive`_ , `string`_ , `**kwargs`_ )
find_previous( `name`_ , `attrs`_ , `recursive`_ , `string`_ , `**kwargs`_ )
这2个方法通过 `.previous_elements`_ 属性对当前节点前面 [5]_ 的tag和字符串进行迭代, ``find_all_previous()`` 方法返回所有符合条件的节点, ``find_previous()`` 方法返回第一个符合条件的节点.
::
first_link = soup.a
first_link
# Elsie
first_link.find_all_previous("p")
# [ Once upon a time there were three little sisters; ... The Dormouse's story 标签包含了我们开始查找的标签.不要惊讶,这段代码的功能是查找所有出现在指定标签之前的 标签,因为这个 标签包含了开始的标签,所以 标签一定是在之前出现的.
CSS选择器
------------
Beautiful Soup支持大部分的CSS选择器 ` ... Hello Howdy, y'all Pip-pip, old fruit Bonjour mes amis Hello Howdy, y'all Pip-pip, old fruit I wish I was bold. I wish I was bold.Extremely bold
Attributes
............
一个tag可能有很多个属性. tag ```` 有一个 "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('No longer bold
``NavigableString`` 对象支持 `遍历文档树`_ 和 `搜索文档树`_ 中定义的大部分属性, 并非全部.尤其是,一个字符串不能包含其它内容(tag能够包含字符串或是其它tag),字符串不支持 ``.contents`` 或 ``.string`` 属性或 ``find()`` 方法.
如果想在Beautiful Soup之外使用 ``NavigableString`` 对象,需要调用 ``unicode()`` 方法,将该对象转换成普通的Unicode字符串,否则就算Beautiful Soup已方法已经执行结束,该对象的输出也会带有对象的引用地址.这样会浪费内存.
BeautifulSoup
----------------
``BeautifulSoup`` 对象表示的是一个文档的全部内容.大部分时候,可以把它当作 ``Tag`` 对象,它支持 `遍历文档树`_ 和 `搜索文档树`_ 中描述的大部分的方法.
因为 ``BeautifulSoup`` 对象并不是真正的HTML或XML的tag,所以它没有name和attribute属性.但有时查看它的 ``.name`` 属性是很方便的,所以 ``BeautifulSoup`` 对象包含了一个值为 "[document]" 的特殊属性 ``.name``
::
soup.name
# u'[document]'
注释及特殊字符串
-----------------
``Tag`` , ``NavigableString`` , ``BeautifulSoup`` 几乎覆盖了html和xml中的所有内容,但是还有一些特殊对象.容易让人担心的内容是文档的注释部分:
::
markup = ""
soup = BeautifulSoup(markup)
comment = soup.b.string
type(comment)
# Extremely bold
del tag['class']
del tag['id']
tag
# Extremely bold
修改 .string
-------------
给tag的 ``.string`` 属性赋值,就相当于用当前的内容替代了原来的内容:
::
markup = 'I linked to example.com'
soup = BeautifulSoup(markup)
tag = soup.a
tag.string = "New link text."
tag
# New link text.
注意: 如果当前的tag包含了其它tag,那么给它的 ``.string`` 属性赋值会覆盖掉原有的所有内容包括子tag
append()
----------
``Tag.append()`` 方法想tag中添加内容,就好像Python的列表的 ``.append()`` 方法:
::
soup = BeautifulSoup("Foo")
soup.a.append("Bar")
soup
# FooBar
soup.a.contents
# [u'Foo', u'Bar']
NavigableString() 和 .new_tag()
-----------------------------------------
如果想添加一段文本内容到文档中也没问题,可以调用Python的 ``append()`` 方法
或调用 ``NavigableString`` 的构造方法:
::
soup = BeautifulSoup("")
tag = soup.b
tag.append("Hello")
new_string = NavigableString(" there")
tag.append(new_string)
tag
# Hello there.
tag.contents
# [u'Hello', u' there']
如果想要创建一段注释,或 ``NavigableString`` 的任何子类, 只要调用 NavigableString 的构造方法:
::
from bs4 import Comment
new_comment = soup.new_string("Nice to see you.", Comment)
tag.append(new_comment)
tag
# Hello there
tag.contents
# [u'Hello', u' there', u'Nice to see you.']
# 这是Beautiful Soup 4.2.1 中新增的方法
创建一个tag最好的方法是调用工厂方法 ``BeautifulSoup.new_tag()`` :
::
soup = BeautifulSoup("")
original_tag = soup.b
new_tag = soup.new_tag("a", href="http://www.example.com")
original_tag.append(new_tag)
original_tag
#
new_tag.string = "Link text."
original_tag
# Link text.
第一个参数作为tag的name,是必填,其它参数选填
insert()
--------
``Tag.insert()`` 方法与 ``Tag.append()`` 方法类似,区别是不会把新元素添加到父节点 ``.contents`` 属性的最后,而是把元素插入到指定的位置.与Python列表总的 ``.insert()`` 方法的用法下同:
::
markup = 'I linked to example.com'
soup = BeautifulSoup(markup)
tag = soup.a
tag.insert(1, "but did not endorse ")
tag
# I linked to but did not endorse example.com
tag.contents
# [u'I linked to ', u'but did not endorse', example.com]
insert_before() 和 insert_after()
-----------------------------------
``insert_before()`` 方法在当前tag或文本节点前插入内容:
::
soup = BeautifulSoup("stop")
tag = soup.new_tag("i")
tag.string = "Don't"
soup.b.string.insert_before(tag)
soup.b
# Don'tstop
``insert_after()`` 方法在当前tag或文本节点后插入内容:
::
soup.b.i.insert_after(soup.new_string(" ever "))
soup.b
# Don't ever stop
soup.b.contents
# [Don't, u' ever ', u'stop']
clear()
--------
``Tag.clear()`` 方法移除当前tag的内容:
::
markup = 'I linked to example.com'
soup = BeautifulSoup(markup)
tag = soup.a
tag.clear()
tag
#
extract()
----------
``PageElement.extract()`` 方法将当前tag移除文档树,并作为方法结果返回:
::
markup = 'I linked to example.com'
soup = BeautifulSoup(markup)
a_tag = soup.a
i_tag = soup.i.extract()
a_tag
# I linked to
i_tag
# example.com
print(i_tag.parent)
None
这个方法实际上产生了2个文档树: 一个是用来解析原始文档的 ``BeautifulSoup`` 对象,另一个是被移除并且返回的tag.被移除并返回的tag可以继续调用 ``extract`` 方法:
::
my_string = i_tag.string.extract()
my_string
# u'example.com'
print(my_string.parent)
# None
i_tag
#
decompose()
------------
``Tag.decompose()`` 方法将当前节点移除文档树并完全销毁:
::
markup = 'I linked to example.com'
soup = BeautifulSoup(markup)
a_tag = soup.a
soup.i.decompose()
a_tag
# I linked to
replace_with()
---------------
``PageElement.replace_with()`` 方法移除文档树中的某段内容,并用新tag或文本节点替代它:
::
markup = 'I linked to example.com'
soup = BeautifulSoup(markup)
a_tag = soup.a
new_tag = soup.new_tag("b")
new_tag.string = "example.net"
a_tag.i.replace_with(new_tag)
a_tag
# I linked to example.net
``replace_with()`` 方法返回被替代的tag或文本节点,可以用来浏览或添加到文档树其它地方
wrap()
------
``PageElement.wrap()`` 方法可以对指定的tag元素进行包装 [8]_ ,并返回包装后的结果:
::
soup = BeautifulSoup("
Sacr\xe9 bleu!
''' soup = BeautifulSoup(markup) print(soup.prettify()) # # # # # ## Sacré bleu! #
# # 注意,输出文档中的标签的编码设置已经修改成了与输出编码一致的UTF-8. 如果不想用UTF-8编码输出,可以将编码方式传入 ``prettify()`` 方法: :: print(soup.prettify("latin-1")) # # # # ... 还可以调用 ``BeautifulSoup`` 对象或任意节点的 ``encode()`` 方法,就像Python的字符串调用 ``encode()`` 方法一样: :: soup.p.encode("latin-1") # 'Sacr\xe9 bleu!
' soup.p.encode("utf-8") # 'Sacr\xc3\xa9 bleu!
' 如果文档中包含当前编码不支持的字符,那么这些字符将呗转换成一系列XML特殊字符引用,下面例子中包含了Unicode编码字符SNOWMAN: :: markup = u"\N{SNOWMAN}" snowman_soup = BeautifulSoup(markup) tag = snowman_soup.b SNOWMAN字符在UTF-8编码中可以正常显示(看上去像是☃),但有些编码不支持SNOWMAN字符,比如ISO-Latin-1或ASCII,那么在这些编码中SNOWMAN字符会被转换成“☃”: :: print(tag.encode("utf-8")) # ☃ print tag.encode("latin-1") # ☃ print tag.encode("ascii") # ☃ Unicode, Dammit! (乱码, 靠!) ----------------------------- 译者备注: UnicodeDammit 是BS内置库, 主要用来猜测文档编码. `编码自动检测`_ 功能可以在Beautiful Soup以外使用,检测某段未知编码时,可以使用这个方法: :: from bs4 import UnicodeDammit dammit = UnicodeDammit("Sacr\xc3\xa9 bleu!") print(dammit.unicode_markup) # Sacré bleu! dammit.original_encoding # 'utf-8' 如果Python中安装了 ``chardet`` 或 ``cchardet`` 那么编码检测功能的准确率将大大提高. 输入的字符越多,检测结果越精确,如果事先猜测到一些可能编码, 那么可以将猜测的编码作为参数,这样将优先检测这些编码: :: dammit = UnicodeDammit("Sacr\xe9 bleu!", ["latin-1", "iso-8859-1"]) print(dammit.unicode_markup) # Sacré bleu! dammit.original_encoding # 'latin-1' `编码自动检测`_ 功能中有2项功能是Beautiful Soup库中用不到的 智能引号 ........... 使用Unicode时,Beautiful Soup还会智能的把引号 [10]_ 转换成HTML或XML中的特殊字符: :: markup = b"I just \x93love\x94 Microsoft Word\x92s smart quotes
" UnicodeDammit(markup, ["windows-1252"], smart_quotes_to="html").unicode_markup # u'I just “love” Microsoft Word’s smart quotes
' UnicodeDammit(markup, ["windows-1252"], smart_quotes_to="xml").unicode_markup # u'I just “love” Microsoft Word’s smart quotes
' 也可以把引号转换为ASCII码: :: UnicodeDammit(markup, ["windows-1252"], smart_quotes_to="ascii").unicode_markup # u'I just "love" Microsoft Word\'s smart quotes
' 很有用的功能,但是Beautiful Soup没有使用这种方式.默认情况下,Beautiful Soup把引号转换成Unicode: :: UnicodeDammit(markup, ["windows-1252"]).unicode_markup # u'I just \u201clove\u201d Microsoft Word\u2019s smart quotes
' 矛盾的编码 ........... 有时文档的大部分都是用UTF-8,但同时还包含了Windows-1252编码的字符,就像微软的智能引号 [10]_ 一样. 一些包含多个信息的来源网站容易出现这种情况. ``UnicodeDammit.detwingle()`` 方法可以把这类文档转换成纯UTF-8编码格式,看个简单的例子: :: snowmen = (u"\N{SNOWMAN}" * 3) quote = (u"\N{LEFT DOUBLE QUOTATION MARK}I like snowmen!\N{RIGHT DOUBLE QUOTATION MARK}") doc = snowmen.encode("utf8") + quote.encode("windows_1252") 这段文档很杂乱,snowmen是UTF-8编码,引号是Windows-1252编码,直接输出时不能同时显示snowmen和引号,因为它们编码不同: :: print(doc) # ☃☃☃�I like snowmen!� print(doc.decode("windows-1252")) # ☃☃☃“I like snowmen!” 如果对这段文档用UTF-8解码就会得到 ``UnicodeDecodeError`` 异常,如果用Windows-1252解码就回得到一堆乱码. 幸好, ``UnicodeDammit.detwingle()`` 方法会吧这段字符串转换成UTF-8编码,允许我们同时显示出文档中的snowmen和引号: :: new_doc = UnicodeDammit.detwingle(doc) print(new_doc.decode("utf8")) # ☃☃☃“I like snowmen!” ``UnicodeDammit.detwingle()`` 方法只能解码包含在UTF-8编码中的Windows-1252编码内容,但这解决了最常见的一类问题. 在创建 ``BeautifulSoup`` 或 ``UnicodeDammit`` 对象前一定要先对文档调用 ``UnicodeDammit.detwingle()`` 确保文档的编码方式正确.如果尝试去解析一段包含Windows-1252编码的UTF-8文档,就会得到一堆乱码,比如: ☃☃☃“I like snowmen!”. ``UnicodeDammit.detwingle()`` 方法在Beautiful Soup 4.1.0版本中新增 比较对象是否相同 ================= 两个 ``NavigableString`` 或 ``Tag`` 对象具有相同的HTML或XML结构时, Beautiful Soup就判断这两个对象相同. 这个例子中, 2个 标签在 BS 中是相同的, 尽管他们在文档树的不同位置, 但是具有相同的表象: "pizza" :: markup = "I want pizza and more pizza!
" soup = BeautifulSoup(markup, 'html.parser') first_b, second_b = soup.find_all('b') print first_b == second_b # True print first_b.previous_element == second_b.previous_element # False 如果想判断两个对象是否严格的指向同一个对象可以通过 ``is`` 来判断 :: print first_b is second_b # False 复制Beautiful Soup对象 ====================== ``copy.copy()`` 方法可以复制任意 ``Tag`` 或 ``NavigableString`` 对象 :: import copy p_copy = copy.copy(soup.p) print p_copy #I want pizza and more pizza!
复制后的对象跟与对象是相等的, 但指向不同的内存地址 :: print soup.p == p_copy # True print soup.p is p_copy # False 源对象和复制对象的区别是源对象在文档树中, 而复制后的对象是独立的还没有添加到文档树中. 复制后对象的效果跟调用了 ``extract()`` 方法相同. :: print p_copy.parent # None 这是因为相等的对象不能同时插入相同的位置 解析部分文档 ============ 如果仅仅因为想要查找文档中的标签而将整片文档进行解析,实在是浪费内存和时间.最快的方法是从一开始就把标签以外的东西都忽略掉. ``SoupStrainer`` 类可以定义文档的某段内容,这样搜索文档时就不必先解析整篇文档,只会解析在 ``SoupStrainer`` 中定义过的文档. 创建一个 ``SoupStrainer`` 对象并作为 ``parse_only`` 参数给 ``BeautifulSoup`` 的构造方法即可. SoupStrainer ------------- ``SoupStrainer`` 类接受与典型搜索方法相同的参数:`name`_ , `attrs`_ , `recursive`_ , `string`_ , `**kwargs`_ 。下面举例说明三种 ``SoupStrainer`` 对象: :: from bs4 import SoupStrainer only_a_tags = SoupStrainer("a") only_tags_with_id_link2 = SoupStrainer(id="link2") def is_short_string(string): return len(string) < 10 only_short_strings = SoupStrainer(string=is_short_string) 再拿“爱丽丝”文档来举例,来看看使用三种 ``SoupStrainer`` 对象做参数会有什么不同: :: html_doc = """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.
...
""" print(BeautifulSoup(html_doc, "html.parser", parse_only=only_a_tags).prettify()) # # Elsie # # # Lacie # # # Tillie # print(BeautifulSoup(html_doc, "html.parser", parse_only=only_tags_with_id_link2).prettify()) # # Lacie # print(BeautifulSoup(html_doc, "html.parser", parse_only=only_short_strings).prettify()) # Elsie # , # Lacie # and # Tillie # ... # 还可以将 ``SoupStrainer`` 作为参数传入 `搜索文档树`_ 中提到的方法.这可能不是个常用用法,所以还是提一下: :: soup = BeautifulSoup(html_doc) soup.find_all(only_short_strings) # [u'\n\n', u'\n\n', u'Elsie', u',\n', u'Lacie', u' and\n', u'Tillie', # u'\n\n', u'...', u'\n'] 常见问题 ======== 代码诊断 ---------- 如果想知道Beautiful Soup到底怎样处理一份文档,可以将文档传入 ``diagnose()`` 方法(Beautiful Soup 4.2.0中新增),Beautiful Soup会输出一份报告,说明不同的解析器会怎样处理这段文档,并标出当前的解析过程会使用哪种解析器: :: from bs4.diagnose import diagnose data = open("bad.html").read() diagnose(data) # Diagnostic running on Beautiful Soup 4.2.0 # Python version 2.7.3 (default, Aug 1 2012, 05:16:07) # I noticed that html5lib is not installed. Installing it may help. # Found lxml version 2.3.2.0 # # Trying to parse your data with html.parser # Here's what html.parser did with the document: # ... ``diagnose()`` 方法的输出结果可能帮助你找到问题的原因,如果不行,还可以把结果复制出来以便寻求他人的帮助 文档解析错误 ------------- 文档解析错误有两种.一种是崩溃,Beautiful Soup尝试解析一段文档结果却抛除了异常,通常是 ``HTMLParser.HTMLParseError`` .还有一种异常情况,是Beautiful Soup解析后的文档树看起来与原来的内容相差很多. 这些错误几乎都不是Beautiful Soup的原因,这不会是因为Beautiful Soup得代码写的太优秀,而是因为Beautiful Soup没有包含任何文档解析代码.异常产生自被依赖的解析器,如果解析器不能很好的解析出当前的文档,那么最好的办法是换一个解析器.更多细节查看 `安装解析器`_ 章节. 最常见的解析错误是 ``HTMLParser.HTMLParseError: malformed start tag`` 和 ``HTMLParser.HTMLParseError: bad end tag`` .这都是由Python内置的解析器引起的,解决方法是 `安装lxml或html5lib`_ 最常见的异常现象是当前文档找不到指定的Tag,而这个Tag光是用眼睛就足够发现的了. ``find_all()`` 方法返回 [] ,而 ``find()`` 方法返回 None .这是Python内置解析器的又一个问题: 解析器会跳过那些它不知道的tag.解决方法还是 `安装lxml或html5lib`_ 版本错误 ---------- * ``SyntaxError: Invalid syntax`` (异常位置在代码行: ``ROOT_TAG_NAME = u'[document]'`` ),因为Python2版本的代码没有经过迁移就在Python3中窒息感 * ``ImportError: No module named HTMLParser`` 因为在Python3中执行Python2版本的Beautiful Soup * ``ImportError: No module named html.parser`` 因为在Python2中执行Python3版本的Beautiful Soup * ``ImportError: No module named BeautifulSoup`` 因为在没有安装BeautifulSoup3库的Python环境下执行代码,或忘记了BeautifulSoup4的代码需要从 ``bs4`` 包中引入 * ``ImportError: No module named bs4`` 因为当前Python环境下还没有安装BeautifulSoup4 解析成XML ---------- 默认情况下,Beautiful Soup会将当前文档作为HTML格式解析,如果要解析XML文档,要在 ``BeautifulSoup`` 构造方法中加入第二个参数 "xml": :: soup = BeautifulSoup(markup, "xml") 当然,还需要 `安装lxml`_ 解析器的错误 ------------ * 如果同样的代码在不同环境下结果不同,可能是因为两个环境下使用不同的解析器造成的.例如这个环境中安装了lxml,而另一个环境中只有html5lib, `解析器之间的区别`_ 中说明了原因.修复方法是在 ``BeautifulSoup`` 的构造方法中中指定解析器 * 因为HTML标签是 `大小写敏感