本文目录导读:

针对您提到的“SofaScore综合评分”(通常指球员/球队的赛后评分),这里提供一个实用Python脚本,用于抓取SofaScore的评分数据。
重要声明:
- 遵守条款:SofaScore有严格的Robots协议和反爬机制,此脚本仅用于个人学习、研究或少量数据获取,请勿用于高频抓取或商业用途。
- 技术限制:SofaScore的数据通过动态API(GraphQL)加载,直接抓取HTML无效,此脚本模拟浏览器请求其内部API。
- 反爬应对:如果遇到403错误或验证码,请降低抓取频率或使用代理。
方案:使用Python + Requests 直接请求SofaScore API
核心原理:
SofaScore前端会向 https://www.sofascore.com/api/v1/ 发送AJAX请求获取JSON数据,我们模拟这个请求,并带上合适的 User-Agent 和 Referer。
脚本1:获取指定球队与指定联赛的近期比赛评分
import requests
import json
import time
from datetime import datetime
HEADERS = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36',
'Accept': 'application/json, text/plain, */*',
'Referer': 'https://www.sofascore.com/',
'Origin': 'https://www.sofascore.com',
'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8',
}
def get_team_id(team_name):
"""通过搜索接口获取队伍ID (简化版,需要知道确切的英文名)"""
search_url = "https://www.sofascore.com/api/v1/search/all"
params = {'q': team_name}
try:
resp = requests.get(search_url, headers=HEADERS, params=params, timeout=10)
data = resp.json()
# 优先匹配足球队
for item in data.get('results', []):
if item.get('type') == 'team' and item.get('sport') == 'football':
return item.get('id'), item.get('name')
except Exception as e:
print(f"搜索失败: {e}")
return None, None
def get_team_last_matches(team_id, num=5):
"""获取球队最近比赛ID及评分"""
url = f"https://www.sofascore.com/api/v1/team/{team_id}/events/last/{num}"
try:
resp = requests.get(url, headers=HEADERS, timeout=10)
data = resp.json()
matches = data.get('events', [])
result = []
for match in matches:
match_id = match.get('id')
home_team = match.get('homeTeam', {}).get('name')
away_team = match.get('awayTeam', {}).get('name')
# 获取该场比赛的详细评分(SofaScore评分)
rating_info = get_match_player_ratings(match_id, team_id)
result.append({
'event_id': match_id,
'match': f"{home_team} vs {away_team}",
'date': datetime.fromtimestamp(match.get('startTimestamp', 0)).strftime('%Y-%m-%d'),
'ratings': rating_info
})
time.sleep(1) # 礼貌性延时
return result
except Exception as e:
print(f"获取比赛列表失败: {e}")
return []
def get_match_player_ratings(event_id, team_id):
"""获取单场比赛中指定球队所有球员的 SofaScore 评分"""
url = f"https://www.sofascore.com/api/v1/event/{event_id}/lineups"
try:
resp = requests.get(url, headers=HEADERS, timeout=10)
data = resp.json()
players = []
# 查找我们关注的队伍
for side in ['home', 'away']:
lineup = data.get(side, {})
if lineup.get('teamId') == team_id:
for player in lineup.get('players', []):
# 综合评分字段是 'statistics' 里的 'rating' 或 'averageRating',需要看具体位置
stats = player.get('statistics', {})
rating = stats.get('rating') or stats.get('averageRating') or 'N/A'
players.append({
'name': player.get('name'),
'position': player.get('position'),
'rating': rating,
'is_substitute': player.get('substitute', False)
})
# 仅返回我们关注球队的球员
return players
return [] # 如果没有找到该队伍
except Exception as e:
print(f"获取阵容评分失败: {e}")
return []
def main():
team_name = input("请输入球队英文名 (如: Manchester City): ")
team_id, found_name = get_team_id(team_name)
if not team_id:
print("未找到该球队,请尝试更精确的英文名")
return
print(f"找到球队: {found_name} (ID: {team_id})")
matches = get_team_last_matches(team_id, num=3) # 取最近3场
print("\n=== 最近比赛及评分统计 ===")
for idx, m in enumerate(matches, 1):
print(f"\n--- 比赛 {idx}: {m['match']} ({m['date']}) ---")
if m['ratings']:
# 计算平均分(排除替补)
starter_ratings = [p['rating'] for p in m['ratings'] if not p['is_substitute'] and isinstance(p['rating'], (int, float))]
if starter_ratings:
avg = sum(starter_ratings) / len(starter_ratings)
print(f"首发平均评分: {avg:.2f}")
print("主要球员评分:")
# 按评分排序
sorted_players = sorted(m['ratings'], key=lambda x: x['rating'] if isinstance(x['rating'], (int, float)) else 0, reverse=True)
for p in sorted_players[:5]: # 展示前5名
print(f" {p['name']} ( {'首发' if not p['is_substitute'] else '替补'} ): {p['rating']}")
else:
print("暂无评分数据(可能未开赛或被隐藏)")
if __name__ == "__main__":
main()
脚本2:统计指定单场比赛的两队综合评分(更常用)
如果您已经有了比赛ID,可以直接获取整场比赛的评分。
import requests
HEADERS = { ... } # 同脚本1
def get_match_ratings(event_id):
"""获取一场比赛所有球员的平均评分(简化版)"""
url = f"https://www.sofascore.com/api/v1/event/{event_id}/lineups"
resp = requests.get(url, headers=HEADERS, timeout=10)
data = resp.json()
stats = {'home': {}, 'away': {}}
for side in ['home', 'away']:
lineup = data.get(side, {})
team_name = lineup.get('teamName', 'Unknown')
player_ratings = []
for player in lineup.get('players', []):
rating = player.get('statistics', {}).get('rating')
if rating is not None: # 过滤掉未上场球员
player_ratings.append(rating)
if player_ratings:
avg_rating = sum(player_ratings) / len(player_ratings)
max_rating = max(player_ratings)
min_rating = min(player_ratings)
else:
avg_rating = max_rating = min_rating = 0
stats[side] = {
'team': team_name,
'count': len(player_ratings), # 有评分的球员数
'avg': round(avg_rating, 2),
'max': max_rating,
'min': min_rating
}
return stats
if __name__ == '__main__':
event_id = input("请输入比赛ID (可以从URL中获取, https://www.sofascore.com/event/1234567: 输入 1234567): ")
stats = get_match_ratings(event_id)
print(f"主队 {stats['home']['team']}: 平均评分 {stats['home']['avg']} (最高 {stats['home']['max']}, 最低 {stats['home']['min']})")
print(f"客队 {stats['away']['team']}: 平均评分 {stats['away']['avg']} (最高 {stats['away']['max']}, 最低 {stats['away']['min']})")
运行说明与注意事项:
-
安装依赖:运行前请确认已安装
requests库。pip install requests
-
综合评分”:SofaScore的球员综合评分是赛后由算法生成的(基于数据事件、触球、传球成功率、对抗等),该评分在比赛结束后几小时才稳定,如果比赛刚结束,可能返回
null。 -
反爬与封禁:
- 脚本中已加入
time.sleep(1)避免请求过快。 - 如果频繁使用,IP可能被SofaScore封禁,届时返回403错误,建议使用代理或降低频率。
- 如果输出乱码或请求失败,请检查
HEADERS中的User-Agent是否最新。
- 脚本中已加入
-
动态数据变化:SofaScore的API结构可能会更新,如果脚本失效,可能需要根据最新的网络请求(F12 -> Network -> Fetch/XHR)调整JSON解析路径。
高级用法:使用 Selenium(终极方案)
如果上述方法失效(因为SofaScore有严格的风控),您可以考虑使用 Selenium 控制浏览器,等待页面渲染完成后再抓取DOM中的评分元素,这虽然慢,但最稳定,不受API变动影响。
# 简略示例(需安装 selenium 和 webdriver-manager)
from selenium import webdriver
from selenium.webdriver.common.by import By
import time
driver = webdriver.Chrome() # 或 Firefox
driver.get("https://www.sofascore.com/team/football/manchester-city/17") # 示例
# 等待页面加载,点击“赛程”或滚动
time.sleep(5)
# 通过XPath提取评分元素
ratings = driver.find_elements(By.XPATH, '//div[contains(@class,"rating")]')
for r in ratings[:10]:
print(r.text)
driver.quit()
脚本已经能够满足“统计综合评分”的需求,请根据实际网络环境,优先尝试脚本1(直接API),如果发现数据字段层级有变,建议打开浏览器F12开发者工具,查看网络请求中 lineups 的JSON结构,微调解析逻辑即可。