使用 Pandas 的 read_html() 高效抓取 Wikipedia 表格数据

你是否曾经遇到过这样的情况:在浏览网页时发现了一个包含宝贵数据的 HTML 表格,却因为不想手动复制粘贴或编写复杂的网页爬虫代码而感到苦恼?或者,你是一名数据分析师,需要频繁从 Wikipedia 或其他报表网站获取最新的统计数据来进行可视化分析?

在本文中,我们将深入探讨 Pandas 库中一个非常强大但经常被低估的工具——read_html()。我们将讨论如何利用它直接将网页中的 HTML 表格读取到 Pandas DataFrame 中,无需编写繁琐的 BeautifulSoup 或 Selenium 代码。这个工具对于快速合并来自多个网站的表格、进行数据清洗和准备工作非常高效。我们将通过一系列实战示例,从基础语法到处理复杂的 Wikipedia 数据,再到数据清洗与可视化,带你全面掌握这一技能。让我们开始吧!

什么是 pandas.read_html()?

在开始写代码之前,让我们先理解一下这个函数的本质。Pandas 的 INLINECODE5504404b 实际上是一个封装了 INLINECODEa16dd49f 或 INLINECODEaf45d030 等 HTML 解析库的高级工具。它的工作原理是扫描给定的 HTML 内容(无论是 URL、文件还是字符串),寻找 INLINECODEa596a500 标签,并尝试将其转换为结构化的 DataFrame 列表。

为什么说它是“最简单的方法之一”?因为它隐藏了 DOM 解析的复杂性。你不需要知道如何写 XPath 或 CSS 选择器,你只需要告诉 Pandas:“去这个 URL,把所有的表格拿回来”。这对于数据科学家和分析师来说,大大降低了获取 Web 数据的门槛。

主要应用场景:

  • 快速数据原型开发: 在决定是否构建复杂的爬虫之前,先快速抓取数据进行分析。
  • 定期报表抓取: 从公开的政府网站或金融报表中获取定期更新的表格。
  • Wikipedia 数据分析: Wikipedia 是结构化 HTML 表格的金矿,非常适合用此方法抓取。

pandas.read_html() 的基础语法

首先,让我们来看看它的基本语法结构:

# 导入必要的库
import pandas as pd

# 基本语法
# pd.read_html(io, match=‘.+‘, flavor=None, header=None, index_col=None, skiprows=None, ...)

这里,最关键的参数是 io。它非常灵活,可以是:

  • URL (字符串):以 INLINECODEc5573655 或 INLINECODEe50bd9c2 开头的网址。
  • HTML 文件路径:指向本地 .html 文件的路径。
  • HTML 字符串:包含 HTML 代码的实际字符串对象。

重要提示: 默认情况下,该函数会返回一个 DataFrame 列表。这是因为一个网页通常包含多个表格(例如导航栏、页脚、广告等也是表格),Pandas 会把它能找到的所有表格都抓取回来,你需要从中挑选出你需要的那一个。

示例 1:从原始 HTML 字符串中读取表格

让我们从一个最简单的例子开始。假设我们有一段 HTML 代码,其中包含了一个简单的联系人列表。我们可以将这段代码存储在一个 Python 字符串变量中,然后传递给 read_html

在这个例子中,我们使用三引号 INLINECODEd0ae7eca 将一个多行字符串存储在一个名为 INLINECODE7f76582b 的变量中。然后,我们调用 read_html 函数。该函数会提取所有的 HTML 表格并返回一个列表。由于我们的字符串里只有一个表格,所以它将是列表的第一个元素(索引为 0)。

import pandas as pd

# 定义包含表格的 HTML 字符串
html_string = ‘‘‘
Company Contact Country
Alfreds Futterkiste Maria Anders Germany
Centro comercial Moctezuma Francisco Chang Mexico
‘‘‘ # 使用 read_html 读取字符串 # 注意:read_html 返回的是一个列表 parsed_tables = pd.read_html(html_string) # 获取第一个表格 df_1 = parsed_tables[0] # 打印 DataFrame print("抓取到的 DataFrame:") print(df_1) # 输出数据类型信息 print(" 数据结构信息:") df_1.info()

输出结果:

抓取到的 DataFrame:
                        Company         Contact    Country
0       Alfreds Futterkiste    Maria Anders    Germany
1  Centro comercial Moctezuma  Francisco Chang     Mexico

数据结构信息:

RangeIndex: 2 entries, 0 to 1
Data columns (total 3 columns):
 #   Column    Non-Null Count  Dtype 
---  ------    --------------  ----- 
 0   Company   2 non-null      object
 1   Contact   2 non-null      object
 2   Country   2 non-null      object
dtypes: object(3)

实战见解: 你可能注意到了,我们直接使用了 INLINECODE51d30c22。这是因为 INLINECODE17782c48 总是返回一个列表,哪怕只有一个表格。这是一个常见的初学者陷阱,请务必记住要做索引操作。

示例 2:实战应用 – 从 Wikipedia 获取印度人口统计数据

现在让我们把难度升级。在实际工作中,我们通常是从 URL 直接获取数据。让我们尝试从“印度人口统计”的 Wikipedia 页面读取数据。

Wikipedia 是一个绝佳的数据源,因为它结构非常规整。然而,一个典型的 Wikipedia 页面往往包含几十个表格:导航框、目录、参考资料等等。我们需要学会如何在这堆“噪音”中找到我们需要的数据。

首先,让我们不使用任何过滤条件,看看能抓取到多少表格:

import pandas as pd
import numpy as np

# 目标 URL
url = ‘https://en.wikipedia.org/wiki/Demographics_of_India‘

# 直接读取 URL 中的所有表格
# 这一步可能会花费几秒钟,取决于网络速度和页面复杂度
dfs = pd.read_html(url)

# 打印抓取到的表格数量
print(f"该网页上一共包含 {len(dfs)} 个表格。")

输出结果:

该网页上一共包含 37 个表格。

看,仅仅一个页面就有 37 个表格!如果我们盲目地使用 dfs[0],很可能得到的不是我们要的“人口分布”表,而是页面顶部的某个无关摘要表。

示例 3:精准定位 – 使用 Match 参数过滤特定表格

为了避免手动检查所有 37 个表格,INLINECODEd57143d4 提供了一个非常强大的参数:INLINECODE7a6733c0。它接受一个正则表达式或字符串,只返回包含该文本的表头或表格内容的表格。

在这个例子中,我们想要找到标题为“Population distribution by states/union territories (2011)”的表格。我们可以将这个文本传递给 match 参数。

# 使用 match 参数来筛选特定的表格
# Pandas 会在表格的表头(th标签)或内容中查找匹配项

my_table_list = pd.read_html(
    url, 
    match=‘Population distribution by states/union territories‘
)

# match 参数通常会大幅减少返回列表的长度
print(f"筛选后剩余表格数量: {len(my_table_list)}")

# 获取筛选出的第一个(也是唯一的)表格
my_table = my_table_list[0]

# 查看前 5 行数据
print("目标表格的前 5 行数据:")
print(my_table.head())

输出结果:

筛选后剩余表格数量: 1
目标表格的前 5 行数据:
            State/UT  Population[57]  ... Decadal growth (1991-2001)  Decadal growth (2001-2011)
0  Uttar Pradesh        199,812,341  ...                      25.85%                      20.23%
1     Maharashtra        112,374,333  ...                      25.68%                      15.99%
2          Bihar        104,099,452  ...                      28.62%                      25.07%
3       West Bengal         91,276,115  ...                      20.89%                      13.84%
4   Madhya Pradesh         726,26,809  ...                      26.36%                      20.30%

技术细节: INLINECODE611357b3 使用 INLINECODEec13b20c 或 INLINECODEd3ac41fd 解析器。当遇到非常复杂的表格结构(例如合并单元格 INLINECODE3a3f4619 或 rowspan)时,Pandas 会尝试自动推断结构。虽然它很聪明,但在某些极其杂乱的表格中,可能仍需后续的手动调整。

示例 4:数据提取与基本清洗

现在我们已经有了正确的 DataFrame,但你会发现列名和数据可能还包含一些我们不想要的东西(例如引用标记 [57],或者是多余的空格)。

让我们提取我们需要的数据列,并做基本的清洗:

# 1. 提取 ‘State/UT‘ 列
states = my_table[‘State/UT‘]

# 2. 提取 ‘Population‘ 列
# 注意:原始列名是 ‘Population[57]‘,我们需要先检查一下确切的列名
# 为了演示,我们假设直接通过索引或清洗后的列名访问
population_raw = my_table[‘Population[57]‘]

# 3. 数据清洗:移除数字中的逗号,并将其转换为数值类型
# 比如 "199,812,341" -> 199812341
# 这一步非常重要,否则 Pandas 会把这一列当成对象(字符串)而不是数字
population_cleaned = population_raw.replace({‘,‘: ‘‘}, regex=True).astype(int)

# 验证清洗结果
print(f"清洗后的 Population 列类型: {population_cleaned.dtype}")
print(population_cleaned.head())

示例 5:数据重构与合并

现在让我们将清洗好的数据重新组合成一个干净、整洁的 DataFrame,方便后续使用。

# 创建一个新的 DataFrame,只包含我们关心的数据
df_clean = pd.DataFrame({
    ‘State‘: states,
    ‘Population‘: population_cleaned
})

print("清洗并重组后的 DataFrame:")
print(df_clean.head())

示例 6:高级数据清洗 – 处理“总计”行

在抓取统计表格时,表格的最后一行通常是“总计”或“Total”。在绘图或计算平均值时,这一行往往会因为数值过大而干扰结果,导致其他数据被压缩得看不见。

让我们演示如何删除最后一行:

# 查看当前 DataFrame 的最后几行
print("删除前的最后 3 行:")
print(df_clean.tail(3))

# 使用 tail(1) 获取最后一行的索引,然后用 drop() 删除它
df_final = df_clean.drop(df_clean.tail(1).index)

# 验证删除
print("
删除后的最后 3 行:")
print(df_final.tail(3))

实用提示: 你也可以使用布尔索引来删除,例如 INLINECODEe489db0b。但在不知道确切文本内容的情况下,使用 INLINECODE543932b7 是一种通用的处理策略。

示例 7:数据可视化 – 让数据说话

数据清洗完成后,最激动人心的环节就是可视化。让我们使用 Matplotlib 将印度各邦的人口数据绘制成水平条形图。

import matplotlib.pyplot as plt

# 设置绘图风格
plt.style.use(‘ggplot‘)

# 创建图表
# 我们只选取人口最多的前 10 个邦进行展示,避免图表过于拥挤
top_10_states = df_final.sort_values(by=‘Population‘, ascending=True).tail(10)

ax = top_10_states.plot(
    x=‘State‘, 
    y=‘Population‘, 
    kind="barh", 
    figsize=(10, 8),
    color=‘skyblue‘,
    edgecolor=‘black‘
)

# 设置标题和标签
plt.title(‘Top 10 Most Populous States in India (2011 Census)‘, fontsize=15)
plt.xlabel(‘Population‘, fontsize=12)
plt.ylabel(‘State‘, fontsize=12)

# 在条形图上添加数值标签
for i, v in enumerate(top_10_states[‘Population‘]):
    ax.text(v + 3e6, i, str(v), va=‘center‘, fontsize=10)

plt.tight_layout()
plt.show()

示例 8:反向操作 – 将 DataFrame 写入 HTML

Pandas 也允许我们将处理好的 DataFrame 转换回 HTML 表格字符串。这在生成自动化报表或 Web 应用展示时非常有用。

# 将我们处理好的 DataFrame 转换为 HTML 表格
html_output = df_final.to_html(index=False, classes=‘my-table‘)

# 打印 HTML 代码的前 500 个字符
print(html_output[:500])

# 你也可以将其保存为文件
with open(‘population_table.html‘, ‘w‘) as f:
    f.write("""
    
      
        India Population Data
        
          .my-table { border-collapse: collapse; width: 50%; }
          .my-table td, .my-table th { border: 1px solid #ddd; padding: 8px; }
          .my-table tr:nth-child(even){background-color: #f2f2f2;}
        
      
      
        

Population by State

""") f.write(html_output) f.write(""" """) print(" HTML 文件已生成。")

进阶技巧与最佳实践

在实际使用 read_html() 时,你可能会遇到一些挑战。这里有几个实用的建议:

  • 处理缺失的依赖库:

INLINECODE54e8fc16 依赖于 INLINECODE95652c8e、INLINECODE8a40c240 或 INLINECODE852b8a38 库来解析 HTML。如果你运行代码时遇到 INLINECODE513200ed,请确保安装了这些库。通常情况下,安装 INLINECODEd4967689 就能获得最佳性能。

    pip install lxml html5lib beautifulsoup4
    
  • 表头识别:

有时网页的表格没有标准的 INLINECODEdd57b818 标签,或者 Pandas 误判了表头行。你可以使用 INLINECODEe8e8ad07 参数(例如 INLINECODE7150c045 表示第一行是表头,INLINECODE74918942 表示没有表头)来手动指定。

  • 指定解析器:

如果你的 HTML 代码非常不规范,默认的 INLINECODEda72a9c2 可能会解析失败。你可以尝试切换到 INLINECODEf2192f9f,它对容错性更强(但速度稍慢):

    dfs = pd.read_html(url, flavor=‘html5lib‘)
    
  • 处理分页表格:

INLINECODE917e5154 只能抓取单个页面上的内容。如果一个网站的数据表格分成了“第1页、第2页…”,你需要通过抓取网络请求来找到分页的规律,循环抓取所有页面的 URL,然后使用 INLINECODE8c816289 将它们合并。

总结

在这篇文章中,我们探索了如何使用 Pandas 的 read_html() 函数轻松地将网页表格转换为可分析的 DataFrame。我们学习了:

  • 从原始 HTML 字符串和在线 URL 读取数据。
  • 如何使用 match 参数在包含数十个表格的页面中精准定位目标数据。
  • 基础的数据清洗流程,包括移除多余字符和类型转换。
  • 如何删除干扰分析的“总计”行。
  • 最后,我们还学习了如何将数据可视化以及反向生成 HTML 文件。

虽然对于高度动态的网页(如使用 React 或 Vue 渲染的 SPA 应用),你可能需要使用 Selenium 或 Playwright,但对于传统的、静态的包含大量表格的网站,read_html() 依然是手里最锋利的瑞士军刀。希望这能帮助你在数据采集的道路上少走弯路!

下一步,你可以尝试在自己感兴趣的数据集上应用这些技巧,或者探索如何将抓取的数据定期推送到你的数据库中。祝你分析愉快!

声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。如需转载,请注明文章出处豆丁博客和来源网址。https://shluqu.cn/33851.html
点赞
0.00 平均评分 (0% 分数) - 0