>>> from env_helper import info; info()
页面更新时间: 2020-02-22 11:39:11
操作系统/OS: Linux-4.19.0-8-amd64-x86_64-with-debian-10.3 ;Python: 3.7.3

4.5. BeautifulSoup 模块解析 HTML

BeautifUl Soup 是一个模块,用于从 HTML 页面中 提取信息(用于这个目的时,它比正则表达式好很多)。 BeautifUlSoup 模块的名称是 bs4 (表示 Beautiful Soup ,第4版)。要安装它,需要在 命令行中运行 pip install beautifulsoup4 (关于安装第三 方模块的指导,请查看附录A)。虽然安装时使用的名字是 beautifulsup4 ,但要导入它,就使用 import bs4

在本章中,Beautiful Soup 的例子将解析 (即分析并确定其中的一些部分)硬盘上的一个 HTML 文件。在 IDLE 中打开一个新的文件编辑器窗口, 输入以下代码,并保存为 example.html 。 或者,从 http://nostarch.com/automatestuff/ 下载它。

>>> html_cnt = '''<!-- This is the example.html example file.-->
>>>
>>> <html><head><title>The Website Title</title></head>
>>> <body>
>>> <p>Download my <strong>Python</strong> book from <a href="http://
>>> inventwithpython.com">my website</a>.</p>
>>>
>>> <p class="slogan">Learn Python the easy way!</p>
>>> <p>By <span id="author">A1 Sweigart</span></p>
>>> </body></html>
>>> '''

你可以看到,既使一个简单的 HTML 文件, 也包含许多不同的标签和属性。对于复杂的网站, 事情很快就变得令人困惑。 好在, BeautiflilSoup 让处理HTML变得容易很多。

4.5.1. HTML 创建一个 BeautifulSoup 对象

bs4.BeautiflilSoup() 函数调用时需要一个字符串,其中包含将要解析的HTML。 bs4.BeautifulSoup()函数返回一个BeautiflilSoup 对象。 在交互式环境中输入以下代码,同时保持计算机与因特网的连接:

>>> import requests, bs4
>>> res = requests.get('https://www.baidu.com/')
>>> res.raise_for_status()
>>> noStarchSoup = bs4.BeautifulSoup(res.text, 'lxml')
>>> type(noStarchSoup)
bs4.BeautifulSoup

这段代码利用 requests.get() 函数从 No Starch Press 网站下载主页,然后将响应结果的 text 属性传递给 bs4.BeautifulSoup() 。它返回的 BeautifiUSoup 对象 保存在变量 noStarchSoup 中。

也可以向 bs4.BeautifUlSoup() 传递一个 File 对象, 从硬盘加载一个 HTML 文件。

在交互式环境中输入以下代码(确保 example.html 文件 在工作目录中):

>>> exampleFile = open ('RomeoAndJuliet.txt')
>>> exampleSoup = bs4.BeautifulSoup(exampleFile, 'html5lib')
>>> type(exampleSoup)
bs4.BeautifulSoup

有了 BeautiflilSoup 对象之后,就可以利用它的方法, 定位HTML文档中的特定部分。

4.5.2. select() 方法寻找元素

针对你要寻找的元素,调用methodO方法,传入一个 字符串作为CSS“选择器”,这样就可以取得 Web 页面 元素。选择器就像正则表达式:它们指定了要寻找的 模式,在这个例子中,是在HTML页面中寻找,而不是 普通的文本字符串。

完整地讨论 CSS 选择器的语法超出了本书的范围 (在 http://nostarch.com/automatestuff/ 的资源中,有很好的选择器指南), 但这里有一份选择器的简单介绍。

下面展示了大多数常用 CSS 选择器的模式。

传递给select()方法的选择器 | 将匹配
---------------------|---------------------------
soup.select('div') | 所有名为``<div>``的元素
soup.select('#author') | 带有id属性为author的元素
soup.select('.notice') | 所有使用CSS class属性名为notice的元素
soup.select('div span') | 所有在``<div>``元素之内的``<span>``元素
soup.select('div>span') | 所有直接在``<div>``元素之内的``<span>``元素,中间没有其他元素
soup.select('input[name]') | 所有名为``<input>``,并有一个name属性,其值无所谓的元素
soup.select('input[type="button"]') | 所有名为``<input>``,并有一个type属性,其值为button的元素

不同的选择器模式可以组合起来,形成复杂的匹配。 例如, soup.select('p #author') 将匹配所有 id 属性为 author 的元素,只要它也在一个 <p> 元素之内。

select() 方法将返回一个 Tag 对象的列表,这是 Beautiful Soup 表示一个 HTML 元素的方式。 针对 BeautifiilSoup 对象中的 HTML 的每次匹配, 列表中都有一个 Tag 对象。 Tag 值可以传递给 str() 函数,显示它们代表的 HTML 标签。 Tag 值也可以有 attrs 属性,它将该 Tag 的所有 HTML 属性作为一个字典。 利用前面的 example.html 文件,在交互式环境中输入以下代码:

>>> import bs4
>>> exampleFile = open('example.html')
>>> exampleSoup = bs4.BeautifulSoup(exampleFile.read(), 'lxml')
>>> elems = exampleSoup.select('#author')
>>> type(elems)
list
>>> len(elems)
1
>>> type(elems[0])
bs4.element.Tag
>>> elems[0].getText()
'A1 Sweigart'
>>> str(elems[0])
'<span id="author">A1 Sweigart</span>'
>>> elems[0].attrs
{'id': 'author'}

这段代码将带有 id="author" 的元素,从示例 HTML 中找出来。 我们使用 select('#author')返回一个列表,其中包含所有带有 id="author" 的元素。 我们将这个Tag 对象的列表保存在变量中 elemslen(elems) 告诉我们列表中只有一个 Tag 对象,只有一次匹配。 在该元素上调用 getText() 方法,返回该元素的文本,或内部的 HTML。 一个元素的文本是在开始和结束标签之间的内容:在这个例子中,就是 'Al Sweigart'

将该元素传递给 str() ,这将返回一个字符串,其中包含开始和结束标签,以及该元素的文本。 最后, attrs 给了我们一个字典,包含该元素的属性 'id' ,以及 id属性的值 'author'

也可以从 BeautifulSoup 对象中找出 <p> 元素。 在交互式环境中输入以下代码:

>>> pElems = exampleSoup.select('p')
>>> str(pElems[0])
'<p>Download my <strong>Python</strong> book from <a href="http://ninventwithpython.com">my website</a>.</p>'
>>> pElems[0].getText()
'Download my Python book from my website.'
>>> str(pElems[1])
'<p class="slogan">Learn Python the easy way!</p>'
>>> pElems[1].getText()
'Learn Python the easy way!'
>>> str(pElems[2])
'<p>By <span id="author">A1 Sweigart</span></p>'
>>> pElems[2].getText()
'By A1 Sweigart'

这一次, select() 给我们一个列表,包含3次匹配,我们将它保存在 pElems 中。 在 pElems[0]pElems[l]pElems[2] 上使用 str(), 将每个元素显示为一个字符串,并在每个元素上使用 getText() ,显示它的文本。

4.5.3. 通过元素的属性获取数据

Tag 对象的 get() 方法让我们很容易从元素中获取属性值。 向该方法传入一个属性名称的字符串,它将返回该属性的值。 利用 example.html ,在交互式环境中输入以下代码:

>>> import bs4
>>> soup = bs4.BeautifulSoup(open('example.html'), 'lxml')
>>> spanElem = soup.select('span')[0]
>>> str(spanElem)
'<span id="author">A1 Sweigart</span>'
>>> spanElem.get('id')
'author'
>>> spanElem.get('some_nonexistent_addr') == None
True
>>> spanElem.attrs
{'id': 'author'}

这里,我们使用 select() 来寻找所有 <span> 元素,然后将第一个匹配的元素保存在 spanElem 中。 将属性名 'id' 传递给 get() ,返回该属性的值 'author'