除了文本内容外,HTML文档还可能包含以HTML表格形式呈现的结构化数据。使用Beautiful Soup,我们可以将表格数据提取为Python对象,例如列表或字典,必要时可以将其存储在数据库或电子表格中,并进行处理。在本章中,我们将使用Beautiful Soup解析HTML表格。
尽管Beautiful Soup并没有专门用于提取表格数据的功能或方法,但我们可以通过简单的抓取技术实现这一目标。就像SQL中的表或电子表格一样,HTML表格由行和列组成。
HTML使用<table>
标签来构建表格结构。其中有多个嵌套的<tr>
标签,每个标签代表一行。每一行包含<td>
标签来保存该行中每个单元格的数据。第一行通常用于列标题,标题放在<th>
标签中而不是<td>
。
下面的HTML脚本会在浏览器窗口中渲染一个简单的表格:
<html>
<body>
<h2>Beautiful Soup - Parse Table</h2>
<table border="1">
<tr>
<th>Name</th>
<th>Age</th>
<th>Marks</th>
</tr>
<tr class='data'>
<td>Ravi</td>
<td>23</td>
<td>67</td>
</tr>
<tr class='data'>
<td>Anil</td>
<td>27</td>
<td>84</td>
</tr>
</table>
</body>
</html>
请注意,为了区分头部行,数据行使用了一个CSS类data
来进行定制。
我们现在来看看如何解析表格数据。首先,我们通过BeautifulSoup对象获取文档树。然后收集所有列标题到一个列表中。
from bs4 import BeautifulSoup
soup = BeautifulSoup(markup, "html.parser")
tbltag = soup.find('table')
headers = []
headings = tbltag.find_all('th')
for h in headings: headers.append(h.string)
接着,获取带有class='data'
属性的数据行标签,这些标签紧随头部行之后。创建一个字典对象,以列标题作为键,对应于每个单元格的值,并将其追加到字典对象列表中。
rows = tbltag.find_all_next('tr', {'class':'data'})
trows=[]
for i in rows:
row = {}
data = i.find_all('td')
n=0
for j in data:
row[headers[n]] = j.string
n+=1
trows.append(row)
收集到一个字典对象列表trows
中。然后可以将其用于不同用途,例如存储到SQL表中,保存为JSON或Pandas DataFrame对象。
完整的代码如下:
markup = """
<html>
<body>
<p>Beautiful Soup - Parse Table</p>
<table>
<tr>
<th>Name</th>
<th>Age</th>
<th>Marks</th>
</tr>
<tr class='data'>
<td>Ravi</td>
<td>23</td>
<td>67</td>
</tr>
<tr class='data'>
<td>Anil</td>
<td>27</td>
<td>84</td>
</tr>
</table>
</body>
</html>
"""
from bs4 import BeautifulSoup
soup = BeautifulSoup(markup, "html.parser")
tbltag = soup.find('table')
headers = []
headings = tbltag.find_all('th')
for h in headings: headers.append(h.string)
print(headers)
rows = tbltag.find_all_next('tr', {'class':'data'})
trows=[]
for i in rows:
row = {}
data = i.find_all('td')
n=0
for j in data:
row[headers[n]] = j.string
n+=1
trows.append(row)
print(trows)
输出
['Name', 'Age', 'Marks']
[{'Name': 'Ravi', 'Age': '23', 'Marks': '67'}, {'Name': 'Anil', 'Age': '27', 'Marks': '84'}]