实用脚本统计SofaScore综合评分?

wen 实用脚本 3

SofaScore 综合评分统计脚本

SofaScore 没有公开 API,但网页端有内部接口,下面给出一套实用脚本(Python),支持球员评分、球队评分、赛事统计。

实用脚本统计SofaScore综合评分?

核心思路

SofaScore 数据接口域名:https://api.sofascore.com/api/v1/

常用端点: | 用途 | 端点 | |------|------| | 赛事列表 | /unique-tournament/{id}/season/{seasonId}/events/round/{round} | | 比赛详情 | /event/{eventId} | | 球员评分 | /event/{eventId}/lineups | | 比赛统计 | /event/{eventId}/statistics |

⚠️ 必须有 User-Agent,否则返回 403。


Python 脚本

import requests
import time
import json
from statistics import mean
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",
}
BASE = "https://api.sofascore.com/api/v1"
def get_json(url, retry=3):
    for i in range(retry):
        try:
            r = requests.get(url, headers=HEADERS, timeout=15)
            if r.status_code == 200:
                return r.json()
            time.sleep(1.5)
        except Exception as e:
            print(f"[warn] {e}")
            time.sleep(2)
    return None
def get_event_lineups(event_id):
    """获取一场比赛的球员评分"""
    url = f"{BASE}/event/{event_id}/lineups"
    data = get_json(url)
    if not data:
        return None, None
    players = []
    for side in ("home", "away"):
        for p in data.get(side, {}).get("players", []):
            info = p.get("player", {})
            stats = p.get("statistics", {}) or {}
            rating = stats.get("rating")
            if rating is None:
                continue
            players.append({
                "name": info.get("name"),
                "team": side,
                "position": p.get("position"),
                "substitute": p.get("substitute", False),
                "rating": float(rating),
            })
    return players, data
def team_rating(players):
    """按球队聚合平均评分(仅首发)"""
    starters = [p for p in players if not p["substitute"]]
    if not starters:
        return {}
    result = {}
    for side in ("home", "away"):
        arr = [p["rating"] for p in starters if p["team"] == side]
        if arr:
            result[side] = round(mean(arr), 2)
    return result
def stat_overview(event_id):
    """获取整场关键统计数据"""
    url = f"{BASE}/event/{event_id}/statistics"
    data = get_json(url)
    if not data:
        return {}
    summary = {}
    for period in data.get("statistics", []):
        period_name = period.get("period")
        for group in period.get("groups", []):
            for item in group.get("statisticsItems", []):
                key = item.get("name")
                summary.setdefault(period_name, {})[key] = {
                    "home": item.get("home"),
                    "away": item.get("away"),
                }
    return summary
def analyze_event(event_id, verbose=True):
    players, _ = get_event_lineups(event_id)
    if not players:
        print(f"[event {event_id}] 无评分数据")
        return None
    ratings = [p["rating"] for p in players]
    summary = {
        "event_id": event_id,
        "player_count": len(players),
        "avg_rating": round(mean(ratings), 2),
        "max_rating": max(ratings),
        "min_rating": min(ratings),
        "team_avg": team_rating(players),
        "top5": sorted(players, key=lambda x: -x["rating"])[:5],
    }
    if verbose:
        print(f"\n=== Event {event_id} ===")
        print(f"平均评分: {summary['avg_rating']}  最高: {summary['max_rating']}")
        print(f"球队均分: {summary['team_avg']}")
        print("Top 5:")
        for p in summary["top5"]:
            print(f"  {p['rating']:.1f}  {p['name']} ({p['team']})")
    return summary
if __name__ == "__main__":
    # 用赛事 URL 里的数字替换 event_id
    # 例:https://www.sofascore.com/event/12345678
    EVENTS = [12345678, 12345679]
    all_summary = []
    for eid in EVENTS:
        res = analyze_event(eid)
        if res:
            all_summary.append(res)
        time.sleep(2)  # 防止限流
    with open("sofascore_summary.json", "w", encoding="utf-8") as f:
        json.dump(all_summary, f, ensure_ascii=False, indent=2)
    print("\n已保存 sofascore_summary.json")

如何拿 event_id / tournament_id

  1. 打开 SofaScore 网页任意比赛
  2. URL 形如 https://www.sofascore.com/event/12345678/...,中间数字即 event_id
  3. 要批量抓整轮赛事:
    • 比赛 URL .../unique-tournament/17/season/61627 → tournament=17,season=61627
    • 调 /unique-tournament/17/season/61627/events/round/10 拿到该轮所有 event_id

批量采集示例:

def events_of_round(tid, sid, rnd):
    url = f"{BASE}/unique-tournament/{tid}/season/{sid}/events/round/{rnd}"
    data = get_json(url)
    return [e["id"] for e in data.get("events", [])]

注意事项

项 说明
频率 建议 ≥1.5s/次,否则易被封 IP
UA 必须带浏览器 UA
Cloudflare 大量请求会触发 403,可考虑 cloudscraper 或代理池
商用 SofaScore 数据版权归其所有,仅供个人学习
订阅者评分 该接口只返回官方评分,不含 SofaScore 用户投票分;用户投票需要抓 /event/{id}/polls

可选增强

  • 持久化:写入 SQLite / CSV

    import csv
    with open("ratings.csv", "w", newline="", encoding="utf-8") as f:
      w = csv.writer(f)
      w.writerow(["event_id", "name", "team", "position", "rating"])
      for s in all_summary:
          for p in s["top5"]:
              w.writerow([s["event_id"], p["name"], p["team"], p["position"], p["rating"]])
  • 可视化:用 matplotlib 画球队均分柱状图

  • 增量更新:按日期查询 /sport/football/scheduled-events/2024-05-01


你的目标是单个联赛整赛季统计,还是具体某几场比赛?告诉我我可以把脚本改成对应的批量采集版本。

抱歉,评论功能暂时关闭!