Beautiful Soup - 快速指南


Beautiful Soup - 概述

在当今世界,我们有大量免费的非结构化数据/信息(主要是网络数据)。有时免费提供的数据很容易阅读,有时则不然。无论您的数据如何获得,网络抓取都是非常有用的工具,可以将非结构化数据转换为更易于阅读和分析的结构化数据。换句话说,收集、组织和分析大量数据的一种方法是通过网络抓取。首先让我们了解一下什么是网络抓取。

什么是网页抓取?

抓取只是(通过各种手段)提取、复制和筛选数据的过程。

当我们从网络(例如网页或网站)中抓取或提取数据或提要时,称为网络抓取。

因此,网络抓取也称为网络数据提取或网络收获,是从网络中提取数据。简而言之,网络抓取为开发人员提供了一种从互联网收集和分析数据的方法。

为什么要进行网页抓取?

网页抓取提供了一种出色的工具,可以自动执行人类在浏览时所做的大部分事情。网络抓取在企业中有多种使用方式 -

研究数据

聪明的分析师(如研究人员或记者)使用网络抓取工具,而不是手动从网站收集和清理数据。

产品价格及人气比较

目前有一些服务使用网络抓取工具从众多在线网站收集数据,并用它来比较产品的受欢迎程度和价格。

搜索引擎优化监控

有许多 SEO 工具,例如 Ahrefs、Seobility、SEMrush 等,用于竞争分析和从客户网站提取数据。

搜索引擎

有一些大型 IT 公司的业务完全依赖于网络抓取。

销售和营销

通过网络抓取收集的数据可以被营销人员用来分析不同的利基市场和竞争对手,也可以被销售专家用来销售内容营销或社交媒体推广服务。

为什么使用 Python 进行网页抓取?

Python 是最流行的网络抓取语言之一,因为它可以非常轻松地处理大多数与网络抓取相关的任务。

以下是为什么选择 python 进行网页抓取的一些要点:

使用方便

大多数开发人员都认为 Python 非常容易编写代码。我们不必使用任何花括号“{}”或分号“;” 任何地方,这使得在开发网络抓取工具时更具可读性和易于使用。

庞大的图书馆支持

Python 提供了大量的库来满足不同的需求,因此它适用于网页抓取以及数据可视化、机器学习等。

易于解释的语法

Python 是一种非常易读的编程语言,因为 Python 语法很容易理解。Python 的表达能力很强,代码缩进可以帮助用户区分代码中的不同块或范围。

动态类型语言

Python 是一种动态类型语言,这意味着分配给变量的数据表明该变量是什么类型。它可以节省大量时间并加快工作速度。

庞大的社区

Python 社区非常庞大,无论您在编写代码时遇到什么困难,它都能为您提供帮助。

美汤简介

Beautiful Soup 是一个 Python 库,以《爱丽丝梦游仙境》中 Lewis Carroll 的同名诗命名。Beautiful Soup 是一个 Python 包,顾名思义,它解析不需要的数据,并通过修复错误的 HTML 来帮助组织和格式化混乱的 Web 数据,并以易于遍历的 XML 结构呈现给我们。

简而言之,Beautiful Soup 是一个 python 包,它允许我们从 HTML 和 XML 文档中提取数据。

Beautiful Soup - 安装

由于BeautifulSoup不是标准的python库,我们需要先安装它。我们将安装最新的 BeautifulSoup 4 库(也称为 BS4)。

为了隔离我们的工作环境以免干扰现有设置,让我们首先创建一个虚拟环境。

创建虚拟环境(可选)

虚拟环境允许我们为特定项目创建独立的 python 工作副本,而不会影响外部设置。

安装任何 python 软件包机器的最佳方法是使用 pip,但是,如果尚未安装 pip(您可以在命令或 shell 提示符中使用 –“pip –version”进行检查),则可以通过给出以下命令进行安装 -

Linux环境

$sudo apt-get install python-pip

Windows环境

要在 Windows 中安装 pip,请执行以下操作 -

>python get-pip.py

就这样,pip 现在已经安装在你的 Windows 机器上了。

您可以通过运行以下命令来验证您安装的 pip -

>pip --version
pip 19.2.3 from c:\users\yadur\appdata\local\programs\python\python37\lib\site-packages\pip (python 3.7)

安装虚拟环境

在命令提示符中运行以下命令 -

>pip install virtualenv

运行后,您将看到以下屏幕截图 -

虚拟环境

以下命令将在当前目录中创建一个虚拟环境(“myEnv”) -

>virtualenv myEnv

截屏

启用

要激活虚拟环境,请运行以下命令 -

>myEnv\Scripts\activate
虚拟环境

在上面的屏幕截图中,您可以看到我们有“myEnv”作为前缀,这告诉我们我们处于虚拟环境“myEnv”下。

要退出虚拟环境,请运行 deactivate。

(myEnv) C:\Users\yadur>deactivate
C:\Users\yadur>

我们的虚拟环境已经准备好了,现在让我们安装 beautifulsoup。

安装BeautifulSoup

由于BeautifulSoup不是标准库,我们需要安装它。我们将使用 BeautifulSoup 4 包(称为 bs4)。

Linux机器

要使用系统包管理器在 Debian 或 Ubuntu Linux 上安装 bs4,请运行以下命令 -

$sudo apt-get install python-bs4 (for python 2.x)
$sudo apt-get install python3-bs4 (for python 3.x)

您可以使用 easy_install 或 pip 安装 bs4 (以防您在使用系统打包程序安装时发现问题)。

$easy_install beautifulsoup4
$pip install beautifulsoup4

(如果您使用的是 python3,您可能需要分别使用 easy_install3 或 pip3)

Windows机

在 Windows 中安装 beautifulsoup4 非常简单,特别是如果您已经安装了 pip。

>pip install beautifulsoup4
Beautiful Soup4

现在beautifulsoup4已经安装在我们的机器上。说一下安装后遇到的一些问题。

安装后出现问题

在 Windows 机器上,您可能会遇到安装错误版本的错误,主要是通过 -

  • 错误:ImportError“没有名为 HTMLParser 的模块”,那么您必须在 Python 3 下运行 python 2 版本的代码。

  • 错误:ImportError“没有名为 html.parser 的模块”错误,那么您必须在 Python 2 下运行 Python 3 版本的代码。

摆脱上述两种情况的最佳方法是再次重新安装 BeautifulSoup,完全删除现有安装。

如果您在 ROOT_TAG_NAME = u'[document]' 行上收到SyntaxError“无效语法”,那么您需要将 python 2 代码转换为 python 3,只需安装包 -

$ python3 setup.py install

或者通过在 bs4 目录上手动运行 python 的 2 到 3 转换脚本 -

$ 2to3-3.2 -w bs4

安装解析器

默认情况下,Beautiful Soup 支持Python 标准库中包含的HTML 解析器,但它也支持许多外部第三方Python 解析器,例如lxml 解析器或html5lib 解析器。

要安装 lxml 或 html5lib 解析器,请使用命令 -

Linux机器

$apt-get install python-lxml
$apt-get insall python-html5lib

Windows机

$pip install lxml
$pip install html5lib
安装解析器

一般来说,用户使用lxml来提高速度,如果您使用旧版本的python 2(2.7.3版本之前)或python 3(3.2.2之前版本),建议使用lxml或html5lib解析器,因为python的内置HTML解析器是处理旧版本不太好。

跑美汤

现在是时候在其中一个 html 页面中测试我们的 Beautiful Soup 包了(以网页 – https://www.tutorialspoint.com/index.htm为例,您可以选择您想要的任何其他网页)并从中提取一些信息它。

在下面的代码中,我们尝试从网页中提取标题 -

from bs4 import BeautifulSoup
import requests
url = "https://www.tutorialspoint.com/index.htm"
req = requests.get(url)
soup = BeautifulSoup(req.text, "html.parser")
print(soup.title)

输出

<title>H2O, Colab, Theano, Flutter, KNime, Mean.js, Weka, Solidity, Org.Json, AWS QuickSight, JSON.Simple, Jackson Annotations, Passay, Boon, MuleSoft, Nagios, Matplotlib, Java NIO, PyTorch, SLF4J, Parallax Scrolling, Java Cryptography</title>

一项常见任务是提取网页中的所有 URL。为此,我们只需要添加以下代码行 -

for link in soup.find_all('a'):
print(link.get('href'))

输出

https://www.tutorialspoint.com/index.htm
https://www.tutorialspoint.com/about/about_careers.htm
https://www.tutorialspoint.com/questions/index.php
https://www.tutorialspoint.com/online_dev_tools.htm
https://www.tutorialspoint.com/codingground.htm
https://www.tutorialspoint.com/current_affairs.htm
https://www.tutorialspoint.com/upsc_ias_exams.htm
https://www.tutorialspoint.com/tutor_connect/index.php
https://www.tutorialspoint.com/whiteboard.htm
https://www.tutorialspoint.com/netmeeting.php
https://www.tutorialspoint.com/index.htm
https://www.tutorialspoint.com/tutorialslibrary.htm
https://www.tutorialspoint.com/videotutorials/index.php
https://store.tutorialspoint.com
https://www.tutorialspoint.com/gate_exams_tutorials.htm
https://www.tutorialspoint.com/html_online_training/index.asp
https://www.tutorialspoint.com/css_online_training/index.asp
https://www.tutorialspoint.com/3d_animation_online_training/index.asp
https://www.tutorialspoint.com/swift_4_online_training/index.asp
https://www.tutorialspoint.com/blockchain_online_training/index.asp
https://www.tutorialspoint.com/reactjs_online_training/index.asp
https://www.tutorix.com
https://www.tutorialspoint.com/videotutorials/top-courses.php
https://www.tutorialspoint.com/the_full_stack_web_development/index.asp
….
….
https://www.tutorialspoint.com/online_dev_tools.htm
https://www.tutorialspoint.com/free_web_graphics.htm
https://www.tutorialspoint.com/online_file_conversion.htm
https://www.tutorialspoint.com/netmeeting.php
https://www.tutorialspoint.com/free_online_whiteboard.htm
https://www.tutorialspoint.com
https://www.facebook.com/tutorialspointindia
https://plus.google.com/u/0/+tutorialspoint
http://www.twitter.com/tutorialspoint
http://www.linkedin.com/company/tutorialspoint
https://www.youtube.com/channel/UCVLbzhxVTiTLiVKeGV7WEBg
https://www.tutorialspoint.com/index.htm
/about/about_privacy.htm#cookies
/about/faq.htm
/about/about_helping.htm
/about/contact_us.htm

同样,我们可以使用 beautifulsoup4 提取有用的信息。

现在让我们更多地了解上面例子中的“汤”。

Beautiful Soup - 汤页面

在前面的代码示例中,我们使用字符串方法通过漂亮的构造函数解析文档。另一种方法是通过打开文件句柄传递文档。

from bs4 import BeautifulSoup
with open("example.html") as fp:
   soup = BeautifulSoup(fp)
soup = BeautifulSoup("<html>data</html>")

首先将文档转换为 Unicode,然后将 HTML 实体转换为 Unicode 字符:</p>

import bs4
html = '''<b>tutorialspoint</b>, <i>&web scraping &data science;</i>'''
soup = bs4.BeautifulSoup(html, 'lxml')
print(soup)

输出

<html><body><b>tutorialspoint</b>, <i>&web scraping &data science;</i></body></html>

BeautifulSoup 然后使用 HTML 解析器解析数据,或者您明确地告诉它使用 XML 解析器进行解析。

HTML 树结构

在研究 HTML 页面的不同组件之前,让我们首先了解 HTML 树结构。

HTML 树结构

文档树中的根元素是 html,它可以有父元素、子元素和兄弟元素,这由它在树结构中的位置决定。要在 HTML 元素、属性和文本之间移动,您必须在树结构中的节点之间移动。

让我们假设网页如下所示 -

教程点在线图书馆

转换为 html 文档如下 -

<html><head><title>TutorialsPoint</title></head><h1>Tutorialspoint Online Library</h1><p<<b>It's all Free</b></p></body></html>

这仅仅意味着,对于上面的 html 文档,我们有一个 html 树结构,如下所示 -

HTML文档

Beautiful Soup - 各种物体

当我们将 html 文档或字符串传递给 beautifulsoup 构造函数时,beautifulsoup 基本上将复杂的 html 页面转换为不同的 python 对象。下面我们将讨论四种主要类型的对象:

  • 标签

  • 导航字符串

  • Beautiful Soup

  • 评论

标记对象

HTML 标签用于定义各种类型的内容。BeautifulSoup 中的标签对象对应于实际页面或文档中的 HTML 或 XML 标签。

>>> from bs4 import BeautifulSoup
>>> soup = BeautifulSoup('<b class="boldest">TutorialsPoint</b>')
>>> tag = soup.html
>>> type(tag)
<class 'bs4.element.Tag'>

标签包含很多属性和方法,标签的两个重要特征是它的名称和属性。

名称(标签.名称)

每个标签都包含一个名称,可以通过“.name”作为后缀进行访问。tag.name 将返回它的标签类型。

>>> tag.name
'html'

但是,如果我们更改标签名称,相同的内容将反映在 BeautifulSoup 生成的 HTML 标记中。

>>> tag.name = "Strong"
>>> tag
<Strong><body><b class="boldest">TutorialsPoint</b></body></Strong>
>>> tag.name
'Strong'

属性(标签.attrs)

标签对象可以具有任意数量的属性。标签<b class=”boldest”>有一个属性“class”,其值为“boldest”。任何不是标签的东西基本上都是一个属性并且必须包含一个值。您可以通过访问键(如上例中访问“class”)或直接通过“.attrs”访问来访问属性

>>> tutorialsP = BeautifulSoup("<div class='tutorialsP'></div>",'lxml')
>>> tag2 = tutorialsP.div
>>> tag2['class']
['tutorialsP']

我们可以对标签的属性进行各种修改(添加/删除/修改)。

>>> tag2['class'] = 'Online-Learning'
>>> tag2['style'] = '2007'
>>>
>>> tag2
<div class="Online-Learning" style="2007"></div>
>>> del tag2['style']
>>> tag2
<div class="Online-Learning"></div>
>>> del tag['class']
>>> tag
<b SecondAttribute="2">TutorialsPoint</b>
>>>
>>> del tag['SecondAttribute']
>>> tag
</b>
>>> tag2['class']
'Online-Learning'
>>> tag2['style']
KeyError: 'style'

多值属性

某些 HTML5 属性可以有多个值。最常用的是可以有多个 CSS 值的类属性。其他包括“rel”、“rev”、“headers”、“accesskey”和“accept-charset”。beautiful soup中的多值属性如列表所示。

>>> from bs4 import BeautifulSoup
>>>
>>> css_soup = BeautifulSoup('<p class="body"></p>')
>>> css_soup.p['class']
['body']
>>>
>>> css_soup = BeautifulSoup('<p class="body bold"></p>')
>>> css_soup.p['class']
['body', 'bold']

然而,如果任何属性包含多个值,但它不是任何版本的 HTML 标准的多值属性,beautiful soup 会保留该属性 -

>>> id_soup = BeautifulSoup('<p id="body bold"></p>')
>>> id_soup.p['id']
'body bold'
>>> type(id_soup.p['id'])
<class 'str'>

如果将标签转换为字符串,则可以合并多个属性值。

>>> rel_soup = BeautifulSoup("<p> tutorialspoint Main <a rel='Index'> Page</a></p>")
>>> rel_soup.a['rel']
['Index']
>>> rel_soup.a['rel'] = ['Index', ' Online Library, Its all Free']
>>> print(rel_soup.p)
<p> tutorialspoint Main <a rel="Index Online Library, Its all Free"> Page</a></p>

通过使用“get_attribute_list”,您获得的值始终是列表、字符串,无论它是否是多值。

id_soup.p.get_attribute_list(‘id’)

但是,如果将文档解析为“xml”,则不存在多值属性 -

>>> xml_soup = BeautifulSoup('<p class="body bold"></p>', 'xml')
>>> xml_soup.p['class']
'body bold'

导航字符串

navigablestring 对象用于表示标签的内容。要访问内容,请使用带有标签的“.string”。

>>> from bs4 import BeautifulSoup
>>> soup = BeautifulSoup("<h2 id='message'>Hello, Tutorialspoint!</h2>")
>>>
>>> soup.string
'Hello, Tutorialspoint!'
>>> type(soup.string)
>

您可以用另一个字符串替换该字符串,但无法编辑现有字符串。

>>> soup = BeautifulSoup("<h2 id='message'>Hello, Tutorialspoint!</h2>")
>>> soup.string.replace_with("Online Learning!")
'Hello, Tutorialspoint!'
>>> soup.string
'Online Learning!'
>>> soup
<html><body><h2 id="message">Online Learning!</h2></body></html>

Beautiful Soup

BeautifulSoup 是当我们尝试抓取网络资源时创建的对象。所以,这是我们正在尝试抓取的完整文档。大多数时候,它被视为标签对象。

>>> from bs4 import BeautifulSoup
>>> soup = BeautifulSoup("<h2 id='message'>Hello, Tutorialspoint!</h2>")
>>> type(soup)
<class 'bs4.BeautifulSoup'>
>>> soup.name
'[document]'

评论

评论对象说明了网络文档的评论部分。它只是 NavigableString 的一种特殊类型。

>>> soup = BeautifulSoup('<p><!-- Everything inside it is COMMENTS --></p>')
>>> comment = soup.p.string
>>> type(comment)
<class 'bs4.element.Comment'>
>>> type(comment)
<class 'bs4.element.Comment'>
>>> print(soup.p.prettify())
<p>
<!-- Everything inside it is COMMENTS -->
</p>

NavigableString 对象

navigablestring 对象用于表示标签内的文本,而不是标签本身。

Beautiful Soup - 按标签导航

在本章中,我们将讨论按标签导航。

下面是我们的 html 文档 -

>>> html_doc = """
<html><head><title>Tutorials Point</title></head>
<body>
<p class="title"><b>The Biggest Online Tutorials Library, It's all Free</b></p>
<p class="prog">Top 5 most used Programming Languages are:
<a href="https://www.tutorialspoint.com/java/java_overview.htm" class="prog" id="link1">Java</a>,
<a href="https://www.tutorialspoint.com/cprogramming/index.htm" class="prog" id="link2">C</a>,
<a href="https://www.tutorialspoint.com/python/index.htm" class="prog" id="link3">Python</a>,
<a href="https://www.tutorialspoint.com/javascript/javascript_overview.htm" class="prog" id="link4">JavaScript</a> and
<a href="https://www.tutorialspoint.com/ruby/index.htm" class="prog" id="link5">C</a>;
as per online survey.</p>
<p class="prog">Programming Languages</p>
"""
>>>
>>> from bs4 import BeautifulSoup
>>> soup = BeautifulSoup(html_doc, 'html.parser')
>>>

基于上述文档,我们将尝试从文档的一个部分移动到另一部分。

下降

任何 HTML 文档中最重要的元素之一是标签,它可能包含其他标签/字符串(标签的子标签)。Beautiful Soup 提供了不同的方式来导航和迭代标签的子代。

使用标签名称导航

搜索解析树的最简单方法是按标签名称搜索标签。如果您想要 <head> 标签,请使用 soup.head -

>>> soup.head
<head>&t;title>Tutorials Point</title></head>
>>> soup.title
<title>Tutorials Point</title>

获取 <body> 标记中的特定标记(例如第一个 <b> 标记)。

>>> soup.body.b
<b>The Biggest Online Tutorials Library, It's all Free</b>

使用标签名称作为属性只会为您提供该名称的第一个标签 -

>>> soup.a
<a class="prog" href="https://www.tutorialspoint.com/java/java_overview.htm" id="link1">Java</a>

要获取所有标签的属性,您可以使用 find_all() 方法 -

>>> soup.find_all("a")
[<a class="prog" href="https://www.tutorialspoint.com/java/java_overview.htm" id="link1">Java</a>, <a class="prog" href="https://www.tutorialspoint.com/cprogramming/index.htm" id="link2">C</a>, <a class="prog" href="https://www.tutorialspoint.com/python/index.htm" id="link3">Python</a>, <a class="prog" href="https://www.tutorialspoint.com/javascript/javascript_overview.htm" id="link4">JavaScript</a>, <a class="prog" href="https://www.tutorialspoint.com/ruby/index.htm" id="link5">C</a>]>>> soup.find_all("a")
[<a class="prog" href="https://www.tutorialspoint.com/java/java_overview.htm" id="link1">Java</a>, <a class="prog" href="https://www.tutorialspoint.com/cprogramming/index.htm" id="link2">C</a>, <a class="prog" href="https://www.tutorialspoint.com/python/index.htm" id="link3">Python</a>, <a class="prog" href="https://www.tutorialspoint.com/javascript/javascript_overview.htm" id="link4">JavaScript</a>, <a class="prog" href="https://www.tutorialspoint.com/ruby/index.htm" id="link5">C</a>]

.contents 和 .children

我们可以通过其 .contents 在列表中搜索标签的子级 -

>>> head_tag = soup.head
>>> head_tag
<head><title>Tutorials Point</title></head>
>>> Htag = soup.head
>>> Htag
<head><title>Tutorials Point</title></head>
>>>
>>> Htag.contents
[<title>Tutorials Point</title>
>>>
>>> Ttag = head_tag.contents[0]
>>> Ttag
<title>Tutorials Point</title>
>>> Ttag.contents
['Tutorials Point']

BeautifulSoup 对象本身有子对象。在这种情况下, <html> 标签是 BeautifulSoup 对象的子代 -

>>> len(soup.contents)
2
>>> soup.contents[1].name
'html'

字符串没有 .contents,因为它不能包含任何内容 -

>>> text = Ttag.contents[0]
>>> text.contents
self.__class__.__name__, attr))
AttributeError: 'NavigableString' object has no attribute 'contents'

不要将它们作为列表获取,而是使用 .children 生成器来访问标签的子项 -

>>> for child in Ttag.children:
print(child)
Tutorials Point

。后人

.descendants 属性允许您递归地迭代标签的所有子代 -

它的直接子代及其直接子代的子代等等 -

>>> for child in Htag.descendants:
print(child)
<title>Tutorials Point</title>
Tutorials Point

<head> 标签只有一个子标签,但它有两个后代:<title> 标签和 <title> 标签的子标签。beautifulsoup 对象只有一个直接子对象(<html> 标签),但它有很多后代 -

>>> len(list(soup.children))
2
>>> len(list(soup.descendants))
33

。细绳

如果该标签只有一个子项,并且该子项是 NavigableString,则该子项将以 .string 形式提供 -

>>> Ttag.string
'Tutorials Point'

如果一个标签的唯一子标签是另一个标签,并且该标签具有 .string,则父标签被视为与其子标签具有相同的 .string -

>>> Htag.contents
[<title>Tutorials Point</title>]
>>>
>>> Htag.string
'Tutorials Point'

但是,如果一个标签包含多个内容,则不清楚 .string 应该引用什么,因此 .string 被定义为 None -

>>> print(soup.html.string)
None

.strings 和 stripped_strings

如果标签内有多个内容,您仍然可以只查看字符串。使用 .strings 生成器 -

>>> for string in soup.strings:
print(repr(string))
'\n'
'Tutorials Point'
'\n'
'\n'
"The Biggest Online Tutorials Library, It's all Free"
'\n'
'Top 5 most used Programming Languages are: \n'
'Java'
',\n'
'C'
',\n'
'Python'
',\n'
'JavaScript'
' and\n'
'C'
';\n \nas per online survey.'
'\n'
'Programming Languages'
'\n'

要删除多余的空格,请使用 .stripped_strings 生成器 -

>>> for string in soup.stripped_strings:
print(repr(string))
'Tutorials Point'
"The Biggest Online Tutorials Library, It's all Free"
'Top 5 most used Programming Languages are:'
'Java'
','
'C'
','
'Python'
','
'JavaScript'
'and'
'C'
';\n \nas per online survey.'
'Programming Languages'

往上走

在“家谱”类比中,每个标签和每个字符串都有一个父级:包含它的标签:

.parent

要访问元素的父元素,请使用 .parent 属性。

>>> Ttag = soup.title
>>> Ttag
<title>Tutorials Point</title>
>>> Ttag.parent
<head>title>Tutorials Point</title></head>

在我们的 html_doc 中,标题字符串本身有一个父级:包含它的 <title> 标签 -

>>> Ttag.string.parent
<title>Tutorials Point</title>

像 <html> 这样的顶级标签的父级是 Beautifulsoup 对象本身 -

>>> htmltag = soup.html
>>> type(htmltag.parent)
<class 'bs4.BeautifulSoup'>

Beautifulsoup 对象的 .parent 定义为 None -

>>> print(soup.parent)
None

。父母

要迭代所有父元素,请使用 .parents 属性。

>>> link = soup.a
>>> link
<a class="prog" href="https://www.tutorialspoint.com/java/java_overview.htm" id="link1">Java</a>
>>>
>>> for parent in link.parents:
if parent is None:
print(parent)
else:
print(parent.name)
p
body
html
[document]

横着走

下面是一份简单的文件 -

>>> sibling_soup = BeautifulSoup("<a><b>TutorialsPoint</b><c><strong>The Biggest Online Tutorials Library, It's all Free</strong></b></a>")
>>> print(sibling_soup.prettify())
<html>
<body>
   <a>
      <b>
         TutorialsPoint
      </b>
      <c>
         <strong>
            The Biggest Online Tutorials Library, It's all Free
         </strong>
      </c>
   </a>
</body>
</html>

在上面的文档中, <b> 和 <c> 标记处于同一级别,并且它们都是同一标记的子级。<b> 和 <c> 标记都是同级标记。

.next_sibling 和 .previous_sibling

使用 .next_sibling 和 .previous_sibling 在解析树同一级别的页面元素之间导航:

>>> sibling_soup.b.next_sibling
<c><strong>The Biggest Online Tutorials Library, It's all Free</strong></c>
>>>
>>> sibling_soup.c.previous_sibling
<b>TutorialsPoint</b>

<b> 标记有 .next_sibling 但没有 .previous_sibling,因为在树的同一级别上 <b> 标记之前没有任何内容,<c> 标记也是如此。

>>> print(sibling_soup.b.previous_sibling)
None
>>> print(sibling_soup.c.next_sibling)
None

这两个字符串不是兄弟姐妹,因为它们没有相同的父级。

>>> sibling_soup.b.string
'TutorialsPoint'
>>>
>>> print(sibling_soup.b.string.next_sibling)
None

.next_siblings 和 .previous_siblings

要迭代标签的同级标签,请使用 .next_siblings 和 .previous_siblings。

>>> for sibling in soup.a.next_siblings:
print(repr(sibling))
',\n'
<a class="prog" href="https://www.tutorialspoint.com/cprogramming/index.htm" id="link2">C</a>
',\n'
>a class="prog" href="https://www.tutorialspoint.com/python/index.htm" id="link3">Python</a>
',\n'
<a class="prog" href="https://www.tutorialspoint.com/javascript/javascript_overview.htm" id="link4">JavaScript</a>
' and\n'
<a class="prog" href="https://www.tutorialspoint.com/ruby/index.htm"
id="link5">C</a>
';\n \nas per online survey.'
>>> for sibling in soup.find(id="link3").previous_siblings:
print(repr(sibling))
',\n'
<a class="prog" href="https://www.tutorialspoint.com/cprogramming/index.htm" id="link2">C</a>
',\n'
<a class="prog" href="https://www.tutorialspoint.com/java/java_overview.htm" id="link1">Java</a>
'Top 5 most used Programming Languages are: \n'

来回

现在让我们回到前面的“html_doc”示例中的前两行 -

&t;html><head><title>Tutorials Point</title></head>
<body>
<h4 class="tagLine"><b>The Biggest Online Tutorials Library, It's all Free</b></h4>

HTML 解析器获取上述字符串并将其转换为一系列事件,例如“打开 <html> 标签”、“打开 <head> 标签”、“打开 <title> 标签”、“添加字符串”、 “关闭 </title> 标签”、“关闭 </head> 标签”、“打开 <h4> 标签”等等。BeautifulSoup 提供了不同的方法来重建文档的初始解析。

.next_element 和 .previous_element

标签或字符串的 .next_element 属性指向随后立即解析的内容。有时它看起来与 .next_sibling 类似,但并不完全一样。以下是“html_doc”示例文档中的最终 <a> 标记。

>>> last_a_tag = soup.find("a", id="link5")
>>> last_a_tag
<a class="prog" href="https://www.tutorialspoint.com/ruby/index.htm" id="link5">C</a>
>>> last_a_tag.next_sibling
';\n \nas per online survey.'

然而,<a> 标签的 .next_element(在 <a> 标签之后立即解析的内容)不是该句子的其余部分:它是单词“C”:

>>> last_a_tag.next_element
'C'

上述Behave是因为在原始标记中,字母“C”出现在分号之前。解析器遇到了 <a> 标记,然后是字母“C”,然后是结束 </a> 标记,然后是分号和句子的其余部分。分号与 <a> 标记处于同一级别,但首先遇到字母“C”。

.previous_element 属性与 .next_element 完全相反。它指向紧接在此元素之前解析的任何元素。

>>> last_a_tag.previous_element
' and\n'
>>>
>>> last_a_tag.previous_element.next_element
<a class="prog" href="https://www.tutorialspoint.com/ruby/index.htm" id="link5">C</a>

.next_elements 和 .previous_elements

我们使用这些迭代器向前和向后移动到一个元素。

>>> for element in last_a_tag.next_e lements:
print(repr(element))
'C'
';\n \nas per online survey.'
'\n'
<p class="prog">Programming Languages</p>
'Programming Languages'
'\n'

Beautiful Soup - 寻找树

Beautifulsoup 有很多方法可以让我们搜索解析树。两个最常见和最常用的方法是 find() 和 find_all()。

在讨论 find() 和 find_all() 之前,让我们看一下可以传递到这些方法中的不同过滤器的一些示例。

过滤器种类

我们有不同的过滤器,我们可以将它们传递到这些方法中,并且理解这些过滤器至关重要,因为这些过滤器在整个搜索 API 中一次又一次地使用。我们可以根据标签的名称、其属性、字符串文本或这些的混合来使用这些过滤器。

一个字符串

最简单的过滤器类型之一是字符串。将字符串传递给搜索方法,Beautifulsoup 将针对该字符串执行匹配。

下面的代码将找到文档中的所有 <p> 标签 -

>>> markup = BeautifulSoup('<p>Top Three</p><p><pre>Programming Languages are:</pre></p><p><b>Java, Python, Cplusplus</b></p>')
>>> markup.find_all('p')
[<p>Top Three</p>, <p></p>, <p><b>Java, Python, Cplusplus</b></p>]

正则表达式

您可以找到以给定字符串/标签开头的所有标签。在此之前,我们需要导入 re 模块才能使用正则表达式。

>>> import re
>>> markup = BeautifulSoup('<p>Top Three</p><p><pre>Programming Languages are:</pre></p><p><b>Java, Python, Cplusplus</b></p>')
>>>
>>> markup.find_all(re.compile('^p'))
[<p>Top Three</p>, <p></p>, <pre>Programming Languages are:</pre>, <p><b>Java, Python, Cplusplus</b></p>]

列表

您可以通过提供列表来传递多个标签来查找。下面的代码找到所有 <b> 和 <pre> 标签 -

>>> markup.find_all(['pre', 'b'])
[<pre>Programming Languages are:</pre>, <b>Java, Python, Cplusplus</b>]

真的

True 将返回它可以找到的所有标签,但不返回它们自己的字符串 -

>>> markup.find_all(True)
[<html><body><p>Top Three</p><p></p><pre>Programming Languages are:</pre>
<p><b>Java, Python, Cplusplus</b> </p> </body></html>, 
<body><p>Top Three</p><p></p><pre> Programming Languages are:</pre><p><b>Java, Python, Cplusplus</b></p>
</body>, 
<p>Top Three</p>, <p></p>, <pre>Programming Languages are:</pre>, <p><b>Java, Python, Cplusplus</b></p>, <b>Java, Python, Cplusplus</b>]

仅返回上述汤中的标签 -

>>> for tag in markup.find_all(True):
(tag.name)
'html'
'body'
'p'
'p'
'pre'
'p'
'b'

找到所有()

您可以使用 find_all 从页面响应中提取特定标记的所有出现,如下所示:

句法

find_all(name, attrs, recursive, string, limit, **kwargs)

让我们从 IMDB 中提取一些有趣的数据——有史以来的“评分最高的电影”。

>>> url="https://www.imdb.com/chart/top/?ref_=nv_mv_250"
>>> content = requests.get(url)
>>> soup = BeautifulSoup(content.text, 'html.parser')
#Extract title Page
>>> print(soup.find('title'))
<title>IMDb Top 250 - IMDb</title>

#Extracting main heading
>>> for heading in soup.find_all('h1'):
   print(heading.text)
Top Rated Movies

#Extracting sub-heading
>>> for heading in soup.find_all('h3'):
   print(heading.text)
   
IMDb Charts
You Have Seen
   IMDb Charts
   Top India Charts
Top Rated Movies by Genre
Recently Viewed

从上面我们可以看到 find_all 将为我们提供所有符合我们定义的搜索条件的项目。我们可以与 find_all() 一起使用的所有过滤器都可以与 find() 和其他搜索方法(如 find_parents() 或 find_siblings())一起使用。

寻找()

上面我们已经看到,find_all()用于扫描整个文档来查找除了某些内容之外的所有内容,要求是只找到一个结果。如果你知道文档只包含一个<body>标签,那么搜索整个文档就是浪费时间。一种方法是每次调用 limit=1 的 find_all() ,否则我们可以使用 find() 方法执行相同的操作 -

句法

find(name, attrs, recursive, string, **kwargs)

因此,下面两种不同的方法给出了相同的输出 -

>>> soup.find_all('title',limit=1)
[<title>IMDb Top 250 - IMDb</title>]
>>>
>>> soup.find('title')
<title>IMDb Top 250 - IMDb</title>

在上面的输出中,我们可以看到 find_all() 方法返回一个包含单个项目的列表,而 find() 方法返回单个结果。

find() 和 find_all() 方法之间的另一个区别是 -

>>> soup.find_all('h2')
[]
>>>
>>> soup.find('h2')

如果 soup.find_all() 方法找不到任何内容,它将返回空列表,而 find() 返回 None 。

find_parents() 和 find_parent()

与遍历树的 find_all() 和 find() 方法不同,find_parents() 和 find_parents method() 执行相反的操作,它们向上遍历树并查看标签(或字符串)的父级。

句法

find_parents(name, attrs, string, limit, **kwargs)
find_parent(name, attrs, string, **kwargs)

>>> a_string = soup.find(string="The Godfather")
>>> a_string
'The Godfather'
>>> a_string.find_parents('a')
[<a href="/title/tt0068646/" title="Francis Ford Coppola (dir.), Marlon Brando, Al Pacino">The Godfather</a>]
>>> a_string.find_parent('a')
<a href="/title/tt0068646/" title="Francis Ford Coppola (dir.), Marlon Brando, Al Pacino">The Godfather</a>
>>> a_string.find_parent('tr')
<tr>

<td class="posterColumn">
<span data-value="2" name="rk"></span>
<span data-value="9.149038526210072" name="ir"></span>
<span data-value="6.93792E10" name="us"></span>
<span data-value="1485540" name="nv"></span>
<span data-value="-1.850961473789928" name="ur"></span>
<a href="/title/tt0068646/"> <img alt="The Godfather" height="67" src="https://m.media-amazon.com/images/M/MV5BM2MyNjYxNmUtYTAwNi00MTYxLWJmNWYtYzZlODY3ZTk3OTFlXkEyXkFqcGdeQXVyNzkwMjQ5NzM@._V1_UY67_CR1,0,45,67_AL_.jpg" width="45"/>
</a> </td>
<td class="titleColumn">
2.
<a href="/title/tt0068646/" title="Francis Ford Coppola (dir.), Marlon Brando, Al Pacino">The Godfather</a>
<span class="secondaryInfo">(1972)</span>
</td>
<td class="ratingColumn imdbRating">
<strong title="9.1 based on 1,485,540 user ratings">9.1</strong>
</td>
<td class="ratingColumn">
<div class="seen-widget seen-widget-tt0068646 pending" data-titleid="tt0068646">
<div class="boundary">
<div class="popover">
<span class="delete"> </span><ol><li>1<li>2<li>3<li>4<li>5<li>6<li>7<li>8<li>9<li>10</li>0</li></li></li></li&td;</li></li></li></li></li></ol> </div>
</div>
<div class="inline">
<div class="pending"></div>
<div class="unseeable">NOT YET RELEASED</div>
<div class="unseen"> </div>
<div class="rating"></div>
<div class="seen">Seen</div>
</div>
</div>
</td>
<td class="watchlistColumn">

<div class="wlb_ribbon" data-recordmetrics="true" data-tconst="tt0068646"></div>
</td>
</tr>
>>>
>>> a_string.find_parents('td')
[<td class="titleColumn">
2.
<a href="/title/tt0068646/" title="Francis Ford Coppola (dir.), Marlon Brando, Al Pacino">The Godfather</a>
<span class="secondaryInfo">(1972)</span>
</td>]

还有其他八种类似的方法 -

find_next_siblings(name, attrs, string, limit, **kwargs)
find_next_sibling(name, attrs, string, **kwargs)

find_previous_siblings(name, attrs, string, limit, **kwargs)
find_previous_sibling(name, attrs, string, **kwargs)

find_all_next(name, attrs, string, limit, **kwargs)
find_next(name, attrs, string, **kwargs)

find_all_previous(name, attrs, string, limit, **kwargs)
find_previous(name, attrs, string, **kwargs)

在哪里,

find_next_siblings()find_next_sibling()方法将迭代当前元素之后的所有同级元素。

find_previous_siblings()find_previous_sibling()方法将迭代当前元素之前的所有同级元素。

find_all_next()find_next()方法将迭代当前元素之后的所有标签和字符串。

find_all_previousfind_previous()方法将迭代当前元素之前的所有标签和字符串。

CSS 选择器

BeautifulSoup 库支持最常用的 CSS 选择器。您可以在 select() 方法的帮助下使用 CSS 选择器搜索元素。

这里有一些例子 -

>>> soup.select('title')
[<title>IMDb Top 250 - IMDb</title>, <title>IMDb Top Rated Movies</title>]
>>>
>>> soup.select("p:nth-of-type(1)")
[<p>The Top Rated Movie list only includes theatrical features.</p>, <p> class="imdb-footer__copyright _2-iNNCFskmr4l2OFN2DRsf">© 1990-2019 by IMDb.com, Inc.</p>]
>>> len(soup.select("p:nth-of-type(1)"))
2
>>> len(soup.select("a"))
609
>>> len(soup.select("p"))
2

>>> soup.select("html head title")
[<title>IMDb Top 250 - IMDb</title>, <title>IMDb Top Rated Movies</title>]
>>> soup.select("head > title")
[<title>IMDb Top 250 - IMDb</title>]

#print HTML code of the tenth li elemnet
>>> soup.select("li:nth-of-type(10)")
[<li class="subnav_item_main">
<a href="/search/title?genres=film_noir&sort=user_rating,desc&title_type=feature&num_votes=25000,">Film-Noir
</a> </li>]

Beautiful Soup-修改树

BeautifulSoup 的重要方面之一是搜索解析树,它允许您根据您的要求对 Web 文档进行更改。我们可以使用标签的属性来更改标签的属性,例如 .name、.string 或 .append() 方法。它允许您借助 .new_string() 和 .new_tag() 方法向现有标签添加新标签和字符串。还有其他方法,例如 .insert()、.insert_before() 或 .insert_after() 对 HTML 或 XML 文档进行各种修改。

更改标签名称和属性

创建汤后,可以轻松进行修改,例如重命名标签、修改其属性、添加新属性和删除属性。

>>> soup = BeautifulSoup('<b class="bolder">Very Bold</b>')
>>> tag = soup.b

修改和添加新属性如下 -

>>> tag.name = 'Blockquote'
>>> tag['class'] = 'Bolder'
>>> tag['id'] = 1.1
>>> tag
<Blockquote class="Bolder" id="1.1">Very Bold</Blockquote>

删除属性如下 -

>>> del tag['class']
>>> tag
<Blockquote id="1.1">Very Bold</Blockquote>
>>> del tag['id']
>>> tag
<Blockquote>Very Bold</Blockquote>

修改.string

您可以轻松修改标签的 .string 属性 -

>>> markup = '<a href="https://www.tutorialspoint.com/index.htm">Must for every <i>Learner>/i<</a>'
>>> Bsoup = BeautifulSoup(markup)
>>> tag = Bsoup.a
>>> tag.string = "My Favourite spot."
>>> tag
<a href="https://www.tutorialspoint.com/index.htm">My Favourite spot.</a>

从上面我们可以看到,如果标签包含任何其他标签,它们及其所有内容都会被新数据替换。

附加()

使用 tag.append() 方法将新数据/内容添加到现有标签。它与Python列表中的append()方法非常相似。

>>> markup = '<a href="https://www.tutorialspoint.com/index.htm">Must for every <i>Learner</i></a>'
>>> Bsoup = BeautifulSoup(markup)
>>> Bsoup.a.append(" Really Liked it")
>>> Bsoup
<html><body><a href="https://www.tutorialspoint.com/index.htm">Must for every <i>Learner</i> Really Liked it</a></body></html>
>>> Bsoup.a.contents
['Must for every ', <i>Learner</i>, ' Really Liked it']

NavigableString() 和 .new_tag()

如果您想将字符串添加到文档中,可以通过使用append()或NavigableString()构造函数轻松完成 -

>>> soup = BeautifulSoup("<b></b>")
>>> tag = soup.b
>>> tag.append("Start")
>>>
>>> new_string = NavigableString(" Your")
>>> tag.append(new_string)
>>> tag
<b>Start Your</b>
>>> tag.contents
['Start', ' Your']

注意:如果您在访问 NavigableString() 函数时发现任何名称错误,如下所示:

NameError:名称“NavigableString”未定义

只需从 bs4 包导入 NavigableString 目录 -

>>> from bs4 import NavigableString

我们可以解决上面的错误。

您可以向现有标签添加注释,也可以添加 NavigableString 的其他子类,只需调用构造函数即可。

>>> from bs4 import Comment
>>> adding_comment = Comment("Always Learn something Good!")
>>> tag.append(adding_comment)
>>> tag
<b>Start Your<!--Always Learn something Good!--></b>
>>> tag.contents
['Start', ' Your', 'Always Learn something Good!']

添加全新标签(不附加到现有标签)可以使用 Beautifulsoup 内置方法 BeautifulSoup.new_tag() 来完成 -

>>> soup = BeautifulSoup("<b></b>")
>>> Otag = soup.b
>>>
>>> Newtag = soup.new_tag("a", href="https://www.tutorialspoint.com")
>>> Otag.append(Newtag)
>>> Otag
<b><a href="https://www.tutorialspoint.com"></a></b>

仅需要第一个参数,即标记名称。

插入()

与 python 列表上的 .insert() 方法类似,tag.insert() 将插入新元素,但与 tag.append() 不同,新元素不一定位于其父元素内容的末尾。可以在任意位置添加新元素。

>>> markup = '<a href="https://www.djangoproject.com/community/">Django Official website <i>Huge Community base</i></a>'
>>> soup = BeautifulSoup(markup)
>>> tag = soup.a
>>>
>>> tag.insert(1, "Love this framework ")
>>> tag
<a href="https://www.djangoproject.com/community/">Django Official website Love this framework <i>Huge Community base</i></a>
>>> tag.contents
['Django Official website ', 'Love this framework ', <i>Huge Community base</i
>]
>>>

insert_before() 和 insert_after()

要在解析树中的某些内容之前插入一些标签或字符串,我们使用 insert_before() -

>>> soup = BeautifulSoup("Brave")
>>> tag = soup.new_tag("i")
>>> tag.string = "Be"
>>>
>>> soup.b.string.insert_before(tag)
>>> soup.b
<b><i>Be</i>Brave</b>

类似地,要在解析树中的某些内容之后插入一些标签或字符串,请使用 insert_after()。

>>> soup.b.i.insert_after(soup.new_string(" Always "))
>>> soup.b
<b><i>Be</i> Always Brave</b>
>>> soup.b.contents
[<i>Be</i>, ' Always ', 'Brave']

清除()

要删除标签的内容,请使用 tag.clear() -

>>> markup = '<a href="https://www.tutorialspoint.com/index.htm">For <i>technical & Non-technical&lr;/i> Contents</a>'
>>> soup = BeautifulSoup(markup)
>>> tag = soup.a
>>> tag
<a href="https://www.tutorialspoint.com/index.htm">For <i>technical & Non-technical</i> Contents</a>
>>>
>>> tag.clear()
>>> tag
<a href="https://www.tutorialspoint.com/index.htm"></a>

提炼()

要从树中删除标签或字符串,请使用 PageElement.extract()。

>>> markup = '<a href="https://www.tutorialspoint.com/index.htm">For <i&gr;technical & Non-technical</i> Contents</a>'
>>> soup = BeautifulSoup(markup)
>>> a_tag = soup.a
>>>
>>> i_tag = soup.i.extract()
>>>
>>> a_tag
<a href="https://www.tutorialspoint.com/index.htm">For Contents</a>
>>>
>>> i_tag
<i>technical & Non-technical</i>
>>>
>>> print(i_tag.parent)
None

分解()

tag.decompose() 从树中删除标签并删除其所有内容。

>>> markup = '<a href="https://www.tutorialspoint.com/index.htm">For <i>technical & Non-technical</i> Contents</a>'
>>> soup = BeautifulSoup(markup)
>>> a_tag = soup.a
>>> a_tag
<a href="https://www.tutorialspoint.com/index.htm">For <i>technical & Non-technical</i> Contents</a>
>>>
>>> soup.i.decompose()
>>> a_tag
<a href="https://www.tutorialspoint.com/index.htm">For Contents</a>
>>>

用。。。来代替()

顾名思义,pageElement.replace_with() 函数将用树中的新标签或字符串替换旧标签或字符串 -

>>> markup = '<a href="https://www.tutorialspoint.com/index.htm">Complete Python <i>Material</i></a>'
>>> soup = BeautifulSoup(markup)
>>> a_tag = soup.a
>>>
>>> new_tag = soup.new_tag("Official_site")
>>> new_tag.string = "https://www.python.org/"
>>> a_tag.i.replace_with(new_tag)
<i>Material</i>
>>>
>>> a_tag
<a href="https://www.tutorialspoint.com/index.htm">Complete Python <Official_site>https://www.python.org/</Official_site></a>

在上面的输出中,您已经注意到,replace_with() 返回被替换的标签或字符串(如我们示例中的“Material”),因此您可以检查它或将其添加回树的其他部分。

裹()

pageElement.wrap() 在您指定的标签中包含一个元素并返回一个新的包装器 -

>>> soup = BeautifulSoup("<p>tutorialspoint.com</p>")
>>> soup.p.string.wrap(soup.new_tag("b"))
<b>tutorialspoint.com</b>
>>>
>>> soup.p.wrap(soup.new_tag("Div"))
<Div><p><b>tutorialspoint.com</b></p></Div>

展开()

tag.unwrap() 与wrap() 正好相反,它将标签替换为该标签内的任何内容。

>>> soup = BeautifulSoup('<a href="https://www.tutorialspoint.com/">I liked <i>tutorialspoint</i></a>')
>>> a_tag = soup.a
>>>
>>> a_tag.i.unwrap()
<i></i>
>>> a_tag
<a href="https://www.tutorialspoint.com/">I liked tutorialspoint</a>

从上面,您已经注意到像replace_with()一样,unwrap()返回被替换的标签。

下面是 unwrap() 的另一个示例,以便更好地理解它 -

>>> soup = BeautifulSoup("<p>I <strong>AM</strong> a <i>text</i>.</p>")
>>> soup.i.unwrap()
<i></i>
>>> soup
<html><body><p>I <strong>AM</strong> a text.</p></body></html>

unwrap() 非常适合剥离标记。

Beautiful Soup - 编码

所有 HTML 或 XML 文档均以某种特定编码(如 ASCII 或 UTF-8)编写。然而,当您将该 HTML/XML 文档加载到 BeautifulSoup 时,它已被转换为 Unicode。

>>> markup = "<p>I will display £</p>"
>>> Bsoup = BeautifulSoup(markup)
>>> Bsoup.p
<p>I will display £</p>
>>> Bsoup.p.string
'I will display £'

上面的Behave是因为BeautifulSoup内部使用了名为Unicode的子库,Dammit来检测文档的编码,然后将其转换为Unicode。

然而,Unicode 并非总是正确,该死的猜对了。由于逐字节搜索文档来猜测编码,因此需要花费大量时间。如果您已经知道编码并将其作为 from_encoding 传递给 BeautifulSoup 构造函数,则可以节省一些时间并避免错误。

下面是 BeautifulSoup 将 ISO-8859-8 文档错误识别为 ISO-8859-7 的示例 -

>>> markup = b"<h1>\xed\xe5\xec\xf9</h1>"
>>> soup = BeautifulSoup(markup)
>>> soup.h1
<h1>νεμω</h1>
>>> soup.original_encoding
'ISO-8859-7'
>>>

要解决上述问题,请使用 from_encoding 将其传递给 BeautifulSoup -

>>> soup = BeautifulSoup(markup, from_encoding="iso-8859-8")
>>> soup.h1
<h1>ולש </h1>
>>> soup.original_encoding
'iso-8859-8'
>>>

BeautifulSoup 4.4.0 添加的另一个新功能是 except_encoding。当您不知道正确的编码但确定 Unicode 时,可以使用它,该死的显示错误的结果。

>>> soup = BeautifulSoup(markup, exclude_encodings=["ISO-8859-7"])

输出编码

BeautifulSoup 的输出是 UTF-8 文档,与 BeautifulSoup 输入的文档无关。在文档下方,波兰语字符采用 ISO-8859-2 格式。

html_markup = """
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<HTML>
<HEAD>
<META HTTP-EQUIV="content-type" CONTENT="text/html; charset=iso-8859-2">
</HEAD>
<BODY>
ą ć ę ł ń ó ś ź ż Ą Ć Ę Ł Ń Ó Ś Ź Ż
</BODY>
</HTML>
"""


>>> soup = BeautifulSoup(html_markup)
>>> print(soup.prettify())
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
   <head>
      <meta content="text/html; charset=utf-8" http-equiv="content-type"/>
   </head>
   <body>
      ą ć ę ł ń ó ś ź ż Ą Ć Ę Ł Ń Ó Ś Ź Ż
   </body>
</html>

在上面的示例中,如果您注意到,<meta> 标记已被重写,以反映从 BeautifulSoup 生成的文档现在为 UTF-8 格式。

如果您不希望生成的输出为 UTF-8,您可以在 prettify() 中指定所需的编码。

>>> print(soup.prettify("latin-1"))
b'<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">\n<html>\n <head>\n <meta content="text/html; charset=latin-1" http-equiv="content-type"/>\n </head>\n <body>\n ą ć ę ł ń \xf3 ś ź ż Ą Ć Ę Ł Ń \xd3 Ś Ź Ż\n </body>\n</html>\n'

在上面的示例中,我们对完整的文档进行了编码,但是您可以对 soup 中的任何特定元素进行编码,就像它们是 python 字符串一样 -

>>> soup.p.encode("latin-1")
b'<p>0My first paragraph.</p>'
>>> soup.h