免费足球数据一步到位:Understat异步Python包快速搭建你的xG分析工作台
【免费下载链接】understatAn asynchronous Python package for https://understat.com/.项目地址: https://gitcode.com/gh_mirrors/un/understat
周五下午,我收到一位做足球内容的朋友的求助:他要赶一篇英超复盘稿,需要过去五个赛季的射手数据、每场比赛的射门分布、还有各队的高位逼抢指标。他试过手动去数据站翻表格,半小时只整理出两轮比赛;试过抓包,发现页面全是JavaScript动态渲染,正则根本抠不出东西;最后看了一眼商业数据API的报价单,默默关掉了网页。
如果你也卡在"数据拿不到"这一步,那这个开源的Understat异步Python包值得你花五分钟认识一下。它把 understat.com 上的海量足球数据封装成了十几个简单的异步方法,免费、开箱即用,xG、xA、PPDA、OPPDA 这些专业指标都能直接拉到本地。下面我带你从零跑通一条完整的数据链路,顺便把高效用法和避坑点一次讲清楚。
先聊点实在的:为什么"免费足球数据"这么难到手
在做技术选型之前,得先明白痛点出在哪。市面上的足球数据来源无非三条路:
- 商业API:数据全、更新快,但年费动辄上万美元,个人和小团队根本吃不消;
- 自建爬虫:目标站点普遍用JS异步渲染数据,直接抓HTML只能拿到空壳,还要处理反爬、登录、数据清洗一堆麻烦事;
- 手动整理:适合单场比赛,一旦涉及多赛季、多联赛,效率低到让人崩溃。
而 Understat 这类站点其实藏着一个"后门":它的数据接口本身是公开的 JSON 接口,只是藏在页面脚本里,普通浏览器用户看不见罢了。Understat异步Python包做的,就是把这些接口整理成一套风格统一的 Python 方法——你只管传联赛名、赛季号,剩下的解析工作它全包了。
说白了,这个包就像一把"翻译钥匙",把网站后台的JSON数据翻译成你能直接用的Python列表和字典。
认识主角:这个包一口气能给你哪些数据
整个包只有一个核心类Understat,构造函数接收一个aiohttp.ClientSession,之后所有数据请求都复用这一个连接。支持六大联赛:英超(epl)、西甲(la_liga)、德甲(bundesliga)、意甲(serie_a)、法甲(ligue_1)、俄超(rfpl)。
接口速查表如下,按使用频率排列:
| 方法 | 一句话说明 | 返回内容 |
|---|---|---|
get_league_players | 某联赛某赛季的全部球员统计 | 进球、xG、xA、射门、关键传球等 |
get_league_table | 联赛积分榜 | 积分、xG、PPDA、OPPDA等18列 |
get_league_results | 已结束的比赛 | 比分、双方xG、比赛时间 |
get_league_fixtures | 未开始的赛程 | 对阵、时间 |
get_team_results/get_team_fixtures | 单支球队的比赛结果与赛程 | 同上,聚焦单队 |
get_team_players/get_team_stats | 球队球员明细与赛季统计 | 球员数据或球队汇总 |
get_player_shots | 单个球员的全部射门 | 射门位置、脚法、结果、xG |
get_player_matches/get_player_stats | 球员出场记录与场均数据 | 按场次或按位置聚合 |
get_match_players/get_match_shots | 单场比赛的出场名单与射门 | 主客两队字典结构 |
get_stats | 各联赛按月的统计快照 | 联赛+月份分组数据 |
所有方法返回的都是 Python 原生的列表或字典,字段与网站页面一一对应,不需要你自己写任何 HTML 解析逻辑。完整的方法签名和输出示例可以翻仓库里的官方文档:docs/classes/understat.rst。
首次实战:三行核心代码拿下英超射手榜
第一步:安装,两条路任选
最省事的方式是直接用 pip:
pip install understat如果你更想用最新源码,也可以把仓库克隆到本地再装:
git clone https://gitcode.com/gh_mirrors/un/understat cd understat pip install .装完之后建议顺手跑一遍测试,确认环境没问题:
pytest tests/测试用例都放在 tests/ 目录里,覆盖了每个接口的常规调用和过滤参数,既是质量保障,也是很好的用法示范。
第二步:写第一个查询脚本
新建一个top_scorers.py,贴入下面这段代码:
import asyncio import aiohttp from understat import Understat async def main(): # 整个会话只创建一次,复用连接 async with aiohttp.ClientSession() as session: understat = Understat(session) # 拉取英超2023赛季全部球员数据 players = await understat.get_league_players("epl", 2023) # 注意:接口返回的数值是字符串,先转float再排序 top = sorted(players, key=lambda p: float(p["goals"]), reverse=True)[:10] print("英超2023赛季射手榜 TOP10") for i, p in enumerate(top, 1): print(f"{i}. {p['player_name']}: {p['goals']}球 xG={float(p['xG']):.2f}") asyncio.run(main())运行方式:
python top_scorers.py跑通之后你会发现,拿到整个联赛几千名球员的完整赛季数据,前后不过一两秒——异步 + 连接复用的优势在这里体现得淋漓尽致。xG 和实际进球摆在一起,谁在"超额完成"、谁在"挥霍机会",一眼就能看出来。
第三步:学会给数据"瘦身"
很多接口都支持过滤,你既可以用字典,也可以用关键字参数,效果一样:
# 两种写法等价:只看利物浦的球员 players = await understat.get_league_players("epl", 2023, {"team_title": "Liverpool"}) players = await understat.get_league_players("epl", 2023, team_title="Liverpool")想查某位球员的射门明细,只需知道他的 Understat ID:
shots = await understat.get_player_shots(619) # 例如某球员的全部射门记录每条射门记录都包含分钟、射门位置坐标、结果(进球/被扑/射偏)、射门方式(左脚/右脚/头球)和当时情境(运动战/定位球/点球),做复盘和可视化都够用。
进阶玩法:三种高频场景的落地写法
场景一:给全联盟做一次"逼抢强度"体检
PPDA(每次防守动作前的传球次数)是衡量球队压迫强度的经典指标,数值越低代表逼抢越凶。get_league_table直接把这个指标算好了,连 OPPDA(对手的PPDA)也一并给出:
async def pressing_check(): async with aiohttp.ClientSession() as session: understat = Understat(session) table = await understat.get_league_table("epl", 2023) header = table[0] # ['Team', 'M', 'W', ..., 'PPDA', 'OPPDA', ...] rows = table[1:] # 去掉表头 # 按PPDA升序排列,逼抢最凶的排最前 rows.sort(key=lambda r: float(r[13])) for r in rows: print(f"{r[0]:<22} PPDA={r[13]} OPPDA={r[14]}") asyncio.run(pressing_check())想单独看主场或客场的表现,把h_a参数换成"home"或"away"即可;想只统计某段时间(比如冬窗后的状态),传start_date和end_date:
table = await understat.get_league_table( "epl", 2023, h_a="away", start_date="2024-01-01", end_date="2024-05-01")日期格式固定为YYYY-MM-DD,这是官方文档里明确写的约定。
场景二:多赛季数据一口气批量抓取
做时间序列分析时,一个赛季的数据往往不够。借助asyncio.gather,可以并发拉取多个赛季,再合并成一个大的数据框:
import asyncio import aiohttp from understat import Understat async def fetch_season(session, season): understat = Understat(session) players = await understat.get_league_players("epl", season) for p in players: p["season"] = season # 打上赛季标记 return players async def main(): async with aiohttp.ClientSession() as session: results = await asyncio.gather( *(fetch_season(session, s) for s in range(2019, 2024)) ) all_players = [p for batch in results for p in batch] print(f"五个赛季共拿到 {len(all_players)} 条球员记录") asyncio.run(main())拿到手后转成pandas.DataFrame,就能直接接上你自己的建模流程了。
场景三:单场比赛的颗粒度复盘
赛后想逐球复盘?用比赛ID直接调射门接口,返回的是以主客队分组的字典结构:
data = await understat.get_match_shots(22073) # 比赛ID home_shots = data["h"] # 主队射门明细 away_shots = data["a"] # 客队射门明细配合get_match_players还能拿到双方出场名单,写战报、做赛后评分的数据底子一下就齐了。
让效率再翻一倍:几个值得养成的习惯
- 复用同一个会话。
aiohttp.ClientSession创建一次、全程复用,能省掉大量重复握手开销,这也是该包设计上就要求你传 session 进来的原因; - 善用并发而非串行。多个独立请求用
asyncio.gather打包发出,十几条请求和一条请求的耗时几乎一样; - 给重复查询加缓存。用
functools.lru_cache或本地文件缓存把已经拉过的数据存下来,避免反复打接口; - 先用过滤再取全量。大部分接口支持
options过滤,能精确到字段值,能过滤就先过滤,数据量小处理也快; - 看清字段类型。接口返回的数值几乎都是字符串(比如
"goals": "27"),做运算前记得先float()转换,这是新手最容易踩的坑。
新手避坑清单:这几点提前知道能少走弯路
- 请求频率别太狠。数据免费不等于可以无限薅,长时间高频请求容易触发对方防护,给自己留点间隔更稳妥;
- 日期参数有格式要求。
get_league_table的起止日期必须是YYYY-MM-DD,格式错了会直接抛异常; - 联赛名走小写别名。传
"epl"、"la_liga"这类短别名即可,包内部会自动映射成站点使用的写法; - 版本升级先看文档。不同小版本的接口细节可能有微调,升级后留意 docs/ 目录下的说明;
- 有问题先看测试。tests/test_understat.py 里几乎覆盖了所有接口的调用姿势,比看源码更快上手。
写在最后:数据到手之后,才是真正精彩的开始
到这里,一条"免费获取足球数据 → 本地分析 → 产出洞察"的完整链路已经打通了。不管是给球队算PPDA、给射手排xG效率榜,还是批量积累多赛季数据做预测模型,Understat异步Python包都能稳稳托住你的数据需求。
想继续深入的话,这几个文件按顺序读最顺:先看 docs/user/installation.rst 搞定环境,再翻 docs/classes/understat.rst 了解每个方法的参数与输出,最后打开核心源码 understat/understat.py 看看数据是怎么被解析、过滤、聚合的——源码不到五百行,读一遍你对"异步数据封装"的理解会提升一大截。🚀
数据只是理解足球的辅助工具,别让工具本身成为门槛。愿你在数据的加持下,看到球场里更多此前被忽略的细节。📊
【免费下载链接】understatAn asynchronous Python package for https://understat.com/.项目地址: https://gitcode.com/gh_mirrors/un/understat
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考