
本文详解在解析 Basketball-Reference 等含 HTML 注释包裹内容的网页时,如何正确提取 class="tooltip make" 这类多类名 div 元素,涵盖注释处理、类名参数语法修正及 CSS 选择器替代方案。
本文详解在解析 basketball-reference 等含 html 注释包裹内容的网页时,如何正确提取 `class="tooltip make"` 这类多类名 div 元素,涵盖注释处理、类名参数语法修正及 css 选择器替代方案。
在使用 BeautifulSoup(bs4)进行网页抓取时,一个常见却易被忽略的问题是:目标元素明明存在于原始 HTML 中,find_all() 却返回空列表。典型场景如 Basketball-Reference 的投篮热图页面(例如 2010 年哈登投篮数据页),其中关键的投篮标记 <div> 被 HTML 注释符 <!-- 和 --> 包裹:
<!-- <div style="top:128px;left:58px;" tip="Nov 20, 2009, OKC vs WAS<br>4th Qtr, 11:02 remaining<br>Made 2-pointer from 20 ft<br>OKC now leads 97-86" class="tooltip make">●</div> -->
即使你已用 Selenium 渲染页面或直接请求 HTML,若未预处理注释,BeautifulSoup 默认不会解析注释内部的内容——它将整段视为 Comment 对象,而非可搜索的标签节点。
✅ 正确做法:三步解决
1. 预处理 HTML:移除注释包裹
在构建 BeautifulSoup 对象前,先对响应文本执行字符串替换,剥离注释边界:
import requests
from bs4 import BeautifulSoup
url = "https://www.php.cn/link/6ffd0a7e2060229ac8e4b94807767abe"
response = requests.get(url)
clean_html = response.text.replace("<!--", "").replace("-->", "")
soup = BeautifulSoup(clean_html, "html.parser")⚠️ 注意:此方法适用于注释结构简单、无嵌套 <!-- --> 的场景。若页面存在复杂注释(如含 --> 子串),建议使用正则 re.sub(r'<!--.*?-->', '', text, flags=re.DOTALL) 更稳健地清除。
2. 修正类名参数:class_ 而非 _class
BeautifulSoup 中,class 是 Python 关键字,因此必须使用 class_ 参数传入类名(带下划线)。错误写法 find_all("div", _class="tooltip make") 会被忽略;正确写法为:
shots = soup.find_all("div", class_="tooltip make")该语法要求 class_ 值精确匹配整个 class 属性字符串(即空格分隔的完整类名序列)。此处 "tooltip make" 必须完全一致,顺序不可颠倒。
3. 推荐替代方案:CSS 选择器(更灵活、语义清晰)
使用 .select() 方法配合 CSS 选择器,不仅语法直观,还天然支持多类名组合(.tooltip.make 表示同时拥有 tooltip 和 make 两个类):
shots = soup.select("div.tooltip.make")✅ 优势:
- 不受注释影响(只要 HTML 已清理);
- 支持链式选择(如 div.tooltip.make[tip] 可进一步筛选含 tip 属性的元素);
- 更接近前端开发习惯,可读性高。
完整可运行示例
import requests
from bs4 import BeautifulSoup
# 1. 获取并清理 HTML
url = "https://www.php.cn/link/6ffd0a7e2060229ac8e4b94807767abe"
html = requests.get(url).text
clean_html = html.replace("<!--", "").replace("-->", "")
# 2. 解析并提取目标 div
soup = BeautifulSoup(clean_html, "html.parser")
shots = soup.select("div.tooltip.make") # 或 soup.find_all("div", class_="tooltip make")
print(f"共找到 {len(shots)} 个投篮标记元素")
if shots:
first = shots[0]
print(f"首个元素 tip 属性:{first.get('tip', 'N/A')}")
print(f"内部文本:{first.get_text(strip=True)}")总结与注意事项
- 注释是隐形障碍:许多体育数据网站(如 Basketball-Reference、Baseball-Reference)为兼容旧浏览器或控制加载逻辑,会将动态生成的 DOM 片段用注释包裹——这不是 JS 渲染问题,而是 HTML 结构特性,必须预处理。
- class_ 参数区分大小写且严格匹配:"tooltip make" ≠ "make tooltip",也不匹配 "tooltip make extra"。
- 优先选用 .select():对于多类名、属性、层级组合查询,CSS 选择器比 find_all() 更简洁可靠。
- 验证 HTML 源码:调试时务必打印 soup.prettify()[:2000] 或搜索 soup.find(string=lambda x: 'tooltip make' in str(x)),确认目标结构确实已存在于解析树中。
掌握这三点,即可稳定提取此类“看似可见却查无此元素”的 HTML 片段。

















