一、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 数据清洗,掌握缺失值、重复值、异常值的系统处理方法。