Pandas入门教程(十二):读取 HTML 表格——read_html 参数详解与网页数据清洗

一、read_html() 是什么?

Pandas 的 pd.read_html() 函数可以自动解析网页中的 HTML 表格数据,将其转换为 DataFrame。这在抓取网页数据、分析股价、获取金融信息等场景下非常有用。

核心特点:

特点 说明
自动解析 自动识别网页中的 <table> 元素
返回列表 返回 DataFrame 列表,每个元素对应一个表格
多种输入 支持 URL、文件路径、HTML 字符串
参数灵活 支持正则匹配、属性筛选、表头指定

前置依赖: read_html() 需要 lxml 和 beautifulsoup4 库支持,安装命令:pip install lxml beautifulsoup4。

二、基本用法

read_html() 函数会查找并读取网页中所有的 HTML 表格元素,返回一个 DataFrame 列表。

1. 读取单个表格

python

复制代码
import pandas as pd

# 从 URL 读取
tables = pd.read_html("https://example.com/table.html")

# 查看找到的表格数量
print(f"找到 {len(tables)} 个表格")

# 获取第一个表格
df = tables[0]
print(df.head())
2. 读取多个表格

一个网页可能包含多个表格,read_html() 返回一个列表,可以通过索引选择:

python

复制代码
import pandas as pd

# 假设网页有多个表格
tables = pd.read_html("https://example.com/data.html")

# 遍历所有表格
for i, table in enumerate(tables):
    print(f"\n=== 表格 {i+1} ===")
    print(f"形状: {table.shape}")
    print(table.head(3))

返回结构说明:

场景 返回值
网页有 1 个表格 列表含 1 个 DataFrame
网页有多个表格 列表含多个 DataFrame
未找到表格 抛出 ValueError
三、常用参数详解
参数 说明 示例
io 输入源,可以是 URL、文件路径或字符串 "page.html"
match 正则匹配,筛选包含特定文本的表格 match="销售额"
header 指定表头行索引 header=0
attrs 通过 HTML 属性筛选表格 attrs={"id": "table1"}
skiprows 跳过指定行 skiprows=[0, 1]
na_values 指定视为缺失值的字符串 na_values=["N/A"]
1. 使用 match 参数筛选表格

python

复制代码
import pandas as pd

# 使用 match 参数只返回包含特定文本的表格
# 例如:只读取包含"股票"二字的表格
tables = pd.read_html(
    "https://example.com/finance.html",
    match="股票"
)

if tables:
    df = tables[0]
    print(df)

match 的作用: 用正则表达式匹配表格中的文本内容,只返回匹配成功的表格。

2. 使用 attrs 参数精确定位表格

python

复制代码
import pandas as pd

# 通过 HTML 属性定位表格
# 例如:获取 id="stock-table" 的表格
tables = pd.read_html(
    "https://example.com/data.html",
    attrs={"id": "stock-table"}   # 查找 id="stock-table" 的 table 元素
)

# 或者通过 class 定位
tables = pd.read_html(
    "https://example.com/data.html",
    attrs={"class": "data-table"}
)

df = tables[0]
print(df)

match 与 attrs 对比:

参数 匹配依据 适用场景
match 表格内的文本内容 按关键词筛选
attrs HTML 元素的属性 按 id/class 精确定位
四、实战示例:从网页抓取股票数据

下面示例演示如何从财经网站抓取股票列表数据:

python

复制代码
import pandas as pd
from io import StringIO

# 创建一个模拟的 HTML 内容用于演示
# 实际使用时替换为真实的 URL
html_content = '''
<table border="1">
    <tr>
        <th>股票代码</th>
        <th>股票名称</th>
        <th>收盘价</th>
        <th>涨跌幅</th>
    </tr>
    <tr>
        <td>600519</td>
        <td>贵州茅台</td>
        <td>1850.00</td>
        <td>2.35%</td>
    </tr>
    <tr>
        <td>000858</td>
        <td>五粮液</td>
        <td>185.50</td>
        <td>-1.20%</td>
    </tr>
    <tr>
        <td>601318</td>
        <td>中国平安</td>
        <td>52.30</td>
        <td>0.85%</td>
    </tr>
</table>
'''

# 从字符串读取表格
tables = pd.read_html(StringIO(html_content))

df = tables[0]

print("原始表格:")
print(df)

# 数据清洗:处理涨跌幅列
df["涨跌幅"] = df["涨跌幅"].str.replace("%", "").astype(float)
df["收盘价"] = df["收盘价"].astype(float)

print("清洗后的数据:")
print(df)

StringIO 的作用: 把字符串伪装成文件对象,这样 pandas 会按读取文件流的逻辑处理,而不是误判为路径或 URL。

输出结果:

text

复制代码
原始表格:
     股票代码  股票名称     收盘价     涨跌幅
0  600519  贵州茅台  1850.0   2.35%
1     858   五粮液   185.5  -1.20%
2  601318  中国平安    52.3   0.85%

清洗后的数据:
     股票代码  股票名称     收盘价   涨跌幅
0  600519  贵州茅台  1850.0  2.35
1     858   五粮液   185.5 -1.20
2  601318  中国平安    52.3  0.85

注意: 股票代码 000858 读入后变成了 858,因为 pandas 自动将其识别为整数。如需保留前导零,可指定 dtype=str。

读取结果的问题分析:

问题 原因 解决方案
000858 变成 858 自动推断为整数 读取后转字符串并补齐,或后续处理时补零
涨跌幅 带 % 原始数据是百分比字符串 str.replace("%", "") 后转 float
收盘价 是字符串 可能含非数字字符 astype(float) 转换
五、读取 Wikipedia 表格

python

复制代码
import pandas as pd

# 读取 Wikipedia 页面中的表格
url = "https://en.wikipedia.org/wiki/List_of_countries_by_population_(United_Nations)"

try:
    tables = pd.read_html(url)
    print(f"该页面共有 {len(tables)} 个表格")

    # 通常第一个表格是我们需要的
    df = tables[0]
    print(df.head(10))
except Exception as e:
    print(f"读取失败: {e}")
    print("请确保已安装必要的库: pip install lxml beautifulsoup4")

实用技巧:

技巧 说明
try/except 网络请求可能失败,需捕获异常
先看表格数量 len(tables) 判断是否找到
逐表查看 遍历 tables 找到目标表格
match 筛选 按关键词定位目标表格
六、数据清洗与处理

从网页读取的表格通常需要进一步清洗才能用于分析。

1. 常见清洗操作

python

复制代码
import pandas as pd
from io import StringIO

# 模拟一个包含各种脏数据的表格
html_content = '''
<table>
    <tr><th>日期</th><th>产品</th><th>销量</th><th>备注</th></tr>
    <tr><td>2024-01-01</td><td>产品A</td><td>1,234</td><td>N/A</td></tr>
    <tr><td>2024-01-02</td><td>产品B</td><td>567</td><td>缺货</td></tr>
    <tr><td>2024-01-03</td><td>产品C</td><td>--</td><td>未统计</td></tr>
</table>
'''

df = pd.read_html(StringIO(html_content))[0]
print("原始数据:")
print(df)

# 清洗步骤
# 1. 重命名列
df.columns = ["日期", "产品", "销量", "备注"]

# 2. 处理缺失值
df = df.replace(["N/A", "--", "未统计", "缺货"], pd.NA)

# 3. 清理数值列(去除逗号)
df["销量"] = df["销量"].str.replace(",", "").replace(pd.NA, None)
df["销量"] = pd.to_numeric(df["销量"], errors="coerce")

# 4. 转换日期
df["日期"] = pd.to_datetime(df["日期"])

print("清洗后:")
print(df)
print("\n数据类型:")
print(df.dtypes)

输出结果:

text

复制代码
原始数据:
           日期   产品    销量   备注
0  2024-01-01  产品A  1234  NaN
1  2024-01-02  产品B   567   缺货
2  2024-01-03  产品C    --  未统计

清洗后:
          日期   产品      销量    备注
0 2024-01-01  产品A  1234.0   NaN
1 2024-01-02  产品B   567.0  <NA>
2 2024-01-03  产品C     NaN  <NA>

数据类型:
日期    datetime64[ns]
产品            object
销量           float64
备注            object
dtype: object

清洗步骤解析:

步骤 操作 作用
1 df.columns = [...] 统一列名
2 df.replace([...], pd.NA) 多种脏值统一为缺失值
3 str.replace(",", "") 去除千位分隔符
4 pd.to_numeric(errors="coerce") 无法转换的转为 NaN
5 pd.to_datetime() 字符串日期转日期类型
2. 关键函数说明

pd.to_numeric(errors="coerce") 的作用:

errors 取值 行为
'raise'(默认) 无法转换时抛异常
'coerce' 无法转换的设为 NaN
'ignore' 保持原样不转换

df.replace() 与 pd.NA:

python

复制代码
# 多个脏值统一替换为缺失值
df = df.replace(["N/A", "--", "未统计", "缺货"], pd.NA)

提示: pd.NA 是 Pandas 的可空缺失值标记,比 np.nan 更通用。

七、HTML 表格处理流程图
八、常见参数组合速查
需求 写法
读取所有表格 pd.read_html(url)
取第一个表格 pd.read_html(url)[0]
按关键词筛选 pd.read_html(url, match="股票")
按 id 定位 pd.read_html(url, attrs={"id": "table1"})
按 class 定位 pd.read_html(url, attrs={"class": "data-table"})
指定表头行 pd.read_html(url, header=1)
跳过行 pd.read_html(url, skiprows=[0, 1])
指定缺失值 pd.read_html(url, na_values=["N/A"])
读取 HTML 字符串 pd.read_html(StringIO(html_str))
九、注意事项与常见问题

1. 安装依赖库

read_html() 需要 lxml 和 beautifulsoup4 库:

bash

复制代码
pip install lxml beautifulsoup4

2. 网络问题

读取网络 URL 时可能遇到网络延迟或访问限制,建议添加适当的超时设置或使用本地缓存。

python

复制代码
# 先下载到本地,再读取
import requests

response = requests.get(url, timeout=10)
with open("page.html", "w", encoding="utf-8") as f:
    f.write(response.text)

tables = pd.read_html("page.html")

3. 表格结构复杂

某些网页的表格使用合并单元格、嵌套表格等复杂结构,可能导致解析失败。此时可以尝试使用 BeautifulSoup 直接解析 HTML。

4. 数据完整性

网页数据可能随时变化,建议在读取后验证数据完整性,并与原始数据源进行比对。

5. 合规提醒

抓取网页数据时,请遵守网站的 robots.txt 规则和相关法律法规,不要频繁请求以免对服务器造成负担。

十、常见问题排查
问题现象 可能原因 解决方案
ImportError: lxml not found 未安装依赖 pip install lxml beautifulsoup4
ValueError: No tables found 页面无表格或需 JS 渲染 检查页面源码,或改用 Selenium
HTTPError 403 被网站拒绝访问 添加请求头或降低频率
表格数量不符预期 页面有多个表格 用 match 或 attrs 筛选
数据错位 合并单元格 用 BeautifulSoup 手动解析
数字带逗号 千位分隔符 str.replace(",", "")
日期是字符串 未转换类型 pd.to_datetime()
中文乱码 编码问题 指定 encoding 或请求头
十一、小结

本篇你掌握了 Pandas 读取 HTML 表格的完整能力:

  • 基本用法 :pd.read_html() 返回 DataFrame 列表,通过索引取表格。

  • 筛选参数 :match 按文本匹配,attrs 按 HTML 属性定位。

  • 其他参数 :header 指定表头,skiprows 跳过行,na_values 指定缺失值。

  • 字符串读取 :用 StringIO 将 HTML 字符串包装为文件对象。

  • 数据清洗 :replace 统一脏值,to_numeric 转换数值,to_datetime 解析日期。

  • 注意事项:安装依赖、处理网络问题、遵守 robots 规则。

pd.read_html() 是抓取网页表格数据的利器,它能快速将 HTML 表格转换为 DataFrame。抓取的数据通常需要进一步处理才能用于分析,建议结合 Pandas 的数据清洗功能一起使用。下一篇我们将学习 Pandas 数据清洗,掌握缺失值、重复值、异常值的系统处理方法。

相关推荐
codists1 小时前
《crafting interpreters》阅读笔记:4.6.2
python
2601_967979121 小时前
机器学习必修课:经典算法与Python实战
python·算法·机器学习·实战·课程
cpolar技术支持2 小时前
listen(5) 只排得住 5 个连接:第 6 个客户端卡在 connect 超时,进程活着端口却显示假活不放行
python·socket·tcp·cpolar·故障排查
梯度下降者3 小时前
我校学子斩获 2025 全国高新技术竞赛 Python 赛道全国一等奖
python·教育·竞赛·信息技术·人才培养
小小张说故事3 小时前
Python 的 memoryview 为什么能零拷贝:拆开 buffer protocol、cast 和那道 BufferError
python
Hvitur3 小时前
Java - Lambda
java·开发语言·python
2601_967980114 小时前
win10如何把python加入环境变量
python·win10·安装教程·命令行·环境变量
锋行天下11 小时前
langgraph 后端发自定义数据,前端接收方法概括
人工智能·python
benchmark_cc13 小时前
用 QuantDash 读取单只股票五档盘口:抽样、缺失值检查与复现记录
python·数据分析·pandas·量化交易·股票数据·quantdash
曲径海盗14 小时前
【Python 学习第 55 天】列表基础
python·学习笔记·编程入门