Whoscored 评分对比实用脚本
下面给你一套可直接用的 Python 脚本方案,用于抓取/对比 Whoscored 球员评分,由于 Whoscored 有较强的反爬(Cloudflare + 动态渲染),这里给出几种实用路线,按稳妥程度排序。

方案一:使用 soccerdata 库(最推荐,最稳定)
soccerdata 是开源项目,内置了 Whoscored 的解析器,能绕过不少反爬。
pip install soccerdata
import soccerdata as sd
import pandas as pd
# 初始化 Whoscored 接口
ws = sd.WhoScored(
leagues='ENG-Premier League',
seasons='2023-2024'
)
# 1. 读取某场比赛的球员评分
# match_id 从 whoscored URL 获取,如 https://www.whoscored.com/Matches/1728888/...
match_stats = ws.read_events(match_id=1728888)
print(match_stats.head())
# 2. 读取赛季球员评分(聚合数据)
player_ratings = ws.read_player_season_stats(stat_type='summary')
print(player_ratings[['player', 'team', 'rating']].sort_values('rating', ascending=False).head(20))
优点:自动处理请求头、缓存、解析;缺点:偶尔因 Whoscored 改版需更新版本。
方案二:Playwright 动态抓取(自己控制)
Whoscored 的评分是 JS 渲染的,requests 拿不到,用 Playwright 最实用。
pip install playwright playwright install chromium
from playwright.sync_api import sync_playwright
import pandas as pd
import re, json
def get_match_ratings(match_url: str):
with sync_playwright() as p:
browser = p.chromium.launch(headless=True)
ctx = browser.new_context(
user_agent=("Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
"AppleWebKit/537.36 (KHTML, like Gecko) "
"Chrome/122.0 Safari/537.36")
)
page = ctx.new_page()
# 拦截 API 响应(Whoscored 内部用 JSON 接口)
captured = []
def on_response(resp):
if "PlayerStatistics" in resp.url or "player-statistics" in resp.url:
try:
captured.append(resp.json())
except Exception:
pass
page.on("response", on_response)
page.goto(match_url, wait_until="networkidle", timeout=60000)
page.wait_for_timeout(3000)
browser.close()
return captured
if __name__ == "__main__":
url = "https://www.whoscored.com/Matches/1728888/Live/England-Premier-League-2023-2024-Arsenal-Chelsea"
data = get_match_ratings(url)
print(json.dumps(data, indent=2, ensure_ascii=False)[:2000])
核心思路:不去硬解析 HTML,而是监听 XHR/fetch 响应,直接拿它的内部 JSON,字段干净(含 rating、playerId、teamId)。
方案三:多场/多球员评分对比脚本
假设你已经能拿到每场的 player -> rating 数据(来自上面任一方案),做对比:
import pandas as pd
# 示例:两场比赛的球员评分
match_a = pd.DataFrame({
"player": ["Saka", "Ødegaard", "Havertz", "Rice"],
"rating": [8.2, 7.9, 7.1, 7.5],
"team": ["Arsenal"] * 4,
})
match_b = pd.DataFrame({
"player": ["Saka", "Ødegaard", "Havertz", "Rice"],
"rating": [7.4, 8.3, 6.8, 7.9],
"team": ["Arsenal"] * 4,
})
# 合并对比
cmp = match_a.merge(match_b, on="player", suffixes=("_A", "_B"))
cmp["diff"] = (cmp["rating_B"] - cmp["rating_A"]).round(2)
cmp = cmp.sort_values("diff", ascending=False)
print(cmp)
# 可视化
import matplotlib.pyplot as plt
plt.rcParams["font.sans-serif"] = ["SimHei"] # 中文
plt.rcParams["axes.unicode_minus"] = False
x = range(len(cmp))
plt.bar([i-0.2 for i in x], cmp["rating_A"], width=0.4, label="比赛A")
plt.bar([i+0.2 for i in x], cmp["rating_B"], width=0.4, label="比赛B")
plt.xticks(list(x), cmp["player"])
plt.ylabel("Whoscored 评分")"球员评分对比")
plt.legend()
plt.tight_layout()
plt.savefig("rating_compare.png", dpi=150)
输出示例:
player rating_A rating_B diff
0 Ødegaard 7.9 8.3 0.40
1 Rice 7.5 7.9 0.44
2 Havertz 7.1 6.8 -0.30
3 Saka 8.2 7.4 -0.80 ← 注意排序需按 diff
几个实用要点
| 问题 | 建议 |
|---|---|
| 被 Cloudflare 拦 | 用 Playwright + 真实 UA,或直接用 soccerdata |
| 想批量抓 | 加 time.sleep(3~5),并用缓存(见下) |
| 评分字段 | 内部 JSON 里是 rating,HTML 里显示为 85 之类 |
| 想按位置/球队筛选 | JSON 里有 playerId、teamId、position |
| 长期使用 | 缓存到本地 SQLite/parquet,别每次都爬 |
简单缓存:
from pathlib import Path
import json
CACHE = Path("cache"); CACHE.mkdir(exist_ok=True)
def cached(key, fetch_fn):
f = CACHE / f"{key}.json"
if f.exists():
return json.loads(f.read_text(encoding="utf-8"))
data = fetch_fn()
f.write_text(json.dumps(data, ensure_ascii=False), encoding="utf-8")
return data
- 要省事 → 用
soccerdata,三行代码拿到评分表。 - 要可控/抓内部 JSON → 用 Playwright 监听
PlayerStatistics接口。 - 对比分析 → 合并 DataFrame +
diff列 + 柱状图。
如果你告诉我具体需求(对比某两名球员整个赛季的评分走势,还是对比两支球队某场比赛的球员评分),我可以把脚本改成更贴合你场景的版本。