您可以使用read_html
直接从网站抓取 HTML 表格并将其转换为数据框。它简化了从网页提取数据的过程。
The read_html
函数接受一个包含 URL 或指向 HTML 文件的文件路径的字符串,提取该 HTML 页面中包含的所有表,并返回 DataFrame 列表。
每个 DataFrame 对应 HTML 页面上的一个表。
默认情况下,read_html
函数使用 lxml、BeautifulSoup 和 html5lib 库来解析 HTML 页面。
这意味着您需要在 Python 环境中安装这些库才能使用此功能。
!pip install lxml beautifulsoup4 html5lib
语法和参数
The read_html
函数于Pandas非常灵活,允许多个参数来控制数据提取。该函数的语法如下:
pandas.read_html(io, match='.+', flavor=None, header=None, index_col=None, skiprows=None, attrs=None, parse_dates=False, thousands=', ', encoding=None, decimal='.', converters=None, na_values=None, keep_default_na=True, displayed_only=True)
下面简单解释一下参数:
-
io
:此参数采用一个字符串,可以是 URL、文件路径或 HTML 内容。
-
match
:这是表应该匹配才能提取的正则表达式。默认值为‘.+’,表示提取所有表。
-
flavor
:在引擎盖下使用的解析引擎。默认为None
它使用“lxml”和“beautiful soup”,但您也可以指定“html5lib”。
-
header
:此参数采用一个整数或整数序列,用于定义用作列名称的行。默认为None
这意味着列名是从表标题行推断出来的。
-
index_col
:这需要一个整数或整数序列来定义要设置为索引(MultiIndex)的列。
-
skiprows
:此参数采用一个整数或整数序列来跳过指定的行。
-
attrs
:这是表格标签应包含的 HTML 属性的字典。
-
parse_dates
:该参数用于自动解析表中的日期。它默认为False
.
-
converters
, na_values
这些分别是用于转换数据和 NA 值的其他参数。
重要的是要注意read_html
函数返回 DataFrame 列表。
使用 Pandas read_html 提取 HTML 表
The pandas.read_html
函数允许您从本地 HTML 文件、URL 或任何包含 HTML 的类似文件的对象中提取表。
假设您有一个 HTML 文件(样本1.html)与 Python 文件位于同一目录中,它包含两个简单的表。
现在,让我们使用以下命令从该页面读取 HTML 表read_html
:
import pandas as pd
file_path = "sample.html"
tables = pd.read_html(file_path)
让我们检查表的数量并显示第一个表:
print("Number of tables in file: ", len(tables))
print("First table:")
print(tables[0])
Output:
Number of tables in file: 2
First table:
Header 1 Header 2 Header 3
0 Data 1.1 Data 1.2 Data 1.3
1 Data 2.1 Data 2.2 Data 2.3
2 Data 3.1 Data 3.2 Data 3.3
输出将显示 HTML 文件中的表格数量以及第一个表格的内容。
我们将从维基百科的“按人口统计的国家和附属国列表”中提取数据。
import pandas as pd
url = "https://en.wikipedia.org/wiki/List_of_countries_and_dependencies_by_population"
tables = pd.read_html(url)
The pd.read_html(url)
函数返回 DataFrame 列表。以下是我们如何检查表的数量并显示第一个表:
print("Number of tables on site: ", len(tables))
print("First table:")
print(tables[0])
Output:
Number of tables on site: 3
First table:
0 1
0 NaN This article needs to be updated. The reason g...
如您所见,输出是页面中的第一个表。您可能会注意到这不是我们感兴趣的表格(按人口排列的国家/地区)。
这是因为我们正在抓取的页面包含多个表。您可以指定正确的索引,但还有另一种方法可以指定要提取的表。这可以通过使用 HTML 属性来完成。
使用表 HTML 属性
如果表格具有 HTML 属性,例如id
or class
,您可以使用它们来提取特定的表。
The attrs
参数输入read_html
接受要匹配的属性字典。然后,该函数仅提取具有匹配属性的表。
让我们看一个如何使用的例子attrs
:
url = "https://en.wikipedia.org/wiki/List_of_countries_and_dependencies_by_population"
tables = pd.read_html(url, attrs={"class": "wikitable"})
df = tables[0]
与提取所有表然后挑选出您需要的表相比,这是一种更有针对性的表提取方式。
请记住,属性名称和值区分大小写,并且必须与 HTML 源中的名称和值完全匹配,但是如果表没有属性并且我们想要有针对性的方式怎么办?
使用正则表达式进行抓取
Pandas read_html
函数提供了match
参数允许我们使用正则表达式根据内容匹配特定的表。
当网页或文件有大量没有属性的表,并且我们希望根据某些条件过滤表时,这特别有用。
如果您查看我们正在抓取的维基百科页面,您会注意到我们需要抓取的表是唯一包含“美国”一词的表:
import pandas as pd
url = "https://en.wikipedia.org/wiki/List_of_countries_and_dependencies_by_population"
tables = pd.read_html(url, match='United States')
检查与正则表达式匹配的表的数量并显示第一个表:
print("Number of matched tables on site: ", len(tables))
print("First matched table:")
print(tables[0])
Output:
Number of matched tables on site: 1
First matched table:
Rank ... Notes
...
该表包含国家及其人口详细信息。
您需要使用有效的索引来访问特定的 DataFrame。如果没有表与正则表达式匹配,则ValueError
将被提高。
处理不一致的表结构
处理 HTML 表格时,您可能会遇到带有合并单元格或嵌套表格(表格中的表格)的表格。让我们看看如何read_html
函数将处理这些。
合并单元格
对于水平合并的单元格(跨越多列的单元格)或垂直合并的单元格(跨越多行的单元格),Pandas 将在跨越的列中重复单元格值。
这是一个带有水平合并单元格的 HTML 表格:
<table>
<tr>
<td colspan="2">Merged</td>
<td>Regular</td>
</tr>
<tr>
<td>A</td>
<td>B</td>
<td>C</td>
</tr>
</table>
当我们使用read_html
解析此 HTML:
df = pd.read_html('sample.html')[0]
print(df)
Output:
0 1 2
0 Merged Merged Regular
1 A B C
正如您所看到的,合并的单元格值(“合并”)已在两个跨列中重复。
这是一个带有垂直合并单元格的 HTML 表格:
<table>
<tr>
<td rowspan="2">Merged</td>
<td>Regular</td>
</tr>
<tr>
<td>C</td>
</tr>
</table>
当我们使用read_html
解析此 HTML:
df = pd.read_html(sample.html)[0]
print(df)
Output:
0 1
0 Merged Regular
1 Merged C
在这种情况下,合并的单元格值(“合并”)在两个跨行中重复。
嵌套表
对于嵌套表,read_html
自动分离外表和内表。每个表都成为返回列表中的一个单独的 DataFrame。
假设您有嵌套的 HTML 表 (嵌套 HTML 示例).
当我们跑步时read_html
相反,它返回两个表,外部表和内部表:
tables = pd.read_html('nested_tables.html')
print(tables[0])
Output:
Header 1 Header 2 Header 3
0 Data 1 Data 2 Nested Header 1 Nested Header 2 Nested Data ...
1 Data 3 Data 4 Data 5
这是外面的桌子。
对于内表:
tables = pd.read_html('nested_tables.html')
print(tables[1])
Output:
Nested Header 1 Nested Header 2
0 Nested Data 1 Nested Data 2
1 Nested Data 3 Nested Data 4
设置自定义 NA 值
The na_values
中的参数read_html
函数允许您指定此类自定义 NA 值。该函数将替换这些NaN
生成的 DataFrame 中的值。
考虑一个 HTML 表格,其中空单元格由单词“Empty”和“Blank”表示(na_values 样本)
这是如何使用的示例na_values
:
na_values = ['Empty', 'Blank']
tables = pd.read_html('na_values.html', na_values=na_values)
Output:
Header 1 Header 2 Header 3
0 Data 1 NaN Data 3
1 Data 4 NaN Data 6
2 Data 7 Data 8 Data 9
现在,当 Pandas 遇到任何指定值时na_values
在解析 HTML 表时,它将把它们视为NaN
.
设置索引
使用时read_html
,您可以指定index_col
参数,在表提取过程中直接将某一列设置为DataFrame的索引。
这可以节省您额外的致电步骤set_index
然后。
The index_col
参数采用一个整数或整数序列,表示要设置为索引的列号。列号从 0 开始。
使用方法如下:
url = "https://en.wikipedia.org/wiki/List_of_countries_and_dependencies_by_population"
tables = pd.read_html(url, index_col=1, match='Notes')
df = tables[0]
这里我们使用了match='Notes'
因为我们要使用的表是唯一包含该单词的表。
现在,“Country”在表提取时直接设置为DataFrame的索引。您可以使用国家/地区名称来访问特定行:
print(df.loc['United States'])
Output:
Country / Dependency Country / Dependency
Rank Rank 3
Population Numbers 334933000
% of the world NaN
Date Date 25 Jun 2023
Source (official or from the United Nations) Source (official or from the United Nations) National population clock[7]
Notes Notes [d]
通过使用index_col
in read_html
,您可以简化数据提取和准备过程。
In read_html
,您可以使用header
参数指定表中的哪一行用作列标题。当表标题不在第一行或表具有多个标题行时,这尤其有用。
The header
参数接受一个整数或一个整数列表,表示要用作标题的行号。请记住,行号从 0 开始。
假设表格的标题位于第二行(索引 1):
url = "https://en.wikipedia.org/wiki/List_of_countries_and_dependencies_by_population"
tables = pd.read_html(url, header=1, match='Notes')
df = tables[0]
这将使表中的第二行作为标题。
在多个标头的情况下,您可以传递整数列表:
tables = pd.read_html(url, header=[0, 1])
df = tables[0]
跳行
The skiprows
参数输入read_html
允许您指定要跳过的行。
它需要一个整数或一个整数序列来表示要跳过的行号。
这是一个例子:
url = "https://en.wikipedia.org/wiki/List_of_countries_and_dependencies_by_population"
tables = pd.read_html(url, skiprows=0, match='Notes')
df = tables[0]
生成的 DataFrame 将不包含表的第一行。
您还可以通过传递列表来跳过多个不连续的行:
tables = pd.read_html(url, skiprows=[0, 2])
转变内容
您可能想要删除测量单位、将字符串转换为整数或应用某些其他转换。
您可以使用以下方法实现此目的converters
参数输入read_html
.
The converters
参数接受一个字典,其中键是列名或列号,值是将应用于相应列中每个值的函数。
考虑一个带有百分比的 HTMl 表 (转换器样本):
让我们定义一个函数来从百分比列中删除“%”:
def convert_percent(val):
new_val = val.replace('%', '')
return float(new_val)
tables = pd.read_html('converters.html', converters={'Percentage': convert_percent})
df = tables[0]
Output:
Header 1 Header 2 Percentage
0 Data 1 Data 2 25.0
1 Data 3 Data 4 50.0
2 Data 5 Data 6 75.0
The extract_links
中的参数read_html
函数允许从 HTML 表数据中提取超链接。
该参数接受多个值:None
, "all"
, "header"
, "body"
, and "footer"
.
-
None: 这是默认选项。不提取任何链接,并且链接文本作为常规单元格值包含在内。
-
“all”:从表的所有部分提取链接。每个包含链接的单元格都表示为一个元组,其中第一个元素是单元格文本,第二个元素是链接。
-
“header”: 仅表头中的链接(内部
<th>
元素)被提取。
-
“body”:仅表体中的链接(内部
<td>
元素)被提取。
-
“footer”:仅表页脚中的链接(内部
<tfoot>
元素)被提取。
考虑一个 HTML 表,其中在页眉、正文和页脚中包含链接(提取链接样本):
仅从标题中提取链接:
tables = pd.read_html('extract_links.html', extract_links='header')
df = tables[0]
Output:
(Header Link, https://example.com) (Header 2, None) (Header 3, None)
0 Body Link Data 2 Data 3
1 Data 4 Data 5 Data 6
2 Data 7 Data 8 Footer Link
仅从正文链接中提取链接:
tables = pd.read_html('extract_links.html', extract_links='body')
df = tables[0]
Output:
Header Link Header 2 Header 3
0 (Body Link, https://example.com) (Data 2, None) (Data 3, None)
1 (Data 4, None) (Data 5, None) (Data 6, None)
2 Data 7 Data 8 Footer Link
仅提取页脚链接:
tables = pd.read_html('extract_links.html', extract_links='footer')
df = tables[0]
Output:
Header Link Header 2 Header 3
0 Body Link Data 2 Data 3
1 Data 4 Data 5 Data 6
2 (Data 7, None) (Data 8, None) (Footer Link, https://example.com)
要提取所有链接:
(Header Link, https://example.com) ... (Header 3, None)
0 (Body Link, https://example.com) ... (Data 3, None)
1 (Data 4, None) ... (Data 6, None)
2 (Data 7, None) ... (Footer Link, https://example.com)
请注意,此功能从 Pandas 1.3.0 版本开始可用。
pandas.read_html() 限制
以下是其他一些潜在的限制pandas.read_html
:
-
无动态内容:
read_html
无法处理动态加载的内容。如果网站的内容根据用户交互而更改或在初始页面加载后加载(如无限滚动),则read_html
将无法捕获此数据。
-
无需提交表单或进行身份验证:如果页面需要用户交互,例如表单提交或身份验证,
read_html
无法执行这些操作。
-
有限的 CSS 选择器支持:它不支持特定或复杂的 CSS 选择器。因此,如果您需要选择网页中非严格表格的非常特定的部分,那么
read_html
可能还不够。
-
没有多页抓取:如果您感兴趣的数据分布在多个页面(分页),
read_html
将无法自动遍历这些页面。
-
无法处理非表格数据:如果您感兴趣的数据不是表格格式(例如段落、标题、列表等),
read_html
将无法提取它。
-
标头和 Cookie:Web 抓取库(如 requests、Beautiful Soup 和 Selenium)提供了对 HTTP 标头和 cookie 的更多控制,这在处理需要设置某些标头或 cookie 的网站时至关重要。
-
Robots.txt 尊重:Scrapy 等网络抓取工具遵守网站 robots.txt 文件中设置的规则。这是道德网络抓取的一个重要方面,但不是由
pandas.read_html
.
但是,您可以使用 Python 网页抓取克服 pandas.read_html 限制.
进一步阅读
https://pandas.pydata.org/docs/reference/api/pandas.read_html.html