Beautiful Soup 4.12.0 文档 ========================== .. py:module:: bs4 `Beautiful Soup `_ 是一个 可以从 HTML 或 XML 文件中提取数据的 Python 库。它能用你喜欢的解析器和习惯的方式实现 文档树的导航、查找、和修改。它会帮你节省数小时甚至数天的工作时间。 这篇文档介绍了 Beautiful Soup 4 中所有主要特性,并附带例子。文档会展示这个库的适合场景, 工作原理,怎样使用,如何达到预期效果,以及如何处理异常情况。 文档覆盖了 Beautful Soup 4.12.0 版本,文档中的例子使用 Python 3.8 版本编写。 你可能在寻找 `Beautiful Soup3 `_ 的文档,Beautiful Soup 3 目前已经停止开发,并且自 2020年12月31日以后就停止维护了。 如果想要了解 Beautiful Soup 3 和 Beautiful Soup 4 的不同,参考 `迁移到 BS4`_。 这篇文档已经被翻译成多种语言: * `这篇文档当然还有中文版 `_ , (`Github 地址 `_). * このページは日本語で利用できます(`外部リンク `_) * `이 문서는 한국어 번역도 가능합니다. `_ * `Este documento também está disponível em Português do Brasil. `_ * `Эта документация доступна на русском языке. `_ 寻求帮助 -------- 如果有关于 Beautiful Soup 4 的疑问,或遇到了问题,可以发送邮件到 `讨论组 `_。 如果问题中包含要解析的 HTML 代码,那么请在你的问题描述中附带这段HTML文档的 `代码诊断`_ [1]_。 如果报告文档中的错误,请指出具体文档的语言版本。 快速开始 ======== 下面的一段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.

...

""" 上面的 *爱丽丝* 文档经过 Beautiful Soup 的解析后,会得到一个 :py:class:`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. # # ... 这是你想要的吗?是的话,继续看下去。 安装 Beautiful Soup ====================== 如果你用的是新版的 Debain 或 Ubuntu,那么可以通过系统的软件包管理来安装: :kbd:`$ apt-get install python3-bs4` Beautiful Soup 4 通过 PyPi 发布,所以如果无法使用系统包管理安装,那么 也可以通过 ``easy_install`` 或 ``pip`` 来安装。包的名字是 ``beautifulsoup4``。 确保使用的是与 Python 版本对应的 ``pip`` 或 ``easy_install`` 版本 (他们的名字也可能是 ``pip3`` 和 ``easy_install`` )。 :kbd:`$ easy_install beautifulsoup4` :kbd:`$ pip install beautifulsoup4` (在 PyPi 中还有一个名字是 ``BeautifulSoup`` 的包,但那可能不是你想要的,那是 `Beautiful Soup3`_ 版本。因为很多项目还在使用BS3, 所以 ``BeautifulSoup`` 包依然有效。但是新项目中,应该安装 ``beautifulsoup4``。) 如果没有安装 ``easy_install`` 或 ``pip`` ,那也可以 `下载 BS4 的源码 `_ , 然后通过 ``setup.py`` 来安装。 :kbd:`$ Python setup.py install` 如果上述安装方法都行不通,根据 Beautiful Soup 的协议,可以将项目的代码打包在 你的项目中,这样无须安装即可使用。 Beautiful Soup 用 Python 3.10 版本开发,但也可以在当前的其它版本中运行。 安装解析器 -------------- Beautiful Soup 支持 Python 标准库中的 HTML 解析器,还支持一些第三方的解析器, 其中一个是 `lxml parser `_ 。根据安装方法的不同, 可以选择下列方法来安装 lxml: :kbd:`$ apt-get install Python-lxml` :kbd:`$ easy_install lxml` :kbd:`$ pip install lxml` 另一个可供选择的解析器是纯 Python 实现的 `html5lib `_ , html5lib 的解析方式与浏览器相同,根据安装方法的不同,可以选择下列方法来安装html5lib: :kbd:`$ apt-get install python-html5lib` :kbd:`$ easy_install html5lib` :kbd:`$ pip install html5lib` 下表描述了几种解析器的优缺点: +-------------------+-------------------------------------------+---------------------------+------------------------------------------+ | 解析器 | 使用方法 | 优势 | 劣势 | +===================+===========================================+===========================+==========================================+ || Python 标准库 || ``BeautifulSoup(markup, "html.parser")`` || - Python的内置标准库 || - 速度没有 lxml 快,容错没有 html5lib强 | || || || - 执行速度较快 || | || || || - 容错能力强 || | +-------------------+-------------------------------------------+---------------------------+------------------------------------------+ || lxml HTML 解析器 || ``BeautifulSoup(markup, "lxml")`` || - 速度快 || - 额外的 C 依赖 | || || || - 容错能力强 || | || || || || | +-------------------+-------------------------------------------+---------------------------+------------------------------------------+ || lxml XML 解析器 || ``BeautifulSoup(markup, ["lxml-xml"])`` || - 速度快 || - 额外的 C 依赖 | || || ``BeautifulSoup(markup, "xml")`` || - 唯一支持 XML 的解析器 || | +-------------------+-------------------------------------------+---------------------------+------------------------------------------+ || html5lib || ``BeautifulSoup(markup, "html5lib")`` || - 最好的容错性 || - 速度慢 | || || || - 以浏览器的方式解析文档 || - 额外的 Python 依赖 | || || || - 生成 HTML5 格式的文档 || | +-------------------+-------------------------------------------+---------------------------+------------------------------------------+ 如果可以,推荐使用 lxml 来获得更高的速度。 注意,如果一段文档格式不标准,那么在不同解析器生成的 Beautiful Soup 数可能不一样。 查看 `解析器之间的区别`_ 了解更多细节。 如何使用 ======== 解析文档是,将文档传入 :py:class:`BeautifulSoup` 的构造方法。也可以传入一段字符串 或一个文件句柄: :: from bs4 import BeautifulSoup with open("index.html") as fp: soup = BeautifulSoup(fp, 'html.parser') soup = BeautifulSoup("a web page", 'html.parser') 首先,文档被转换成 Unicode,并且 HTML 中的实体也都被转换成 Unicode 编码 :: print(BeautifulSoup("Sacré bleu!", "html.parser")) # Sacré bleu! 然后,Beautiful Soup 选择最合适的解析器来解析这段文档。如果指定了解析器那么 Beautiful Soup 会选择指定的解析器来解析文档。(参考 `解析成XML`_ )。 对象的种类 ========== Beautiful Soup 将复杂的 HTML 文档转换成一个复杂的由 Python 对象构成的树形结构,但处理对象 的过程只包含 4 种类型的对象: :py:class:`Tag`, :py:class:`NavigableString`, :py:class:`BeautifulSoup`, 和 :py:class:`Comment`。 :py:class:`Tag` ``Tag`` 对象与 XML 或 HTML 原生文档中的 tag 相同: :: soup = BeautifulSoup('Extremely bold', 'html.parser') tag = soup.b type(tag) # Tag有很多属性和方法,在 `遍历文档树`_ 和 `搜索文档树`_ 中有详细解释。 现在介绍一下 tag 中最重要的属性: name 和 attributes。 .. py:attribute:: name 每个 tag 都有一个名字: :: tag.name # u'b' 如果改变了 tag 的 name,那将影响所有通过当前 Beautiful Soup 对象生成的HTML文档: :: tag.name = "blockquote" tag #
Extremely bold
.. py:attribute:: attrs 一个 HTML 或 XML 的 tag 可能有很多属性。tag ```` 有 一个 "id" 的属性,值为 "boldest"。你可以想处理一个字段一样来处理 tag 的属性: :: tag = BeautifulSoup('bold', 'html.parser').b tag['id'] # 'boldest' 也可以直接"点"取属性,比如: ``.attrs`` : :: tag.attrs # {u'class': u'boldest'} tag 的属性可以被添加、删除或修改。再说一次,tag的属性操作方法与字典一样 :: tag['id'] = 'verybold' tag['another-attribute'] = 1 tag # del tag['id'] del tag['another-attribute'] tag # bold tag['id'] # KeyError: 'id' tag.get('id') # None .. _multivalue: 多值属性 ---------- HTML 4 定义了一系列可以包含多个值的属性。在 HTML5 中移除了一些,却增加更多。 最常见的多值的属性是 ``class`` (一个 tag 可以有多个 CSS class)。还有一些 属性 ``rel``、 ``rev``、 ``accept-charset``、 ``headers``、 ``accesskey``。 默认情况,Beautiful Soup 中将多值属性解析为一个列表: :: css_soup = BeautifulSoup('

', 'html.parser') css_soup.p['class'] # ['body'] css_soup = BeautifulSoup('

', 'html.parser') css_soup.p['class'] # ['body', 'strikeout'] If an attribute `looks` like it has more than one value, but it's not a multi-valued attribute as defined by any version of the HTML standard, Beautiful Soup will leave the attribute alone:: id_soup = BeautifulSoup('

', 'html.parser') id_soup.p['id'] # 'my id' 如果某个属性看起来好像有多个值,但在任何版本的 HTML 定义中都没有将其定义为多值属性, 那么 Beautiful Soup 会将这个属性作为单值返回 :: id_soup = BeautifulSoup('

', 'html.parser') id_soup.p['id'] # 'my id' 将 tag 转换成字符串时,多值属性会合并为一个值 :: rel_soup = BeautifulSoup('

Back to the homepage

', 'html.parser') rel_soup.a['rel'] # ['index', 'first'] rel_soup.a['rel'] = ['index', 'contents'] print(rel_soup.p) #

Back to the homepage

若想强制将所有属性当做多值进行解析,可以在 :py:class:`BeautifulSoup` 构造方法中设置 ``multi_valued_attributes=None`` 参数: :: no_list_soup = BeautifulSoup('

', 'html.parser', multi_valued_attributes=None) no_list_soup.p['class'] # 'body strikeout' 或者使用 ``get_attribute_list`` 方法来获取多值列表,不管是不是一个多值属性: :: id_soup.p.get_attribute_list('id') # ["my id"] 如果以 XML 方式解析文档,则没有多值属性: :: xml_soup = BeautifulSoup('

', 'xml') xml_soup.p['class'] # 'body strikeout' 但是,可以通过配置 ``multi_valued_attributes`` 参数来修改: :: class_is_multi= { '*' : 'class'} xml_soup = BeautifulSoup('

', 'xml', multi_valued_attributes=class_is_multi) xml_soup.p['class'] # ['body', 'strikeout'] 可能实际当中并不需要修改默认配置,默认采用的是 HTML 标准: :: from bs4.builder import builder_registry builder_registry.lookup('html').DEFAULT_CDATA_LIST_ATTRIBUTES .. py:class:: NavigableString 可遍历的字符串 ---------------- 字符串对应 tag 中的一段文本。Beautiful Soup 用 :py:class:`NavigableString` 类来包装 tag 中的字符串: :: soup = BeautifulSoup('Extremely bold', 'html.parser') tag = soup.b tag.string # 'Extremely bold' type(tag.string) # 一个 :py:class:`NavigableString` 对象与 Python 中的Unicode 字符串相同, 并且还支持包含在 `遍历文档树`_ 和 `搜索文档树`_ 中的一些特性。通过 ``str`` 方法可以直接将 :py:class:`NavigableString` 对象转换成 Unicode 字符串: :: unicode_string = str(tag.string) unicode_string # 'Extremely bold' type(unicode_string) # tag 中包含的字符串不能直接编辑,但是可以被替换成其它的字符串,用 :ref:`replace_with()` 方法: :: tag.string.replace_with("No longer bold") tag #
No longer bold
:py:class:`NavigableString` 对象支持 `遍历文档树`_ 和 `搜索文档树`_ 中定义的大部分属性, 并非全部。尤其是,一个字符串不能包含其它内容(tag 能够包含字符串或是其它 tag),字符串不支持 ``.contents`` 或 ``.string`` 属性或 ``find()`` 方法。 如果想在 Beautiful Soup 之外使用 :py:class:`NavigableString` 对象,需要调用 ``unicode()`` 方法,将该对象转换成普通的Unicode字符串,否则就算 Beautiful Soup 方法已经执行结束,该对象的输出 也会带有对象的引用地址。这样会浪费内存。 .. py:class:: BeautifulSoup ------------------------------- ``BeautifulSoup`` 对象表示的是一个文档的全部内容。大部分时候,可以把它当作 ``Tag`` 对象, 它支持 `遍历文档树`_ 和 `搜索文档树`_ 中描述的大部分的方法。 因为 ``BeautifulSoup`` 对象并不是真正的HTML或XML的tag,所以它没有name和attribute属性。 但有时查看它的 ``.name`` 属性是很方便的,所以 ``BeautifulSoup`` 对象包含了一个 值为 "[document]" 的特殊属性 ``.name`` :: soup.name # u'[document]' 注释及特殊字符串 ----------------- :py:class:`Tag`, :py:class:`NavigableString`, :py:class:`BeautifulSoup` 几乎覆盖了html和xml中的所有内容,但是还有一些特殊对象。容易让人担心的内容是文档的注释部分: :: markup = "" soup = BeautifulSoup(markup, 'html.parser') comment = soup.b.string type(comment) # :py:class:`Comment` 对象是一个特殊类型的 :py:class:`NavigableString` 对象: :: comment # u'Hey, buddy. Want to buy a used parser' 但是当它出现在 HTML 文档中时,:py:class:`Comment` 对象会使用特殊的格式输出: :: print(soup.b.prettify()) # # # 针对 HTML 文档 ^^^^^^^^^^^^^^^^^^ Beautiful Soup 定义了一些 :py:class:`NavigableString` 子类来处理特定的 HTML 标签。 通过忽略页面中表示程序指令的字符串,可以更容易挑出页面的 body 内容。 (这些类是在 Beautiful Soup 4.9.0 版本中添加的,html5lib 解析器不会使用它们) .. py:class:: Stylesheet 有一种 :py:class:`NavigableString` 子类表示嵌入的 CSS 脚本; 内容是 ``