
本文详解为何用beautifulsoup按类名查找ipl赛程元素返回空列表,并提供无需selenium、纯requests+正则解析javascript数据源的高效解决方案。
本文详解为何用beautifulsoup按类名查找ipl赛程元素返回空列表,并提供无需selenium、纯requests+正则解析javascript数据源的高效解决方案。
IPL官网(如 https://www.iplt20.com/matches/results/2008)采用前端动态渲染技术:页面初始HTML中不包含实际比赛数据,而是通过JavaScript异步加载并注入DOM。因此,当你使用 requests.get() 获取HTML后交由 BeautifulSoup 解析时,soup.find_all('div', {'class': 'vn-shedule-desk col-100 floatLft'}) 必然返回空列表——该类名对应的60个比赛区块根本不存在于原始响应中。
✅ 正确思路是:绕过渲染层,直取数据源。IPL官方将结构化赛事数据以 JavaScript 函数调用形式托管在CDN上,例如:
- 赛季元数据:
https://scores.iplt20.com/ipl/mc/competition.js - 各赛季赛程详情:
https://ipl-stats-sports-mechanic.s3.ap-south-1.amazonaws.com/ipl/feeds/{competition_id}-matchschedule.js
这些JS文件本质是包裹了JSON数据的函数调用(如 oncomptetion({...}) 或 MatchSchedule({...})),只需提取其中的JSON字符串即可还原完整结构化数据。
以下是完整可运行的解决方案(已适配最新IPL数据结构):
import requests
import re
import json
import pandas as pd
# 1. 获取所有赛季 CompetitionID
url_competitions = "https://scores.iplt20.com/ipl/mc/competition.js"
response = requests.get(url_competitions)
if response.status_code != 200:
raise Exception(f"Failed to fetch competitions: {response.status_code}")
# 提取 oncomptetion({ ... }) 中的 JSON 字符串
match = re.search(r'oncomptetion\((\{.*?\})\);?', response.text, re.DOTALL)
if not match:
raise ValueError("Cannot extract competition data from JS")
competition_data = json.loads(match.group(1))
seasons = [item["CompetitionID"] for item in competition_data.get("competition", [])]
# 2. 遍历每个赛季,拉取对应赛程
all_matches = []
for comp_id in seasons:
js_url = f"https://ipl-stats-sports-mechanic.s3.ap-south-1.amazonaws.com/ipl/feeds/{comp_id}-matchschedule.js"
resp = requests.get(js_url)
if resp.status_code != 200:
print(f"Warning: Skipped season {comp_id}, HTTP {resp.status_code}")
continue
# 提取 MatchSchedule({ ... }) 中的 JSON
m = re.search(r'MatchSchedule\((\{.*?\})\);?', resp.text, re.DOTALL)
if not m:
continue
try:
schedule_data = json.loads(m.group(1))
matches = schedule_data.get("Matchsummary", [])
all_matches.extend(matches)
except (json.JSONDecodeError, KeyError) as e:
print(f"Parse error for {comp_id}: {e}")
# 3. 构建DataFrame,便于分析
df = pd.DataFrame(all_matches)
print(f"✅ Total matches scraped: {len(df)}")
print(df[["MatchDesc", "MatchDate", "Team1Name", "Team2Name", "MatchStatus"]].head())? 关键注意事项:
- ✅ 无需浏览器自动化:完全避免 Selenium 的启动开销与维护成本;
- ⚠️ JS函数签名可能变更:若某天
oncomptetion(...)改为initCompetition(...),需同步更新正则表达式; - ? CDN路径稳定性高但非官方API:该数据源长期稳定(IPL多年沿用),但仍建议添加异常处理与重试逻辑用于生产环境;
- ? 字段名以实际JS返回为准:
Matchsummary结构含MatchDesc,MatchDate,VenueName,MatchResult,TossWinner,PlayerOfTheMatch等丰富字段,可按需筛选; - ? 支持全赛季统一采集:一次运行即可获取2008–2024全部赛季数据,远超单页手动抓取效率。
总结:面对JavaScript动态渲染页面,优先排查其背后的API或静态数据接口。本方案不仅解决了“查不到元素”的表层问题,更建立了可扩展、高性能、易维护的IPL赛事数据管道——这才是专业网络数据采集应有的工程思维。


















