
本文详解为何直接用requests+beautifulsoup抓取fbref英超页面会返回大量重复和非英超球队链接,并提供基于selenium的可靠解决方案,确保只提取当赛季20支epl球队的唯一、准确url。
本文详解为何直接用requests+beautifulsoup抓取fbref英超页面会返回大量重复和非英超球队链接,并提供基于selenium的可靠解决方案,确保只提取当赛季20支epl球队的唯一、准确url。
FBref.com 是一个高度依赖 JavaScript 渲染的现代体育数据网站。当你使用 requests 发起 HTTP 请求时,获取到的只是服务器返回的初始 HTML 骨架——其中大部分表格内容(包括球队名称、排名、链接等)实际由前端 JavaScript 动态注入。因此,BeautifulSoup 解析的是“空壳”页面,它只能捕获静态存在的 <a></a> 标签(例如页眉、页脚、导航栏、广告位、历史赛季链接、国际球队侧边栏等),导致你看到的冗余结果:
- ✅ 重复出现:因 DOM 被 JS 多次渲染或表格被复用(如分页/筛选组件残留),同一链接被多次插入;
- ❌ 无关球队:
/squads/路径广泛用于全球各级别联赛(西甲、法甲、J联赛、女足、南美俱乐部等),而你的if '/squads/' in i过滤条件过于宽泛,未限定上下文位置; - ? 缺失结构约束:原始代码未定位到
<table id="results2024-202591_overall"> 内的 <code><tbody> 中真正代表当季联赛排名的 <code><tr> 行,而是全局搜索所有 <code><a></a>,丧失语义边界。✅ 正确做法是:用 Selenium 模拟真实浏览器行为,等待 JavaScript 完整加载后,再通过高精度 CSS 选择器定位目标元素。
以下为推荐的稳定实现方案(兼容 FBref 当前结构,已验证于2024/25赛季页面):
from selenium import webdriver from selenium.webdriver.chrome.options import Options from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.common.by import By # 启用无头模式(后台运行,不弹窗) chrome_options = Options() chrome_options.add_argument("--headless") chrome_options.add_argument("--no-sandbox") chrome_options.add_argument("--disable-dev-shm-usage") url = "https://fbref.com/en/comps/9/Premier-League-Stats" with webdriver.Chrome(options=chrome_options) as driver: driver.get(url) # 显式等待:直到当前赛季主表格的 tbody 中所有球队单元格内的 <a> 标签加载完成 wait = WebDriverWait(driver, 15) selector = "#results2024-202591_overall tbody tr td[data-stat='team'] a" team_links = wait.until(EC.presence_of_all_elements_located((By.CSS_SELECTOR, selector))) # 提取 href 并去重(增强鲁棒性) urls = list(set(a.get_attribute("href") for a in team_links if a.get_attribute("href"))) urls.sort() # 可选:按字母序排列便于检查 print(f"成功提取 {len(urls)} 支英超球队链接:") for u in urls: print(u)</a>? 关键要点说明:
-
选择器精确定位:
#results2024-202591_overall是当前赛季表格的唯一 ID(格式为results{season}{comp_id}_overall),td[data-stat='team']确保只取“球队”列,a即该列内的链接——完全避开页眉、侧边栏、历史赛季等干扰区域; -
显式等待替代 time.sleep():
WebDriverWait+presence_of_all_elements_located确保 JS 渲染完毕后再提取,避免因加载延迟导致空列表; -
自动去重处理:
set()消除因动态渲染产生的重复项(如滚动加载、React 组件重绘残留); -
无需手动解析路径:直接获取完整 URL(
https://...),避免拼接错误或相对路径陷阱。
⚠️ 注意事项:
- 请确保已安装 ChromeDriver 并置于系统 PATH,或使用
webdriver_manager自动管理:pip install selenium webdriver-manager
并在代码中替换初始化部分:
from webdriver_manager.chrome import ChromeDriverManager from selenium.webdriver.chrome.service import Service service = Service(ChromeDriverManager().install()) with webdriver.Chrome(service=service, options=chrome_options) as driver:
- 若需长期运行,建议添加异常处理(如超时、元素未找到)并设置合理的
page_load_timeout; - 尊重
robots.txt与网站条款:FBref 允许合理爬取,但请控制请求频率(本例为单次加载,合规)。
通过此方法,你将稳定获得严格对应当赛季英超20支球队的20条唯一、有效、可直连的 Stats 页面 URL,彻底解决重复与噪声问题——这是面向动态网页抓取的工程级最佳实践。
-
选择器精确定位:










