pandas.read_html 默认使用 BeautifulSoup 的 .text 属性提取表格内容,会无分隔地合并单元格内所有子文本(如主值与 tooltip),导致数据污染;本文提供一种安全、可复用的 monkey-patch 方案,通过重写 _text_getter 方法,利用 get_text(separator=...) 实现结构化分离,并支持后续正则解析。
`pandas.read_html` 默认使用 beautifulsoup 的 `.text` 属性提取表格内容,会无分隔地合并单元格内所有子文本(如主值与 tooltip),导致数据污染;本文提供一种安全、可复用的 monkey-patch 方案,通过重写 `_text_getter` 方法,利用 `get_text(separator=...)` 实现结构化分离,并支持后续正则解析。
在网页爬取中,动态渲染的表格常将辅助信息(如 tooltip、悬停提示)嵌套在 <td> 标签内部——例如 Gladiabots 排行榜中,Score 单元格实际包含 <span class="barLabel tooltipable">6129<span class="tooltip">Max 6129</span></span>。而 pandas.read_html(底层依赖 BeautifulSoup)默认调用 tag.text,该属性会静默拼接所有后代文本节点,且不插入任何分隔符,最终得到 "6129Max 6129" 或 "44721173534 pts" 这类不可靠字符串。
直接后处理(如按数字/非数字切分)极易出错:XP LVL 的主值(如 447)与 tooltip 中的积分(如 21173534)均为纯数字,无法通过正则可靠区分边界。因此,更稳健的思路是在 HTML 解析阶段就引入语义分隔符。
read_html 在解析时使用 bs4 的 Tag 对象,并通过内部 parser 的 _text_getter 方法获取文本。我们可安全覆盖该方法,改用 tag.get_text(separator="_", strip=True) —— 它会在每个子元素文本间插入指定分隔符(如 _),保留结构可解析性:
from pandas.io.html import _BeautifulSoupHtml5LibFrameParserdef _text_getter(self, obj): return obj.get_text(separator="_", strip=True)# ⚠️ 动态注入:仅影响后续 read_html 调用_BeautifulSoupHtml5LibFrameParser._text_getter = _text_getter
✅ 优势:无需修改 pandas 源码,不侵入全局环境,且兼容 flavor="html5lib"(对动态页面至关重要)。
立即学习“前端免费学习笔记(深入)”;
应用上述 patch 后,read_html 返回的 DataFrame 中,Score 和 XP LVL 列将形如 "6129_Max 6129" 和 "447_21173534 pts":
import pandas as pddf = pd.read_html( "https://stats.gladiabots.com/pantheon?", header=0, flavor="html5lib")[0]# 提取 Score 主值与 Max 值score_parts = df.pop("Score").str.extract(r"(?P<Score>d+)_(?:Max )?(?P<Max_Score>d+)")# 提取 XP LVL 主值与积分(忽略 "pts" 单位)xp_parts = df.pop("XP LVL").str.extract(r"(?P<XP_LVL>d+)_(?P<Points>d+)")# 合并回原表result = pd.concat([df, score_parts, xp_parts], axis=1)print(result[["Score", "Max_Score", "XP_LVL", "Points"]].head())
输出:
Score Max_Score XP_LVL Points0 6129 6129 447 211735341 5888 6025 344 159429782 5555 5586 119 4688941
通过这一轻量级增强,read_html 不再是“黑盒文本提取器”,而成为可精准捕获 HTML 语义层级的结构化数据入口——让 tooltip 从干扰项,变为可编程的数据维度。