在使用 Pandas 的read_html()提取 HTML 表格时,一个常见陷阱是目标表格被注释标签包裹,导致第二张表格返回空 DataFrame;必须先清除注释后再进行解析。
使用 Pandas 的 read_html() 爬取网页表格时,很容易遇到一个隐藏的坑:目标表格被 HTML 注释符()包裹。从原始 HTML 来看,表格确实存在,但标准 HTML 解析器(如 BeautifulSoup)默认会跳过注释节点,read_html() 内部使用的解析器也是如此。因此,即使通过 soup.find_all() 也无法定位到该表格,直接传入包含注释的字符串也会因结构不可见而导致解析失败——最终返回一个空 DataFrame。
在上面这段代码中,“stats_keeper”这张表实际上隐藏在注释块内部(查看页面源代码即可确认)。soup.find_all('table', {'id': 'stats_keeper'}) 很可能会返回空列表或引发索引错误,而后续 pd.read_html(str(table2)) 传入的字符串仍带有注释语法,解析器无法识别,自然只能返回空表。
那么正确的做法是什么?最佳方案是:避开 BeautifulSoup,直接在原始响应文本上处理,彻底清除所有 HTML 注释标记,再交给 pd.read_html() 统一解析。同时,利用 StringIO 将清理后的字符串包装成类文件对象,可有效避免许多兼容性问题:
from io import StringIO
import pandas as pd
import requests
url = "https://fbref.com/en/comps/9/keepers/Premier-League-Stats"
response = requests.get(url)
# 清理注释:简单替换(适用于无嵌套注释的场景)
cleaned_html = response.text.replace('', '')
# 使用 StringIO 包装,确保 read_html 能正确读取
html_io = StringIO(cleaned_html)
# 一次性解析指定 ID 的表格(更健壮,避免索引错误)
df1 = pd.read_html(html_io, attrs={'id': 'stats_squads_keeper_for'}, fla vor='bs4')[0]
html_io.seek(0) # 重置指针位置
df2 = pd.read_html(html_io, attrs={'id': 'stats_keeper'}, fla vor='bs4')[0]
print("第一张表(球队门将统计)形状:", df1.shape)
print("第二张表(球员门将统计)形状:", df2.shape)
实践中有几个关键点需要特别注意:
replace('', '')是一种轻量级方案,适用于注释不嵌套、结构简单的页面(如 fbref)。如果页面存在多层嵌套注释,建议改用正则表达式re.sub(r'', '', text, flags=re.DOTALL),更加安全可靠。- 每次调用
pd.read_html()之前,务必重置StringIO指针(seek(0)),否则第二次读取会从末尾开始,结果依然为空。 - 显式指定
fla vor='bs4'可提升解析的稳定性,尤其是在环境中同时安装了多个 HTML 解析器的情况下。 - 如果仍出现错误,建议将清理后的
cleaned_html保存到本地,手动检查目标
