使用 Beautiful Soup 的 find 方法搜索 DOM

我们可以使用 Beautiful Soup 的 find 方法对 DOM 执行简单的搜索。 这些方法为我们提供了更加灵活和强大的构造,用于查找不依赖于这些元素的层次结构的元素。 在本节中,我们将研究这些函数的几种常见用法,以定位 DOM 中的各种元素。

准备工作

如果您想将以下内容剪切并粘贴到 ipython 中,您可以在 02/02_bs4_find.py 中找到示例。

怎么做

我们将从一个新的 iPython 会话开始,并首先加载行星页面:

In [1]: import requests
...: from bs4 import BeautifulSoup
...: html = requests.get("http://localhost:8080/planets.html").text
...: soup = BeautifulSoup(html, "lxml")
...:

在前面的示例中,为了访问表中的所有 <tr>,我们使用链式属性语法来获取表,然后需要获取子项和迭代器。 这确实有一个问题,因为子元素可能是 <tr> 以外的元素。 仅获取 <tr> 子元素的更优选方法是使用 findAll。

让我们首先找到 <table>:

In [4]: table = soup.find("table")
...: str(table)[:100]
...:
Out[4]: '<table border="1" id="planetsTable">\n<tr
id="planetHeader">\n<th>\n</th>\n<th>\r\n Nam'

这告诉 soup 对象查找文档中的第一个 <table> 元素。 从该元素中,我们可以使用 findAll 找到作为表的后代的所有 <tr> 元素:

In [8]: [str(tr)[:50] for tr in table.findAll("tr")]
Out[8]:
['<tr id="planetHeader">\n<th>\n</th>\n<th>\r\n ',
'<tr class="planet" id="planet1" name="Mercury">\n<t',
'<tr class="planet" id="planet2" name="Venus">\n<td>',
'<tr class="planet" id="planet3" name="Earth">\n<td>',
'<tr class="planet" id="planet4" name="Mars">\n<td>\n',
'<tr class="planet" id="planet5" name="Jupiter">\n<t',
'<tr class="planet" id="planet6" name="Saturn">\n<td',
'<tr class="planet" id="planet7" name="Uranus">\n<td',
'<tr class="planet" id="planet8" name="Neptune">\n<t',
'<tr class="planet" id="planet9" name="Pluto">\n<td>']

请注意,这些是后代,而不是直系孩子。 将查询更改为 “td” 以查看差异。 它们不是 <td> 的直接子元素,但每行都有多个 <td> 元素。 总共会找到 54 个 <td> 元素。

如果我们只想要包含行星数据的行,这里就会出现一个小问题。 还包括表头。 我们可以通过利用目标行的 id 属性来解决这个问题。 以下查找 id 值为 “planet3” 的行。

In [14]: table.find("tr", {"id": "planet3"})
...:
Out[14]:
<tr class="planet" id="planet3" name="Earth">
<td><img src="img/earth-150x150.png"/></td>
<td>Earth</td>
<td>5.97</td>
<td>12756</td>
<td>
The name Earth comes from the Indo-European base
'er,'which produced the Germanic noun 'ertho,' and ultimately German
'erde,'
Dutch 'aarde,' Scandinavian 'jord,' and English
'earth.' Related forms include Greek 'eraze,' meaning
'on the ground,' and Welsh 'erw,' meaning 'a piece of
land.'
</td>
<td>
<a href="https://en.wikipedia.org/wiki/Earth">Wikipedia</a>
</td>
</tr>

惊人的! 我们利用了这样一个事实:该页面使用此属性来表示具有实际数据的表行。

现在让我们更进一步,收集每个行星的质量,并将名称和质量放入字典中:

In [18]: items = dict()
...: planet_rows = table.findAll("tr", {"class": "planet"})
...: for i in planet_rows:
...: tds = i.findAll("td")
...: items[tds[1].text.strip()] = tds[2].text.strip()
...:
In [19]: items
Out[19]:
{'Earth': '5.97',
'Jupiter': '1898',
'Mars': '0.642',
'Mercury': '0.330',
'Neptune': '102',
'Pluto': '0.0146',
'Saturn': '568',
'Uranus': '86.8',
'Venus': '4.87'}

就像这样,我们从页面中嵌入的内容创建了一个很好的数据结构。