表格数据是网络上最好的数据来源之一。它们可以存储大量有用信息而不会丢失其易于阅读的格式,使其成为数据相关项目的金矿。
了解 HTML 表格的结构
从视觉上看,HTML 表格是一组以表格格式显示信息的行和列。对于本教程,我们将抓取上面的表格:
为了能够抓取该表中包含的数据,我们需要更深入地研究它的编码。
一般来说,HTML 表格实际上是使用以下 HTML 标签构建的:
- <table>:它标志着 HTML 表格的开始
- <th> 或<thead>:将一行定义为表格的标题
- <tbody>:表示数据所在的部分
- <tr> : 表示表格中的一行
- <td>:定义表格中的一个单元格
然而,正如我们将在现实生活场景中看到的那样,并非所有开发人员在构建他们的表时都遵守这些约定,这使得一些项目比其他项目更难。尽管如此,了解它们的工作原理对于找到正确的方法至关重要。
让我们在浏览器中输入表格的 URL (https://datatables.net/examples/styling/stripe.html) 并检查页面以了解幕后情况。
这就是为什么这是一个练习使用 Python 抓取表格数据的好页面的原因。有一个明确的 <table> 标签对打开和关闭表格,所有相关数据都在 <tbody> 标签内。它只显示与前端选择的条目数相匹配的十行。
关于这个表还有一点需要了解的是,它总共有 57 个我们想要抓取的条目,并且似乎有两种解决方案可以访问数据。首先是单击下拉菜单并选择“100”以显示所有条目:
或单击下一步按钮以在分页中移动。
那么会是哪一个呢?这些解决方案中的任何一个都会给我们的脚本增加额外的复杂性,因此,让我们先检查一下从哪里提取数据。
当然,因为这是一个 HTML 表,所以所有数据都应该在 HTML 文件本身上,而不需要 AJAX 注入。要验证这一点,请右键单击>查看页面源代码。接下来,复制几个单元格并在源代码中搜索它们。
我们对来自不同分页单元格的更多条目做了同样的事情,是的,看起来我们所有的目标数据都在那里,即使前端没有显示它。
有了这些信息,我们就可以开始编写代码了!
使用 Python 的 Beautiful Soup 抓取 HTML 表
因为我们要抓取的所有员工数据都在 HTML 文件中,所以我们可以使用Requests库发送 HTTP 请求并使用Beautiful Soup解析响应。
注意:如果您不熟悉网络抓取,我们已经为初学者创建了 Python 中的网络抓取教程。尽管您可以在没有经验的情况下跟随,但从基础开始总是一个好主意。
1. 发送我们的主要请求
让我们为项目创建一个名为python-html-table的新目录,然后是一个名为bs4-table-scraper的新文件夹,最后,创建一个新的python_table_scraper.py文件.54
从终端,让我们pip3 install requests beautifulsoup4
将它们导入到我们的项目中,如下所示:
import requests
from bs4 import BeautifulSoup
要使用 Requests 发送 HTTP 请求,我们需要做的就是设置一个 URL 并通过 requests.get() 传递它,将返回的 HTML 存储在响应变量中并打印 response.status_code。
注意:如果您是 Python 的新手,您可以使用命令 python3 python_table_scraper.py 从终端运行您的代码。
url = 'https://datatables.net/examples/styling/stripe.html'
response = requests.get(url)
print(response.status_code)
如果它正常工作,它将返回 200 状态码。其他任何事情都意味着您的 IP 被网站安装的反抓取系统拒绝。一个潜在的解决方案是向您的脚本添加自定义标题,以使您的脚本看起来更人性化——但这可能还不够。另一种解决方案是使用网络抓取 API 为您处理所有这些复杂性。
2. 使用 Beautiful Soup 构建解析器
在提取数据之前,我们需要将原始 HTML 转换为格式化或解析数据。我们将把这个解析后的 HTML 存储到一个汤对象中,如下所示:
soup = BeautifulSoup(response.text, 'html.parser')
从这里,我们可以使用 HTML 标签及其属性遍历解析树。
如果我们回到页面上的表格,我们已经看到表格被包含在<table>
带有 class 的标签之间stripe dataTable
,我们可以使用它来选择表格。
table = soup.find('table', class_ = 'stripe')
print(table)
注意:经过测试,添加第二个类(dataTable)没有返回元素。实际上,在返回元素中,表的类只有stripe。您也可以使用 id = 'example'。
这是它返回的内容:
现在我们抓取了表格,我们可以遍历行并抓取我们想要的数据。
3. 循环遍历 HTML 表
回想一下表格的结构,每一行都由一个<tr>
元素表示,其中有<td>
一个包含数据的元素,所有这些都包裹在一个<tbody>
标签对之间。
为了提取数据,我们将创建两个用于外观,一个用于获取<tbody>
表的部分(所有行所在的位置),另一个用于将所有行存储到我们可以使用的变量中:
for employee_data in table.find_all('tbody'):
rows = employee_data.find_all('tr')
print(rows)
在行中,我们将存储<tr>
在表的主体部分中找到的所有元素。如果您遵循我们的逻辑,下一步是将每个单独的行存储到一个对象中并循环遍历它们以找到所需的数据。
首先,让我们尝试使用.querySelectorAll()方法在浏览器控制台上选择第一个员工的姓名。这种方法的一个非常有用的特性是,我们可以越来越深入地实现层次结构,实现大于 (>) 符号来定义父元素(左侧)和我们想要抓取的子元素(右侧)。
从那里,我们可以像这样编写我们的代码:
for row in rows:
name = row.find_all('td')[0].text
print(name)
简单来说,我们逐行获取每一行,并找到里面的所有单元格,一旦我们有了列表,我们只抓取索引中的第一个(位置 0)并使用 .text 方法完成抓取元素的文本,忽略我们不需要的 HTML 数据。
他们在那里,一个包含所有员工姓名的列表!其余的,我们只需遵循相同的逻辑:
position = row.find_all('td')[1].text
office = row.find_all('td')[2].text
age = row.find_all('td')[3].text
start_date = row.find_all('td')[4].text
salary = row.find_all('td')[5].text
但是,将所有这些数据打印在我们的控制台上并不是很有帮助。相反,让我们将这些数据存储为一种新的、更有用的格式。
4. 将表格数据存储到 JSON 文件中
虽然我们可以轻松地创建一个 CSV 文件并将我们的数据发送到那里,但如果我们可以使用抓取的数据创建新的东西,那将不是最易于管理的格式。
不过,这是我们几个月前做的一个项目,解释了如何创建一个 CSV 文件来存储抓取的数据。
好消息是 Python 有自己的 JSON 模块来处理 JSON 对象,所以我们不需要安装任何东西,只需导入它。
import json
但是,在我们继续创建 JSON 文件之前,我们需要将所有这些抓取的数据变成一个列表。为此,我们将在循环之外创建一个空数组。
employee_list = []
然后将数据附加到它,每个循环都将一个新对象附加到数组中。
employee_list.append({ 'Name': name, 'Position': position, 'Office': office, 'Age': age, 'Start date': start_date, 'salary': salary })
如果我们print(employee_list)
,结果如下:
还是有点乱,但我们有一组准备好转换为 JSON 的对象。
注意:作为测试,我们打印了 的长度,employee_list
它返回 57,这是我们抓取的正确行数(行现在是数组中的对象)。
将列表导入 JSON 只需要两行代码:
with open('json_data', 'w') as json_file:
json.dump(employee_list, json_file, indent=2)
- 首先,我们打开一个新文件,传入我们想要的文件名
(json_data)
和“w”,因为我们想要向它写入数据。 - 接下来,我们使用该
.dump()
函数从数组中转储数据(
employee_list
)
,indent=2
因此每个对象都有自己的行,而不是所有内容都在一个不可读的行中。