本文目录导读:

这是一个非常专业且实用的话题,在足球数据分析中,WhoScored 的评分系统是目前公开数据中最被广泛引用的权威评分之一(另一个是SofaScore)。
由于我不能直接抓取实时数据(且该网站有反爬机制),我将为你提供基于实用脚本(Python)的比对逻辑、实现方法以及两个评分系统在统计口径上的核心差异,你可以直接复制代码,修改请求头后用于分析。
第一部分:核心差异对比(你必须知道的)
在写代码之前,先明确这两个评分系统的逻辑,否则你算出来的“差距”毫无意义:
| 维度 | WhoScored (WS) | SofaScore (SS) | 对评分的影响 |
|---|---|---|---|
| 满分机制 | 0 封顶(极难达到) | 0 封顶(但算法相对宽松) | WS 的 8.5+ 属于神级表现,SS 的 8.5+ 较多见。 |
| 权重核心 | 防守数据权重极高(抢断、解围、拦截) | 进攻数据权重较高(关键传球、创造机会) | 防守型后腰在 WS 评分通常比 SS 高 2~0.5。 |
| 处理球 | 极度惩罚“无效控球”和“回传” | 对“横传”和“控球率”惩罚较小 | 传控型中场(如若日尼奥)在 WS 容易低分。 |
| 门将 | 扑救难度系数较高 | 对“失球”扣分更狠 | 面对大量射门的门将 WS 分高。 |
| 加成 | 无特定位置加成 | 有位置加成(边后卫传中多给高分) | 边后卫在 SS 更容易拿高分。 |
第二部分:Python 实用脚本(提取与比对)
以下脚本模拟了从 API 抓取到数据后的清洗与对齐逻辑,由于两家官网的 JSON 结构不同,脚本核心是将两个数据源合并到同一个 DataFrame 中,按球员ID对齐,计算差值。
import pandas as pd
import requests
import time
import random
from datetime import datetime
# 注意:实际应用中,你需要通过抓包获取 X-Requested-With 和 Cookie
# 这里提供模拟请求头,实际使用需替换为你的 User-Agent
HEADERS = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36',
'Accept': 'application/json, text/plain, */*',
'Referer': 'https://www.whoscored.com/',
'X-Requested-With': 'XMLHttpRequest'
}
def fetch_whoscored_ratings(match_id):
"""
伪代码:实际需要解析 WS 的 matchcentre 数据
返回:{player_id: ws_rating}
"""
# 真实接口示例(需根据抓包获取)
url = f"https://www.whoscored.com/matches/{match_id}/live"
# ... 此处省略复杂JS逆向过程(他们使用了obfuscated token)
# 模拟返回数据(以下为示例结构)
simulated_data = [
{'id': 101, 'name': 'Salah', 'rating': 8.9},
{'id': 102, 'name': 'Van Dijk', 'rating': 7.2},
{'id': 103, 'name': 'Mac Allister', 'rating': 6.5},
]
return {item['id']: item['rating'] for item in simulated_data}
def fetch_sofascore_ratings(event_id):
"""
SofaScore 的 API 相对开放,可通过其内部API获取
返回:{player_id: ss_rating}
"""
# SofaScore 公开的 event API
url = f"https://api.sofascore.com/api/v1/event/{event_id}/lineups"
resp = requests.get(url, headers=HEADERS, timeout=10)
data = resp.json()
players_rating = {}
# 解析两个数组(home 和 away)
for team in ['home', 'away']:
for player in data.get(team, {}).get('players', []):
# SofaScore 字段是 'statistics' 里的 'rating'
rating = player.get('statistics', {}).get('rating')
if rating is not None:
players_rating[player.get('id')] = rating
return players_rating
def create_comparison_table(ws_data, ss_data):
"""
核心比对逻辑:合并两个字典,计算差值
"""
# 将两个字典转为 DataFrame
df_ws = pd.DataFrame(list(ws_data.items()), columns=['player_id', 'WhoScored_Rating'])
df_ss = pd.DataFrame(list(ss_data.items()), columns=['player_id', 'SofaScore_Rating'])
# 全连接(保留所有球员,即使只有一个平台有数据)
merged = pd.merge(df_ws, df_ss, on='player_id', how='outer')
# 计算评分差(WS - SS)
merged['Rating_Deviation'] = merged['WhoScored_Rating'] - merged['SofaScore_Rating']
# 添加差异标记:超过0.5分为“明显高”,低于-0.5为“明显低”
def tag_difference(row):
if pd.isna(row['Rating_Deviation']):
return 'Missing_Data'
if row['Rating_Deviation'] > 0.5:
return 'WS_Higher'
elif row['Rating_Deviation'] < -0.5:
return 'SS_Higher'
else:
return 'Consistent'
merged['Verdict'] = merged.apply(tag_difference, axis=1)
return merged
if __name__ == "__main__":
# 用户输入比赛ID
match_id_ws = "123456" # WhoScored 的比赛ID
event_id_ss = "987654" # SofaScore 的比赛ID
print("开始抓取 WhoScored 数据...")
ws_ratings = fetch_whoscored_ratings(match_id_ws)
time.sleep(random.uniform(1, 2)) # 模拟礼貌爬取
print("开始抓取 SofaScore 数据...")
ss_ratings = fetch_sofascore_ratings(event_id_ss)
# 生成比对表
result_df = create_comparison_table(ws_ratings, ss_ratings)
# 打印结果
print("\n" + "="*70)
print("球员评分对比报告 (WhoScored vs SofaScore)")
print("="*70)
print(result_df.to_string(index=False))
# 统计差异最大的球员
print("\n▶ 最大分歧榜(WS 比 SS 高):")
high_ws = result_df.dropna(subset=['Rating_Deviation']).nlargest(3, 'Rating_Deviation')
print(high_ws[['player_id', 'WhoScored_Rating', 'SofaScore_Rating', 'Rating_Deviation']].to_string(index=False))
print("\n▶ 最大分歧榜(SS 比 WS 高):")
high_ss = result_df.dropna(subset=['Rating_Deviation']).nsmallest(3, 'Rating_Deviation')
print(high_ss[['player_id', 'WhoScored_Rating', 'SofaScore_Rating', 'Rating_Deviation']].to_string(index=False))
第三部分:如何将脚本用于实战?(破局指南)
如果你不会逆向 JS(WhoScored 的 Token 加密较难),有两条路可以走:
-
使用第三方开源项目(推荐):
- GitHub 上有现成的
whoscored-api项目(如who-scored-api)封装了解析逻辑,你只需要pip install whoscored然后传入账号密码即可。 - SofaScore 的 API 相对开放,可直接用
requests请求,无需逆向。
- GitHub 上有现成的
-
使用`统计口径**模拟法(专业分析师的常用手段):
- 如果抓不到 WS 的精确数据,可以抓 FBref 或 Opta 的数据,用回归模型模拟 WS 的权重逻辑(抢断+解围高权重),对比 SofaScore 的原始评分。
第四部分:解读结果的黄金法则
当你运行脚本得到下表时,不要只看分数差异,要结合上下文:
| 差异情境 | 球员类型 | 专业解读( |
|---|---|---|
| WS 远高于 SS | 防守型后腰/中卫 | 这是正常现象,该球员大概率贡献了极高成功率的抢断和解围,但丢失球权也多,WS更看重防守数据。 |
| SS 远高于 WS | 边锋/前腰(创造力强) | 该球员可能传出了多次关键传球(SS权重高),但传球成功率极低,且丢球权极多(WS扣分狠)。 |
| 两者都高(>8.0) | 前锋 | 真正统治级表现,既完成了终结(WS看重),又创造了大量机会(SS看重)。 |
| 两者都低(<6.0) | 门将/中卫 | 该球员很可能有致命失误或失球责任,且没有扑救/解围数据来找补。 |
附赠:可视化对比(快速洞察)
你可以使用 matplotlib 绘制散点图,X轴是SofaScore评分,Y轴是WhoScored评分,对角线为 y = x。
- 如果散点集中在对角线下方,说明 WS整体给分比SS更严格;
- 如果点偏离较远(残差大),则重点关注该球员。
import matplotlib.pyplot as plt
# 假设 df 已有数据
plt.figure(figsize=(10, 8))
plt.scatter(df['SofaScore_Rating'], df['WhoScored_Rating'], c='blue', alpha=0.6)
plt.plot([5, 10], [5, 10], 'r--', lw=2, label='Perfect Agreement')
plt.xlabel('SofaScore Rating')
plt.ylabel('WhoScored Rating')'WhoScored vs SofaScore Rating Comparison')
plt.grid(True, alpha=0.3)
plt.legend()
plt.show()
这个脚本能直接帮你跑出两家评分的差值,但请记住,不存在“谁更准”,只存在“谁更贴合你想要的战术视角”,如果你想知道一个球员是否“隐形了”,看WS;如果你想知道一个球员是否“创造了机会”,看SS。