Merge ~phoenixsite/beautifulsoup:master into beautifulsoup:master

Proposed by Carlos Romero
Status: Merged
Merged at revision: 59f273d402dc5c5ce48f78d043b701ab0b1f7764
Proposed branch: ~phoenixsite/beautifulsoup:master
Merge into: beautifulsoup:master
Diff against target: 3872 lines (+1586/-723)
2 files modified
doc.zh/source/conf.py (+1/-1)
doc.zh/source/index.rst (+1585/-722)
Reviewer Review Type Date Requested Status
Leonard Richardson Approve
Review via email: mp+458648@code.launchpad.net

Commit message

Updated the Chinese translation to version 4.12.0 from DeronW (https://groups.google.com/g/beautifulsoup/c/2U4r9sxFw7w).

Description of the change

I included the changes from DeronW's Github repo to my BS4 Launchpad repo to easily merge the changes to the master branch.

To post a comment you must log in.
Revision history for this message
Carlos Romero (phoenixsite) wrote :

Sorry, I didn't included the link to the Spanish translation.

Revision history for this message
Leonard Richardson (leonardr) :
review: Approve
Revision history for this message
Leonard Richardson (leonardr) wrote :

Thank you for taking care of this. I've restored the link to the Spanish translation.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/doc.zh/source/conf.py b/doc.zh/source/conf.py
2index 102c3cf..1014dad 100644
3--- a/doc.zh/source/conf.py
4+++ b/doc.zh/source/conf.py
5@@ -50,7 +50,7 @@ copyright = u'2012, Leonard Richardson'
6 # The short X.Y version.
7 version = '4'
8 # The full version, including alpha/beta/rc tags.
9-release = '4.2.0'
10+release = '4.12.0'
11
12 # The language for content autogenerated by Sphinx. Refer to documentation
13 # for a list of supported languages.
14diff --git a/doc.zh/source/index.rst b/doc.zh/source/index.rst
15index 05b9cfc..4e099cd 100644
16--- a/doc.zh/source/index.rst
17+++ b/doc.zh/source/index.rst
18@@ -1,38 +1,47 @@
19-.. BeautifulSoup文档 documentation master file, created by
20- Deron Wang on Fri Nov 29 13:49:30 2013.
21- You can adapt this file completely to your liking, but it should at least
22- contain the root `toctree` directive.
23
24-Beautiful Soup 4.4.0 文档
25+Beautiful Soup 4.12.0 文档
26 ==========================
27
28-`Beautiful Soup <http://www.crummy.com/software/BeautifulSoup/>`_ 是一个可以从HTML或XML文件中提取数据的Python库.它能够通过你喜欢的转换器实现惯用的文档导航,查找,修改文档的方式.Beautiful Soup会帮你节省数小时甚至数天的工作时间.
29+.. py:module:: bs4
30
31-这篇文档介绍了BeautifulSoup4中所有主要特性,并且有小例子.让我来向你展示它适合做什么,如何工作,怎样使用,如何达到你想要的效果,和处理异常情况.
32+`Beautiful Soup <http://www.crummy.com/software/BeautifulSoup/>`_ 是一个
33+可以从 HTML 或 XML 文件中提取数据的 Python 库。它能用你喜欢的解析器和习惯的方式实现
34+文档树的导航、查找、和修改。它会帮你节省数小时甚至数天的工作时间。
35
36-文档中出现的例子在Python2.7和Python3.2中的执行结果相同
37+这篇文档介绍了 Beautiful Soup 4 中所有主要特性,并附带例子。文档会展示这个库的适合场景,
38+工作原理,怎样使用,如何达到预期效果,以及如何处理异常情况。
39
40-你可能在寻找 `Beautiful Soup3 <http://www.crummy.com/software/BeautifulSoup/bs3/documentation.html>`_ 的文档,Beautiful Soup 3 目前已经停止开发,我们推荐在现在的项目中使用Beautiful Soup 4, `移植到BS4 <http://www.baidu.com>`_
41+文档覆盖了 Beautful Soup 4.12.0 版本,文档中的例子使用 Python 3.8 版本编写。
42
43-这篇帮助文档已经被翻译成了其它语言:
44+你可能在寻找 `Beautiful Soup3 <http://www.crummy.com/software/BeautifulSoup/bs3/documentation.html>`_
45+的文档,Beautiful Soup 3 目前已经停止开发,并且自 2020年12月31日以后就停止维护了。
46+如果想要了解 Beautiful Soup 3 和 Beautiful Soup 4 的不同,参考 `迁移到 BS4`_。
47
48-* `这篇文档当然还有中文版. <https://www.crummy.com/software/BeautifulSoup/bs4/doc.zh/>`_
49+这篇文档已经被翻译成多种语言:
50+
51+* `这篇文档当然还有中文版 <https://www.crummy.com/software/BeautifulSoup/bs4/doc.zh/>`_ ,
52+ (`Github 地址 <https://github.com/DeronW/beautifulsoup>`_).
53 * このページは日本語で利用できます(`外部リンク <http://kondou.com/BS4/>`_)
54 * `이 문서는 한국어 번역도 가능합니다. <https://www.crummy.com/software/BeautifulSoup/bs4/doc.ko/>`_
55-* `Este documento também está disponível em Português do Brasil. <https://www.crummy.com/software/BeautifulSoup/bs4/doc.ptbr/>`_
56-* `Este documento también está disponible en una traducción al español. <https://www.crummy.com/software/BeautifulSoup/bs4/doc.es/>`_
57-* `Эта документация доступна на русском языке. <https://www.crummy.com/software/BeautifulSoup/bs4/doc.ru/>`_
58-
59+* `Este documento também está disponível em Português do Brasil.
60+ <https://www.crummy.com/software/BeautifulSoup/bs4/doc.ptbr>`_
61+* `Эта документация доступна на русском языке.
62+ <https://www.crummy.com/software/BeautifulSoup/bs4/doc.ru/>`_
63
64 寻求帮助
65 --------
66
67-如果你有关于BeautifulSoup的问题,可以发送邮件到 `讨论组 <https://groups.google.com/forum/?fromgroups#!forum/beautifulsoup>`_ .如果你的问题包含了一段需要转换的HTML代码,那么确保你提的问题描述中附带这段HTML文档的 `代码诊断`_ [1]_
68+如果有关于 Beautiful Soup 4 的疑问,或遇到了问题,可以发送邮件到 `讨论组
69+<https://groups.google.com/forum/?fromgroups#!forum/beautifulsoup>`_。
70+
71+如果问题中包含要解析的 HTML 代码,那么请在你的问题描述中附带这段HTML文档的 `代码诊断`_ [1]_。
72+
73+如果报告文档中的错误,请指出具体文档的语言版本。
74
75 快速开始
76 ========
77
78-下面的一段HTML代码将作为例子被多次用到.这是 *爱丽丝梦游仙境的* 的一段内容(以后内容中简称为 *爱丽丝* 的文档):
79+下面的一段HTML代码将作为例子被多次用到。这是 `爱丽丝梦游仙境` 的一段内容(以后简称 *爱丽丝* 的文档):
80
81 ::
82
83@@ -50,7 +59,8 @@ Beautiful Soup 4.4.0 文档
84 <p class="story">...</p>
85 """
86
87-使用BeautifulSoup解析这段代码,能够得到一个 ``BeautifulSoup`` 的对象,并能按照标准的缩进格式的结构输出:
88+上面的 *爱丽丝* 文档经过 Beautiful Soup 的解析后,会得到一个 :py:class:`BeautifulSoup` 的对象,
89+一个嵌套结构的对象:
90
91 ::
92
93@@ -80,7 +90,7 @@ Beautiful Soup 4.4.0 文档
94 # Lacie
95 # </a>
96 # and
97- # <a class="sister" href="http://example.com/tillie" id="link3">
98+ # <a class="sister" href="http://example.com/tillie" id="link2">
99 # Tillie
100 # </a>
101 # ; and they lived at the bottom of a well.
102@@ -91,7 +101,7 @@ Beautiful Soup 4.4.0 文档
103 # </body>
104 # </html>
105
106-几个简单的浏览结构化数据的方法:
107+这是几个简单的浏览结构化数据的方法:
108
109 ::
110
111@@ -124,7 +134,7 @@ Beautiful Soup 4.4.0 文档
112 soup.find(id="link3")
113 # <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>
114
115-从文档中找到所有<a>标签的链接:
116+常见任务之一,就是从文档中找到所有 <a> 标签的链接:
117
118 ::
119
120@@ -134,7 +144,7 @@ Beautiful Soup 4.4.0 文档
121 # http://example.com/lacie
122 # http://example.com/tillie
123
124-从文档中获取所有文字内容:
125+另一种常见任务,是从文档中获取所有文字内容:
126
127 ::
128
129@@ -151,152 +161,142 @@ Beautiful Soup 4.4.0 文档
130 #
131 # ...
132
133-这是你想要的吗?别着急,还有更好用的
134+这是你想要的吗?是的话,继续看下去。
135
136 安装 Beautiful Soup
137 ======================
138
139-如果你用的是新版的Debain或ubuntu,那么可以通过系统的软件包管理来安装:
140-
141-``$ apt-get install Python-bs4``
142-
143-Beautiful Soup 4 通过PyPi发布,所以如果你无法使用系统包管理安装,那么也可以通过 ``easy_install`` 或 ``pip`` 来安装.包的名字是 ``beautifulsoup4`` ,这个包兼容Python2和Python3.
144-
145-``$ easy_install beautifulsoup4``
146+如果你用的是新版的 Debain 或 Ubuntu,那么可以通过系统的软件包管理来安装:
147
148-``$ pip install beautifulsoup4``
149-
150-(在PyPi中还有一个名字是 ``BeautifulSoup`` 的包,但那可能不是你想要的,那是 `Beautiful Soup3 <http://www.crummy.com/software/BeautifulSoup/bs3/documentation.html>`_ 的发布版本,因为很多项目还在使用BS3, 所以 ``BeautifulSoup`` 包依然有效.但是如果你在编写新项目,那么你应该安装的 ``beautifulsoup4`` )
151-
152-如果你没有安装 ``easy_install`` 或 ``pip`` ,那你也可以 `下载BS4的源码 <http://www.crummy.com/software/BeautifulSoup/download/4.x/>`_ ,然后通过setup.py来安装.
153-
154-``$ Python setup.py install``
155-
156-如果上述安装方法都行不通,Beautiful Soup的发布协议允许你将BS4的代码打包在你的项目中,这样无须安装即可使用.
157-
158-作者在Python2.7和Python3.2的版本下开发Beautiful Soup, 理论上Beautiful Soup应该在所有当前的Python版本中正常工作
159-
160-安装完成后的问题
161------------------
162+:kbd:`$ apt-get install python3-bs4`
163
164-Beautiful Soup发布时打包成Python2版本的代码,在Python3环境下安装时,会自动转换成Python3的代码,如果没有一个安装的过程,那么代码就不会被转换.
165+Beautiful Soup 4 通过 PyPi 发布,所以如果无法使用系统包管理安装,那么
166+也可以通过 ``easy_install`` 或 ``pip`` 来安装。包的名字是 ``beautifulsoup4``。
167+确保使用的是与 Python 版本对应的 ``pip`` 或 ``easy_install`` 版本
168+(他们的名字也可能是 ``pip3`` 和 ``easy_install`` )。
169
170-如果代码抛出了 ``ImportError`` 的异常: "No module named HTMLParser", 这是因为你在Python3版本中执行Python2版本的代码.
171+:kbd:`$ easy_install beautifulsoup4`
172
173+:kbd:`$ pip install beautifulsoup4`
174
175-如果代码抛出了 ``ImportError`` 的异常: "No module named html.parser", 这是因为你在Python2版本中执行Python3版本的代码.
176+(在 PyPi 中还有一个名字是 ``BeautifulSoup`` 的包,但那可能不是你想要的,那是
177+`Beautiful Soup3`_ 版本。因为很多项目还在使用BS3, 所以 ``BeautifulSoup``
178+包依然有效。但是新项目中,应该安装 ``beautifulsoup4``。)
179
180-如果遇到上述2种情况,最好的解决方法是重新安装BeautifulSoup4.
181+如果没有安装 ``easy_install`` 或 ``pip`` ,那也可以 `下载 BS4 的源码
182+<http://www.crummy.com/software/BeautifulSoup/download/4.x/>`_ ,
183+然后通过 ``setup.py`` 来安装。
184
185-如果在ROOT_TAG_NAME = u'[document]'代码处遇到 ``SyntaxError`` "Invalid syntax"错误,需要将把BS4的Python代码版本从Python2转换到Python3. 可以重新安装BS4:
186+:kbd:`$ Python setup.py install`
187
188-``$ Python3 setup.py install``
189+如果上述安装方法都行不通,根据 Beautiful Soup 的协议,可以将项目的代码打包在
190+你的项目中,这样无须安装即可使用。
191
192-或在bs4的目录中执行Python代码版本转换脚本
193-
194-``$ 2to3-3.2 -w bs4``
195+Beautiful Soup 用 Python 3.10 版本开发,但也可以在当前的其它版本中运行。
196
197 安装解析器
198-------------
199+--------------
200
201-Beautiful Soup支持Python标准库中的HTML解析器,还支持一些第三方的解析器,其中一个是 `lxml <http://lxml.de/>`_ .根据操作系统不同,可以选择下列方法来安装lxml:
202+Beautiful Soup 支持 Python 标准库中的 HTML 解析器,还支持一些第三方的解析器,
203+其中一个是 `lxml parser <http://lxml.de/>`_ 。根据安装方法的不同,
204+可以选择下列方法来安装 lxml:
205
206-``$ apt-get install Python-lxml``
207+:kbd:`$ apt-get install Python-lxml`
208
209-``$ easy_install lxml``
210+:kbd:`$ easy_install lxml`
211
212-``$ pip install lxml``
213+:kbd:`$ pip install lxml`
214
215-另一个可供选择的解析器是纯Python实现的 `html5lib <http://code.google.com/p/html5lib/>`_ , html5lib的解析方式与浏览器相同,可以选择下列方法来安装html5lib:
216+另一个可供选择的解析器是纯 Python 实现的 `html5lib <http://code.google.com/p/html5lib/>`_ ,
217+html5lib 的解析方式与浏览器相同,根据安装方法的不同,可以选择下列方法来安装html5lib:
218
219-``$ apt-get install Python-html5lib``
220+:kbd:`$ apt-get install python-html5lib`
221
222-``$ easy_install html5lib``
223+:kbd:`$ easy_install html5lib`
224
225-``$ pip install html5lib``
226+:kbd:`$ pip install html5lib`
227
228-下表列出了主要的解析器,以及它们的优缺点:
229+下表描述了几种解析器的优缺点:
230
231-+-----------------------+---------------------------+---------------------------+---------------------------+
232-| 解析器 | 使用方法 | 优势 | 劣势 |
233-+=======================+===========================+===========================+===========================+
234-| Python标准库 | ``BeautifulSoup(markup, | - Python的内置标准库 | - Python 2.7.3 or 3.2.2)前|
235-| | "html.parser")`` | - 执行速度适中 | 的版本中文档容错能力差 |
236-| | | - 文档容错能力强 | |
237-| | | | |
238-+-----------------------+---------------------------+---------------------------+---------------------------+
239-| lxml HTML 解析器 | ``BeautifulSoup(markup, | - 速度快 | - 需要安装C语言库 |
240-| | "lxml")`` | - 文档容错能力强 | |
241-| | | | |
242-+-----------------------+---------------------------+---------------------------+---------------------------+
243-| lxml XML 解析器 | ``BeautifulSoup(markup, | - 速度快 | - 需要安装C语言库 |
244-| | ["lxml-xml"])`` | - 唯一支持XML的解析器 | |
245-| | | | |
246-| | ``BeautifulSoup(markup, | | |
247-| | "xml")`` | | |
248-+-----------------------+---------------------------+---------------------------+---------------------------+
249-| html5lib | ``BeautifulSoup(markup, | - 最好的容错性 | - 速度慢 |
250-| | "html5lib")`` | - 以浏览器的方式解析文档 | - 不依赖外部扩展 |
251-| | | - 生成HTML5格式的文档 | |
252-+-----------------------+---------------------------+---------------------------+---------------------------+
253++-------------------+-------------------------------------------+---------------------------+------------------------------------------+
254+| 解析器 | 使用方法 | 优势 | 劣势 |
255++===================+===========================================+===========================+==========================================+
256+|| Python 标准库 || ``BeautifulSoup(markup, "html.parser")`` || - Python的内置标准库 || - 速度没有 lxml 快,容错没有 html5lib强 |
257+|| || || - 执行速度较快 || |
258+|| || || - 容错能力强 || |
259++-------------------+-------------------------------------------+---------------------------+------------------------------------------+
260+|| lxml HTML 解析器 || ``BeautifulSoup(markup, "lxml")`` || - 速度快 || - 额外的 C 依赖 |
261+|| || || - 容错能力强 || |
262+|| || || || |
263++-------------------+-------------------------------------------+---------------------------+------------------------------------------+
264+|| lxml XML 解析器 || ``BeautifulSoup(markup, ["lxml-xml"])`` || - 速度快 || - 额外的 C 依赖 |
265+|| || ``BeautifulSoup(markup, "xml")`` || - 唯一支持 XML 的解析器 || |
266++-------------------+-------------------------------------------+---------------------------+------------------------------------------+
267+|| html5lib || ``BeautifulSoup(markup, "html5lib")`` || - 最好的容错性 || - 速度慢 |
268+|| || || - 以浏览器的方式解析文档 || - 额外的 Python 依赖 |
269+|| || || - 生成 HTML5 格式的文档 || |
270++-------------------+-------------------------------------------+---------------------------+------------------------------------------+
271
272-推荐使用lxml作为解析器,因为效率更高. 在Python2.7.3之前的版本和Python3中3.2.2之前的版本,必须安装lxml或html5lib, 因为那些Python版本的标准库中内置的HTML解析方法不够稳定.
273+如果可以,推荐使用 lxml 来获得更高的速度。
274
275-提示: 如果一段HTML或XML文档格式不正确的话,那么在不同的解析器中返回的结果可能是不一样的,查看 `解析器之间的区别`_ 了解更多细节
276+注意,如果一段文档格式不标准,那么在不同解析器生成的 Beautiful Soup 数可能不一样。
277+查看 `解析器之间的区别`_ 了解更多细节。
278
279 如何使用
280 ========
281
282-将一段文档传入BeautifulSoup 的构造方法,就能得到一个文档的对象, 可以传入一段字符串或一个文件句柄.
283+解析文档是,将文档传入 :py:class:`BeautifulSoup` 的构造方法。也可以传入一段字符串
284+或一个文件句柄:
285
286 ::
287
288 from bs4 import BeautifulSoup
289
290- soup = BeautifulSoup(open("index.html"))
291+ with open("index.html") as fp:
292+ soup = BeautifulSoup(fp, 'html.parser')
293
294- soup = BeautifulSoup("<html>data</html>")
295+ soup = BeautifulSoup("<html>a web page</html>", 'html.parser')
296
297-首先,文档被转换成Unicode,并且HTML的实例都被转换成Unicode编码
298+首先,文档被转换成 Unicode,并且 HTML 中的实体也都被转换成 Unicode 编码
299
300 ::
301
302- BeautifulSoup("Sacr&eacute; bleu!")
303- <html><head></head><body>Sacré bleu!</body></html>
304+ print(BeautifulSoup("<html><head></head><body>Sacr&eacute; bleu!</body></html>", "html.parser"))
305+ # <html><head></head><body>Sacré bleu!</body></html>
306
307-然后,Beautiful Soup选择最合适的解析器来解析这段文档,如果手动指定解析器那么Beautiful Soup会选择指定的解析器来解析文档.(参考 `解析成XML`_ ).
308+然后,Beautiful Soup 选择最合适的解析器来解析这段文档。如果指定了解析器那么 Beautiful Soup
309+会选择指定的解析器来解析文档。(参考 `解析成XML`_ )。
310
311 对象的种类
312 ==========
313
314-Beautiful Soup将复杂HTML文档转换成一个复杂的树形结构,每个节点都是Python对象,所有对象可以归纳为4种:
315-``Tag`` , ``NavigableString`` , ``BeautifulSoup`` , ``Comment`` .
316+Beautiful Soup 将复杂的 HTML 文档转换成一个复杂的由 Python 对象构成的树形结构,但处理对象
317+的过程只包含 4 种类型的对象: :py:class:`Tag`, :py:class:`NavigableString`,
318+:py:class:`BeautifulSoup`, 和 :py:class:`Comment`。
319
320-Tag
321------
322-
323-``Tag`` 对象与XML或HTML原生文档中的tag相同:
324+:py:class:`Tag`
325+``Tag`` 对象与 XML 或 HTML 原生文档中的 tag 相同:
326
327 ::
328
329- soup = BeautifulSoup('<b class="boldest">Extremely bold</b>')
330+ soup = BeautifulSoup('<b class="boldest">Extremely bold</b>', 'html.parser')
331 tag = soup.b
332 type(tag)
333 # <class 'bs4.element.Tag'>
334
335-Tag有很多方法和属性,在 `遍历文档树`_ 和 `搜索文档树`_ 中有详细解释.现在介绍一下tag中最重要的属性: name和attributes
336+Tag有很多属性和方法,在 `遍历文档树`_ 和 `搜索文档树`_ 中有详细解释。
337+现在介绍一下 tag 中最重要的属性: name 和 attributes。
338
339-Name
340-.....
341+.. py:attribute:: name
342
343-每个tag都有自己的名字,通过 ``.name`` 来获取:
344+每个 tag 都有一个名字:
345
346 ::
347
348 tag.name
349 # u'b'
350
351-如果改变了tag的name,那将影响所有通过当前Beautiful Soup对象生成的HTML文档:
352+如果改变了 tag 的 name,那将影响所有通过当前 Beautiful Soup 对象生成的HTML文档:
353
354 ::
355
356@@ -304,107 +304,161 @@ Name
357 tag
358 # <blockquote class="boldest">Extremely bold</blockquote>
359
360-Attributes
361-............
362+.. py:attribute:: attrs
363
364-一个tag可能有很多个属性. tag ``<b class="boldest">`` 有一个 "class" 的属性,值为 "boldest" . tag的属性的操作方法与字典相同:
365+一个 HTML 或 XML 的 tag 可能有很多属性。tag ``<b id="boldest">`` 有
366+一个 "id" 的属性,值为 "boldest"。你可以想处理一个字段一样来处理 tag 的属性:
367
368 ::
369
370- tag['class']
371- # u'boldest'
372+ tag = BeautifulSoup('<b id="boldest">bold</b>', 'html.parser').b
373+ tag['id']
374+ # 'boldest'
375
376-也可以直接"点"取属性, 比如: ``.attrs`` :
377+也可以直接"点"取属性,比如: ``.attrs`` :
378
379 ::
380
381 tag.attrs
382 # {u'class': u'boldest'}
383
384-tag的属性可以被添加,删除或修改. 再说一次, tag的属性操作方法与字典一样
385+tag 的属性可以被添加、删除或修改。再说一次,tag的属性操作方法与字典一样
386
387 ::
388
389- tag['class'] = 'verybold'
390- tag['id'] = 1
391- tag
392- # <blockquote class="verybold" id="1">Extremely bold</blockquote>
393+ tag['id'] = 'verybold'
394+ tag['another-attribute'] = 1
395+ tag
396+ # <b another-attribute="1" id="verybold"></b>
397
398- del tag['class']
399- del tag['id']
400- tag
401- # <blockquote>Extremely bold</blockquote>
402+ del tag['id']
403+ del tag['another-attribute']
404+ tag
405+ # <b>bold</b>
406
407- tag['class']
408- # KeyError: 'class'
409- print(tag.get('class'))
410- # None
411+ tag['id']
412+ # KeyError: 'id'
413+ tag.get('id')
414+ # None
415+
416+.. _multivalue:
417
418 多值属性
419-``````````
420+----------
421
422-HTML 4定义了一系列可以包含多个值的属性.在HTML5中移除了一些,却增加更多.最常见的多值的属性是 class (一个tag可以有多个CSS的class). 还有一些属性 ``rel`` , ``rev`` , ``accept-charset`` , ``headers`` , ``accesskey`` . 在Beautiful Soup中多值属性的返回类型是list:
423+HTML 4 定义了一系列可以包含多个值的属性。在 HTML5 中移除了一些,却增加更多。
424+最常见的多值的属性是 ``class`` (一个 tag 可以有多个 CSS class)。还有一些
425+属性 ``rel``、 ``rev``、 ``accept-charset``、 ``headers``、 ``accesskey``。
426+默认情况,Beautiful Soup 中将多值属性解析为一个列表:
427
428 ::
429
430- css_soup = BeautifulSoup('<p class="body strikeout"></p>')
431- css_soup.p['class']
432- # ["body", "strikeout"]
433+ css_soup = BeautifulSoup('<p class="body"></p>', 'html.parser')
434+ css_soup.p['class']
435+ # ['body']
436+
437+ css_soup = BeautifulSoup('<p class="body strikeout"></p>', 'html.parser')
438+ css_soup.p['class']
439+ # ['body', 'strikeout']
440+
441+ If an attribute `looks` like it has more than one value, but it's not
442+ a multi-valued attribute as defined by any version of the HTML
443+ standard, Beautiful Soup will leave the attribute alone::
444
445- css_soup = BeautifulSoup('<p class="body"></p>')
446- css_soup.p['class']
447- # ["body"]
448+ id_soup = BeautifulSoup('<p id="my id"></p>', 'html.parser')
449+ id_soup.p['id']
450+ # 'my id'
451
452-如果某个属性看起来好像有多个值,但在任何版本的HTML定义中都没有被定义为多值属性,那么Beautiful Soup会将这个属性作为字符串返回
453+如果某个属性看起来好像有多个值,但在任何版本的 HTML 定义中都没有将其定义为多值属性,
454+那么 Beautiful Soup 会将这个属性作为单值返回
455
456 ::
457
458- id_soup = BeautifulSoup('<p id="my id"></p>')
459- id_soup.p['id']
460- # 'my id'
461+ id_soup = BeautifulSoup('<p id="my id"></p>', 'html.parser')
462+ id_soup.p['id']
463+ # 'my id'
464
465-将tag转换成字符串时,多值属性会合并为一个值
466+将 tag 转换成字符串时,多值属性会合并为一个值
467
468 ::
469
470- rel_soup = BeautifulSoup('<p>Back to the <a rel="index">homepage</a></p>')
471+ rel_soup = BeautifulSoup('<p>Back to the <a rel="index first">homepage</a></p>', 'html.parser')
472 rel_soup.a['rel']
473- # ['index']
474+ # ['index', 'first']
475 rel_soup.a['rel'] = ['index', 'contents']
476 print(rel_soup.p)
477 # <p>Back to the <a rel="index contents">homepage</a></p>
478
479-如果转换的文档是XML格式,那么tag中不包含多值属性
480+若想强制将所有属性当做多值进行解析,可以在 :py:class:`BeautifulSoup` 构造方法中设置
481+``multi_valued_attributes=None`` 参数:
482+
483+::
484+
485+ no_list_soup = BeautifulSoup('<p class="body strikeout"></p>', 'html.parser', multi_valued_attributes=None)
486+ no_list_soup.p['class']
487+ # 'body strikeout'
488+
489+或者使用 ``get_attribute_list`` 方法来获取多值列表,不管是不是一个多值属性:
490+
491+::
492+
493+ id_soup.p.get_attribute_list('id')
494+ # ["my id"]
495+
496+如果以 XML 方式解析文档,则没有多值属性:
497
498 ::
499
500 xml_soup = BeautifulSoup('<p class="body strikeout"></p>', 'xml')
501 xml_soup.p['class']
502- # u'body strikeout'
503+ # 'body strikeout'
504+
505+但是,可以通过配置 ``multi_valued_attributes`` 参数来修改:
506+
507+::
508+
509+ class_is_multi= { '*' : 'class'}
510+ xml_soup = BeautifulSoup('<p class="body strikeout"></p>', 'xml', multi_valued_attributes=class_is_multi)
511+ xml_soup.p['class']
512+ # ['body', 'strikeout']
513+
514+可能实际当中并不需要修改默认配置,默认采用的是 HTML 标准:
515+
516+::
517
518-可以遍历的字符串
519+ from bs4.builder import builder_registry
520+ builder_registry.lookup('html').DEFAULT_CDATA_LIST_ATTRIBUTES
521+
522+.. py:class:: NavigableString
523+
524+可遍历的字符串
525 ----------------
526
527-字符串常被包含在tag内.Beautiful Soup用 ``NavigableString`` 类来包装tag中的字符串:
528+字符串对应 tag 中的一段文本。Beautiful Soup 用 :py:class:`NavigableString`
529+类来包装 tag 中的字符串:
530
531 ::
532
533+ soup = BeautifulSoup('<b class="boldest">Extremely bold</b>', 'html.parser')
534+ tag = soup.b
535 tag.string
536- # u'Extremely bold'
537+ # 'Extremely bold'
538 type(tag.string)
539 # <class 'bs4.element.NavigableString'>
540
541-一个 ``NavigableString`` 字符串与Python中的Unicode字符串相同,并且还支持包含在 `遍历文档树`_ 和 `搜索文档树`_ 中的一些特性. 通过 ``unicode()`` 方法可以直接将 ``NavigableString`` 对象转换成Unicode字符串:
542+一个 :py:class:`NavigableString` 对象与 Python 中的Unicode 字符串相同,
543+并且还支持包含在 `遍历文档树`_ 和 `搜索文档树`_ 中的一些特性。通过 ``str`` 方法可以直接将
544+:py:class:`NavigableString` 对象转换成 Unicode 字符串:
545
546 ::
547
548- unicode_string = unicode(tag.string)
549+ unicode_string = str(tag.string)
550 unicode_string
551- # u'Extremely bold'
552+ # 'Extremely bold'
553 type(unicode_string)
554- # <type 'unicode'>
555+ # <type 'str'>
556
557-tag中包含的字符串不能编辑,但是可以被替换成其它的字符串,用 `replace_with()`_ 方法:
558+tag 中包含的字符串不能直接编辑,但是可以被替换成其它的字符串,用 :ref:`replace_with()` 方法:
559
560 ::
561
562@@ -412,16 +466,24 @@ tag中包含的字符串不能编辑,但是可以被替换成其它的字符串,
563 tag
564 # <blockquote>No longer bold</blockquote>
565
566-``NavigableString`` 对象支持 `遍历文档树`_ 和 `搜索文档树`_ 中定义的大部分属性, 并非全部.尤其是,一个字符串不能包含其它内容(tag能够包含字符串或是其它tag),字符串不支持 ``.contents`` 或 ``.string`` 属性或 ``find()`` 方法.
567+:py:class:`NavigableString` 对象支持 `遍历文档树`_ 和 `搜索文档树`_ 中定义的大部分属性,
568+并非全部。尤其是,一个字符串不能包含其它内容(tag 能够包含字符串或是其它 tag),字符串不支持
569+``.contents`` 或 ``.string`` 属性或 ``find()`` 方法。
570
571-如果想在Beautiful Soup之外使用 ``NavigableString`` 对象,需要调用 ``unicode()`` 方法,将该对象转换成普通的Unicode字符串,否则就算Beautiful Soup已方法已经执行结束,该对象的输出也会带有对象的引用地址.这样会浪费内存.
572+如果想在 Beautiful Soup 之外使用 :py:class:`NavigableString` 对象,需要调用 ``unicode()``
573+方法,将该对象转换成普通的Unicode字符串,否则就算 Beautiful Soup 方法已经执行结束,该对象的输出
574+也会带有对象的引用地址。这样会浪费内存。
575
576-BeautifulSoup
577-----------------
578+.. py:class:: BeautifulSoup
579+
580+-------------------------------
581
582-``BeautifulSoup`` 对象表示的是一个文档的全部内容.大部分时候,可以把它当作 ``Tag`` 对象,它支持 `遍历文档树`_ 和 `搜索文档树`_ 中描述的大部分的方法.
583+``BeautifulSoup`` 对象表示的是一个文档的全部内容。大部分时候,可以把它当作 ``Tag`` 对象,
584+它支持 `遍历文档树`_ 和 `搜索文档树`_ 中描述的大部分的方法。
585
586-因为 ``BeautifulSoup`` 对象并不是真正的HTML或XML的tag,所以它没有name和attribute属性.但有时查看它的 ``.name`` 属性是很方便的,所以 ``BeautifulSoup`` 对象包含了一个值为 "[document]" 的特殊属性 ``.name``
587+因为 ``BeautifulSoup`` 对象并不是真正的HTML或XML的tag,所以它没有name和attribute属性。
588+但有时查看它的 ``.name`` 属性是很方便的,所以 ``BeautifulSoup`` 对象包含了一个
589+值为 "[document]" 的特殊属性 ``.name``
590
591 ::
592
593@@ -431,24 +493,25 @@ BeautifulSoup
594 注释及特殊字符串
595 -----------------
596
597-``Tag`` , ``NavigableString`` , ``BeautifulSoup`` 几乎覆盖了html和xml中的所有内容,但是还有一些特殊对象.容易让人担心的内容是文档的注释部分:
598+:py:class:`Tag`, :py:class:`NavigableString`, :py:class:`BeautifulSoup`
599+几乎覆盖了html和xml中的所有内容,但是还有一些特殊对象。容易让人担心的内容是文档的注释部分:
600
601 ::
602
603 markup = "<b><!--Hey, buddy. Want to buy a used parser?--></b>"
604- soup = BeautifulSoup(markup)
605+ soup = BeautifulSoup(markup, 'html.parser')
606 comment = soup.b.string
607 type(comment)
608 # <class 'bs4.element.Comment'>
609
610-``Comment`` 对象是一个特殊类型的 ``NavigableString`` 对象:
611+:py:class:`Comment` 对象是一个特殊类型的 :py:class:`NavigableString` 对象:
612
613 ::
614
615 comment
616 # u'Hey, buddy. Want to buy a used parser'
617
618-但是当它出现在HTML文档中时, ``Comment`` 对象会使用特殊的格式输出:
619+但是当它出现在 HTML 文档中时,:py:class:`Comment` 对象会使用特殊的格式输出:
620
621 ::
622
623@@ -457,23 +520,61 @@ BeautifulSoup
624 # <!--Hey, buddy. Want to buy a used parser?-->
625 # </b>
626
627-Beautiful Soup中定义的其它类型都可能会出现在XML的文档中: ``CData`` , ``ProcessingInstruction`` , ``Declaration`` , ``Doctype`` .与 ``Comment`` 对象类似,这些类都是 ``NavigableString`` 的子类,只是添加了一些额外的方法的字符串独享.下面是用CDATA来替代注释的例子:
628
629-::
630+针对 HTML 文档
631+^^^^^^^^^^^^^^^^^^
632
633- from bs4 import CData
634- cdata = CData("A CDATA block")
635- comment.replace_with(cdata)
636+Beautiful Soup 定义了一些 :py:class:`NavigableString` 子类来处理特定的 HTML 标签。
637+通过忽略页面中表示程序指令的字符串,可以更容易挑出页面的 body 内容。
638+(这些类是在 Beautiful Soup 4.9.0 版本中添加的,html5lib 解析器不会使用它们)
639
640- print(soup.b.prettify())
641- # <b>
642- # <![CDATA[A CDATA block]]>
643- # </b>
644+.. py:class:: Stylesheet
645+
646+有一种 :py:class:`NavigableString` 子类表示嵌入的 CSS 脚本;
647+内容是 ``<style>`` 标签内部的所有字符串。
648+
649+.. py:class:: Script
650+
651+有一种 :py:class:`NavigableString` 子类表示嵌入的 JavaScript 脚本;
652+内容是 ``<script>`` 标签内部的所有字符串。
653+
654+.. py:class:: Template
655+
656+有一种 :py:class:`NavigableString` 子类表示嵌入的 HTML 模板,
657+内容是 ``<template>`` 标签内部的所有字符串。
658+
659+针对 XML 文档
660+^^^^^^^^^^^^^^^^^
661+
662+Beautiful Soup 定义了一些 :py:class:`NavigableString` 子类来处理 XML 文档中的特定
663+字符串。比如 :py:class:`Comment`,这些 :py:class:`NavigableString` 的子类生成字符
664+串时会添加额外内容。
665+
666+.. py:class:: Declaration
667+
668+有一种 :py:class:`NavigableString` 子类表示 XML 文档开头的
669+`declaration <https://www.w3.org/TR/REC-xml/#sec-prolog-dtd>`_ 。
670+
671+.. py:class:: Doctype
672+
673+有一种 :py:class:`NavigableString` 子类表示可能出现在 XML 文档开头的
674+`document type
675+declaration <https://www.w3.org/TR/REC-xml/#dt-doctype>`_ 。
676+
677+.. py:class:: CData
678+
679+有一种 :py:class:`NavigableString` 子类表示
680+`CData section <https://www.w3.org/TR/REC-xml/#sec-cdata-sect>`_。
681+
682+.. py:class:: ProcessingInstruction
683+
684+有一种 :py:class:`NavigableString` 子类表示 `XML 处理指令
685+<https://www.w3.org/TR/REC-xml/#sec-pi>`_。
686
687 遍历文档树
688 ==========
689
690-还拿"爱丽丝梦游仙境"的文档来做例子:
691+还是用"爱丽丝"的文档来做例子:
692
693 ::
694
695@@ -499,14 +600,15 @@ Beautiful Soup中定义的其它类型都可能会出现在XML的文档中: ``CD
696 子节点
697 -------
698
699-一个Tag可能包含多个字符串或其它的Tag,这些都是这个Tag的子节点.Beautiful Soup提供了许多操作和遍历子节点的属性.
700+tag 可能包含多个字符串或其它的 tag,这些都是这个 Tag 的子节点。Beautiful Soup 提供了许多查找
701+和操作子节点的方法。
702
703-注意: Beautiful Soup中字符串节点不支持这些属性,因为字符串没有子节点
704+注意: Beautiful Soup中字符串节点不支持这些属性,因为字符串没有子节点。
705
706-tag的名字
707-..........
708+Tag 的名字
709+^^^^^^^^^^^^
710
711-操作文档树最简单的方法就是告诉它你想获取的tag的name.如果想获取 <head> 标签,只要用 ``soup.head`` :
712+操作文档树最简单的方法就是告诉它你想获取的 tag 的 name。如果想获取 <head> 标签,只要用 ``soup.head``:
713
714 ::
715
716@@ -516,7 +618,8 @@ tag的名字
717 soup.title
718 # <title>The Dormouse's story</title>
719
720-这是个获取tag的小窍门,可以在文档树的tag中多次调用这个方法.下面的代码可以获取<body>标签中的第一个<b>标签:
721+这是个获取tag的小窍门,可以在文档树的tag中多次调用这个方法。下面的代码可以获取 <body> 标签中的
722+第一个 <b> 标签:
723
724 ::
725
726@@ -530,7 +633,8 @@ tag的名字
727 soup.a
728 # <a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>
729
730-如果想要得到所有的<a>标签,或是通过名字得到比一个tag更多的内容的时候,就需要用到 `Searching the tree` 中描述的方法,比如: find_all()
731+如果想要得到所有的 <a> 标签,或是比通过名字获取内容更复杂的方法时,就需要用到 `搜索文档树`_
732+中描述的方法,比如: `find_all()`
733
734 ::
735
736@@ -539,10 +643,10 @@ tag的名字
737 # <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>,
738 # <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>]
739
740-.contents 和 .children
741-........................
742+``.contents`` 和 ``.children``
743+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
744
745-tag的 ``.contents`` 属性可以将tag的子节点以列表的方式输出:
746+Tag 的 ``.contents`` 属性可以将 tag 的全部子节点以列表的方式输出:
747
748 ::
749
750@@ -559,7 +663,8 @@ tag的 ``.contents`` 属性可以将tag的子节点以列表的方式输出:
751 title_tag.contents
752 # [u'The Dormouse's story']
753
754-``BeautifulSoup`` 对象本身一定会包含子节点,也就是说<html>标签也是 ``BeautifulSoup`` 对象的子节点:
755+:py:class:`BeautifulSoup` 对象一定会包含子节点。下面例子中 <html> 标签就是 ``BeautifulSoup``
756+对象的子节点:
757
758 ::
759
760@@ -568,7 +673,7 @@ tag的 ``.contents`` 属性可以将tag的子节点以列表的方式输出:
761 soup.contents[0].name
762 # u'html'
763
764-字符串没有 ``.contents`` 属性,因为字符串没有子节点:
765+字符串没有 ``.contents`` 属性,因为字符串没有子节点:
766
767 ::
768
769@@ -576,7 +681,7 @@ tag的 ``.contents`` 属性可以将tag的子节点以列表的方式输出:
770 text.contents
771 # AttributeError: 'NavigableString' object has no attribute 'contents'
772
773-通过tag的 ``.children`` 生成器,可以对tag的子节点进行循环:
774+通过 tag 的 ``.children`` 生成器,可以对 tag 的子节点进行循环:
775
776 ::
777
778@@ -584,17 +689,23 @@ tag的 ``.contents`` 属性可以将tag的子节点以列表的方式输出:
779 print(child)
780 # The Dormouse's story
781
782-.descendants
783-..............
784+如果想要修改 tag 的子节点,使用 `修改文档树`_ 中描述的方法。不要直接修改 ``contents`` 列表:
785+那样会导致细微且难以定位的问题。
786+
787+``.descendants``
788+^^^^^^^^^^^^^^^^
789
790-``.contents`` 和 ``.children`` 属性仅包含tag的直接子节点.例如,<head>标签只有一个直接子节点<title>
791+``.contents`` 和 ``.children`` 属性仅包含 tag 的直接子节点。例如,<head> 标签只有一个直接
792+子节点 <title>
793
794 ::
795
796 head_tag.contents
797 # [<title>The Dormouse's story</title>]
798
799-但是<title>标签也包含一个子节点:字符串 “The Dormouse’s story”,这种情况下字符串 “The Dormouse’s story”也属于<head>标签的子孙节点. ``.descendants`` 属性可以对所有tag的子孙节点进行递归循环 [5]_ :
800+但是 <title> 标签也包含一个子节点:字符串 “The Dormouse’s story”。这种情况下字符串
801+“The Dormouse’s story” 也属于 <head> 标签的子节点。 ``.descendants`` 属性可以对
802+所有 tag 的子孙节点进行递归循环 [5]_ ,包括子节点,子节点的子节点:
803
804 ::
805
806@@ -603,7 +714,8 @@ tag的 ``.contents`` 属性可以将tag的子节点以列表的方式输出:
807 # <title>The Dormouse's story</title>
808 # The Dormouse's story
809
810-上面的例子中, <head>标签只有一个子节点,但是有2个子孙节点:<head>节点和<head>的子节点, ``BeautifulSoup`` 有一个直接子节点(<html>节点),却有很多子孙节点:
811+上面的例子中,<head> 标签只有一个子节点,但是有 2 个子孙节点: <head> 标签和 <head> 的子节点。
812+:py:class:`BeautifulSoup` 对象只有一个直接子节点(<html> 节点),却有很多子孙节点:
813
814 ::
815
816@@ -612,17 +724,22 @@ tag的 ``.contents`` 属性可以将tag的子节点以列表的方式输出:
817 len(list(soup.descendants))
818 # 25
819
820-.string
821-........
822
823-如果tag只有一个 ``NavigableString`` 类型子节点,那么这个tag可以使用 ``.string`` 得到子节点:
824+.. _.string:
825+
826+``.string``
827+^^^^^^^^^^^
828+
829+如果 tag 只有一个 ``NavigableString`` 类型子节点,那么这个tag可以使用 ``.string``
830+得到子节点:
831
832 ::
833
834 title_tag.string
835 # u'The Dormouse's story'
836
837-如果一个tag仅有一个子节点,那么这个tag也可以使用 ``.string`` 方法,输出结果与当前唯一子节点的 ``.string`` 结果相同:
838+如果一个tag仅有一个子节点,那么这个tag也可以使用 ``.string`` 方法,输出结果与当前唯一
839+子节点的 ``.string`` 结果相同:
840
841 ::
842
843@@ -632,17 +749,20 @@ tag的 ``.contents`` 属性可以将tag的子节点以列表的方式输出:
844 head_tag.string
845 # u'The Dormouse's story'
846
847-如果tag包含了多个子节点,tag就无法确定 ``.string`` 方法应该调用哪个子节点的内容, ``.string`` 的输出结果是 ``None`` :
848+如果tag包含了多个子节点,tag就无法确定 ``.string`` 方法应该调用哪个子节点的内容,
849+``.string`` 的输出结果是 ``None`` :
850
851 ::
852
853 print(soup.html.string)
854 # None
855
856+.. _string-generators:
857+
858 .strings 和 stripped_strings
859-.............................
860+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
861
862-如果tag中包含多个字符串 [2]_ ,可以使用 ``.strings`` 来循环获取:
863+如果 tag 中包含多个字符串 [2]_ ,可以使用 ``.strings`` 来循环获取:
864
865 ::
866
867@@ -663,7 +783,7 @@ tag的 ``.contents`` 属性可以将tag的子节点以列表的方式输出:
868 # u'...'
869 # u'\n'
870
871-输出的字符串中可能包含了很多空格或空行,使用 ``.stripped_strings`` 可以去除多余空白内容:
872+输出的字符串中可能包含了很多空格或空行,使用 ``.stripped_strings`` 可以去除多余空白内容:
873
874 ::
875
876@@ -680,17 +800,20 @@ tag的 ``.contents`` 属性可以将tag的子节点以列表的方式输出:
877 # u';\nand they lived at the bottom of a well.'
878 # u'...'
879
880-全部是空格的行会被忽略掉,段首和段末的空白会被删除
881+全部是空格的行会被忽略掉,段首和段末的空白会被删除
882
883 父节点
884 -------
885
886-继续分析文档树,每个tag或字符串都有父节点:被包含在某个tag中
887+继续分析文档树,每个 tag 或字符串都有父节点: 包含当前内容的 tag
888+
889+.. _.parent:
890
891 .parent
892-........
893+^^^^^^^^^^^^^
894
895-通过 ``.parent`` 属性来获取某个元素的父节点.在例子“爱丽丝”的文档中,<head>标签是<title>标签的父节点:
896+通过 ``.parent`` 属性来获取某个元素的父节点。在例子“爱丽丝”的文档中,<head> 标签是
897+<title> 标签的父节点:
898
899 ::
900
901@@ -700,14 +823,14 @@ tag的 ``.contents`` 属性可以将tag的子节点以列表的方式输出:
902 title_tag.parent
903 # <head><title>The Dormouse's story</title></head>
904
905-文档title的字符串也有父节点:<title>标签
906+文档的 title 字符串也有父节点: <title> 标签
907
908 ::
909
910 title_tag.string.parent
911 # <title>The Dormouse's story</title>
912
913-文档的顶层节点比如<html>的父节点是 ``BeautifulSoup`` 对象:
914+文档的顶层节点比如 <html> 的父节点是 ``BeautifulSoup`` 对象:
915
916 ::
917
918@@ -722,10 +845,13 @@ tag的 ``.contents`` 属性可以将tag的子节点以列表的方式输出:
919 print(soup.parent)
920 # None
921
922+.. _.parents:
923+
924 .parents
925-..........
926+^^^^^^^^^^^^
927
928-通过元素的 ``.parents`` 属性可以递归得到元素的所有父辈节点,下面的例子使用了 ``.parents`` 方法遍历了<a>标签到根节点的所有节点.
929+通过元素的 ``.parents`` 属性可以递归得到元素的所有父辈节点,下面的例子使用了 ``.parents``
930+方法遍历了 <a> 标签到根节点的所有节点。
931
932 ::
933
934@@ -750,10 +876,8 @@ tag的 ``.contents`` 属性可以将tag的子节点以列表的方式输出:
935
936 ::
937
938- sibling_soup = BeautifulSoup("<a><b>text1</b><c>text2</c></b></a>")
939+ sibling_soup = BeautifulSoup("<a><b>text1</b><c>text2</c></a>", 'html.parser')
940 print(sibling_soup.prettify())
941- # <html>
942- # <body>
943 # <a>
944 # <b>
945 # text1
946@@ -762,15 +886,14 @@ tag的 ``.contents`` 属性可以将tag的子节点以列表的方式输出:
947 # text2
948 # </c>
949 # </a>
950- # </body>
951- # </html>
952
953-因为<b>标签和<c>标签是同一层:他们是同一个元素的子节点,所以<b>和<c>可以被称为兄弟节点.一段文档以标准格式输出时,兄弟节点有相同的缩进级别.在代码中也可以使用这种关系.
954+因为 <b> 标签和 <c> 标签是同一层: 他们是同一个元素的子节点,所以 <b> 和 <c> 可以被称为兄弟节点。
955+一段文档以标准格式输出时,兄弟节点有相同的缩进级别。在代码中也可以使用这种关系。
956
957 .next_sibling 和 .previous_sibling
958-....................................
959+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
960
961-在文档树中,使用 ``.next_sibling`` 和 ``.previous_sibling`` 属性来查询兄弟节点:
962+在文档树中,使用 ``.next_sibling`` 和 ``.previous_sibling`` 属性来查询兄弟节点:
963
964 ::
965
966@@ -780,7 +903,9 @@ tag的 ``.contents`` 属性可以将tag的子节点以列表的方式输出:
967 sibling_soup.c.previous_sibling
968 # <b>text1</b>
969
970-<b>标签有 ``.next_sibling`` 属性,但是没有 ``.previous_sibling`` 属性,因为<b>标签在同级节点中是第一个.同理,<c>标签有 ``.previous_sibling`` 属性,却没有 ``.next_sibling`` 属性:
971+<b> 标签有 ``.next_sibling`` 属性,但是没有 ``.previous_sibling`` 属性,
972+因为 <b> 标签在同级节点中是第一个。同理,<c>标签有 ``.previous_sibling`` 属性,
973+却没有 ``.next_sibling`` 属性:
974
975 ::
976
977@@ -789,7 +914,7 @@ tag的 ``.contents`` 属性可以将tag的子节点以列表的方式输出:
978 print(sibling_soup.c.next_sibling)
979 # None
980
981-例子中的字符串“text1”和“text2”不是兄弟节点,因为它们的父节点不同:
982+例子中的字符串 "text1" 和 "text2" 不是兄弟节点,因为它们的父节点不同:
983
984 ::
985
986@@ -799,7 +924,8 @@ tag的 ``.contents`` 属性可以将tag的子节点以列表的方式输出:
987 print(sibling_soup.b.string.next_sibling)
988 # None
989
990-实际文档中的tag的 ``.next_sibling`` 和 ``.previous_sibling`` 属性通常是字符串或空白. 看看“爱丽丝”文档:
991+实际文档中的 tag 的 ``.next_sibling`` 和 ``.previous_sibling`` 属性通常是字符串或空白。
992+看看“爱丽丝”文档:
993
994 ::
995
996@@ -807,7 +933,8 @@ tag的 ``.contents`` 属性可以将tag的子节点以列表的方式输出:
997 <a href="http://example.com/lacie" class="sister" id="link2">Lacie</a>
998 <a href="http://example.com/tillie" class="sister" id="link3">Tillie</a>
999
1000-如果以为第一个<a>标签的 ``.next_sibling`` 结果是第二个<a>标签,那就错了,真实结果是第一个<a>标签和第二个<a>标签之间的顿号和换行符:
1001+如果以为第一个 <a> 标签的 ``.next_sibling`` 结果是第二个 <a> 标签,那就错了,
1002+真实结果是第一个 <a> 标签和第二个<a> 标签之间的顿号和换行符:
1003
1004 ::
1005
1006@@ -825,8 +952,10 @@ tag的 ``.contents`` 属性可以将tag的子节点以列表的方式输出:
1007 link.next_sibling.next_sibling
1008 # <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>
1009
1010+.. _sibling-generators:
1011+
1012 .next_siblings 和 .previous_siblings
1013-......................................
1014+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
1015
1016 通过 ``.next_siblings`` 和 ``.previous_siblings`` 属性可以对当前节点的兄弟节点迭代输出:
1017
1018@@ -834,21 +963,19 @@ tag的 ``.contents`` 属性可以将tag的子节点以列表的方式输出:
1019
1020 for sibling in soup.a.next_siblings:
1021 print(repr(sibling))
1022- # u',\n'
1023- # <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>
1024- # u' and\n'
1025- # <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>
1026- # u'; and they lived at the bottom of a well.'
1027- # None
1028+ # ',\n'
1029+ # <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>
1030+ # ' and\n'
1031+ # <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>
1032+ # '; and they lived at the bottom of a well.'
1033
1034 for sibling in soup.find(id="link3").previous_siblings:
1035 print(repr(sibling))
1036- # ' and\n'
1037- # <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>
1038- # u',\n'
1039- # <a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>
1040- # u'Once upon a time there were three little sisters; and their names were\n'
1041- # None
1042+ # ' and\n'
1043+ # <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>
1044+ # ',\n'
1045+ # <a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>
1046+ # 'Once upon a time there were three little sisters; and their names were\n'
1047
1048 回退和前进
1049 ----------
1050@@ -860,14 +987,20 @@ tag的 ``.contents`` 属性可以将tag的子节点以列表的方式输出:
1051 <html><head><title>The Dormouse's story</title></head>
1052 <p class="title"><b>The Dormouse's story</b></p>
1053
1054-HTML解析器把这段字符串转换成一连串的事件: "打开<html>标签","打开一个<head>标签","打开一个<title>标签","添加一段字符串","关闭<title>标签","打开<p>标签",等等.Beautiful Soup提供了重现解析器初始化过程的方法.
1055+HTML解析器把这段字符串转换成一连串的事件: "打开<html>标签","打开一个<head>标签",
1056+"打开一个<title>标签","添加一段字符串","关闭<title>标签","打开<p>标签",等等。
1057+Beautiful Soup提供了重现解析器初始化过程的方法。
1058+
1059+.. _element-generators:
1060
1061 .next_element 和 .previous_element
1062-...................................
1063+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
1064
1065-``.next_element`` 属性指向解析过程中下一个被解析的对象(字符串或tag),结果可能与 ``.next_sibling`` 相同,但通常是不一样的.
1066+``.next_element`` 属性指向解析过程中下一个被解析的对象(字符串或tag),
1067+结果可能与 ``.next_sibling`` 相同,但通常是不一样的。
1068
1069-这是“爱丽丝”文档中最后一个<a>标签,它的 ``.next_sibling`` 结果是一个字符串,因为当前的解析过程 [2]_ 因为当前的解析过程因为遇到了<a>标签而中断了:
1070+这是“爱丽丝”文档中最后一个 <a> 标签,它的 ``.next_sibling`` 结果是一个字符串,
1071+因为当前的解析过程 [2]_ 因为当前的解析过程因为遇到了<a>标签而中断了:
1072
1073 ::
1074
1075@@ -878,16 +1011,20 @@ HTML解析器把这段字符串转换成一连串的事件: "打开<html>标签"
1076 last_a_tag.next_sibling
1077 # '; and they lived at the bottom of a well.'
1078
1079-但这个<a>标签的 ``.next_element`` 属性结果是在<a>标签被解析之后的解析内容,不是<a>标签后的句子部分,应该是字符串"Tillie":
1080+但这个 <a> 标签的 ``.next_element`` 属性结果是在 <a> 标签被解析之后的解析内容,
1081+不是 <a> 标签后的句子部分,而是字符串 "Tillie":
1082
1083 ::
1084
1085 last_a_tag.next_element
1086 # u'Tillie'
1087
1088-这是因为在原始文档中,字符串“Tillie” 在分号前出现,解析器先进入<a>标签,然后是字符串“Tillie”,然后关闭</a>标签,然后是分号和剩余部分.分号与<a>标签在同一层级,但是字符串“Tillie”会被先解析.
1089+这是因为在原始文档中,字符串 “Tillie” 在分号前出现,解析器先进入 <a> 标签,
1090+然后是字符串 “Tillie”,然后关闭 </a> 标签,然后是分号和剩余部分。
1091+分号与 <a> 标签在同一层级,但是字符串 “Tillie” 会先被解析。
1092
1093-``.previous_element`` 属性刚好与 ``.next_element`` 相反,它指向当前被解析的对象的前一个解析对象:
1094+``.previous_element`` 属性刚好与 ``.next_element`` 相反,
1095+它指向当前被解析的对象的前一个解析对象:
1096
1097 ::
1098
1099@@ -897,9 +1034,10 @@ HTML解析器把这段字符串转换成一连串的事件: "打开<html>标签"
1100 # <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>
1101
1102 .next_elements 和 .previous_elements
1103-.....................................
1104+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
1105
1106-通过 ``.next_elements`` 和 ``.previous_elements`` 的迭代器就可以向前或向后访问文档的解析内容,就好像文档正在被解析一样:
1107+通过 ``.next_elements`` 和 ``.previous_elements`` 的迭代器就可以向前或向后
1108+访问文档的解析内容,就好像文档正在被解析一样:
1109
1110 ::
1111
1112@@ -914,9 +1052,10 @@ HTML解析器把这段字符串转换成一连串的事件: "打开<html>标签"
1113 # None
1114
1115 搜索文档树
1116-==========
1117+============
1118
1119-Beautiful Soup定义了很多搜索方法,这里着重介绍2个: ``find()`` 和 ``find_all()`` .其它方法的参数和用法类似,请读者举一反三.
1120+Beautiful Soup 定义了很多相似的文档搜索方法,这里着重介绍2个: ``find()`` 和 ``find_all()``,
1121+其它方法的参数和用法类似,所以一笔带过。
1122
1123 再以“爱丽丝”文档作为例子:
1124
1125@@ -939,29 +1078,38 @@ Beautiful Soup定义了很多搜索方法,这里着重介绍2个: ``find()`` 和
1126 from bs4 import BeautifulSoup
1127 soup = BeautifulSoup(html_doc, 'html.parser')
1128
1129-使用 ``find_all()`` 类似的方法可以查找到想要查找的文档内容
1130+使用 ``find_all()`` 这种过滤方法,就可以检索想要查找的文档内容。
1131+
1132+过滤器类型
1133+-------------
1134
1135-过滤器
1136-------
1137+介绍 ``find_all()`` 或类似方法前,先介绍一下这些方法可以使用哪些过滤器的类型 [3]_,
1138+这些过滤器在搜索的 API 中反复出现。过滤器可以作用在 tag 的 name 上,节点的属性上,
1139+字符串上或与他们混合使用。
1140
1141-介绍 ``find_all()`` 方法前,先介绍一下过滤器的类型 [3]_ ,这些过滤器贯穿整个搜索的API.过滤器可以被用在tag的name中,节点的属性中,字符串中或他们的混合中.
1142+.. _字符串:
1143
1144 字符串
1145-............
1146+^^^^^^^^
1147
1148-最简单的过滤器是字符串.在搜索方法中传入一个字符串参数,Beautiful Soup会查找与字符串完整匹配的内容,下面的例子用于查找文档中所有的<b>标签:
1149+最简单的过滤器是字符串。在搜索方法中传入一个字符串参数,Beautiful Soup
1150+会查找与字符串完整匹配的内容,下面的例子用于查找文档中所有的 <b> 标签:
1151
1152 ::
1153
1154 soup.find_all('b')
1155 # [<b>The Dormouse's story</b>]
1156
1157-如果传入字节码参数,Beautiful Soup会当作UTF-8编码,可以传入一段Unicode 编码来避免Beautiful Soup解析编码出错
1158+如果传入字节码参数,Beautiful Soup会当作UTF-8编码,可以传入一段Unicode 编码来避免
1159+Beautiful Soup 解析编码出错。
1160+
1161+.. _正则表达式:
1162
1163 正则表达式
1164-..........
1165+^^^^^^^^^^^
1166
1167-如果传入正则表达式作为参数,Beautiful Soup会通过正则表达式的 ``search()`` 来匹配内容.下面例子中找出所有以b开头的标签,这表示<body>和<b>标签都应该被找到:
1168+如果传入正则表达式作为参数,Beautiful Soup 会通过正则表达式的 ``match()`` 来匹配内容。
1169+下面例子中找出所有以 b 开头的标签,这种情况下 <body> 和 <b> 标签都会被找到:
1170
1171 ::
1172
1173@@ -971,7 +1119,7 @@ Beautiful Soup定义了很多搜索方法,这里着重介绍2个: ``find()`` 和
1174 # body
1175 # b
1176
1177-下面代码找出所有名字中包含"t"的标签:
1178+下面代码找出所有名字中包含 "t" 的标签:
1179
1180 ::
1181
1182@@ -980,10 +1128,13 @@ Beautiful Soup定义了很多搜索方法,这里着重介绍2个: ``find()`` 和
1183 # html
1184 # title
1185
1186+.. _列表:
1187+
1188 列表
1189-....
1190+^^^^^^
1191
1192-如果传入列表参数,Beautiful Soup会将与列表中任一元素匹配的内容返回.下面代码找到文档中所有<a>标签和<b>标签:
1193+如果传入列表参数,Beautiful Soup会 将与列表中任一元素匹配的内容返回。
1194+下面代码找到文档中所有 <a> 标签和 <b> 标签:
1195
1196 ::
1197
1198@@ -993,10 +1144,12 @@ Beautiful Soup定义了很多搜索方法,这里着重介绍2个: ``find()`` 和
1199 # <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>,
1200 # <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>]
1201
1202+.. _True:
1203+
1204 True
1205-.....
1206+^^^^^^
1207
1208-``True`` 可以匹配任何值,下面代码查找到所有的tag,但是不会返回字符串节点
1209+``True`` 可以匹配任何值,下面代码查找到所有的 tag,但是不会返回字符串节点
1210
1211 ::
1212
1213@@ -1014,19 +1167,23 @@ True
1214 # a
1215 # p
1216
1217-方法
1218-....
1219+.. _函数:
1220
1221-如果没有合适过滤器,那么还可以定义一个方法,方法只接受一个元素参数 [4]_ ,如果这个方法返回 ``True`` 表示当前元素匹配并且被找到,如果不是则反回 ``False``
1222+函数
1223+^^^^^^
1224
1225-下面方法校验了当前元素,如果包含 ``class`` 属性却不包含 ``id`` 属性,那么将返回 ``True``:
1226+如果没有合适过滤器,那么还可以定义一个函数方法,参数是一个元素 [4]_ ,
1227+如果这个方法返回 ``True`` 表示当前元素匹配并且被找到,如果不是则反回 ``False``。
1228+
1229+下面方法实现的匹配功能是,如果包含 ``class`` 属性却不包含 ``id`` 属性,
1230+那么将返回 ``True``:
1231
1232 ::
1233
1234 def has_class_but_no_id(tag):
1235 return tag.has_attr('class') and not tag.has_attr('id')
1236
1237-将这个方法作为参数传入 ``find_all()`` 方法,将得到所有<p>标签:
1238+将这个方法作为参数传入 ``find_all()`` 方法,将得到所有 <p> 标签:
1239
1240 ::
1241
1242@@ -1035,21 +1192,20 @@ True
1243 # <p class="story">Once upon a time there were...</p>,
1244 # <p class="story">...</p>]
1245
1246-返回结果中只有<p>标签没有<a>标签,因为<a>标签还定义了"id",没有返回<html>和<head>,因为<html>和<head>中没有定义"class"属性.
1247-
1248-通过一个方法来过滤一类标签属性的时候, 这个方法的参数是要被过滤的属性的值, 而不是这个标签.
1249-下面的例子是找出 ``href`` 属性不符合指定正则的 ``a`` 标签.
1250-
1251-::
1252+返回结果中只有 <p> 标签,没有 <a> 标签,因为 <a> 标签还定义了"id",
1253+没有返回 <html> 和 <head>,因为 <html> 和 <head> 中没有定义 "class" 属性。
1254
1255+如果通过方法来筛选特殊属性,比如 ``href``,传入方法的参数应该是对应属性的值,
1256+而不是整个元素。下面的例子是找出那些 ``a`` 标签中的 ``href`` 属性不匹配指定正则::
1257
1258 def not_lacie(href):
1259 return href and not re.compile("lacie").search(href)
1260+
1261 soup.find_all(href=not_lacie)
1262 # [<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>,
1263 # <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>]
1264
1265-标签过滤方法可以使用复杂方法. 下面的例子可以过滤出前后都有文字的标签.
1266+标签过滤方法可以使用复杂方法。下面的例子可以过滤出前后都有文字的标签。
1267
1268 ::
1269
1270@@ -1071,9 +1227,11 @@ True
1271 find_all()
1272 -----------
1273
1274-find_all( `name`_ , `attrs`_ , `recursive`_ , `string`_ , `**kwargs`_ )
1275+find_all(`name`_ , `attrs`_ , `recursive <recursive>`_ , `string <string>`_ ,
1276+`**kwargs <kwargs>`_ )
1277
1278-``find_all()`` 方法搜索当前tag的所有tag子节点,并判断是否符合过滤器的条件.这里有几个例子:
1279+``find_all()`` 方法搜索当前 tag 的所有子节点,并判断是否符合过滤器的条件。
1280+`过滤器类型`_ 中已经举过几个例子,这里再展示几个新例子:
1281
1282 ::
1283
1284@@ -1095,12 +1253,17 @@ find_all( `name`_ , `attrs`_ , `recursive`_ , `string`_ , `**kwargs`_ )
1285 soup.find(string=re.compile("sisters"))
1286 # u'Once upon a time there were three little sisters; and their names were\n'
1287
1288-有几个方法很相似,还有几个方法是新的,参数中的 ``string`` 和 ``id`` 是什么含义? 为什么 ``find_all("p", "title")`` 返回的是CSS Class为"title"的<p>标签? 我们来仔细看一下 ``find_all()`` 的参数
1289+有几个方法很相似,还有几个方法是新的,参数中的 ``string`` 和 ``id`` 是什么含义?
1290+为什么 ``find_all("p", "title")`` 返回的是CSS Class为"title"的<p>标签?
1291+我们来仔细看一下 ``find_all()`` 的参数
1292+
1293+.. _name:
1294
1295 name 参数
1296-..........
1297+^^^^^^^^^^^
1298
1299-``name`` 参数可以查找所有名字为 ``name`` 的tag,字符串对象会被自动忽略掉.
1300+传一个值给 ``name`` 参数,就可以查找所有名字为 ``name`` 的 tag。所有文本都会被忽略掉,
1301+因为它们不匹配标签名字。
1302
1303 简单的用法如下:
1304
1305@@ -1109,19 +1272,24 @@ name 参数
1306 soup.find_all("title")
1307 # [<title>The Dormouse's story</title>]
1308
1309-重申: 搜索 ``name`` 参数的值可以使任一类型的 `过滤器`_ ,字符窜,正则表达式,列表,方法或是 ``True`` .
1310+回忆 `过滤器类型`_ 中描述的内容,搜索 ``name`` 的参数值可以是:
1311+字符串、正则表达式、列表、方法或是 ``True`` 。
1312+
1313+.. _kwargs:
1314
1315 keyword 参数
1316-..............
1317+^^^^^^^^^^^^^^^
1318
1319-如果一个指定名字的参数不是搜索内置的参数名,搜索时会把该参数当作指定名字tag的属性来搜索,如果包含一个名字为 ``id`` 的参数,Beautiful Soup会搜索每个tag的"id"属性.
1320+如果动态参数中出现未能识别的参数名,搜索时会把该参数当作 tag 属性来搜索,
1321+比如搜索参数中包含一个名字为 ``id`` 的参数,Beautiful Soup 会搜索每个
1322+tag 上的 ``id`` 属性
1323
1324 ::
1325
1326 soup.find_all(id='link2')
1327 # [<a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>]
1328
1329-如果传入 ``href`` 参数,Beautiful Soup会搜索每个tag的"href"属性:
1330+如果传入 ``href`` 参数,Beautiful Soup会搜索每个 tag 的 ``href`` 属性
1331
1332 ::
1333
1334@@ -1130,7 +1298,7 @@ keyword 参数
1335
1336 搜索指定名字的属性时可以使用的参数值包括 `字符串`_ , `正则表达式`_ , `列表`_, `True`_ .
1337
1338-下面的例子在文档树中查找所有包含 ``id`` 属性的tag,无论 ``id`` 的值是什么:
1339+下面的例子在文档树中查找所有包含 ``id`` 属性的 tag,无论 ``id`` 的值是什么:
1340
1341 ::
1342
1343@@ -1139,14 +1307,14 @@ keyword 参数
1344 # <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>,
1345 # <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>]
1346
1347-使用多个指定名字的参数可以同时过滤tag的多个属性:
1348+使用多个指定名字的参数可以同时过滤多个 tag 属性:
1349
1350 ::
1351
1352 soup.find_all(href=re.compile("elsie"), id='link1')
1353 # [<a class="sister" href="http://example.com/elsie" id="link1">three</a>]
1354
1355-有些tag属性在搜索不能使用,比如HTML5中的 data-* 属性:
1356+有些 tag 属性在搜索不能使用,比如HTML5中的 data-* 属性:
1357
1358 ::
1359
1360@@ -1154,17 +1322,33 @@ keyword 参数
1361 data_soup.find_all(data-foo="value")
1362 # SyntaxError: keyword can't be an expression
1363
1364-但是可以通过 ``find_all()`` 方法的 ``attrs`` 参数定义一个字典参数来搜索包含特殊属性的tag:
1365+这种情况下可以通过 ``find_all()`` 方法的 ``attrs`` 参数定义一个字典参数
1366+来搜索包含特殊属性的 tag:
1367
1368 ::
1369
1370 data_soup.find_all(attrs={"data-foo": "value"})
1371 # [<div data-foo="value">foo!</div>]
1372
1373+不要使用 "name" 作为关键字参数搜索 HTML 元素,因为 Beautiful Soup 用 ``name``
1374+来识别 tag 本身的名字。换一种方法,你可以这样搜索属性中的 "name" 值
1375+
1376+::
1377+
1378+ name_soup = BeautifulSoup('<input name="email"/>', 'html.parser')
1379+ name_soup.find_all(name="email")
1380+ # []
1381+ name_soup.find_all(attrs={"name": "email"})
1382+ # [<input name="email"/>]
1383+
1384+.. _attrs:
1385+
1386 按CSS搜索
1387-..........
1388+^^^^^^^^^^^
1389
1390-按照CSS类名搜索tag的功能非常实用,但标识CSS类名的关键字 ``class`` 在Python中是保留字,使用 ``class`` 做参数会导致语法错误.从Beautiful Soup的4.1.1版本开始,可以通过 ``class_`` 参数搜索有指定CSS类名的tag:
1391+按照 CSS 类名搜索的功能非常实用,但标识 CSS 类名的关键字 ``class`` 在Python中是保留字,
1392+使用 ``class`` 做参数会导致语法错误。从 Beautiful Soup 4.1.2 版本开始,可以通过 ``class_``
1393+参数搜索有指定CSS类名的 tag:
1394
1395 ::
1396
1397@@ -1173,7 +1357,8 @@ keyword 参数
1398 # <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>,
1399 # <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>]
1400
1401-``class_`` 参数同样接受不同类型的 ``过滤器`` ,字符串,正则表达式,方法或 ``True`` :
1402+作为关键字形式的参数 ``class_`` 同样接受不同类型的 ``过滤器``,字符串、正则表达式、
1403+方法或 ``True`` :
1404
1405 ::
1406
1407@@ -1188,7 +1373,7 @@ keyword 参数
1408 # <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>,
1409 # <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>]
1410
1411-tag的 ``class`` 属性是 `多值属性`_ .按照CSS类名搜索tag时,可以分别搜索tag中的每个CSS类名:
1412+tag 的 ``class`` 属性是 `多值属性`_ 。按照 CSS 类名搜索时,表示匹配到 tag 中任意 CSS 类名:
1413
1414 ::
1415
1416@@ -1199,14 +1384,29 @@ tag的 ``class`` 属性是 `多值属性`_ .按照CSS类名搜索tag时,可以
1417 css_soup.find_all("p", class_="body")
1418 # [<p class="body strikeout"></p>]
1419
1420-搜索 ``class`` 属性时也可以通过CSS值完全匹配:
1421+搜索 ``class`` 属性时也可以通过 CSS 值进行完全匹配:
1422
1423 ::
1424
1425 css_soup.find_all("p", class_="body strikeout")
1426 # [<p class="body strikeout"></p>]
1427
1428-完全匹配 ``class`` 的值时,如果CSS类名的顺序与实际不符,将搜索不到结果:
1429+完全匹配 ``class`` 的值时,如果CSS类名的顺序与实际不符,将搜索不到结果:
1430+
1431+::
1432+
1433+ css_soup.find_all("p", class_="strikeout body")
1434+ # []
1435+
1436+如果想要通过多个 CSS 类型来搜索 tag,应该使用 CSS 选择器
1437+
1438+::
1439+
1440+ css_soup.select("p.strikeout.body")
1441+ # [<p class="body strikeout"></p>]
1442+
1443+在旧版本的 Beautiful Soup 中,可能不支持 ``class_``,这时可以使用 ``attrs`` 实现相同效果。
1444+创建一个字典,包含要搜索的 class 类名(或者正则表达式等形式)
1445
1446 ::
1447
1448@@ -1215,10 +1415,13 @@ tag的 ``class`` 属性是 `多值属性`_ .按照CSS类名搜索tag时,可以
1449 # <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>,
1450 # <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>]
1451
1452-``string`` 参数
1453-...............
1454+.. _string2:
1455+
1456+string 参数
1457+^^^^^^^^^^^^^^^^^^^^^^^
1458
1459-通过 ``string`` 参数可以搜搜文档中的字符串内容.与 ``name`` 参数的可选值一样, ``string`` 参数接受 `字符串`_ , `正则表达式`_ , `列表`_, `True`_ . 看例子:
1460+通过 ``string`` 参数可以搜索文档中的字符串内容。与 ``name`` 参数接受的值一样,
1461+``string`` 参数接受 `字符串`_ , `正则表达式`_ , `列表`_, `函数`_, `True`_ 。看例子:
1462
1463 ::
1464
1465@@ -1238,19 +1441,32 @@ tag的 ``class`` 属性是 `多值属性`_ .按照CSS类名搜索tag时,可以
1466 soup.find_all(string=is_the_only_string_within_a_tag)
1467 # [u"The Dormouse's story", u"The Dormouse's story", u'Elsie', u'Lacie', u'Tillie', u'...']
1468
1469-虽然 ``string`` 参数用于搜索字符串,还可以与其它参数混合使用来过滤tag.Beautiful Soup会找到 ``.string`` 方法与 ``string`` 参数值相符的tag.下面代码用来搜索内容里面包含“Elsie”的<a>标签:
1470+虽然 ``string`` 参数用于搜索字符串,同时也以与其它参数混合使用来搜索 tag。
1471+Beautiful Soup 会过滤那些 ``string`` 值与 ``.string`` 参数相符的 tag。
1472+下面代码用来搜索内容里面包含 “Elsie” 的 <a> 标签:
1473
1474 ::
1475
1476 soup.find_all("a", string="Elsie")
1477 # [<a href="http://example.com/elsie" class="sister" id="link1">Elsie</a>]
1478
1479+``string`` 参数是在 4.4.0 中新增的。早期版本中该参数名为 ``text``。
1480+
1481+::
1482+
1483+ soup.find_all("a", text="Elsie")
1484+ # [<a href="http://example.com/elsie" class="sister" id="link1">Elsie</a>]
1485+
1486 ``limit`` 参数
1487-...............
1488+^^^^^^^^^^^^^^^^
1489
1490-``find_all()`` 方法返回全部的搜索结构,如果文档树很大那么搜索会很慢.如果我们不需要全部结果,可以使用 ``limit`` 参数限制返回结果的数量.效果与SQL中的limit关键字类似,当搜索到的结果数量达到 ``limit`` 的限制时,就停止搜索返回结果.
1491+``find_all()`` 方法会返回全部的搜索结构,如果文档树很大那么搜索会很慢。
1492+如果我们不需要全部结果,可以使用 ``limit`` 参数限制返回结果的数量。
1493+效果与SQL中的limit关键字类似,当搜索到的结果数量达到 ``limit`` 的限制时,
1494+就停止搜索返回结果。
1495
1496-文档树中有3个tag符合搜索条件,但结果只返回了2个,因为我们限制了返回数量:
1497+“爱丽丝”文档例子中有 3 个 tag 符合搜索条件,但下面例子中的结果只返回了 2 个,
1498+因为我们限制了返回数量:
1499
1500 ::
1501
1502@@ -1258,24 +1474,13 @@ tag的 ``class`` 属性是 `多值属性`_ .按照CSS类名搜索tag时,可以
1503 # [<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>,
1504 # <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>]
1505
1506-``recursive`` 参数
1507-...................
1508-
1509-调用tag的 ``find_all()`` 方法时,Beautiful Soup会检索当前tag的所有子孙节点,如果只想搜索tag的直接子节点,可以使用参数 ``recursive=False`` .
1510-
1511-一段简单的文档:
1512+.. _recursive2:
1513
1514-::
1515-
1516- <html>
1517- <head>
1518- <title>
1519- The Dormouse's story
1520- </title>
1521- </head>
1522- ...
1523+``recursive`` 参数
1524+^^^^^^^^^^^^^^^^^^^^^^^
1525
1526-是否使用 ``recursive`` 参数的搜索结果:
1527+如果调用 ``mytag.find_all()`` 方法,Beautiful Soup 会检索 ``mytag`` 的所有子孙节点,
1528+如果只想搜索直接子节点,可以使用参数 ``recursive=False``。查看下面例子
1529
1530 ::
1531
1532@@ -1285,30 +1490,32 @@ tag的 ``class`` 属性是 `多值属性`_ .按照CSS类名搜索tag时,可以
1533 soup.html.find_all("title", recursive=False)
1534 # []
1535
1536-这是文档片段
1537+下面一段简单的文档:
1538
1539 ::
1540
1541- <html>
1542- <head>
1543- <title>
1544- The Dormouse's story
1545- </title>
1546- </head>
1547- ...
1548+ <html>
1549+ <head>
1550+ <title>
1551+ The Dormouse's story
1552+ </title>
1553+ </head>
1554+ ...
1555
1556-<title>标签在 <html> 标签下, 但并不是直接子节点, <head> 标签才是直接子节点.
1557-在允许查询所有后代节点时 Beautiful Soup 能够查找到 <title> 标签.
1558-但是使用了 ``recursive=False`` 参数之后,只能查找直接子节点,这样就查不到 <title> 标签了.
1559+<title> 标签在 <html> 标签之下,但并不是直接子节点,<head> 标签才是直接子节点。
1560+在允许查询所有后代节点时 Beautiful Soup 能够查找到 <title> 标签。
1561+但是使用了 ``recursive=False`` 参数之后,只能查找直接子节点,这样就查不到 <title> 标签了。
1562
1563-Beautiful Soup 提供了多种DOM树搜索方法. 这些方法都使用了类似的参数定义.
1564+Beautiful Soup 提供了多种 DOM 树搜索方法。这些方法都使用了类似的参数定义。
1565 比如这些方法: ``find_all()``: ``name``, ``attrs``, ``text``, ``limit``.
1566-但是只有 ``find_all()`` 和 ``find()`` 支持 ``recursive`` 参数.
1567+但是只有 ``find_all()`` 和 ``find()`` 支持 ``recursive`` 参数。
1568
1569 像调用 ``find_all()`` 一样调用tag
1570 ----------------------------------
1571
1572-``find_all()`` 几乎是Beautiful Soup中最常用的搜索方法,所以我们定义了它的简写方法. ``BeautifulSoup`` 对象和 ``tag`` 对象可以被当作一个方法来使用,这个方法的执行结果与调用这个对象的 ``find_all()`` 方法相同,下面两行代码是等价的:
1573+``find_all()`` 几乎是 Beautiful Soup 中最常用的搜索方法,所以我们定义了它的简写方法。
1574+``BeautifulSoup`` 对象和 ``Tag`` 对象可以被当作一个方法来使用,这个方法的执行结果与
1575+调用这个对象的 ``find_all()`` 方法相同,下面两行代码是等价的:
1576
1577 ::
1578
1579@@ -1325,9 +1532,13 @@ Beautiful Soup 提供了多种DOM树搜索方法. 这些方法都使用了类似
1580 find()
1581 -------
1582
1583-find( `name`_ , `attrs`_ , `recursive`_ , `string`_ , `**kwargs`_ )
1584+find(`name`_ , `attrs`_ , `recursive <recursive>`_ , `string <string>`_ ,
1585+`**kwargs <kwargs>`_ )
1586
1587-``find_all()`` 方法将返回文档中符合条件的所有tag,尽管有时候我们只想得到一个结果.比如文档中只有一个<body>标签,那么使用 ``find_all()`` 方法来查找<body>标签就不太合适, 使用 ``find_all`` 方法并设置 ``limit=1`` 参数不如直接使用 ``find()`` 方法.下面两行代码是等价的:
1588+``find_all()`` 方法将返回文档中符合条件的所有 tag,尽管有时候我们只想得到一个结果。
1589+比如文档中只有一个 <body> 标签,那么使用 ``find_all()`` 方法来查找 <body> 标签就
1590+不太合适,使用 ``find_all`` 方法并设置 ``limit=1`` 参数不如直接使用 ``find()`` 方法。
1591+下面两行代码是等价的:
1592
1593 ::
1594
1595@@ -1337,16 +1548,18 @@ find( `name`_ , `attrs`_ , `recursive`_ , `string`_ , `**kwargs`_ )
1596 soup.find('title')
1597 # <title>The Dormouse's story</title>
1598
1599-唯一的区别是 ``find_all()`` 方法的返回结果是值包含一个元素的列表,而 ``find()`` 方法直接返回结果.
1600+唯一的区别是 ``find_all()`` 方法的返回结果是值包含一个元素的列表,而 ``find()`` 方法
1601+直接返回结果。
1602
1603-``find_all()`` 方法没有找到目标是返回空列表, ``find()`` 方法找不到目标时,返回 ``None`` .
1604+``find_all()`` 方法没有找到目标是返回空列表, ``find()`` 方法找不到目标时,返回 ``None``。
1605
1606 ::
1607
1608 print(soup.find("nosuchtag"))
1609 # None
1610
1611-``soup.head.title`` 是 `tag的名字`_ 方法的简写.这个简写的原理就是多次调用当前tag的 ``find()`` 方法:
1612+``soup.head.title`` 是 `Tag 的名字`_ 方法的简写。这个简写就是通过多次调用 ``find()`` 方
1613+法实现的:
1614
1615 ::
1616
1617@@ -1359,13 +1572,20 @@ find( `name`_ , `attrs`_ , `recursive`_ , `string`_ , `**kwargs`_ )
1618 find_parents() 和 find_parent()
1619 --------------------------------
1620
1621-find_parents( `name`_ , `attrs`_ , `recursive`_ , `string`_ , `**kwargs`_ )
1622+find_parents( `name`_ , `attrs`_ , `recursive <recursive>`_ ,
1623+`string <string>`_ , `**kwargs <kwargs>`_ )
1624
1625-find_parent( `name`_ , `attrs`_ , `recursive`_ , `string`_ , `**kwargs`_ )
1626+find_parent( `name`_ , `attrs`_ , `recursive <recursive>`_ ,
1627+`string <string>`_ , `**kwargs <kwargs>`_ )
1628
1629-我们已经用了很大篇幅来介绍 ``find_all()`` 和 ``find()`` 方法,Beautiful Soup中还有10个用于搜索的API.它们中的五个用的是与 ``find_all()`` 相同的搜索参数,另外5个与 ``find()`` 方法的搜索参数类似.区别仅是它们搜索文档的不同部分.
1630+我们已经用了很大篇幅来介绍 ``find_all()`` 和 ``find()`` 方法,Beautiful Soup 中
1631+还有 10 个用于搜索的 API。它们中有 5 个用的是与 ``find_all()`` 相同的搜索参数,
1632+另外 5 个与 ``find()`` 方法的搜索参数类似。区别仅是它们搜索文档的位置不同。
1633
1634-记住: ``find_all()`` 和 ``find()`` 只搜索当前节点的所有子节点,孙子节点等. ``find_parents()`` 和 ``find_parent()`` 用来搜索当前节点的父辈节点,搜索方法与普通tag的搜索方法相同,搜索文档\搜索文档包含的内容. 我们从一个文档中的一个叶子节点开始:
1635+首先来看看 ``find_parents()`` 和 ``find_parent()``。
1636+记住: ``find_all()`` 和 ``find()`` 只搜索当前节点的所有子节点,孙子节点等。
1637+而这 2 个方法刚好相反,它们用来搜索当前节点的父辈节点。
1638+我们来试试看,从例子文档中的一个深层叶子节点开始:
1639
1640 ::
1641
1642@@ -1383,21 +1603,29 @@ find_parent( `name`_ , `attrs`_ , `recursive`_ , `string`_ , `**kwargs`_ )
1643 # <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>;
1644 # and they lived at the bottom of a well.</p>
1645
1646- a_string.find_parents("p", class_="title")
1647+ a_string.find_parents("p", class="title")
1648 # []
1649
1650-文档中的一个<a>标签是是当前叶子节点的直接父节点,所以可以被找到.还有一个<p>标签,是目标叶子节点的间接父辈节点,所以也可以被找到.包含class值为"title"的<p>标签不是不是目标叶子节点的父辈节点,所以通过 ``find_parents()`` 方法搜索不到.
1651+文档中的一个 <a> 标签是是当前叶子节点的直接父节点,所以可以被找到。
1652+还有一个 <p> 标签,是目标叶子节点的间接父辈节点,所以也可以被找到。
1653+包含 class 值为 "title" 的 <p> 标签不是不是目标叶子节点的父辈节点,
1654+所以通过 ``find_parents()`` 方法搜索不到。
1655
1656-``find_parent()`` 和 ``find_parents()`` 方法会让人联想到 `.parent`_ 和 `.parents`_ 属性.它们之间的联系非常紧密.搜索父辈节点的方法实际上就是对 ``.parents`` 属性的迭代搜索.
1657+``find_parent()`` 和 ``find_parents()`` 方法会让人联想到 `.parent`_ 和 `.parents`_ 属性。
1658+它们之间的联系非常紧密。搜索父辈节点的方法实际上就是对 ``.parents`` 属性的迭代搜索。
1659
1660 find_next_siblings() 和 find_next_sibling()
1661 -------------------------------------------
1662
1663-find_next_siblings( `name`_ , `attrs`_ , `recursive`_ , `string`_ , `**kwargs`_ )
1664+find_next_siblings( `name`_ , `attrs`_ , `recursive <recursive>`_ ,
1665+`string <string>`_ , `**kwargs <kwargs>`_ )
1666
1667-find_next_sibling( `name`_ , `attrs`_ , `recursive`_ , `string`_ , `**kwargs`_ )
1668+find_next_sibling( `name`_ , `attrs`_ , `recursive <recursive>`_ ,
1669+`string <string>`_ , `**kwargs <kwargs>`_ )
1670
1671-这2个方法通过 `.next_siblings`_ 属性对当tag的所有后面解析 [5]_ 的兄弟tag节点进行迭代, ``find_next_siblings()`` 方法返回所有符合条件的后面的兄弟节点, ``find_next_sibling()`` 只返回符合条件的后面的第一个tag节点.
1672+这 2 个方法通过 `.next_siblings <sibling-generators>`_ 属性对当 tag 的所有后面解析 [5]_
1673+的兄弟tag节点进行迭代, ``find_next_siblings()`` 方法返回所有符合条件的后面的兄弟节点,
1674+``find_next_sibling()`` 只返回符合条件的后面的第一个 tag 节点。
1675
1676 ::
1677
1678@@ -1416,11 +1644,15 @@ find_next_sibling( `name`_ , `attrs`_ , `recursive`_ , `string`_ , `**kwargs`_ )
1679 find_previous_siblings() 和 find_previous_sibling()
1680 -----------------------------------------------------
1681
1682-find_previous_siblings( `name`_ , `attrs`_ , `recursive`_ , `string`_ , `**kwargs`_ )
1683+find_previous_siblings( `name`_ , `attrs`_ , `recursive <recursive>`_ ,
1684+`string <string>`_ , `**kwargs <kwargs>`_ )
1685
1686-find_previous_sibling( `name`_ , `attrs`_ , `recursive`_ , `string`_ , `**kwargs`_ )
1687+find_previous_sibling( `name`_ , `attrs`_ , `recursive <recursive>`_ ,
1688+`string <string>`_ , `**kwargs <kwargs>`_ )
1689
1690-这2个方法通过 `.previous_siblings`_ 属性对当前tag的前面解析 [5]_ 的兄弟tag节点进行迭代, ``find_previous_siblings()`` 方法返回所有符合条件的前面的兄弟节点, ``find_previous_sibling()`` 方法返回第一个符合条件的前面的兄弟节点:
1691+这 2 个方法通过 `.previous_siblings <sibling-generators>`_ 属性对当前 tag 的前面解析 [5]_
1692+的兄弟 tag 节点进行迭代, ``find_previous_siblings()`` 方法返回所有符合条件的前面的兄弟节点,
1693+``find_previous_sibling()`` 方法返回第一个符合条件的前面的兄弟节点:
1694
1695 ::
1696
1697@@ -1439,11 +1671,15 @@ find_previous_sibling( `name`_ , `attrs`_ , `recursive`_ , `string`_ , `**kwargs
1698 find_all_next() 和 find_next()
1699 --------------------------------
1700
1701-find_all_next( `name`_ , `attrs`_ , `recursive`_ , `string`_ , `**kwargs`_ )
1702+find_all_next( `name`_ , `attrs`_ , `recursive <recursive>`_ ,
1703+`string <string>`_ , `**kwargs <kwargs>`_ )
1704
1705-find_next( `name`_ , `attrs`_ , `recursive`_ , `string`_ , `**kwargs`_ )
1706+find_next( `name`_ , `attrs`_ , `recursive <recursive>`_ ,
1707+`string <string>`_ , `**kwargs <kwargs>`_ )
1708
1709-这2个方法通过 `.next_elements`_ 属性对当前tag的之后的 [5]_ tag和字符串进行迭代, ``find_all_next()`` 方法返回所有符合条件的节点, ``find_next()`` 方法返回第一个符合条件的节点:
1710+这 2 个方法通过 `.next_elements <element-generators>`_ 属性对当前 tag 的之后的 [5]_
1711+tag 和字符串进行迭代, ``find_all_next()`` 方法返回所有符合条件的节点, ``find_next()``
1712+方法返回第一个符合条件的节点:
1713
1714 ::
1715
1716@@ -1458,16 +1694,22 @@ find_next( `name`_ , `attrs`_ , `recursive`_ , `string`_ , `**kwargs`_ )
1717 first_link.find_next("p")
1718 # <p class="story">...</p>
1719
1720-第一个例子中,字符串 “Elsie”也被显示出来,尽管它被包含在我们开始查找的<a>标签的里面.第二个例子中,最后一个<p>标签也被显示出来,尽管它与我们开始查找位置的<a>标签不属于同一部分.例子中,搜索的重点是要匹配过滤器的条件,并且在文档中出现的顺序而不是开始查找的元素的位置.
1721+第一个例子中,字符串 “Elsie”也被显示出来,尽管它被包含在我们开始查找的 <a> 标签的里面。
1722+第二个例子中,最后一个<p>标签也被显示出来,尽管它与我们开始查找位置的 <a> 标签不属于同一部分。
1723+例子中,搜索的重点是要匹配过滤器的条件,以及元素在文档中出现的顺序要在查找的元素的之后。
1724
1725 find_all_previous() 和 find_previous()
1726 ---------------------------------------
1727
1728-find_all_previous( `name`_ , `attrs`_ , `recursive`_ , `string`_ , `**kwargs`_ )
1729+find_all_previous( `name`_ , `attrs`_ , `recursive <recursive>`_ ,
1730+`string <string>`_ , `**kwargs <kwargs>`_ )
1731
1732-find_previous( `name`_ , `attrs`_ , `recursive`_ , `string`_ , `**kwargs`_ )
1733+find_previous( `name`_ , `attrs`_ , `recursive <recursive>`_ ,
1734+`string <string>`_ , `**kwargs <kwargs>`_ )
1735
1736-这2个方法通过 `.previous_elements`_ 属性对当前节点前面 [5]_ 的tag和字符串进行迭代, ``find_all_previous()`` 方法返回所有符合条件的节点, ``find_previous()`` 方法返回第一个符合条件的节点.
1737+这 2 个方法通过 `.previous_elements <sibling-generators>`_ 属性对当前节点前面 [5]_ 的
1738+tag 和字符串进行迭代, ``find_all_previous()`` 方法返回所有符合条件的节点, ``find_previous()``
1739+方法返回第一个符合条件的节点。
1740
1741 ::
1742
1743@@ -1482,105 +1724,110 @@ find_previous( `name`_ , `attrs`_ , `recursive`_ , `string`_ , `**kwargs`_ )
1744 first_link.find_previous("title")
1745 # <title>The Dormouse's story</title>
1746
1747-``find_all_previous("p")`` 返回了文档中的第一段(class="title"的那段),但还返回了第二段,<p>标签包含了我们开始查找的<a>标签.不要惊讶,这段代码的功能是查找所有出现在指定<a>标签之前的<p>标签,因为这个<p>标签包含了开始的<a>标签,所以<p>标签一定是在<a>之前出现的.
1748+``find_all_previous("p")`` 既返回了文档中的第一段(class="title"的那段),还返回了第二段,
1749+包含了我们开始查找的 <a> 标签的那段。不用惊讶,这段代码的功能是查找所有出现在指定 <a> 标签之前
1750+的 <p> 标签,因为这个 <p> 标签包含了开始的 <a> 标签,所以 <p> 标签当然是在 <a> 之前出现的。
1751
1752-CSS选择器
1753+CSS 选择器
1754 ------------
1755
1756-Beautiful Soup支持大部分的CSS选择器 `<http://www.w3.org/TR/CSS2/selector.html>`_ [6]_ ,
1757-在 ``Tag`` 或 ``BeautifulSoup`` 对象的 ``.select()`` 方法中传入字符串参数,
1758-即可使用CSS选择器的语法找到tag:
1759+BeautifulSoup 对象和 Tag 对象支持通过 ``.css`` 属性实现 CSS 选择器。具体选择功能是通过
1760+`Soup Sieve <https://facelessuser.github.io/soupsieve/>`_ 库实现的,在 PyPI 上通
1761+过关键字 ``soupsieve`` 可以找到。通过 pip 安装 Beautiful Soup 时,Soup Sieve 也会自
1762+动安装,不用其它额外操作。
1763+
1764+Soup Sieve 文档列出了 `当前支持的 CSS 选择器 <https://facelessuser.github.io/soupsieve/selectors/>`_,
1765+下面是一些基本应用
1766
1767 ::
1768
1769- soup.select("title")
1770+ soup.css.select("title")
1771 # [<title>The Dormouse's story</title>]
1772
1773- soup.select("p:nth-of-type(3)")
1774+ soup.css.select("p:nth-of-type(3)")
1775 # [<p class="story">...</p>]
1776
1777-通过tag标签逐层查找:
1778+查找指定层级的 tag:
1779
1780 ::
1781
1782- soup.select("body a")
1783+ soup.css.select("body a")
1784 # [<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>,
1785 # <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>,
1786 # <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>]
1787
1788- soup.select("html head title")
1789+ soup.css.select("html head title")
1790 # [<title>The Dormouse's story</title>]
1791
1792-找到某个tag标签下的直接子标签 [6]_ :
1793+找到某个 tag 标签下的直接子标签 [6]_ :
1794
1795 ::
1796
1797- soup.select("head > title")
1798+ soup.css.select("head > title")
1799 # [<title>The Dormouse's story</title>]
1800
1801- soup.select("p > a")
1802+ soup.css.select("p > a")
1803 # [<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>,
1804 # <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>,
1805 # <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>]
1806
1807- soup.select("p > a:nth-of-type(2)")
1808+ soup.css.select("p > a:nth-of-type(2)")
1809 # [<a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>]
1810
1811- soup.select("p > #link1")
1812+ soup.css.select("p > #link1")
1813 # [<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>]
1814
1815- soup.select("body > a")
1816+ soup.css.select("body > a")
1817 # []
1818
1819 找到兄弟节点标签:
1820
1821 ::
1822
1823- soup.select("#link1 ~ .sister")
1824+ soup.css.select("#link1 ~ .sister")
1825 # [<a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>,
1826 # <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>]
1827
1828- soup.select("#link1 + .sister")
1829+ soup.css.select("#link1 + .sister")
1830 # [<a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>]
1831
1832-通过CSS的类名查找:
1833+通过 CSS 的类名查找:
1834
1835 ::
1836
1837- soup.select(".sister")
1838+ soup.css.select(".sister")
1839 # [<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>,
1840 # <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>,
1841 # <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>]
1842
1843- soup.select("[class~=sister]")
1844+ soup.css.select("[class~=sister]")
1845 # [<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>,
1846 # <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>,
1847 # <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>]
1848
1849-通过tag的id查找:
1850+通过 id 查找 tag:
1851
1852 ::
1853
1854- soup.select("#link1")
1855+ soup.css.select("#link1")
1856 # [<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>]
1857
1858- soup.select("a#link2")
1859+ soup.css.select("a#link2")
1860 # [<a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>]
1861
1862-同时用多种CSS选择器查询元素:
1863+查找符合列表中任意一个选择器的 tag:
1864
1865 ::
1866
1867- soup.select("#link1,#link2")
1868- # [<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>,
1869- # <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>]
1870-
1871+ soup.css.select("#link1,#link2")
1872+ # [<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>,
1873+ # <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>]
1874
1875 通过是否存在某个属性来查找:
1876
1877 ::
1878
1879- soup.select('a[href]')
1880+ soup.css.select('a[href]')
1881 # [<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>,
1882 # <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>,
1883 # <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>]
1884@@ -1589,62 +1836,143 @@ Beautiful Soup支持大部分的CSS选择器 `<http://www.w3.org/TR/CSS2/selecto
1885
1886 ::
1887
1888- soup.select('a[href="http://example.com/elsie"]')
1889+ soup.css.select('a[href="http://example.com/elsie"]')
1890 # [<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>]
1891
1892- soup.select('a[href^="http://example.com/"]')
1893+ soup.css.select('a[href^="http://example.com/"]')
1894 # [<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>,
1895 # <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>,
1896 # <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>]
1897
1898- soup.select('a[href$="tillie"]')
1899+ soup.css.select('a[href$="tillie"]')
1900 # [<a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>]
1901
1902- soup.select('a[href*=".com/el"]')
1903+ soup.css.select('a[href*=".com/el"]')
1904 # [<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>]
1905
1906-通过语言设置来查找:
1907+还有一个 ``select_one()`` 方法,它会返回符合筛选条件的元素列表中的第一个
1908
1909 ::
1910
1911- multilingual_markup = """
1912- <p lang="en">Hello</p>
1913- <p lang="en-us">Howdy, y'all</p>
1914- <p lang="en-gb">Pip-pip, old fruit</p>
1915- <p lang="fr">Bonjour mes amis</p>
1916- """
1917- multilingual_soup = BeautifulSoup(multilingual_markup)
1918- multilingual_soup.select('p[lang|=en]')
1919- # [<p lang="en">Hello</p>,
1920- # <p lang="en-us">Howdy, y'all</p>,
1921- # <p lang="en-gb">Pip-pip, old fruit</p>]
1922+ soup.css.select_one(".sister")
1923+ # <a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>
1924
1925-返回查找到的元素的第一个
1926+为了方便使用,在 BeautifulSoup 或 Tag 对象上直接调用 ``select()`` 和 ``select_one()`` 方法,
1927+中间省略 ``.css`` 属性
1928
1929 ::
1930
1931- soup.select_one(".sister")
1932- # <a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>
1933+ soup.select('a[href$="tillie"]')
1934+ # [<a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>]
1935
1936+ soup.select_one(".sister")
1937+ # <a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>
1938+
1939+CSS 选择器对于熟悉 CSS 语法的人来说非常方便。你可以在 Beautiful Soup 中使用相同的方法。
1940+但是如果你只需要使用 CSS 选择器就够了,那么应该 ``lxml`` 作为文档解析器:因为速度快很多。
1941+但是 Soup Sieve 也有优势,它允许 `组合` 使用 CSS 选择器和 Beautiful Soup 的 API。
1942+
1943+Soup Sieve 高级特性
1944+-------------------
1945+
1946+Soup Sieve 提供的是比 ``select()`` 和 ``select_one()`` 更底层的方法,通过 Tag 或
1947+Beautiful Soup 对象的 ``.css`` 属性,可以调用大部分的 API。下面是支持这种调用方式的方法列表,
1948+查看 `Soup Sieve <https://facelessuser.github.io/soupsieve/>`_ 文档了解全部细节。
1949+
1950+``iselect()`` 方法与 ``select()`` 效果相同,区别是返回的结果是迭代器。
1951+::
1952+
1953+ [tag['id'] for tag in soup.css.iselect(".sister")]
1954+ # ['link1', 'link2', 'link3']
1955+
1956+``closest()`` 方法与 ``find_parent()`` 方法相似,返回符合 CSS 选择器的 Tag 对象的最近父级。
1957+
1958+::
1959+
1960+ elsie = soup.css.select_one(".sister")
1961+ elsie.css.closest("p.story")
1962+ # <p class="story">Once upon a time there were three little sisters; and their names were
1963+ # <a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>,
1964+ # <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a> and
1965+ # <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>;
1966+ # and they lived at the bottom of a well.</p>
1967+
1968+``match()`` 方法返回布尔结果,标记指定 Tag 是否符合指定筛选器
1969+
1970+::
1971+
1972+ # elsie.css.match("#link1")
1973+ True
1974+
1975+ # elsie.css.match("#link2")
1976+ False
1977+
1978+``filter()`` 方法返回 tag 直接子节点中符合筛选器的节点列表
1979+
1980+::
1981+
1982+ [tag.string for tag in soup.find('p', 'story').css.filter('a')]
1983+ # ['Elsie', 'Lacie', 'Tillie']
1984+
1985+``escape()`` 方法可以对 CSS 标识符中的特殊字符进行转义,否则是非法 CSS 标识符
1986
1987-对于熟悉CSS选择器语法的人来说这是个非常方便的方法.Beautiful Soup也支持CSS选择器API,
1988-如果你仅仅需要CSS选择器的功能,那么直接使用 ``lxml`` 也可以,
1989-而且速度更快,支持更多的CSS选择器语法,但Beautiful Soup整合了CSS选择器的语法和自身方便使用API.
1990+::
1991+
1992+ soup.css.escape("1-strange-identifier")
1993+ # '\\31 -strange-identifier'
1994+
1995+CSS 筛选器中的命名空间
1996+------------------------
1997+
1998+如果解析的 XML 文档中定义了命名空间,那么 CSS 筛选器中也可以使用
1999+
2000+::
2001+
2002+ from bs4 import BeautifulSoup
2003+ xml = """<tag xmlns:ns1="http://namespace1/" xmlns:ns2="http://namespace2/">
2004+ <ns1:child>I'm in namespace 1</ns1:child>
2005+ <ns2:child>I'm in namespace 2</ns2:child>
2006+ </tag> """
2007+ namespace_soup = BeautifulSoup(xml, "xml")
2008+
2009+ namespace_soup.css.select("child")
2010+ # [<ns1:child>I'm in namespace 1</ns1:child>, <ns2:child>I'm in namespace 2</ns2:child>]
2011+
2012+ namespace_soup.css.select("ns1|child")
2013+ # [<ns1:child>I'm in namespace 1</ns1:child>]
2014+
2015+Beautiful Soup 尝试自动匹配解析文档中的命名空间前缀,除此之外,你还可以自定义目录的缩写
2016+
2017+::
2018+
2019+ namespaces = dict(first="http://namespace1/", second="http://namespace2/")
2020+ namespace_soup.css.select("second|child", namespaces=namespaces)
2021+ # [<ns1:child>I'm in namespace 2</ns1:child>]
2022+
2023+支持 CSS 筛选器的历史版本
2024+-------------------------
2025+
2026+``.css`` 属性是在 Beautiful Soup 4.12.0 中添加的。在此之前,只能使用 ``.select()`` 和
2027+ ``.select_one()`` 方法。
2028+
2029+Soup Sieve 是在 Beautiful Soup 4.7.0 开始集成的。早期版本中有 ``.select()`` 方法,但
2030+仅能支持最常用的 CSS 选择器。
2031
2032
2033 修改文档树
2034 ===========
2035
2036-Beautiful Soup的强项是文档树的搜索,但同时也可以方便的修改文档树
2037+Beautiful Soup 的强项是文档树的搜索,但也支持修改文档数,或者编写新的 HTML、XML 文档。
2038
2039-修改tag的名称和属性
2040--------------------
2041+修改 tag 的名称和属性
2042+----------------------
2043
2044-在 `Attributes`_ 的章节中已经介绍过这个功能,但是再看一遍也无妨. 重命名一个tag,改变属性的值,添加或删除属性:
2045+在 :py:attr:`Tag.attrs` 的章节中已经介绍过这个功能,但是再看一遍也无妨。重命名一个 tag,
2046+改变属性的值,添加或删除属性
2047
2048 ::
2049
2050- soup = BeautifulSoup('<b class="boldest">Extremely bold</b>')
2051+ soup = BeautifulSoup('<b class="boldest">Extremely bold</b>', 'html.parser')
2052 tag = soup.b
2053
2054 tag.name = "blockquote"
2055@@ -1658,47 +1986,64 @@ Beautiful Soup的强项是文档树的搜索,但同时也可以方便的修改
2056 tag
2057 # <blockquote>Extremely bold</blockquote>
2058
2059-修改 .string
2060--------------
2061+修改 ``.string``
2062+------------------
2063
2064-给tag的 ``.string`` 属性赋值,就相当于用当前的内容替代了原来的内容:
2065+如果设置 tag 的 ``.string`` 属性值,就相当于用新的内容替代了原来的内容:
2066
2067 ::
2068
2069 markup = '<a href="http://example.com/">I linked to <i>example.com</i></a>'
2070- soup = BeautifulSoup(markup)
2071+ soup = BeautifulSoup(markup, 'html.parser')
2072
2073 tag = soup.a
2074 tag.string = "New link text."
2075 tag
2076 # <a href="http://example.com/">New link text.</a>
2077
2078-注意: 如果当前的tag包含了其它tag,那么给它的 ``.string`` 属性赋值会覆盖掉原有的所有内容包括子tag
2079+注意:如果 tag 原本包含了其它子节点,原有的所有内容包括子 tag 都会被覆盖掉。
2080
2081-append()
2082-----------
2083+``append()``
2084+--------------
2085
2086-``Tag.append()`` 方法想tag中添加内容,就好像Python的列表的 ``.append()`` 方法:
2087+向 tag 中添加内容可以使用 ``Tag.append()`` 方法,就好像调用 Python 列表的 ``.append()`` 方法:
2088
2089 ::
2090
2091- soup = BeautifulSoup("<a>Foo</a>")
2092+ soup = BeautifulSoup("<a>Foo</a>", 'html.parser')
2093 soup.a.append("Bar")
2094
2095 soup
2096- # <html><head></head><body><a>FooBar</a></body></html>
2097+ # <a>FooBar</a>
2098+ soup.a.contents
2099+ # ['Foo', 'Bar']
2100+
2101+``extend()``
2102+--------------
2103+
2104+从 Beautiful Soup 4.7.0 版本开始,tag 增加了 ``.extend()`` 方法,可以把一个列表中内容,
2105+按顺序全部添加到一个 tag 当中
2106+
2107+::
2108+
2109+ soup = BeautifulSoup("<a>Soup</a>", 'html.parser')
2110+ soup.a.extend(["'s", " ", "on"])
2111+
2112+ soup
2113+ # <a>Soup's on</a>
2114 soup.a.contents
2115- # [u'Foo', u'Bar']
2116+ # ['Soup', ''s', ' ', 'on']
2117
2118 NavigableString() 和 .new_tag()
2119 -----------------------------------------
2120
2121-如果想添加一段文本内容到文档中也没问题,可以调用Python的 ``append()`` 方法
2122-或调用 ``NavigableString`` 的构造方法:
2123+如果想添加一段文本内容到文档中,可以将一个 Python 字符串对象传给 ``append()`` 方法,
2124+或调用 ``NavigableString`` 构造方法:
2125
2126 ::
2127
2128- soup = BeautifulSoup("<b></b>")
2129+ from bs4 import NavigableString
2130+ soup = BeautifulSoup("<b></b>", 'html.parser')
2131 tag = soup.b
2132 tag.append("Hello")
2133 new_string = NavigableString(" there")
2134@@ -1706,27 +2051,27 @@ NavigableString() 和 .new_tag()
2135 tag
2136 # <b>Hello there.</b>
2137 tag.contents
2138- # [u'Hello', u' there']
2139+ # ['Hello', ' there']
2140
2141-如果想要创建一段注释,或 ``NavigableString`` 的任何子类, 只要调用 NavigableString 的构造方法:
2142+如果想要创建一段注释,或其它 ``NavigableString`` 的子类,只要调用构造方法:
2143
2144 ::
2145
2146 from bs4 import Comment
2147- new_comment = soup.new_string("Nice to see you.", Comment)
2148+ new_comment = Comment("Nice to see you.")
2149 tag.append(new_comment)
2150 tag
2151 # <b>Hello there<!--Nice to see you.--></b>
2152 tag.contents
2153- # [u'Hello', u' there', u'Nice to see you.']
2154+ # ['Hello', ' there', 'Nice to see you.']
2155
2156-# 这是Beautiful Soup 4.2.1 中新增的方法
2157+`(这是 Beautiful Soup 4.4.0 中新增的方法)`
2158
2159-创建一个tag最好的方法是调用工厂方法 ``BeautifulSoup.new_tag()`` :
2160+如果需要新创建一个 tag,最好的方法是调用工厂方法 ``BeautifulSoup.new_tag()``
2161
2162 ::
2163
2164- soup = BeautifulSoup("<b></b>")
2165+ soup = BeautifulSoup("<b></b>", 'html.parser')
2166 original_tag = soup.b
2167
2168 new_tag = soup.new_tag("a", href="http://www.example.com")
2169@@ -1738,58 +2083,62 @@ NavigableString() 和 .new_tag()
2170 original_tag
2171 # <b><a href="http://www.example.com">Link text.</a></b>
2172
2173-第一个参数作为tag的name,是必填,其它参数选填
2174+只有第一个参数用作 tag 的 name,是必填的。
2175
2176 insert()
2177 --------
2178
2179-``Tag.insert()`` 方法与 ``Tag.append()`` 方法类似,区别是不会把新元素添加到父节点 ``.contents`` 属性的最后,而是把元素插入到指定的位置.与Python列表总的 ``.insert()`` 方法的用法下同:
2180+``Tag.insert()`` 方法与 ``Tag.append()`` 方法类似,区别是不会把新元素添加到
2181+父节点 ``.contents`` 属性的最后。而是把元素插入到按顺序指定的位置。与 Python 列表
2182+中的 ``.insert()`` 方法的用法相同
2183
2184 ::
2185
2186 markup = '<a href="http://example.com/">I linked to <i>example.com</i></a>'
2187- soup = BeautifulSoup(markup)
2188+ soup = BeautifulSoup(markup, 'html.parser')
2189 tag = soup.a
2190
2191 tag.insert(1, "but did not endorse ")
2192 tag
2193 # <a href="http://example.com/">I linked to but did not endorse <i>example.com</i></a>
2194 tag.contents
2195- # [u'I linked to ', u'but did not endorse', <i>example.com</i>]
2196+ # ['I linked to ', 'but did not endorse', <i>example.com</i>]
2197
2198 insert_before() 和 insert_after()
2199 -----------------------------------
2200
2201-``insert_before()`` 方法在当前tag或文本节点前插入内容:
2202+``insert_before()`` 方法可以在文档树中直接在目标之前添加 tag 或文本
2203
2204 ::
2205
2206- soup = BeautifulSoup("<b>stop</b>")
2207+ soup = BeautifulSoup("<b>leave</b>", 'html.parser')
2208 tag = soup.new_tag("i")
2209 tag.string = "Don't"
2210 soup.b.string.insert_before(tag)
2211 soup.b
2212- # <b><i>Don't</i>stop</b>
2213+ # <b><i>Don't</i>leave</b>
2214
2215-``insert_after()`` 方法在当前tag或文本节点后插入内容:
2216+``insert_after()`` 方法可以在文档树中直接在目标之后添加 tag 或文本
2217
2218 ::
2219
2220- soup.b.i.insert_after(soup.new_string(" ever "))
2221+ div = soup.new_tag('div')
2222+ div.string = 'ever'
2223+ soup.b.i.insert_after(" you ", div)
2224 soup.b
2225- # <b><i>Don't</i> ever stop</b>
2226+ # <b><i>Don't</i> you <div>ever</div> leave</b>
2227 soup.b.contents
2228- # [<i>Don't</i>, u' ever ', u'stop']
2229+ # [<i>Don't</i>, ' you', <div>ever</div>, 'leave']
2230
2231 clear()
2232 --------
2233
2234-``Tag.clear()`` 方法移除当前tag的内容:
2235+``Tag.clear()`` 方法可以移除 tag 的内容:
2236
2237 ::
2238
2239 markup = '<a href="http://example.com/">I linked to <i>example.com</i></a>'
2240- soup = BeautifulSoup(markup)
2241+ soup = BeautifulSoup(markup, 'html.parser')
2242 tag = soup.a
2243
2244 tag.clear()
2245@@ -1799,12 +2148,12 @@ clear()
2246 extract()
2247 ----------
2248
2249-``PageElement.extract()`` 方法将当前tag移除文档树,并作为方法结果返回:
2250+``PageElement.extract()`` 方法将当前 tag 或文本从文档树中移除,并返回被删除的内容:
2251
2252 ::
2253
2254 markup = '<a href="http://example.com/">I linked to <i>example.com</i></a>'
2255- soup = BeautifulSoup(markup)
2256+ soup = BeautifulSoup(markup, 'html.parser')
2257 a_tag = soup.a
2258
2259 i_tag = soup.i.extract()
2260@@ -1816,15 +2165,16 @@ extract()
2261 # <i>example.com</i>
2262
2263 print(i_tag.parent)
2264- None
2265+ # None
2266
2267-这个方法实际上产生了2个文档树: 一个是用来解析原始文档的 ``BeautifulSoup`` 对象,另一个是被移除并且返回的tag.被移除并返回的tag可以继续调用 ``extract`` 方法:
2268+这个方法实际上产生了 2 个文档树: 一个是原始文档的 ``BeautifulSoup`` 对象,
2269+另一个是被移除并且返回的文档树。还可以在新生成的文档树上继续调用 ``extract`` 方法:
2270
2271 ::
2272
2273 my_string = i_tag.string.extract()
2274 my_string
2275- # u'example.com'
2276+ # 'example.com'
2277
2278 print(my_string.parent)
2279 # None
2280@@ -1834,71 +2184,135 @@ extract()
2281 decompose()
2282 ------------
2283
2284-``Tag.decompose()`` 方法将当前节点移除文档树并完全销毁:
2285+``Tag.decompose()`` 方法会将前节点从文档书中移除并完全销毁:
2286
2287 ::
2288
2289 markup = '<a href="http://example.com/">I linked to <i>example.com</i></a>'
2290- soup = BeautifulSoup(markup)
2291+ soup = BeautifulSoup(markup, 'html.parser')
2292 a_tag = soup.a
2293+ i_tag = soup.i
2294
2295- soup.i.decompose()
2296-
2297+ i_tag.decompose()
2298 a_tag
2299 # <a href="http://example.com/">I linked to</a>
2300
2301+被 decompose 的 Tag 或者 `NavigableString` 是不稳定的,什么时候都不要使用它。如果不确定
2302+某些内容是否被 decompose 了,可以通过 ``.decomposed`` 属性进行检查 `(Beautiful Soup 4.9.0 新增)`
2303+
2304+::
2305+
2306+ i_tag.decomposed
2307+ # True
2308+
2309+ a_tag.decomposed
2310+ # False
2311+
2312+.. _replace_with():
2313+
2314 replace_with()
2315 ---------------
2316
2317-``PageElement.replace_with()`` 方法移除文档树中的某段内容,并用新tag或文本节点替代它:
2318+``PageElement.replace_with()`` 方法移除文档树中的某段内容,并用新 tag 或文本节点替代它:
2319
2320 ::
2321
2322 markup = '<a href="http://example.com/">I linked to <i>example.com</i></a>'
2323- soup = BeautifulSoup(markup)
2324+ soup = BeautifulSoup(markup, 'html.parser')
2325 a_tag = soup.a
2326
2327 new_tag = soup.new_tag("b")
2328- new_tag.string = "example.net"
2329+ new_tag.string = "example.com"
2330 a_tag.i.replace_with(new_tag)
2331
2332 a_tag
2333- # <a href="http://example.com/">I linked to <b>example.net</b></a>
2334+ # <a href="http://example.com/">I linked to <b>example.com</b></a>
2335+
2336+ bold_tag = soup.new_tag("b")
2337+ bold_tag.string = "example"
2338+ i_tag = soup.new_tag("i")
2339+ i_tag.string = "net"
2340+ a_tag.b.replace_with(bold_tag, ".", i_tag)
2341+
2342+ a_tag
2343+ # <a href="http://example.com/">I linked to <b>example</b>.<i>net</i></a>
2344
2345-``replace_with()`` 方法返回被替代的tag或文本节点,可以用来浏览或添加到文档树其它地方
2346+``replace_with()`` 方法返回被替代的 tag 或文本节点,可以用来检查或添加到文档树其它地方。
2347+
2348+`传递多个参数给 replace_with() 方法在 Beautiful Soup 4.10.0 版本中新增`
2349
2350 wrap()
2351-------
2352+--------
2353
2354-``PageElement.wrap()`` 方法可以对指定的tag元素进行包装 [8]_ ,并返回包装后的结果:
2355+``PageElement.wrap()`` 方法可以对指定的tag元素进行包装 [8]_ ,并返回包装后的结果:
2356
2357 ::
2358
2359- soup = BeautifulSoup("<p>I wish I was bold.</p>")
2360+ soup = BeautifulSoup("<p>I wish I was bold.</p>", 'html.parser')
2361 soup.p.string.wrap(soup.new_tag("b"))
2362 # <b>I wish I was bold.</b>
2363
2364 soup.p.wrap(soup.new_tag("div"))
2365 # <div><p><b>I wish I was bold.</b></p></div>
2366
2367-该方法在 Beautiful Soup 4.0.5 中添加
2368+该方法在 Beautiful Soup 4.0.5 中添加。
2369
2370 unwrap()
2371 ---------
2372
2373-``Tag.unwrap()`` 方法与 ``wrap()`` 方法相反.将移除tag内的所有tag标签,该方法常被用来进行标记的解包:
2374+``Tag.unwrap()`` 方法与 ``wrap()`` 方法相反。它将用 tag 内内容来替换 tag 本身,
2375+该方法常被用来解包内容:
2376
2377 ::
2378
2379 markup = '<a href="http://example.com/">I linked to <i>example.com</i></a>'
2380- soup = BeautifulSoup(markup)
2381+ soup = BeautifulSoup(markup, 'html.parser')
2382 a_tag = soup.a
2383
2384 a_tag.i.unwrap()
2385 a_tag
2386 # <a href="http://example.com/">I linked to example.com</a>
2387
2388-与 ``replace_with()`` 方法相同, ``unwrap()`` 方法返回被移除的tag
2389+与 ``replace_with()`` 方法相同,``unwrap()`` 方法会返回被移除的 tag。
2390+
2391+smooth()
2392+----------
2393+
2394+调用了一堆修改文档树的方法后,可能剩下的是 2 个或更多个彼此衔接的 NavigableString 对象。
2395+Beautiful Soup 处理起来没有问题,但在刚刚解析的文档树中,可能会出现非预期情况
2396+
2397+::
2398+
2399+ soup = BeautifulSoup("<p>A one</p>", 'html.parser')
2400+ soup.p.append(", a two")
2401+
2402+ soup.p.contents
2403+ # ['A one', ', a two']
2404+
2405+ print(soup.p.encode())
2406+ # b'<p>A one, a two</p>'
2407+
2408+ print(soup.p.prettify())
2409+ # <p>
2410+ # A one
2411+ # , a two
2412+ # </p>
2413+
2414+这时可以使用 ``Tag.smooth()`` 方法来清理文档树,把相邻的字符串平滑的链接到一起
2415+
2416+::
2417+
2418+ soup.smooth()
2419+
2420+ soup.p.contents
2421+ # ['A one, a two']
2422+
2423+ print(soup.p.prettify())
2424+ # <p>
2425+ # A one, a two
2426+ # </p>
2427+
2428+该方法在 Beautiful Soup 4.8.0 中添加。
2429
2430 输出
2431 ====
2432@@ -1906,12 +2320,13 @@ unwrap()
2433 格式化输出
2434 -----------
2435
2436-``prettify()`` 方法将Beautiful Soup的文档树格式化后以Unicode编码输出,每个XML/HTML标签都独占一行
2437+``prettify()`` 方法将 Beautiful Soup 的文档树格式化后以 Unicode 编码输出,
2438+每个 XML/HTML 标签都独占一行
2439
2440 ::
2441
2442- markup = '<a href="http://example.com/">I linked to <i>example.com</i></a>'
2443- soup = BeautifulSoup(markup)
2444+ markup = '<html><head><body><a href="http://example.com/">I linked to <i>example.com</i></a>'
2445+ soup = BeautifulSoup(markup, 'html.parser')
2446 soup.prettify()
2447 # '<html>\n <head>\n </head>\n <body>\n <a href="http://example.com/">\n...'
2448
2449@@ -1929,7 +2344,7 @@ unwrap()
2450 # </body>
2451 # </html>
2452
2453-``BeautifulSoup`` 对象和它的tag节点都可以调用 ``prettify()`` 方法:
2454+``BeautifulSoup`` 对象的根节点和它的所有 tag 节点都可以调用 ``prettify()`` 方法:
2455
2456 ::
2457
2458@@ -1941,237 +2356,439 @@ unwrap()
2459 # </i>
2460 # </a>
2461
2462+因为格式化会添加额外的空格(为了换行显示),因为 ``prettify()`` 会改变 HTML 文档的内容,
2463+所以不要用来格式化文档。 ``prettify()`` 方法的设计目标是为了帮助更好的显示和理解文档。
2464+
2465 压缩输出
2466 ----------
2467
2468-如果只想得到结果字符串,不重视格式,那么可以对一个 ``BeautifulSoup`` 对象或 ``Tag`` 对象使用Python的 ``unicode()`` 或 ``str()`` 方法:
2469+如果只想得到结果字符串,不重视格式,那么可以对一个 ``BeautifulSoup`` 对象或 ``Tag`` 对象
2470+使用 Python 的 ``unicode()`` 或 ``str()`` 方法:
2471
2472 ::
2473
2474 str(soup)
2475 # '<html><head></head><body><a href="http://example.com/">I linked to <i>example.com</i></a></body></html>'
2476
2477- unicode(soup.a)
2478- # u'<a href="http://example.com/">I linked to <i>example.com</i></a>'
2479+ str(soup.a)
2480+ # '<a href="http://example.com/">I linked to <i>example.com</i></a>'
2481
2482-``str()`` 方法返回UTF-8编码的字符串,可以指定 `编码`_ 的设置.
2483+``str()`` 方法返回 UTF-8 编码的字符串,查看定 `编码`_ 了解更多选项。
2484
2485-还可以调用 ``encode()`` 方法获得字节码或调用 ``decode()`` 方法获得Unicode.
2486+还可以调用 ``encode()`` 方法获得字节码或调用 ``decode()`` 方法获得Unicode。
2487
2488 输出格式
2489 ---------
2490
2491-Beautiful Soup输出是会将HTML中的特殊字符转换成Unicode,比如“&lquot;”:
2492+Beautiful Soup 输出是会将 HTML 中的特殊字符编码转换成 Unicode, 比如 “&lquot;”:
2493
2494 ::
2495
2496- soup = BeautifulSoup("&ldquo;Dammit!&rdquo; he said.")
2497- unicode(soup)
2498- # u'<html><head></head><body>\u201cDammit!\u201d he said.</body></html>'
2499+ soup = BeautifulSoup("&ldquo;Dammit!&rdquo; he said.", 'html.parser')
2500+ str(soup)
2501+ # '“Dammit!” he said.'
2502
2503-如果将文档转换成字符串,Unicode编码会被编码成UTF-8.这样就无法正确显示HTML特殊字符了:
2504+如果将文档转换成字节编码,那么字节码 Unicode 会被编码成 UTF-8。并且无法再转换回 html 中的特殊字符编码:
2505
2506 ::
2507
2508- str(soup)
2509- # '<html><head></head><body>\xe2\x80\x9cDammit!\xe2\x80\x9d he said.</body></html>'
2510+ soup.encode("utf8")
2511+ # b'\xe2\x80\x9cDammit!\xe2\x80\x9d he said.'
2512+
2513+默认情况下,只会转义 & 符号和尖角号。它们会被转义为 "&amp;","&lt;" 和 "&gt;",因此 Beautiful Soup
2514+不会无意间生成错误格式的的 HTML 或 XML
2515+
2516+::
2517+
2518+ soup = BeautifulSoup("<p>The law firm of Dewey, Cheatem, & Howe</p>", 'html.parser')
2519+ soup.p
2520+ # <p>The law firm of Dewey, Cheatem, &amp; Howe</p>
2521+
2522+ soup = BeautifulSoup('<a href="http://example.com/?foo=val1&bar=val2">A link</a>', 'html.parser')
2523+ soup.a
2524+ # <a href="http://example.com/?foo=val1&amp;bar=val2">A link</a>
2525+
2526+修改默认转义规则的方法是,设置 ``prettify()``, ``encode()``, 或 ``decode()`` 方法的 ``formatter``
2527+参数。Beautiful Soup 可以识别 5 种 ``formatter`` 值。
2528+
2529+默认的设置是 ``formatter="minimal"``。处置字符串时 Beautiful Soup 会确保生成合法的 HTML/XML
2530+
2531+::
2532+
2533+ french = "<p>Il a dit &lt;&lt;Sacr&eacute; bleu!&gt;&gt;</p>"
2534+ soup = BeautifulSoup(french, 'html.parser')
2535+ print(soup.prettify(formatter="minimal"))
2536+ # <p>
2537+ # Il a dit &lt;&lt;Sacré bleu!&gt;&gt;
2538+ # </p>
2539+
2540+设置为 ``formatter="html"`` 时,Beautiful Soup 会尽可能把 Unicode 字符转换为 HTML 实体
2541+
2542+::
2543+
2544+ print(soup.prettify(formatter="html"))
2545+ # <p>
2546+ # Il a dit &lt;&lt;Sacr&eacute; bleu!&gt;&gt;
2547+ # </p>
2548+
2549+设置为 ``formatter="html5"`` 时,结果与 ``formatter="html"`` 相似,区别是 Beautiful Soup
2550+会忽略 HTML 标签种空标签里的斜杠符号,比如 “br” 标签
2551+
2552+::
2553+
2554+ br = BeautifulSoup("<br>", 'html.parser').br
2555+
2556+ print(br.encode(formatter="html"))
2557+ # b'<br/>'
2558+
2559+ print(br.encode(formatter="html5"))
2560+ # b'<br>'
2561+
2562+另外,如果属性的值为空字符串的,它会变为 HTML 风格的 boolean 属性
2563+
2564+::
2565+
2566+ option = BeautifulSoup('<option selected=""></option>').option
2567+ print(option.encode(formatter="html"))
2568+ # b'<option selected=""></option>'
2569+
2570+ print(option.encode(formatter="html5"))
2571+ # b'<option selected></option>'
2572+
2573+这种机制在 Beautiful Soup 4.10.0 中添加。
2574+
2575+设置为 ``formatter=None`` 时,Beautiful Soup 在输出时不会修改任何字符串内容。这是效率最高的选项,
2576+但可能导致输出非法的 HTML/XML,比如下面例子
2577+
2578+::
2579+
2580+ print(soup.prettify(formatter=None))
2581+ # <p>
2582+ # Il a dit <<Sacré bleu!>>
2583+ # </p>
2584+
2585+ link_soup = BeautifulSoup('<a href="http://example.com/?foo=val1&bar=val2">A link</a>', 'html.parser')
2586+ print(link_soup.a.encode(formatter=None))
2587+ # b'<a href="http://example.com/?foo=val1&bar=val2">A link</a>'
2588+
2589+格式化对象
2590+---------------
2591+
2592+如果需要更复杂的机制来控制输出内容,可以实例化 Beautiful Soup 的 formatter 实例,
2593+然后用作 ``formatter`` 参数。
2594+
2595+.. py:class:: HTMLFormatter
2596+
2597+可以用来自定义 HTML 文档的格式化规则。
2598+
2599+下面的 formatter 例子,可以将字符串全部转化为大写,不论是文字节点中的字符还是属性值
2600+
2601+::
2602+
2603+ from bs4.formatter import HTMLFormatter
2604+ def uppercase(str):
2605+ return str.upper()
2606+
2607+ formatter = HTMLFormatter(uppercase)
2608+
2609+ print(soup.prettify(formatter=formatter))
2610+ # <p>
2611+ # IL A DIT <<SACRÉ BLEU!>>
2612+ # </p>
2613+
2614+ print(link_soup.a.prettify(formatter=formatter))
2615+ # <a href="HTTP://EXAMPLE.COM/?FOO=VAL1&BAR=VAL2">
2616+ # A LINK
2617+ # </a>
2618+
2619+下面的 formatter 例子,在美化文档时增加缩进长度
2620+
2621+::
2622+
2623+ formatter = HTMLFormatter(indent=8)
2624+ print(link_soup.a.prettify(formatter=formatter))
2625+ # <a href="http://example.com/?foo=val1&bar=val2">
2626+ # A link
2627+ # </a>
2628+
2629+.. py:class:: XMLFormatter
2630+
2631+可以用来自定义 XML 文档的格式化规则。
2632+
2633+编写自定义 formatter
2634+----------------------
2635+
2636+:py:class:`HTMLFormatter` or :py:class:`XMLFormatter` 的子类可以控制更多的输出过程。
2637+例如,Beautiful Soup 默认情况下会对属性中的 tag 进行排序
2638+
2639+::
2640+
2641+ attr_soup = BeautifulSoup(b'<p z="1" m="2" a="3"></p>', 'html.parser')
2642+ print(attr_soup.p.encode())
2643+ # <p a="3" m="2" z="1"></p>
2644+
2645+若想关闭这个功能,可以使用子类的 ``Formatter.attributes()`` 方法,该方法可以控制输出那些属性
2646+以及这些属性的输出顺序。下面的例子会过滤掉文档中的 “m” 属性
2647+
2648+::
2649+
2650+ class UnsortedAttributes(HTMLFormatter):
2651+ def attributes(self, tag):
2652+ for k, v in tag.attrs.items():
2653+ if k == 'm':
2654+ continue
2655+ yield k, v
2656+
2657+ print(attr_soup.p.encode(formatter=UnsortedAttributes()))
2658+ # <p z="1" a="3"></p>
2659+
2660+危险提示:如果创建了 `CData` 对象,对象中的字符串对象始终表示原始内容,不会被格式化方法影响。
2661+Beautiful Soup 输出时依然会调用自定义格式化方法,以防自定义方法中包含自定义的字符串计数方法,
2662+但调用后不会使用返回结果,不影响原来的返回值。
2663+
2664+::
2665+
2666+ from bs4.element import CData
2667+ soup = BeautifulSoup("<a></a>", 'html.parser')
2668+ soup.a.string = CData("one < three")
2669+ print(soup.a.prettify(formatter="html"))
2670+ # <a>
2671+ # <![CDATA[one < three]]>
2672+ # </a>
2673
2674 get_text()
2675 ----------
2676
2677-如果只想得到tag中包含的文本内容,那么可以调用 ``get_text()`` 方法,这个方法获取到tag中包含的所有文版内容包括子孙tag中的内容,并将结果作为Unicode字符串返回:
2678+如果只想得到 tag 中包含的文本内容,那么可以调用 ``get_text()`` 方法,这个方法获取到 tag
2679+包含的所有文本内容,包括子孙 tag 中的可读内容,并将结果作为单独的一个 Unicode 编码字符串返回:
2680
2681 ::
2682
2683 markup = '<a href="http://example.com/">\nI linked to <i>example.com</i>\n</a>'
2684- soup = BeautifulSoup(markup)
2685+ soup = BeautifulSoup(markup, 'html.parser')
2686
2687 soup.get_text()
2688- u'\nI linked to example.com\n'
2689+ '\nI linked to example.com\n'
2690 soup.i.get_text()
2691- u'example.com'
2692+ 'example.com''
2693
2694-可以通过参数指定tag的文本内容的分隔符:
2695+可以通过参数指定 tag 的文本内容的连接符:
2696
2697 ::
2698
2699 # soup.get_text("|")
2700- u'\nI linked to |example.com|\n'
2701+ '\nI linked to |example.com|\n'
2702
2703-还可以去除获得文本内容的前后空白:
2704+还可以去除每一个文本片段内容的前后空白:
2705
2706 ::
2707
2708 # soup.get_text("|", strip=True)
2709- u'I linked to|example.com'
2710+ 'I linked to|example.com'
2711
2712-或者使用 `.stripped_strings`_ 生成器,获得文本列表后手动处理列表:
2713+但这种情况,你可能应该使用 `.stripped_strings <string-generators>`_ 生成器,
2714+获得文本列表后手动处理内容:
2715
2716 ::
2717
2718 [text for text in soup.stripped_strings]
2719- # [u'I linked to', u'example.com']
2720+ # ['I linked to', 'example.com']
2721+
2722+*因为 Beautiful Soup 4.9.0 版本开始使用 lxml 或 html.parser,<script>,<style> 和
2723+<template> 标签中的内容不会被当做普通的 '文本' 来处理,因此这些标签中的内容不会算作页面中的
2724+可读内容的一部分。*
2725+
2726+*Beautiful Soup 4.10.0 版本以后,可以在 NavigableString 对象上调用 get_text(),.strings
2727+或 .stripped_strings 属性,结果会返回对象本身或空,这种用法只有在对混合类型列表迭代时才会用到。*
2728
2729 指定文档解析器
2730 ==============
2731
2732-如果仅是想要解析HTML文档,只要用文档创建 ``BeautifulSoup`` 对象就可以了.Beautiful Soup会自动选择一个解析器来解析文档.但是还可以通过参数指定使用那种解析器来解析当前文档.
2733+如果仅是想要解析HTML文档,只需要创建 ``BeautifulSoup`` 对象时传入文档就可以了。Beautiful Soup
2734+会自动选择一个解析器来解析文档。同时还可以使用额外参数,来指定文档解析器。
2735+
2736+``BeautifulSoup`` 第一个参数应该是要被解析的文档字符串或是文件句柄 -- 待解析文件的句柄,
2737+第二个参数用来标识怎样解析文档。
2738
2739-``BeautifulSoup`` 第一个参数应该是要被解析的文档字符串或是文件句柄,第二个参数用来标识怎样解析文档.如果第二个参数为空,那么Beautiful Soup根据当前系统安装的库自动选择解析器,解析器的优先数序: lxml, html5lib, Python标准库.在下面两种条件下解析器优先顺序会变化:
2740+如果不指定解析器,默认使用已安装的 `最佳` HTML 解析器。Beautiful Soup 把 lxml 解析器排在第一,
2741+然后是 html5lib, 然后是 Python 标准库。在下面两种条件下解析器优先顺序会变化:
2742
2743- * 要解析的文档是什么类型: 目前支持, “html”, “xml”, 和 “html5”
2744- * 指定使用哪种解析器: 目前支持, “lxml”, “html5lib”, 和 “html.parser”
2745+ * 要解析的文档是什么类型: 目前支持, “html”,“xml”,和 “html5”
2746+ * 指定使用哪种解析器: 目前支持,“lxml”,“html5lib”,和 “html.parser”(Python 标准库)
2747
2748-`安装解析器`_ 章节介绍了可以使用哪种解析器,以及如何安装.
2749+`安装解析器`_ 章节介绍了可以使用哪种解析器,以及如何安装。
2750
2751-如果指定的解析器没有安装,Beautiful Soup会自动选择其它方案.目前只有 lxml 解析器支持XML文档的解析,在没有安装lxml库的情况下,创建 ``beautifulsoup`` 对象时无论是否指定使用lxml,都无法得到解析后的对象
2752+如果指定的解析器没有安装,Beautiful Soup会自动选择其它方案。目前只有 lxml 解析器支持XML文档的解析,
2753+在没有安装 lxml 库的情况下,无法自动选择 XML 文档解析器,手动指定 lxml 也不行。
2754
2755 解析器之间的区别
2756 -----------------
2757
2758-Beautiful Soup为不同的解析器提供了相同的接口,但解析器本身是有区别的.同一篇文档被不同的解析器解析后可能会生成不同结构的树型文档.区别最大的是HTML解析器和XML解析器.以下是被Python自带的HTML解析器解析成的HTML片段:
2759+Beautiful Soup 为不同的解析器提供了相同的接口,但解析器本身时有区别的。同一篇文档被不同的解析器解析后
2760+可能会生成不同结构的文档。区别最大的是 HTML 解析器和 XML 解析器,看下面片段被解析成 HTML 结构:
2761
2762 ::
2763
2764- BeautifulSoup("<a><b /></a>")
2765- # <html><head></head><body><a><b></b></a></body></html>
2766+ BeautifulSoup("<a><b/></a>", "html.parser")
2767+ # <a><b></b></a>
2768
2769-因为空标签<b />不符合HTML标准,所以解析器把它解析成<b></b>
2770+因为空标签 <b /> 不符合 HTML 标准,html.parser 解析器把它解析成一对儿 <b></b>。
2771
2772-同样的文档使用XML解析如下(解析XML需要安装lxml库).注意,空标签<b />依然被保留,并且文档前添加了XML头,而不是被包含在<html>标签内:
2773+同样的文档使用 XML 解析结果如下(解析 XML 需要安装 lxml 库)。注意,空标签 <b /> 依然被保留,
2774+并且文档前添加了 XML 头,而不是被包含在 <html> 标签内:
2775
2776 ::
2777
2778- BeautifulSoup("<a><b /></a>", "xml")
2779+ print(BeautifulSoup("<a><b/></a>", "xml"))
2780 # <?xml version="1.0" encoding="utf-8"?>
2781 # <a><b/></a>
2782
2783-HTML解析器之间也有区别,如果被解析的HTML文档是标准格式,那么解析器之间没有任何差别,只是解析速度不同,结果都会返回正确的文档树.
2784+HTML 解析器之间也有区别,如果被解析的HTML文档是标准格式,那么解析器之间没有任何差别。
2785+只是解析速度不同,结果都会返回正确的文档树。
2786
2787-但是如果被解析文档不是标准格式,那么不同的解析器返回结果可能不同.下面例子中,使用lxml解析错误格式的文档,结果</p>标签被直接忽略掉了:
2788+但是如果被解析文档不是标准格式,那么不同的解析器返回结果可能不同。下面例子中,使用 lxml
2789+解析错误格式的文档,结果 </p> 标签被直接忽略掉了:
2790
2791 ::
2792
2793 BeautifulSoup("<a></p>", "lxml")
2794 # <html><body><a></a></body></html>
2795
2796-使用html5lib库解析相同文档会得到不同的结果:
2797+使用 html5lib 库解析相同文档会得到不同的结果:
2798
2799 ::
2800
2801 BeautifulSoup("<a></p>", "html5lib")
2802 # <html><head></head><body><a><p></p></a></body></html>
2803
2804-html5lib库没有忽略掉</p>标签,而是自动补全了标签,还给文档树添加了<head>标签.
2805+html5lib 库没有忽略掉 </p> 标签,而是自动补全了标签,还给文档树添加了 <head> 标签。
2806
2807-使用pyhton内置库解析结果如下:
2808+使用 pyhton 内置库解析结果如下:
2809
2810 ::
2811
2812 BeautifulSoup("<a></p>", "html.parser")
2813 # <a></a>
2814
2815-与lxml [7]_ 库类似的,Python内置库忽略掉了</p>标签,与html5lib库不同的是标准库没有尝试创建符合标准的文档格式或将文档片段包含在<body>标签内,与lxml不同的是标准库甚至连<html>标签都没有尝试去添加.
2816+与 lxml [7]_ 库类似的,Python 内置库忽略掉了 </p> 标签,与 html5lib 库不同的是标准库没有
2817+尝试创建符合标准的文档格式或将文档片段包含在 <body> 标签内,与lxml不同的是标准库甚至连 <html>
2818+标签都没有尝试去添加。
2819
2820-因为文档片段“<a></p>”是错误格式,所以以上解析方式都能算作"正确",html5lib库使用的是HTML5的部分标准,所以最接近"正确".不过所有解析器的结构都能够被认为是"正常"的.
2821+因为文档片段 “<a></p>” 是错误格式,所以以上解析方式都能算作 "正确",html5lib 库使用的是 HTML5
2822+的部分标准,所以最接近"正确"。不过所有解析器的结构都能够被认为是"正常"的。
2823
2824-不同的解析器可能影响代码执行结果,如果在分发给别人的代码中使用了 ``BeautifulSoup`` ,那么最好注明使用了哪种解析器,以减少不必要的麻烦.
2825+不同的解析器可能影响代码执行结果,如果在分发给别人的代码中使用了 ``BeautifulSoup`` ,
2826+那么最好注明使用了哪种解析器,以减少不必要的麻烦。
2827
2828 编码
2829 ====
2830
2831-任何HTML或XML文档都有自己的编码方式,比如ASCII 或 UTF-8,但是使用Beautiful Soup解析后,文档都被转换成了Unicode:
2832+任何 HTML 或 XML 文档都有自己的编码方式,比如ASCII 或 UTF-8。但是使用 Beautiful Soup 解析后,
2833+文档都被转换成了 Unicode:
2834
2835 ::
2836
2837 markup = "<h1>Sacr\xc3\xa9 bleu!</h1>"
2838- soup = BeautifulSoup(markup)
2839+ soup = BeautifulSoup(markup, 'html.parser')
2840 soup.h1
2841 # <h1>Sacré bleu!</h1>
2842 soup.h1.string
2843- # u'Sacr\xe9 bleu!'
2844+ # 'Sacr\xe9 bleu!'
2845
2846-这不是魔术(但很神奇),Beautiful Soup用了 `编码自动检测`_ 子库来识别当前文档编码并转换成Unicode编码. ``BeautifulSoup`` 对象的 ``.original_encoding`` 属性记录了自动识别编码的结果:
2847+这不是魔术(但很神奇),Beautiful Soup 用了 `编码自动检测 <Unicode, Dammit>`_ 子库来识别当前
2848+文档编码并转换成 Unicode 编码。``BeautifulSoup`` 对象的 ``.original_encoding`` 属性记录了
2849+自动识别编码的结果:
2850
2851 ::
2852
2853 soup.original_encoding
2854 'utf-8'
2855
2856-`编码自动检测`_ 功能大部分时候都能猜对编码格式,但有时候也会出错.有时候即使猜测正确,也是在逐个字节的遍历整个文档后才猜对的,这样很慢.如果预先知道文档编码,可以设置编码参数来减少自动检查编码出错的概率并且提高文档解析速度.在创建 ``BeautifulSoup`` 对象的时候设置 ``from_encoding`` 参数.
2857+`编码自动检测 <Unicode, Dammit>`_ 功能大部分时候都能猜对编码格式,但有时候也会出错。有时候即使
2858+猜测正确,也是在逐个 字节的遍历整个文档后才猜对的,这样很慢。如果预先知道文档编码,可以设置编码参数
2859+来减少自动检查编码 出错的概率并且提高文档解析速度。在创建 ``BeautifulSoup`` 对象的时候设置
2860+``from_encoding`` 参数。
2861
2862-下面一段文档用了ISO-8859-8编码方式,这段文档太短,结果Beautiful Soup以为文档是用ISO-8859-7编码:
2863+下面一段文档用了 ISO-8859-8 编码方式,这段文档太短,结果 Beautiful Soup 以为文档是用 ISO-8859-7 编码:
2864
2865 ::
2866
2867 markup = b"<h1>\xed\xe5\xec\xf9</h1>"
2868- soup = BeautifulSoup(markup)
2869- soup.h1
2870- <h1>νεμω</h1>
2871- soup.original_encoding
2872- 'ISO-8859-7'
2873+ soup = BeautifulSoup(markup, 'html.parser')
2874+ print(soup.h1)
2875+ # <h1>νεμω</h1>
2876+ print(soup.original_encoding)
2877+ # iso-8859-7
2878
2879 通过传入 ``from_encoding`` 参数来指定编码方式:
2880
2881 ::
2882
2883- soup = BeautifulSoup(markup, from_encoding="iso-8859-8")
2884- soup.h1
2885- <h1>םולש</h1>
2886- soup.original_encoding
2887- 'iso8859-8'
2888+ soup = BeautifulSoup(markup, 'html.parser', from_encoding="iso-8859-8")
2889+ print(soup.h1)
2890+ # <h1>םולש</h1>
2891+ print(soup.original_encoding)
2892+ # iso8859-8
2893+
2894+如果仅知道文档采用了 Unicode 编码,但不知道具体编码。可以先自己猜测,猜测错误(依旧是乱码)时,
2895+可以把错误编码作为 ``exclude_encodings`` 参数,这样文档就不会尝试使用这种编码了解码了。
2896
2897-如果仅知道文档采用了Unicode编码, 但不知道具体编码. 可以先自己猜测, 猜测错误(依旧是乱码)时,
2898-可以把错误编码作为 ``exclude_encodings`` 参数, 这样文档就不会尝试使用这种编码了解码了.
2899-译者备注: 在没有指定编码的情况下, BS会自己猜测编码, 把不正确的编码排除掉, BS就更容易猜到正确编码.
2900+译者备注: 在没有指定编码的情况下,BS会自己猜测编码,把不正确的编码排除掉,BS就更容易猜到正确编码。
2901
2902 ::
2903
2904- soup = BeautifulSoup(markup, exclude_encodings=["ISO-8859-7"])
2905- soup.h1
2906- <h1>םולש</h1>
2907- soup.original_encoding
2908- 'WINDOWS-1255'
2909+ soup = BeautifulSoup(markup, 'html.parser', exclude_encodings=["iso-8859-7"])
2910+ print(soup.h1)
2911+ # <h1>םולש</h1>
2912+ print(soup.original_encoding)
2913+ # WINDOWS-1255
2914
2915-猜测结果是 Windows-1255 编码, 猜测结果可能不够准确, 但是 Windows-1255 编码是 ISO-8859-8 的扩展集,
2916-所以猜测结果已经十分接近了, 并且不影响使用. (``exclude_encodings`` 参数是 4.4.0版本的新功能)
2917+猜测的结果 Windows-1255 可能不是 100% 准确,但是 Windows-1255 编码是 ISO-8859-8 的扩展集,
2918+所以猜测结果已经十分接近了,并不影响使用。(``exclude_encodings`` 参数是 4.4.0版本的新功能)
2919
2920-少数情况下(通常是UTF-8编码的文档中包含了其它编码格式的文件),想获得正确的Unicode编码就不得不将文档中少数特殊编码字符替换成特殊Unicode编码,“REPLACEMENT CHARACTER” (U+FFFD, �) [9]_ . 如果Beautifu Soup猜测文档编码时作了特殊字符的替换,那么Beautiful Soup会把 ``UnicodeDammit`` 或 ``BeautifulSoup`` 对象的 ``.contains_replacement_characters`` 属性标记为 ``True`` .这样就可以知道当前文档进行Unicode编码后丢失了一部分特殊内容字符.如果文档中包含�而 ``.contains_replacement_characters`` 属性是 ``False`` ,则表示�就是文档中原来的字符,不是转码失败.
2921+少数情况下(通常是UTF-8编码的文档中包含了其它编码格式的文件),想获得正确的 Unicode 编码就不得不将
2922+文档中少数特殊编码字符替换成特殊 Unicode 编码,“REPLACEMENT CHARACTER” (U+FFFD, �) [9]_ 。
2923+如果 Beautifu Soup 猜测文档编码时作了特殊字符的替换,那么 Beautiful Soup 会把 ``UnicodeDammit``
2924+或 ``BeautifulSoup`` 对象的 ``.contains_replacement_characters`` 属性标记为 ``True`` 。
2925+这样就可以知道当前文档进行 Unicode 编码后丢失了一部分特殊内容字符。如果文档中包含 � 而
2926+``.contains_replacement_characters`` 属性是 ``False`` ,则表示 � 就是文档中原来的字符,
2927+不是转码失败。
2928
2929 输出编码
2930 --------
2931
2932-通过Beautiful Soup输出文档时,不管输入文档是什么编码方式,输出编码均为UTF-8编码,下面例子输入文档是Latin-1编码:
2933+通过 Beautiful Soup 输出文档时,不管输入文档是什么编码方式,输出编码均为UTF-8编码,
2934+下面例子输入文档是 Latin-1 编码:
2935
2936 ::
2937
2938- markup = b'''
2939- <html>
2940- <head>
2941- <meta content="text/html; charset=ISO-Latin-1" http-equiv="Content-type" />
2942- </head>
2943- <body>
2944- <p>Sacr\xe9 bleu!</p>
2945- </body>
2946- </html>
2947- '''
2948-
2949- soup = BeautifulSoup(markup)
2950- print(soup.prettify())
2951- # <html>
2952- # <head>
2953- # <meta content="text/html; charset=utf-8" http-equiv="Content-type" />
2954- # </head>
2955- # <body>
2956- # <p>
2957- # Sacré bleu!
2958- # </p>
2959- # </body>
2960- # </html>
2961+ markup = b'''
2962+ <html>
2963+ <head>
2964+ <meta content="text/html; charset=ISO-Latin-1" http-equiv="Content-type" />
2965+ </head>
2966+ <body>
2967+ <p>Sacr\xe9 bleu!</p>
2968+ </body>
2969+ </html>
2970+ '''
2971
2972-注意,输出文档中的<meta>标签的编码设置已经修改成了与输出编码一致的UTF-8.
2973+ soup = BeautifulSoup(markup, 'html.parser')
2974+ print(soup.prettify())
2975+ # <html>
2976+ # <head>
2977+ # <meta content="text/html; charset=utf-8" http-equiv="Content-type" />
2978+ # </head>
2979+ # <body>
2980+ # <p>
2981+ # Sacré bleu!
2982+ # </p>
2983+ # </body>
2984+ # </html>
2985
2986-如果不想用UTF-8编码输出,可以将编码方式传入 ``prettify()`` 方法:
2987+注意,输出文档中的 <meta> 标签内容中的编码信息已经修改成了与输出编码一致的 UTF-8。
2988+
2989+如果不想用 UTF-8 编码输出,可以将编码方式传入 ``prettify()`` 方法:
2990
2991 ::
2992
2993@@ -2181,103 +2798,107 @@ html5lib库没有忽略掉</p>标签,而是自动补全了标签,还给文档树
2994 # <meta content="text/html; charset=latin-1" http-equiv="Content-type" />
2995 # ...
2996
2997-还可以调用 ``BeautifulSoup`` 对象或任意节点的 ``encode()`` 方法,就像Python的字符串调用 ``encode()`` 方法一样:
2998+还可以调用 ``BeautifulSoup`` 对象或任意节点的 ``encode()`` 方法,就像 Python 的字符串
2999+调用 ``encode()`` 方法一样:
3000
3001 ::
3002
3003 soup.p.encode("latin-1")
3004- # '<p>Sacr\xe9 bleu!</p>'
3005+ # b'<p>Sacr\xe9 bleu!</p>'
3006
3007 soup.p.encode("utf-8")
3008- # '<p>Sacr\xc3\xa9 bleu!</p>'
3009+ # b'<p>Sacr\xc3\xa9 bleu!</p>'
3010
3011-如果文档中包含当前编码不支持的字符,那么这些字符将被转换成一系列XML特殊字符引用,下面例子中包含了Unicode编码字符SNOWMAN:
3012+如果文档中包含当前编码不支持的字符,那么这些字符将被转换成一系列 XML 特殊字符引用,下面例子中
3013+包含了 Unicode 编码字符 SNOWMAN:
3014
3015 ::
3016
3017 markup = u"<b>\N{SNOWMAN}</b>"
3018- snowman_soup = BeautifulSoup(markup)
3019+ snowman_soup = BeautifulSoup(markup, 'html.parser')
3020 tag = snowman_soup.b
3021
3022-SNOWMAN字符在UTF-8编码中可以正常显示(看上去像是☃),但有些编码不支持SNOWMAN字符,比如ISO-Latin-1或ASCII,那么在这些编码中SNOWMAN字符会被转换成“&#9731”:
3023+SNOWMAN 字符在 UTF-8 编码中可以正常显示(看上去是 ☃),但有些编码不支持 SNOWMAN 字符,比如
3024+ISO-Latin-1 或 ASCII,那么在这些编码中 SNOWMAN 字符会被转换成 “&#9731”:
3025
3026 ::
3027
3028 print(tag.encode("utf-8"))
3029- # <b>☃</b>
3030+ # b'<b>\xe2\x98\x83</b>'
3031
3032- print tag.encode("latin-1")
3033- # <b>&#9731;</b>
3034+ print(tag.encode("latin-1"))
3035+ # b'<b>&#9731;</b>'
3036
3037- print tag.encode("ascii")
3038- # <b>&#9731;</b>
3039+ print(tag.encode("ascii"))
3040+ # b'<b>&#9731;</b>'
3041
3042-Unicode, Dammit! (乱码, 靠!)
3043------------------------------
3044+Unicode, Dammit
3045+----------------------
3046
3047-译者备注: UnicodeDammit 是BS内置库, 主要用来猜测文档编码.
3048+译者备注: Unicode Dammit 是 Beautiful Soup 内置库,主要用来猜测文档编码。
3049
3050-`编码自动检测`_ 功能可以在Beautiful Soup以外使用,检测某段未知编码时,可以使用这个方法:
3051+`编码自动检测 <Unicode, Dammit>`_ 功能可以在 Beautiful Soup 以外使用。当遇到一段未知编码
3052+的文档时,可以通过下面方法把它转换为 Unicode 编码
3053
3054 ::
3055
3056 from bs4 import UnicodeDammit
3057- dammit = UnicodeDammit("Sacr\xc3\xa9 bleu!")
3058+ dammit = UnicodeDammit(b"\xc2\xabSacr\xc3\xa9 bleu!\xc2\xbb")
3059 print(dammit.unicode_markup)
3060- # Sacré bleu!
3061+ # «Sacré bleu!»
3062 dammit.original_encoding
3063 # 'utf-8'
3064
3065-如果Python中安装了 ``chardet`` 或 ``cchardet`` 那么编码检测功能的准确率将大大提高.
3066-输入的字符越多,检测结果越精确,如果事先猜测到一些可能编码,
3067-那么可以将猜测的编码作为参数,这样将优先检测这些编码:
3068+如果安装了 Python 的 ``chardet`` 或 ``cchardet`` 库,那么编码检测功能的准确率将大大提高。
3069+输入的字符越多,检测结果越准确,如果事先猜测到一些可能编码,那么可以将猜测的编码作为参数,
3070+这样将优先检测这些编码:
3071
3072 ::
3073
3074-
3075 dammit = UnicodeDammit("Sacr\xe9 bleu!", ["latin-1", "iso-8859-1"])
3076 print(dammit.unicode_markup)
3077 # Sacré bleu!
3078 dammit.original_encoding
3079 # 'latin-1'
3080
3081-`编码自动检测`_ 功能中有2项功能是Beautiful Soup库中用不到的
3082+`编码自动检测 <Unicode, Dammit>`_ 功能中有 2 项功能是 Beautiful Soup 库中用不到的
3083
3084 智能引号
3085-...........
3086+^^^^^^^^^^^
3087
3088-使用Unicode时,Beautiful Soup还会智能的把引号 [10]_ 转换成HTML或XML中的特殊字符:
3089+使用 Unicode 时,Beautiful Soup 还会智能的把引号 [10]_ 转换成 HTML 或 XML 中的特殊字符:
3090
3091 ::
3092
3093 markup = b"<p>I just \x93love\x94 Microsoft Word\x92s smart quotes</p>"
3094
3095 UnicodeDammit(markup, ["windows-1252"], smart_quotes_to="html").unicode_markup
3096- # u'<p>I just &ldquo;love&rdquo; Microsoft Word&rsquo;s smart quotes</p>'
3097+ # '<p>I just &ldquo;love&rdquo; Microsoft Word&rsquo;s smart quotes</p>'
3098
3099 UnicodeDammit(markup, ["windows-1252"], smart_quotes_to="xml").unicode_markup
3100- # u'<p>I just &#x201C;love&#x201D; Microsoft Word&#x2019;s smart quotes</p>'
3101+ # '<p>I just &#x201C;love&#x201D; Microsoft Word&#x2019;s smart quotes</p>'
3102
3103-也可以把引号转换为ASCII码:
3104+也可以把引号转换为 ASCII 码:
3105
3106 ::
3107
3108 UnicodeDammit(markup, ["windows-1252"], smart_quotes_to="ascii").unicode_markup
3109- # u'<p>I just "love" Microsoft Word\'s smart quotes</p>'
3110+ # '<p>I just "love" Microsoft Word\'s smart quotes</p>'
3111
3112-很有用的功能,但是Beautiful Soup没有使用这种方式.默认情况下,Beautiful Soup把引号转换成Unicode:
3113+虽然这个功能很有用,但是 Beautiful Soup 没有使用这种方式。默认情况下,Beautiful Soup
3114+把引号转换成 Unicode:
3115
3116 ::
3117
3118 UnicodeDammit(markup, ["windows-1252"]).unicode_markup
3119- # u'<p>I just \u201clove\u201d Microsoft Word\u2019s smart quotes</p>'
3120+ # '<p>I just “love” Microsoft Word’s smart quotes</p>'
3121
3122 矛盾的编码
3123-...........
3124+^^^^^^^^^^^^^
3125
3126-有时文档的大部分都是用UTF-8,但同时还包含了Windows-1252编码的字符,就像微软的智能引号 [10]_ 一样.
3127-一些包含多个信息的来源网站容易出现这种情况. ``UnicodeDammit.detwingle()``
3128-方法可以把这类文档转换成纯UTF-8编码格式,看个简单的例子:
3129+有时文档的大部分都是用 UTF-8,但同时还包含了 Windows-1252 编码的字符,就像微软的智能引号 [10]_ 一样。
3130+一些包含多个信息的来源网站容易出现这种情况。``UnicodeDammit.detwingle()`` 方法可以把这类文档转换成纯
3131+UTF-8 编码格式,看个简单的例子:
3132
3133 ::
3134
3135@@ -2285,7 +2906,8 @@ Unicode, Dammit! (乱码, 靠!)
3136 quote = (u"\N{LEFT DOUBLE QUOTATION MARK}I like snowmen!\N{RIGHT DOUBLE QUOTATION MARK}")
3137 doc = snowmen.encode("utf8") + quote.encode("windows_1252")
3138
3139-这段文档很杂乱,snowmen是UTF-8编码,引号是Windows-1252编码,直接输出时不能同时显示snowmen和引号,因为它们编码不同:
3140+这段文档很杂乱,snowmen 是 UTF-8 编码,引号是 Windows-1252 编码,直接输出时不能同时显示
3141+snowmen 和引号,因为它们编码不同:
3142
3143 ::
3144
3145@@ -2295,8 +2917,9 @@ Unicode, Dammit! (乱码, 靠!)
3146 print(doc.decode("windows-1252"))
3147 # ☃☃☃“I like snowmen!”
3148
3149-如果对这段文档用UTF-8解码就会得到 ``UnicodeDecodeError`` 异常,如果用Windows-1252解码就回得到一堆乱码.
3150-幸好, ``UnicodeDammit.detwingle()`` 方法会把这段字符串转换成UTF-8编码,允许我们同时显示出文档中的snowmen和引号:
3151+如果对这段文档用 UTF-8 解码就会产生 ``UnicodeDecodeError`` 异常,如果用 Windows-1252
3152+解码就会得到一堆乱码。幸好,``UnicodeDammit.detwingle()`` 方法会把这段字符串转换成 UTF-8
3153+编码,允许我们同时显示出文档中的 snowmen 和引号:
3154
3155 ::
3156
3157@@ -2304,29 +2927,71 @@ Unicode, Dammit! (乱码, 靠!)
3158 print(new_doc.decode("utf8"))
3159 # ☃☃☃“I like snowmen!”
3160
3161-``UnicodeDammit.detwingle()`` 方法只能解码包含在UTF-8编码中的Windows-1252编码内容,但这解决了最常见的一类问题.
3162+``UnicodeDammit.detwingle()`` 方法只能解码包含在 UTF-8 编码中的 Windows-1252 编码内容,
3163+(反过来的话,大概也可以)但这是最常见的用法。
3164+
3165+在创建 ``BeautifulSoup`` 或 ``UnicodeDammit`` 对象前一定要先对文档调用
3166+``UnicodeDammit.detwingle()`` 确保文档的编码方式正确。Beautiful Soup
3167+会假设文档只包含一种编码,如果尝试去解析一段同时包含 UTF-8 和 Windows-1252 编码的文档,
3168+就有可能被误判成整个文档都是 Windows-1252 编码,解析结果就会得到一堆乱码,
3169+比如: ☃☃☃“I like snowmen!”。
3170
3171-在创建 ``BeautifulSoup`` 或 ``UnicodeDammit`` 对象前一定要先对文档调用 ``UnicodeDammit.detwingle()`` 确保文档的编码方式正确.如果尝试去解析一段包含Windows-1252编码的UTF-8文档,就会得到一堆乱码,比如: ☃☃☃“I like snowmen!”.
3172+``UnicodeDammit.detwingle()`` 方法在 Beautiful Soup 4.1.0 版本中新增。
3173
3174-``UnicodeDammit.detwingle()`` 方法在Beautiful Soup 4.1.0版本中新增
3175+行编号
3176+==========
3177+
3178+``html.parser`` 和 ``html5lib`` 解析器可以跟踪原始文档中发现的每个 Tag。查看原始信息可以
3179+使用 ``Tag.sourceline`` (行号)和 ``Tag.sourcepos`` (标签所在行的起始位置)
3180+
3181+::
3182+
3183+ markup = "<p\n>Paragraph 1</p>\n <p>Paragraph 2</p>"
3184+ soup = BeautifulSoup(markup, 'html.parser')
3185+ for tag in soup.find_all('p'):
3186+ print(repr((tag.sourceline, tag.sourcepos, tag.string)))
3187+ # (1, 0, 'Paragraph 1')
3188+ # (3, 4, 'Paragraph 2')
3189+
3190+注意,这两个解析器的 ``sourceline`` 和 ``sourcepos`` 会有些许的不同。html.parser 将
3191+标签开始的 小于号作为标签起始符号,而 html5lib 将标签开始的大于号作为标签起始符号
3192+
3193+::
3194+
3195+ soup = BeautifulSoup(markup, 'html5lib')
3196+ for tag in soup.find_all('p'):
3197+ print(repr((tag.sourceline, tag.sourcepos, tag.string)))
3198+ # (2, 0, 'Paragraph 1')
3199+ # (3, 6, 'Paragraph 2')
3200+
3201+可以在 BeautifulSoup 构造函数中配置 ``store_line_numbers=False`` 来关闭这个功能
3202+
3203+::
3204+
3205+ markup = "<p\n>Paragraph 1</p>\n <p>Paragraph 2</p>"
3206+ soup = BeautifulSoup(markup, 'html.parser', store_line_numbers=False)
3207+ print(soup.p.sourceline)
3208+ # None
3209+
3210+这个功能在 4.8.1 版本中引入,lxml 解析器不支持这个功能。
3211
3212 比较对象是否相同
3213 =================
3214
3215-两个 ``NavigableString`` 或 ``Tag`` 对象具有相同的HTML或XML结构时,
3216-Beautiful Soup就判断这两个对象相同. 这个例子中, 2个 <b> 标签在 BS 中是相同的,
3217-尽管他们在文档树的不同位置, 但是具有相同的表象: "<b>pizza</b>"
3218+两个 ``NavigableString`` 或 ``Tag`` 对象具有相同的 HTML 或 XML 结构时,
3219+Beautiful Soup就判断这两个对象相同。这个例子中,2个 <b> 标签在 BS 中是相同的,
3220+尽管他们在文档树的不同位置,但是具有相同的表象: "<b>pizza</b>"
3221
3222 ::
3223
3224- markup = "<p>I want <b>pizza</b> and more <b>pizza</b>!</p>"
3225- soup = BeautifulSoup(markup, 'html.parser')
3226- first_b, second_b = soup.find_all('b')
3227- print first_b == second_b
3228- # True
3229+ markup = "<p>I want <b>pizza</b> and more <b>pizza</b>!</p>"
3230+ soup = BeautifulSoup(markup, 'html.parser')
3231+ first_b, second_b = soup.find_all('b')
3232+ print(first_b == second_b)
3233+ # True
3234
3235- print first_b.previous_element == second_b.previous_element
3236- # False
3237+ print(first_b.previous_element == second_b.previous_element)
3238+ # False
3239
3240 如果想判断两个对象是否严格的指向同一个对象可以通过 ``is`` 来判断
3241
3242@@ -2342,12 +3007,12 @@ Beautiful Soup就判断这两个对象相同. 这个例子中, 2个 <b> 标签
3243
3244 ::
3245
3246- import copy
3247- p_copy = copy.copy(soup.p)
3248- print p_copy
3249- # <p>I want <b>pizza</b> and more <b>pizza</b>!</p>
3250+ import copy
3251+ p_copy = copy.copy(soup.p)
3252+ print(p_copy)
3253+ # <p>I want <b>pizza</b> and more <b>pizza</b>!</p>
3254
3255-复制后的对象跟与对象是相等的, 但指向不同的内存地址
3256+复制后的对象跟与对象是相等的,但指向不同的内存地址
3257
3258 ::
3259
3260@@ -2357,8 +3022,8 @@ Beautiful Soup就判断这两个对象相同. 这个例子中, 2个 <b> 标签
3261 print soup.p is p_copy
3262 # False
3263
3264-源对象和复制对象的区别是源对象在文档树中, 而复制后的对象是独立的还没有添加到文档树中.
3265-复制后对象的效果跟调用了 ``extract()`` 方法相同.
3266+源对象和复制对象的区别是源对象在文档树中,而复制后的对象是独立的还没有添加到文档树中。
3267+复制后对象的效果跟调用了 ``extract()`` 方法相同。
3268
3269 ::
3270
3271@@ -2367,16 +3032,29 @@ Beautiful Soup就判断这两个对象相同. 这个例子中, 2个 <b> 标签
3272
3273 这是因为相等的对象不能同时插入相同的位置
3274
3275+高级自定义解析
3276+================
3277+
3278+Beautiful Soup 提供多种途径自定义解析器如果解析 HTML 和 XML。本章覆盖了最常用的自定义方法。
3279
3280 解析部分文档
3281-============
3282+-------------
3283
3284-如果仅仅因为想要查找文档中的<a>标签而将整片文档进行解析,实在是浪费内存和时间.最快的方法是从一开始就把<a>标签以外的东西都忽略掉. ``SoupStrainer`` 类可以定义文档的某段内容,这样搜索文档时就不必先解析整篇文档,只会解析在 ``SoupStrainer`` 中定义过的文档. 创建一个 ``SoupStrainer`` 对象并作为 ``parse_only`` 参数给 ``BeautifulSoup`` 的构造方法即可.
3285+如果仅仅因为想要查找文档中的 <a> 标签而将整片文档进行解析,实在是浪费内存和时间。最快的方法
3286+是从一开始 就把 <a> 标签以外的东西都忽略掉。 ``SoupStrainer`` 类可以选择解析哪部分文档内容,
3287+创建一个 ``SoupStrainer`` 对象并作为 ``parse_only`` 参数给 ``BeautifulSoup`` 的构造
3288+方法即可。
3289+
3290+(注意,*这个功能在 html5lib 解析器中无法使用*。如果使用 html5lib 解析器,整篇文档都会被解析,
3291+这是因为 html5lib 会重新排列文档树的结构,如果部分节点不在文档树中,会导致崩溃。为了避免混淆,
3292+下面的例子中 Beautiful Soup 都强制指定使用了 Python 内置解析器。)
3293
3294 SoupStrainer
3295 -------------
3296
3297-``SoupStrainer`` 类接受与典型搜索方法相同的参数:`name`_ , `attrs`_ , `recursive`_ , `string`_ , `**kwargs`_ 。下面举例说明三种 ``SoupStrainer`` 对象:
3298+``SoupStrainer`` 类接受与典型搜索方法相同的参数: `name`_ , `attrs`_ ,
3299+`recursive <recursive>`_ , `string <string>`_ , `**kwargs <kwargs>`_ 。
3300+下面举例说明三种 ``SoupStrainer`` 对象:
3301
3302 ::
3303
3304@@ -2387,7 +3065,7 @@ SoupStrainer
3305 only_tags_with_id_link2 = SoupStrainer(id="link2")
3306
3307 def is_short_string(string):
3308- return len(string) < 10
3309+ return string is not None and len(string) < 10
3310
3311 only_short_strings = SoupStrainer(string=is_short_string)
3312
3313@@ -2395,9 +3073,8 @@ SoupStrainer
3314
3315 ::
3316
3317- html_doc = """
3318- <html><head><title>The Dormouse's story</title></head>
3319- <body>
3320+ html_doc = """<html><head><title>The Dormouse's story</title></head>
3321+ <body>
3322 <p class="title"><b>The Dormouse's story</b></p>
3323
3324 <p class="story">Once upon a time there were three little sisters; and their names were
3325@@ -2434,27 +3111,147 @@ SoupStrainer
3326 # ...
3327 #
3328
3329-还可以将 ``SoupStrainer`` 作为参数传入 `搜索文档树`_ 中提到的方法.这可能不是个常用用法,所以还是提一下:
3330+还可以将 ``SoupStrainer`` 作为参数传入 `搜索文档树`_ 中提到的方法。虽然不常用,但还是提一下:
3331
3332 ::
3333
3334- soup = BeautifulSoup(html_doc)
3335+ soup = BeautifulSoup(html_doc, 'html.parser')
3336 soup.find_all(only_short_strings)
3337- # [u'\n\n', u'\n\n', u'Elsie', u',\n', u'Lacie', u' and\n', u'Tillie',
3338- # u'\n\n', u'...', u'\n']
3339+ # ['\n\n', '\n\n', 'Elsie', ',\n', 'Lacie', ' and\n', 'Tillie',
3340+ # '\n\n', '...', '\n']
3341+
3342+自定义包含多个值的属性
3343+----------------------
3344+
3345+在 HTML 文档中,像 ``class`` 这样的属性的值是一个列表,像 ``id`` 这样的属性的值是一个单一字符串,
3346+因为 HTML 标准定义了这些属性的不同行为
3347+
3348+::
3349+
3350+ markup = '<a class="cls1 cls2" id="id1 id2">'
3351+ soup = BeautifulSoup(markup, 'html.parser')
3352+ soup.a['class']
3353+ # ['cls1', 'cls2']
3354+ soup.a['id']
3355+ # 'id1 id2'
3356+
3357+设置 ``multi_valued_attributes=None`` 可以禁用多值的自动识别,然后全部属性的值都变成一个字符串
3358+
3359+::
3360+
3361+ soup = BeautifulSoup(markup, 'html.parser', multi_valued_attributes=None)
3362+ soup.a['class']
3363+ # 'cls1 cls2'
3364+ soup.a['id']
3365+ # 'id1 id2'
3366+
3367+如果给 ``multi_valued_attributes`` 参数传入一个字典,可以实现一点点解析自定义。如果需要这么做,
3368+查看 ``HTMLTreeBuilder.DEFAULT_CDATA_LIST_ATTRIBUTES`` 了解 Beautiful Soup 的默认配置,
3369+这些均是基于 HTML 标准配置的。
3370+
3371+`(这个功能添加于 Beautiful Soup 4.8.0)`
3372+
3373+处理重复属性
3374+---------------
3375+
3376+使用 ``html.parser`` 解析器时,可以通过设置 ``on_duplicate_attribute`` 参数,来定义当
3377+Beautiful Soup 在 tag 中发现重复的属性名字时如何处理
3378+::
3379+
3380+ markup = '<a href="http://url1/" href="http://url2/">'
3381+
3382+默认行为是,重名属性会使用最后出现的值
3383+
3384+::
3385+
3386+ soup = BeautifulSoup(markup, 'html.parser')
3387+ soup.a['href']
3388+ # http://url2/
3389+
3390+ soup = BeautifulSoup(markup, 'html.parser', on_duplicate_attribute='replace')
3391+ soup.a['href']
3392+ # http://url2/
3393+
3394+当 ``on_duplicate_attribute='ignore'`` 时,Beautiful Soup 会使用第一个出现的值,然后忽略
3395+后出现的值
3396+
3397+::
3398+
3399+ soup = BeautifulSoup(markup, 'html.parser', on_duplicate_attribute='ignore')
3400+ soup.a['href']
3401+ # http://url1/
3402+
3403+(lxml 和 html5lib 总是采用这种处理方式,它们的默认行为不能通过 Beautiful Soup 配置。)
3404+
3405+如果需要复杂的控制,可以传入一个方法,当属性值重复时会被调用
3406+
3407+::
3408+
3409+ def accumulate(attributes_so_far, key, value):
3410+ if not isinstance(attributes_so_far[key], list):
3411+ attributes_so_far[key] = [attributes_so_far[key]]
3412+ attributes_so_far[key].append(value)
3413+
3414+ soup = BeautifulSoup(markup, 'html.parser', on_duplicate_attribute=accumulate)
3415+ soup.a['href']
3416+ # ["http://url1/", "http://url2/"]
3417+
3418+这个特性新增于 Beautiful Soup 4.9.1。
3419+
3420+实例化自定义子类
3421+------------------
3422+
3423+当解析器传递给 Beautiful Soup 一个标签或一个字符串后,Beautiful Soup 会实例化为 `Tag` 或
3424+`NavigableString` 对象,并包含相关信息。如果想修改默认行为,可以让 Beautiful Soup 实例化
3425+`Tag` 或 `NavigableString` 的子类,子类中可以自定义行为
3426+
3427+::
3428+
3429+ from bs4 import Tag, NavigableString
3430+ class MyTag(Tag):
3431+ pass
3432+
3433+
3434+ class MyString(NavigableString):
3435+ pass
3436+
3437+
3438+ markup = "<div>some text</div>"
3439+ soup = BeautifulSoup(markup, 'html.parser')
3440+ isinstance(soup.div, MyTag)
3441+ # False
3442+ isinstance(soup.div.string, MyString)
3443+ # False
3444+
3445+ my_classes = { Tag: MyTag, NavigableString: MyString }
3446+ soup = BeautifulSoup(markup, 'html.parser', element_classes=my_classes)
3447+ isinstance(soup.div, MyTag)
3448+ # True
3449+ isinstance(soup.div.string, MyString)
3450+ # True
3451+
3452+这种用法可用在于 Beautiful Soup 与测试框架集成。
3453+
3454+这个特性新增于 Beautiful Soup 4.8.1。
3455
3456 常见问题
3457 ========
3458
3459+.. _diagnose:
3460+
3461 代码诊断
3462 ----------
3463
3464-如果想知道Beautiful Soup到底怎样处理一份文档,可以将文档传入 ``diagnose()`` 方法(Beautiful Soup 4.2.0中新增),Beautiful Soup会输出一份报告,说明不同的解析器会怎样处理这段文档,并标出当前的解析过程会使用哪种解析器:
3465+如果想知道 Beautiful Soup 到底怎样处理一份文档,可以将文档传入 ``diagnose()``
3466+方法(Beautiful Soup 4.2.0中新增), Beautiful Soup 会输出一份报告,
3467+说明不同的解析器会怎样处理这段文档,并标出当前的解析过程会使用哪种解析器:
3468
3469 ::
3470
3471 from bs4.diagnose import diagnose
3472- data = open("bad.html").read()
3473+ with open("bad.html") as fp:
3474+ data = fp.read()
3475+
3476 diagnose(data)
3477
3478 # Diagnostic running on Beautiful Soup 4.2.0
3479@@ -2466,93 +3263,150 @@ SoupStrainer
3480 # Here's what html.parser did with the document:
3481 # ...
3482
3483-``diagnose()`` 方法的输出结果可能帮助你找到问题的原因,如果不行,还可以把结果复制出来以便寻求他人的帮助
3484+``diagnose()`` 方法的输出结果可能帮助你找到问题的原因,如果不行,还可以把结果复制出来以
3485+便寻求他人的帮助。
3486
3487 文档解析错误
3488 -------------
3489
3490-文档解析错误有两种.一种是崩溃,Beautiful Soup尝试解析一段文档结果却抛除了异常,通常是 ``HTMLParser.HTMLParseError`` .还有一种异常情况,是Beautiful Soup解析后的文档树看起来与原来的内容相差很多.
3491+文档解析错误有两种。一种是崩溃,Beautiful Soup 尝试解析一段文档结果却抛除了异常,通常是
3492+``HTMLParser.HTMLParseError`` 。还有一种异常情况,是Beautiful Soup 解析后的文档树
3493+看起来与原来的内容相差很多。
3494
3495-这些错误几乎都不是Beautiful Soup的原因,这不会是因为Beautiful Soup的代码写的太优秀,而是因为Beautiful Soup没有包含任何文档解析代码.异常产生自被依赖的解析器,如果解析器不能很好的解析出当前的文档,那么最好的办法是换一个解析器.更多细节查看 `安装解析器`_ 章节.
3496+这些错误几乎都不是 Beautiful Soup 的原因,这不是因为 Beautiful Soup 的代码写得多优秀,
3497+而是因为 Beautiful Soup 没有包含任何文档解析代码。异常产生自被依赖的解析器,如果解析器不能
3498+很好的解析出当前的文档,那么最好的办法是换一个解析器。更多细节查看 `安装解析器`_ 章节。
3499
3500-最常见的解析错误是 ``HTMLParser.HTMLParseError: malformed start tag`` 和 ``HTMLParser.HTMLParseError: bad end tag`` .这都是由Python内置的解析器引起的,解决方法是 `安装lxml或html5lib`_
3501+最常见的解析错误是 ``HTMLParser.HTMLParseError: malformed start tag`` 和
3502+``HTMLParser.HTMLParseError: bad end tag`` 。这都是由Python内置的解析器引起的,
3503+解决方法是 `安装 lxml 或 html5lib <安装解析器>`_ 。
3504
3505-最常见的异常现象是当前文档找不到指定的Tag,而这个Tag光是用眼睛就足够发现的了. ``find_all()`` 方法返回 [] ,而 ``find()`` 方法返回 None .这是Python内置解析器的又一个问题: 解析器会跳过那些它不知道的tag.解决方法还是 `安装lxml或html5lib`_
3506+最常见的非预期行为是发现不了一个确定存在稳当中的 Tag。光是用眼睛就能轻易发现,但用 ``find_all()``
3507+方法返回 [] ,用 ``find()`` 方法返回 None 。这是 Python 内置解析器的又一个问题: 解析器会跳过那些
3508+它不知道的 tag。解决方法还是 `安装 lxml 或 html5lib <安装解析器>`_
3509
3510 版本错误
3511 ----------
3512
3513-* ``SyntaxError: Invalid syntax`` (异常位置在代码行: ``ROOT_TAG_NAME = u'[document]'`` ),因为Python2语法的代码(没有经过迁移)直接在Python3中运行
3514+* ``SyntaxError: Invalid syntax`` (异常位置在代码行: ``ROOT_TAG_NAME = u'[document]'`` ),
3515+ 原因是用 Python2 版本的 Beautiful Soup 未经过代码转换,直接在 Python3 中运行。
3516
3517-* ``ImportError: No module named HTMLParser`` 因为在Python3中执行Python2版本的Beautiful Soup
3518+* ``ImportError: No module named HTMLParser`` 因为在 Python3 中执行 Python2 版本的 Beautiful Soup。
3519
3520-* ``ImportError: No module named html.parser`` 因为在Python2中执行Python3版本的Beautiful Soup
3521+* ``ImportError: No module named html.parser`` 因为在 Python2 中执行 Python3 版本的 Beautiful Soup
3522
3523-* ``ImportError: No module named BeautifulSoup`` 因为在没有安装BeautifulSoup3库的Python环境下执行代码,或忘记了BeautifulSoup4的代码需要从 ``bs4`` 包中引入
3524+* ``ImportError: No module named BeautifulSoup`` 因为在没有安装 Beautiful Soup3 库的 Python 环境下执行代码,
3525+ 或忘记了 Beautiful Soup4 的代码需要从 ``bs4`` 包中引入。
3526
3527-* ``ImportError: No module named bs4`` 因为当前Python环境下还没有安装BeautifulSoup4
3528+* ``ImportError: No module named bs4`` 因为当前 Python 环境下还没有安装 Beautiful Soup4。
3529
3530 解析成XML
3531 ----------
3532
3533-默认情况下,Beautiful Soup会将当前文档作为HTML格式解析,如果要解析XML文档,要在 ``BeautifulSoup`` 构造方法中加入第二个参数 "xml":
3534+默认情况下,Beautiful Soup 会将当前文档作为 HTML 格式解析,如果要解析 XML 文档,要在
3535+``BeautifulSoup`` 构造方法中加入第二个参数 "xml":
3536
3537 ::
3538
3539 soup = BeautifulSoup(markup, "xml")
3540
3541-当然,还需要 `安装lxml`_
3542+当然,还需要 `安装 lxml <安装解析器>`_
3543
3544-解析器的错误
3545-------------
3546+其它解析器的错误
3547+------------------
3548
3549-* 如果同样的代码在不同环境下结果不同,可能是因为两个环境下使用不同的解析器造成的.例如这个环境中安装了lxml,而另一个环境中只有html5lib, `解析器之间的区别`_ 中说明了原因.修复方法是在 ``BeautifulSoup`` 的构造方法中中指定解析器
3550+* 如果同样的代码在不同环境下结果不同,可能是因为两个环境下使用不同的解析器造成的。
3551+ 例如这个环境中安装了 lxml,而另一个环境中只有 html5lib, `解析器之间的区别`_ 中说明了原因。
3552+ 修复方法是在 ``BeautifulSoup`` 的构造方法中中指定解析器。
3553
3554-* 因为HTML标签是 `大小写敏感 <http://www.w3.org/TR/html5/syntax.html#syntax>`_ 的,所以3种解析器再出来文档时都将tag和属性转换成小写.例如文档中的 <TAG></TAG> 会被转换为 <tag></tag> .如果想要保留tag的大写的话,那么应该将文档 `解析成XML`_ .
3555+* 因为HTML标签是 `大小写敏感 <http://www.w3.org/TR/html5/syntax.html#syntax>`_ 的,
3556+ 所以解析器会将 tag 和属性都转换成小写。例如文档中的 <TAG></TAG> 会被转换为 <tag></tag> 。
3557+ 如果想要保留 tag 的大写的话,那么应该将文档 `解析成XML`_ 。
3558
3559 杂项错误
3560 --------
3561
3562-* ``UnicodeEncodeError: 'charmap' codec can't encode character u'\xfoo' in position bar`` (或其它类型的 ``UnicodeEncodeError`` )的错误,主要是两方面的错误(都不是Beautiful Soup的原因),第一种是正在使用的终端(console)无法显示部分Unicode,参考 `Python wiki <http://wiki.Python.org/moin/PrintFails>`_ ,第二种是向文件写入时,被写入文件不支持部分Unicode,这时只要用 ``u.encode("utf8")`` 方法将编码转换为UTF-8.
3563+* ``UnicodeEncodeError: 'charmap' codec can't encode character
3564+ '\xfoo' in position bar`` (或其它类型的 ``UnicodeEncodeError`` )的错误,
3565+ 主要是两方面的原因,第一种是正在使用的终端(console)无法显示部分Unicode,参考
3566+ `Python wiki <http://wiki.Python.org/moin/PrintFails>`_ ,第二种是向文件
3567+ 写入时,被写入文件不支持部分 Unicode,这时需要用 ``u.encode("utf8")`` 方法将
3568+ 编码转换为UTF-8。
3569+
3570+* ``KeyError: [attr]`` 因为调用 ``tag['attr']`` 方法而引起,因为这个 tag 没有定义
3571+ 该属性。出错最多的是 ``KeyError: 'href'`` 和 ``KeyError: 'class'`` 。如果不确定
3572+ 某个属性是否存在时,用 ``tag.get('attr')`` 方法去获取它,跟获取 Python 字典的 key 一样。
3573
3574-* ``KeyError: [attr]`` 因为调用 ``tag['attr']`` 方法而引起,因为这个tag没有定义该属性.出错最多的是 ``KeyError: 'href'`` 和 ``KeyError: 'class'`` .如果不确定某个属性是否存在时,用 ``tag.get('attr')`` 方法去获取它,跟获取Python字典的key一样
3575+* ``AttributeError: 'ResultSet' object has no attribute 'foo'`` 错误通常是
3576+ 因为把 ``find_all()`` 的返回结果当作一个 tag 或文本节点使用,实际上返回结果是一个
3577+ 列表或 ``ResultSet`` 对象的字符串,需要对结果进行循环才能得到每个节点的 ``.foo``
3578+ 属性。 或者使用 ``find()`` 方法仅获取到一个节点。
3579
3580-* ``AttributeError: 'ResultSet' object has no attribute 'foo'`` 错误通常是因为把 ``find_all()`` 的返回结果当作一个tag或文本节点使用,实际上返回结果是一个列表或 ``ResultSet`` 对象的字符串,需要对结果进行循环才能得到每个节点的 ``.foo`` 属性.或者使用 ``find()`` 方法仅获取到一个节点
3581+* ``AttributeError: 'NoneType' object has no attribute 'foo'`` 这个错误通常是
3582+ 在调用了 ``find()`` 方法后直节点取某个属性 foo。但是 ``find()`` 方法并没有找到任何
3583+ 结果,所以它的返回值是 ``None`` 。需要找出为什么 ``find()`` 的返回值是 ``None``。
3584
3585-* ``AttributeError: 'NoneType' object has no attribute 'foo'`` 这个错误通常是在调用了 ``find()`` 方法后直节点取某个属性 .foo 但是 ``find()`` 方法并没有找到任何结果,所以它的返回值是 ``None`` .需要找出为什么 ``find()`` 的返回值是 ``None`` .
3586+* ``AttributeError: 'NavigableString' object has no attribute 'foo'`` 这种问题
3587+ 通常是因为吧一个字符串当做一个 tag 来处理。可能在迭代一个列表时,期望其中都是 tag,但实际上
3588+ 列表里既包含 tag 也包含字符串。
3589
3590 如何提高效率
3591 ------------
3592
3593-Beautiful Soup对文档的解析速度不会比它所依赖的解析器更快,如果对计算时间要求很高或者计算机的时间比程序员的时间更值钱,那么就应该直接使用 `lxml <http://lxml.de/>`_ .
3594+Beautiful Soup对文档的解析速度不会比它所依赖的解析器更快,如果对计算时间要求很高或者
3595+计算机的时间比程序员的时间更值钱,那么就应该直接使用 `lxml <http://lxml.de/>`_ 。
3596+
3597+换句话说,还有提高 Beautiful Soup 效率的办法,使用lxml作为解析器。Beautiful Soup
3598+用 lxml 做解析器比用 html5lib 或 Python 内置解析器速度快很多。
3599+
3600+安装 `cchardet <http://pypi.Python.org/pypi/cchardet/>`_ 后文档的解码的编码检测
3601+速度会更快。
3602+
3603+`解析部分文档`_ 不会节省多少解析时间,但是会节省很多内存,并且搜索时也会变得更快。
3604+
3605+翻译这篇文档
3606+=============
3607+
3608+非常感谢欢迎翻译 Beautiful Soup 的文档。翻译内容应当基于 MIT 协议,就像 Beautiful Soup 和
3609+英文文档一样。
3610
3611-换句话说,还有提高Beautiful Soup效率的办法,使用lxml作为解析器.Beautiful Soup用lxml做解析器比用html5lib或Python内置解析器速度快很多.
3612+有两种方式将翻译内容添加到 Beautiful Soup 的网站上:
3613
3614-安装 `cchardet <http://pypi.Python.org/pypi/cchardet/>`_ 后文档的解码的编码检测会速度更快
3615+1. 在 Beautiful Soup 代码库上创建一个分支,添加翻译,然后合并到主分支。就像修改源代码一样。
3616
3617-`解析部分文档`_ 不会节省多少解析时间,但是会节省很多内存,并且搜索时也会变得更快.
3618+2. 向 Beautiful Soup 讨论组里发送一个消息,带上翻译的链接,或翻译内容的附件。
3619+
3620+使用中文或葡萄牙语的翻译作为模型。尤其注意,请翻译源文件 ``doc/source/index.rst``,
3621+而不是 HTML 版本的文档。这样才能将文档发布为多种格式,而不局限于 HTML。
3622
3623 Beautiful Soup 3
3624 =================
3625
3626-Beautiful Soup 3是上一个发布版本,目前已经停止维护.Beautiful Soup 3库目前已经被几个主要的linux平台添加到源里:
3627+Beautiful Soup 3 是上一个发布版本,目前已经停止维护。Beautiful Soup 3 库目前已经被
3628+几个主要的 linux 发行版添加到源里:
3629
3630 ``$ apt-get install Python-beautifulsoup``
3631
3632-在PyPi中分发的包名字是 ``BeautifulSoup`` :
3633+在 PyPi 中分发的包名字是 ``BeautifulSoup`` :
3634
3635 ``$ easy_install BeautifulSoup``
3636
3637 ``$ pip install BeautifulSoup``
3638
3639-或通过 `Beautiful Soup 3.2.0源码包 <http://www.crummy.com/software/BeautifulSoup/bs3/download/3.x/BeautifulSoup-3.2.0.tar.gz>`_ 安装
3640+或通过 `Beautiful Soup 3.2.0源码包
3641+<http://www.crummy.com/software/BeautifulSoup/bs3/download/3.x/BeautifulSoup-3.2.0.tar.gz>`_ 安装。
3642+
3643+如果是通过 ``easy_install beautifulsoup`` 或 ``easy_install
3644+BeautifulSoup`` 安装,然后代码无法运行,那么可能是安装了错误的 Beautiful Soup 3 版本。
3645+应该这样安装 ``easy_install beautifulsoup4``。
3646
3647-Beautiful Soup 3的在线文档查看 `这里 <http://www.crummy.com/software/BeautifulSoup/bs3/documentation.html>`_ .
3648+Beautiful Soup 3 的在线文档查看 `这里 <http://www.crummy.com/software/BeautifulSoup/bs3/documentation.html>`_ .
3649
3650-迁移到BS4
3651+迁移到 BS4
3652 ----------
3653
3654-只要一个小变动就能让大部分的Beautiful Soup 3代码使用Beautiful Soup 4的库和方法----修改 ``BeautifulSoup`` 对象的引入方式:
3655+大部分使用 Beautiful Soup 3 编写的代码都可以在 Beautiful Soup 4 上运行,
3656+只有一个小变动。只要把引用包的名字从 :py:class:`BeautifulSoup` 改为 ``bs4``,比如:
3657
3658 ::
3659
3660@@ -2564,23 +3418,31 @@ Beautiful Soup 3的在线文档查看 `这里 <http://www.crummy.com/software/Be
3661
3662 from bs4 import BeautifulSoup
3663
3664-* 如果代码抛出 ``ImportError`` 异常“No module named BeautifulSoup”,原因可能是尝试执行Beautiful Soup 3,但环境中只安装了Beautiful Soup 4库
3665+* 如果代码抛出 ``ImportError`` 异常 "No module named BeautifulSoup",
3666+ 原因可能是尝试执行 Beautiful Soup 3,但环境中只安装了 Beautiful Soup 4。
3667
3668-* 如果代码跑出 ``ImportError`` 异常“No module named bs4”,原因可能是尝试运行Beautiful Soup 4的代码,但环境中只安装了Beautiful Soup 3.
3669+* 如果代码抛出 ``ImportError`` 异常 "No module named bs4",原因可能是尝试
3670+ 运行 Beautiful Soup 4 的代码,但环境中只安装了Beautiful Soup 3。
3671
3672-虽然BS4兼容绝大部分BS3的功能,但BS3中的大部分方法已经不推荐使用了,就方法按照 `PEP8标准 <http://www.Python.org/dev/peps/pep-0008/>`_ 重新定义了方法名.很多方法都重新定义了方法名,但只有少数几个方法没有向下兼容.
3673+尽管 BS4 兼容绝大部分 BS3 的功能,但 BS3 中的大部分方法已经不推荐使用了,旧方法标记废弃,
3674+并按照 `PEP8标准 <http://www.Python.org/dev/peps/pep-0008/>`_ 重新命名了新方法。
3675+虽然有大量的重命名和修改,但只有少数几个方法没有向下兼容。
3676
3677-上述内容就是BS3迁移到BS4的注意事项
3678+下面内容就是 BS3 迁移到 BS4 的注意事项:
3679
3680-需要的解析器
3681-............
3682+解析器的变化
3683+^^^^^^^^^^^^^^^
3684
3685-Beautiful Soup 3曾使用Python的 ``SGMLParser`` 解析器,这个模块在Python3中已经被移除了.Beautiful Soup 4默认使用系统的 ``html.parser`` ,也可以使用lxml或html5lib扩展库代替.查看 `安装解析器`_ 章节
3686+Beautiful Soup 3 曾使用 Python 的 ``SGMLParser`` 解析器,这个模块在 Python3
3687+中已经被移除了。Beautiful Soup 4 默认使用系统的 ``html.parser`` , 也可以使用
3688+lxml 或 html5lib 扩展库代替。查看 `安装解析器`_ 章节。
3689
3690-因为解析器 ``html.parser`` 与 ``SGMLParser`` 不同. BS4 和 BS3 处理相同的文档会产生不同的对象结构. 使用lxml或html5lib解析文档的时候, 如果添加了 ``html.parser`` 参数, 解析的对象又回发生变化. 如果发生了这种情况, 只能修改对应的处文档结果处理代码了.
3691+因为解析器 ``html.parser`` 与 ``SGMLParser`` 不同。BS4 和 BS3 处理相同的文档会
3692+产生不同的对象结构。使用 lxml 或 html5lib 解析文档的时候,如果添加了 ``html.parser``
3693+参数,解析的对象又会发生变化。如果发生了这种情况,只能修改对应的文档处理代码了。
3694
3695 方法名的变化
3696-............
3697+^^^^^^^^^^^^^^
3698
3699 * ``renderContents`` -> ``encode_contents``
3700
3701@@ -2614,32 +3476,33 @@ Beautiful Soup 3曾使用Python的 ``SGMLParser`` 解析器,这个模块在Pytho
3702
3703 * ``previousSibling`` -> ``previous_sibling``
3704
3705-Beautiful Soup构造方法的参数部分也有名字变化:
3706+Beautiful Soup 构造方法的参数部分也有名字变化:
3707
3708 * ``BeautifulSoup(parseOnlyThese=...)`` -> ``BeautifulSoup(parse_only=...)``
3709
3710 * ``BeautifulSoup(fromEncoding=...)`` -> ``BeautifulSoup(from_encoding=...)``
3711
3712-为了适配Python3,修改了一个方法名:
3713+为了适配 Python3,修改了一个方法名:
3714
3715 * ``Tag.has_key()`` -> ``Tag.has_attr()``
3716
3717-修改了一个属性名,让它看起来更专业点:
3718+修改了一个属性名,让它看起来更专业点:
3719
3720 * ``Tag.isSelfClosing`` -> ``Tag.is_empty_element``
3721
3722-修改了下面3个属性的名字,以免雨Python保留字冲突.这些变动不是向下兼容的,如果在BS3中使用了这些属性,那么在BS4中这些代码无法执行.
3723+修改了下面 3 个属性的名字,以免与 Python 保留字冲突。这些变动不是向下兼容的,如果在 BS3
3724+中使用了这些属性,那么在 BS4 中这些代码无法执行。
3725
3726-* UnicodeDammit.Unicode -> UnicodeDammit.Unicode_markup``
3727+* ``UnicodeDammit.Unicode -> UnicodeDammit.Unicode_markup``
3728
3729 * ``Tag.next`` -> ``Tag.next_element``
3730
3731 * ``Tag.previous`` -> ``Tag.previous_element``
3732
3733 生成器
3734-.......
3735+^^^^^^^
3736
3737-将下列生成器按照PEP8标准重新命名,并转换成对象的属性:
3738+将下列生成器按照 PEP8 标准重新命名,并转换成对象的属性:
3739
3740 * ``childGenerator()`` -> ``children``
3741
3742@@ -2655,7 +3518,7 @@ Beautiful Soup构造方法的参数部分也有名字变化:
3743
3744 * ``parentGenerator()`` -> ``parents``
3745
3746-所以迁移到BS4版本时要替换这些代码:
3747+所以要把这样的代码:
3748
3749 ::
3750
3751@@ -2669,74 +3532,74 @@ Beautiful Soup构造方法的参数部分也有名字变化:
3752 for parent in tag.parents:
3753 ...
3754
3755-(两种调用方法现在都能使用)
3756+(其实老方法也可以继续使用)
3757
3758-BS3中有的生成器循环结束后会返回 ``None`` 然后结束.这是个bug.新版生成器不再返回 ``None`` .
3759+有的生成器循环结束后会返回 ``None`` 然后结束。这是个 bug。新版生成器不再返回 ``None`` 。
3760
3761-BS4中增加了2个新的生成器, `.strings 和 stripped_strings`_ . ``.strings`` 生成器返回NavigableString对象, ``.stripped_strings`` 方法返回去除前后空白的Python的string对象.
3762+BS4 中增加了 2 个新的生成器, `.strings 和 stripped_strings`_ 。 ``.strings`` 生成器
3763+返回 NavigableString 对象, ``.stripped_strings`` 方法返回去除前后空白的 Python 的
3764+string 对象。
3765
3766 XML
3767-....
3768+^^^^^
3769
3770-BS4中移除了解析XML的 ``BeautifulStoneSoup`` 类.如果要解析一段XML文档,使用 ``BeautifulSoup`` 构造方法并在第二个参数设置为“xml”.同时 ``BeautifulSoup`` 构造方法也不再识别 ``isHTML`` 参数.
3771+BS4 中移除了解析 XML 的 ``BeautifulStoneSoup`` 类。如果要解析一段 XML 文档,使用
3772+``BeautifulSoup`` 构造方法并在第二个参数设置为“xml”。同时 ``BeautifulSoup`` 构造
3773+方法也不再识别 ``isHTML`` 参数。
3774
3775-Beautiful Soup处理XML空标签的方法升级了.旧版本中解析XML时必须指明哪个标签是空标签. 构造方法的 ``selfClosingTags`` 参数已经不再使用.新版Beautiful Soup将所有空标签解析为空元素,如果向空元素中添加子节点,那么这个元素就不再是空元素了.
3776+Beautiful Soup 处理 XML 空标签的方法升级了。旧版本中解析 XML 时必须指明哪个标签是空标签。
3777+构造方法的 ``selfClosingTags`` 参数已经不再使用。新版 Beautiful Soup 将所有空标签解析
3778+为空元素,如果向空元素中添加子节点,那么这个元素就不再是空元素了。
3779
3780 实体
3781-.....
3782+^^^^^
3783
3784-HTML或XML实体都会被解析成Unicode字符,Beautiful Soup 3版本中有很多处理实体的方法,在新版中都被移除了. ``BeautifulSoup`` 构造方法也不再接受 ``smartQuotesTo`` 或 ``convertEntities`` 参数. `编码自动检测`_ 方法依然有 ``smart_quotes_to`` 参数,但是默认会将引号转换成Unicode.内容配置项 ``HTML_ENTITIES`` , ``XML_ENTITIES`` 和 ``XHTML_ENTITIES`` 在新版中被移除.因为它们代表的特性已经不再被支持.
3785+输入的 HTML 或 XML 实体都会被解析成 Unicode 字符。Beautiful Soup 3 版本中有很多相似处理
3786+实体的方法,在新版中都被移除了。 ``BeautifulSoup`` 构造方法也不再接受 ``smartQuotesTo``
3787+或 ``convertEntities`` 参数。 `编码自动检测 <Unicode, Dammit>`_ 方法依然有 ``smart_quotes_to``
3788+参数,但是默认会将引号转换成 Unicode。内容配置项 ``HTML_ENTITIES`` , ``XML_ENTITIES`` 和
3789+``XHTML_ENTITIES`` 在新版中被移除。因为它们代表的特性(转换部分而不是全部实体到 Unicode 字符)
3790+已经不再支持。
3791
3792-如果在输出文档时想把Unicode字符转换成HTML实体,而不是输出成UTF-8编码,那就需要用到 `输出格式`_ 的方法.
3793+如果在输出文档时想把 Unicode 字符转回 HTML 实体,而不是输出成 UTF-8 编码,那就需要用到
3794+`输出格式`_ 的方法。
3795
3796 迁移杂项
3797-.........
3798+^^^^^^^^^
3799+
3800+`Tag.string <string>`_ 属性现在是一个递归操作。如果 A 标签只包含了一个 B 标签,那么 A 标签的。
3801+string 属性值与 B 标签的 string 属性值相同。
3802
3803-`Tag.string`_ 属性现在是一个递归操作.如果A标签只包含了一个B标签,那么A标签的.string属性值与B标签的.string属性值相同.
3804+`多值属性`_ 比如 ``class`` 属性包含一个他们的值的列表,而不是一个字符串。这可能会影响到如何按照
3805+CSS 类名哦搜索 tag。
3806
3807-`多值属性`_ 比如 ``class`` 属性包含一个他们的值的列表,而不是一个字符串.这可能会影响到如何按照CSS类名哦搜索tag.
3808+Tag 对象实现了一个 ``__hash__`` 方法,这样当两个 Tag 对象生成相同的摘要时会被认为相等。这可能会
3809+改变你的脚本行为,如果你吧 Tag 对象放到字典或集合中。
3810
3811-如果使用 ``find*`` 方法时同时传入了 `string 参数`_ 和 `name 参数`_ .Beautiful Soup会搜索指定name的tag,并且这个tag的 `Tag.string`_ 属性包含text参数的内容.结果中不会包含字符串本身.旧版本中Beautiful Soup会忽略掉tag参数,只搜索text参数.
3812+如果使用 ``find*`` 方法时同时传入了 `string 参数`_ 和一个指定 tag 的参数比如 `name 参数`_ 。
3813+Beautiful Soup 会搜索符合指定参数的 tag,并且这个 tag 的 `Tag.string <string>`_ 属性包含
3814+`string 参数`_ 参数的内容。结果中 `不会` 包含字符串本身。旧版本中 Beautiful Soup 会忽略掉指定
3815+tag 的参数,只搜索符合 string 的内容。
3816
3817-``BeautifulSoup`` 构造方法不再支持 markupMassage 参数.现在由解析器负责文档的解析正确性.
3818+``BeautifulSoup`` 构造方法不再支持 markupMassage 参数。现在由解析器负责文档的解析正确性。
3819
3820-很少被用到的几个解析器方法在新版中被移除,比如 ``ICantBelieveItsBeautifulSoup`` 和 ``BeautifulSOAP`` .现在由解析器完全负责如何解释模糊不清的文档标记.
3821+很少被用到的几个解析器方法在新版中被移除,比如 ``ICantBelieveItsBeautifulSoup`` 和 ``BeautifulSOAP`` 。
3822+现在由解析器完全负责如何解释模糊不清的文档标记。
3823
3824-``prettify()`` 方法在新版中返回Unicode字符串,不再返回字节流.
3825+``prettify()`` 方法在新版中返回 Unicode 字符串,不再返回字节串。
3826
3827 附录
3828 =====
3829
3830-.. _`BeautifulSoup3 文档`: http://www.crummy.com/software/BeautifulSoup/bs3/documentation.zh.html
3831-.. _name: `name 参数`_
3832-.. _attrs: `按CSS搜索`_
3833-.. _recursive: `recursive 参数`_
3834-.. _string: `string 参数`_
3835-.. _**kwargs: `keyword 参数`_
3836-.. _.next_siblings: `.next_siblings 和 .previous_siblings`_
3837-.. _.previous_siblings: `.next_siblings 和 .previous_siblings`_
3838-.. _.next_elements: `.next_elements 和 .previous_elements`_
3839-.. _.previous_elements: `.next_elements 和 .previous_elements`_
3840-.. _.stripped_strings: `.strings 和 stripped_strings`_
3841-.. _安装lxml: `安装解析器`_
3842-.. _安装lxml或html5lib: `安装解析器`_
3843-.. _编码自动检测: `Unicode, Dammit! (乱码, 靠!)`_
3844-.. _Tag.string: `.string`_
3845-
3846-
3847-.. [1] BeautifulSoup的google讨论组不是很活跃,可能是因为库已经比较完善了吧,但是作者还是会很热心的尽量帮你解决问题的.
3848-.. [2] 文档被解析成树形结构,所以下一步解析过程应该是当前节点的子节点
3849-.. [3] 过滤器只能作为搜索文档的参数,或者说应该叫参数类型更为贴切,原文中用了 ``filter`` 因此翻译为过滤器
3850-.. [4] 元素参数,HTML文档中的一个tag节点,不能是文本节点
3851+.. [1] BeautifulSoup 的 google 讨论组不是很活跃,可能是因为库已经比较完善了吧,但是作者还是会很热心的尽量帮你解决问题的。
3852+.. [2] 文档被解析成树形结构,所以下一步解析过程应该是当前节点的子节点
3853+.. [3] 过滤器只能作为搜索文档的参数,或者说应该叫参数类型更为贴切,原文中用了 ``filter`` 因此翻译为过滤器
3854+.. [4] 元素参数,HTML 文档中的一个 tag 节点,不能是文本节点
3855 .. [5] 采用先序遍历方式
3856-.. [6] CSS选择器是一种单独的文档搜索语法, 参考 http://www.w3school.com.cn/css/css_selector_type.asp
3857+.. [6] CSS选择器是一种单独的文档搜索语法,参考 http://www.w3school.com.cn/css/css_selector_type.asp
3858 .. [7] 原文写的是 html5lib, 译者觉得这是原文档的一个笔误
3859-.. [8] wrap含有包装,打包的意思,但是这里的包装不是在外部包装而是将当前tag的内部内容包装在一个tag里.包装原来内容的新tag依然在执行 `wrap()`_ 方法的tag内
3860-.. [9] 文档中特殊编码字符被替换成特殊字符(通常是�)的过程是Beautful Soup自动实现的,如果想要多种编码格式的文档被完全转换正确,那么,只好,预先手动处理,统一编码格式
3861-.. [10] 智能引号,常出现在microsoft的word软件中,即在某一段落中按引号出现的顺序每个引号都被自动转换为左引号,或右引号.
3862-
3863-原文: http://www.crummy.com/software/BeautifulSoup/bs4/doc/
3864-
3865-翻译: Deron Wang
3866-
3867-查看 `BeautifulSoup3 文档`_
3868+.. [8] wrap含有包装,打包的意思,但是这里的包装不是在外部包装而是将当前tag的内部内容包装在一个tag里。
3869+ 包装原来内容的新tag依然在执行 `wrap()`_ 方法的tag内
3870+.. [9] 文档中特殊编码字符被替换成特殊字符(通常是�)的过程是Beautful Soup自动实现的,
3871+ 如果想要多种编码格式的文档被完全转换正确,那么,只好,预先手动处理,统一编码格式
3872+.. [10] 智能引号,常出现在 microsoft 的 word 软件中,即在某一段落中按引号出现的顺序每个引号都被自动转换为左引号,或右引号。

Subscribers

People subscribed via source and target branches