
本文详解为何用beautifulsoup按class查找ipl赛程元素返回空列表,并提供无需selenium、纯requests+正则解析js数据文件的高效替代方案。
本文详解为何用beautifulsoup按class查找ipl赛程元素返回空列表,并提供无需selenium、纯requests+正则解析js数据文件的高效替代方案。
在使用 requests + BeautifulSoup 进行网页抓取时,若对目标元素(如 <div class="vn-shedule-desk col-100 floatLft">)调用 <code>find_all() 却始终返回空列表([]),根本原因并非选择器错误,而是该内容由前端JavaScript动态加载,并未包含在服务器返回的原始HTML中。IPL官网(iplt20.com)正是典型示例:其比赛数据通过异步加载JS文件(如 competition.js 和 *-matchschedule.js)注入DOM,requests.get() 仅获取静态骨架页,自然无法匹配动态生成的节点。
✅ 正确做法是绕过渲染层,直击数据源头——IPL官方前端实际从CDN加载结构化JSON数据,只需逆向分析网络请求即可获取完整、干净、可编程解析的原始数据。
以下为完整、可运行的解决方案:
import requests
import re
import json
import pandas as pd
# Step 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}")
# 提取 competition.js 中的 JSON 数据(包裹在 oncomptetion(...) 调用内)
match = re.search(r'oncomptetion\((\{.*?\})\);?', response.text, re.DOTALL | re.IGNORECASE)
if not match:
raise ValueError("Cannot extract competition data from JS file")
competition_data = json.loads(match.group(1))
seasons = [item["CompetitionID"] for item in competition_data.get("competition", [])]
# Step 2: 遍历每个赛季,拉取对应比赛日程
all_matches = []
base_match_url = "https://ipl-stats-sports-mechanic.s3.ap-south-1.amazonaws.com/ipl/feeds/{}-matchschedule.js"
for season_id in seasons:
try:
resp = requests.get(base_match_url.format(season_id))
resp.raise_for_status()
# 解析 MatchSchedule(...) 包裹的 JSON
match_js = re.search(r'MatchSchedule\((\{.*?\})\);?', resp.text, re.DOTALL | re.IGNORECASE)
if not match_js:
print(f"Warning: No MatchSchedule data found for season {season_id}")
continue
schedule_data = json.loads(match_js.group(1))
matches = schedule_data.get("Matchsummary", [])
all_matches.extend(matches)
except Exception as e:
print(f"Error processing season {season_id}: {e}")
# Step 3: 构建结构化DataFrame(含2008–2024全赛季数据)
df = pd.DataFrame(all_matches)
print(f"✅ Total matches scraped: {len(df)}")
print(df[["SeasonYear", "MatchDesc", "Team1Name", "Team2Name", "MatchResult", "MatchStatus"]].head())
? 关键要点说明:
- ✨ 零浏览器依赖:全程使用
requests,轻量、快速、稳定,规避Selenium启动慢、易被拦截、维护成本高等问题; - ? 精准定位JS数据接口:
competition.js提供赛季元数据(含CompetitionID),{id}-matchschedule.js提供该赛季全部比赛摘要(Matchsummary数组),二者构成完整数据链; - ⚠️ 注意JS格式陷阱:目标JSON常被函数调用包裹(如
oncomptetion({...})或MatchSchedule({...})),需用正则提取内部JSON字符串再json.loads(); - ? 字段丰富性:
Matchsummary中包含MatchID,SeasonYear,MatchDate,VenueName,Team1Name,Team2Name,MatchResult,MatchStatus,TossWinner,WinningTeam等数十个结构化字段,远超HTML页面展示内容; - ? 跨赛季扩展性:代码自动遍历所有赛季,无需手动修改URL,天然支持未来新增赛季。
? 进阶建议:
- 添加
time.sleep(0.5)防止单IP高频请求; - 使用
session复用连接提升效率; - 对
MatchResult字段做标准化清洗(如统一“Won by X runs/wickets”格式); - 结合
schedule_data.get("MatchDetails", [])可进一步获取每场详细球局数据(若需深度分析)。
该方法不仅解决IPL抓取问题,更是现代动态网站数据采集的通用范式:优先分析XHR/JS资源,而非渲染后DOM——高效、可靠、可持续。










