如何正确爬取HTML注释中的Pandas表格
先来剖析这个经典问题:当你使用 Pandas 的 read_html() 爬取网页表格时,明明在 HTML 源码中清晰看到某个表格结构完整,但解析结果却是一个空 DataFrame——绝大多数情况下,这是因为该表格被 HTML 注释标签 所包裹。注释内容在解析过程中默认被忽略,因此无论是 BeautifulSoup 还是 read_html() 都会自动跳过它。这也正是你提取第一张表、第二张表时屡屡得到空数据的根源所在。

这种情形在实际网站(例如 fbref.com)中尤为普遍。开发者经常将动态加载或备用渲染的表格临时存入注释区域,以兼容不同客户端环境或配合前端 JavaScript 渲染。虽然通过查看网页源码能够发现 # stats_keeper 这个表格的存在,但它实际上被嵌套在 注释结构中。BeautifulSoup 的 find_all() 方法可以定位到它(因为 response.content 获取的是原始字节流),然而在执行 pd.read_html(str(table2)) 时仍然会遭遇失败——read_html() 底层采用标准 HTML 解析器,注释内容默认被过滤掉。
那么解决方案是什么?核心策略只有一句话:在正式解析之前,先将所有 HTML 注释标记彻底清除。不要试图依赖 BeautifulSoup 提前提取,因为它返回的对象仍然保留着注释上下文信息。推荐的做法是:通过 requests.get().text 获取字符串格式的响应内容,随后直接使用 .replace('', '') 进行清洗处理,再将清洗后的内容传递给 pd.read_html()。具体代码如下:
from io import StringIO
import pandas as pd
import requests
url = "https://fbref.com/en/comps/9/keepers/Premier-League-Stats"
response_text = requests.get(url).text
cleaned_html = response_text.replace('', '')
# 直接按 ID 定位并解析,无需 BeautifulSoup 中间步骤
df1 = pd.read_html(StringIO(cleaned_html), attrs={'id': 'stats_squads_keeper_for'})[0]
df2 = pd.read_html(StringIO(cleaned_html), attrs={'id': 'stats_keeper'})[0]
print("First table shape:", df1.shape)
print("Second table shape:", df2.shape) # 现在不再为空
需要留意的关键要点:
- 切勿对
response.content(bytes 类型)直接执行.replace()操作,务必使用.text(str 类型); StringIO转换步骤不可或缺,因为pd.read_html()要求传入类文件对象,而非直接传递字符串;- 如果页面中的注释存在嵌套情况(例如
-->),简单的.replace()可能无法完全覆盖,建议采用正则表达式re.sub(r'', '', text, flags=re.DOTALL)进行安全清除; - 完成一次 HTML 字符串清洗后,可重复利用它来多次调用
pd.read_html(...),从而避免重复发起请求,显著提升处理效率。
这一方法轻量且稳定可靠,巧妙绕过了 DOM 解析阶段注释内容被自动忽略的限制,堪称处理 HTML 注释包裹表格场景下的通用最佳实践方案。

