League Akari 战绩查询工具:LCU/SGP API 数据抓取与本地分析实战

发布时间:2026/9/26 4:21:47
League Akari 战绩查询工具:LCU/SGP API 数据抓取与本地分析实战 英雄联盟玩家对战绩查询这件事的需求早就不是看一眼KDA那么简单了。我打了七八年排位从最早手动截图记战绩到后来用各种第三方工具踩过的坑能写一本书——数据延迟、接口失效、界面卡顿、对局记录丢失每一个都让人抓狂。League Akari 这个项目是我近期研究得比较深入的一个它基于 LCU API 和 SGP API 做数据抓取配合本地数据分析与可视化算是目前开源方案里比较完整的一套。这篇文章不讲虚的我会把它的核心机制、接口调用逻辑、数据抓取流程、常见故障排查以及我自己在实际使用中总结出来的配置技巧全部拆开讲清楚。不管你是想直接用这个工具还是想基于它的思路自己搭一套战绩分析系统下面这些内容都能让你少走弯路。1. 先搞清楚 League Akari 到底在解决什么问题1.1 官方客户端的数据盲区英雄联盟官方客户端本身提供的历史战绩功能说实话能用但不好用。它只保留有限场次的记录筛选维度单一没法做跨赛季对比更别提导出数据做深度分析了。我试过用官方客户端查一个月的排位记录翻页翻到手酸想看看自己在一周内不同时间段的胜率变化根本做不到。这就是 League Akari 这类工具存在的根本原因。它通过 LCU APILeague Client Update API和 SGP APISpectator Game Platform API直接从客户端和服务器拉取原始数据然后在本地做结构化存储和分析。LCU API 是客户端本地暴露的接口响应快、数据全但只在客户端运行时可用SGP API 则是服务器端的接口能拿到更久远的历史记录和更详细的比赛数据但需要处理认证和请求频率的问题。两者结合基本能覆盖一个普通玩家对战绩数据的所有需求近期对局详情、历史赛季数据、英雄胜率统计、队友/对手表现分析、甚至是对局中的经济曲线和伤害分布。1.2 和市面上其他方案的核心差异市面上做战绩查询的工具不少但大多数要么是纯网页端依赖第三方服务器中转数据延迟高要么是简单的客户端插件功能单一只显示当前对局信息。League Akari 的差异化在于三点第一本地化数据处理。所有数据抓取后在本地做解析和存储不经过第三方服务器响应速度快隐私风险低。我用下来最直观的感受就是查一场刚结束的对局数据几乎是秒出不像某些网页工具要等好几秒。第二双接口冗余。LCU API 和 SGP API 互为补充一个挂了另一个还能顶上。实际使用中LCU API 在客户端更新后偶尔会变动SGP API 的认证 token 也有过期问题但两者同时失效的概率很低。第三可扩展的数据分析层。项目本身提供了基础的数据展示但更重要的是它把原始数据以结构化格式暴露出来你可以自己接 Python 做 pandas 分析或者接可视化库做图表。这一点对于想做深度数据分析的玩家来说非常关键。1.3 适合哪些人用这个工具不是所有人都需要。如果你只是偶尔打两把匹配官方客户端够用了。但如果你符合以下任意一种情况League Akari 值得花时间研究排位冲分玩家需要分析自己在不同英雄、不同时间段、不同队友配合下的胜率差异想做数据化复盘比如统计自己前15分钟的经济差和最终胜负的相关性对技术实现感兴趣想学习 LCU API 和 SGP API 的调用方式需要批量导出战绩数据做长期跟踪比如记录整个赛季的段位变化曲线注意使用任何第三方工具都需要遵守游戏官方的用户协议。本文讨论的技术方案仅供学习和研究用途实际使用前请自行确认合规性。2. LCU API 与 SGP API 的调用机制拆解2.1 LCU API 的本地认证流程LCU API 的本质是英雄联盟客户端在本地启动的一个 HTTPS 服务默认监听在127.0.0.1的某个动态端口上。要调用它你需要解决两个问题找到端口拿到认证凭证。端口和凭证都藏在客户端的 lockfile 里。这个文件通常位于游戏安装目录下文件名类似lockfile内容格式是进程名:PID:端口:密码:协议。我用 Python 读取的时候大概是这样处理的import base64 import requests def get_lcu_credentials(lockfile_path): with open(lockfile_path, r) as f: content f.read().strip() parts content.split(:) port parts[2] password parts[3] protocol parts[4] return port, password, protocol def build_auth_header(password): token base64.b64encode(friot:{password}.encode()).decode() return {Authorization: fBasic {token}}拿到端口和认证头之后就可以直接请求 LCU 的各种端点了。比如获取当前召唤师信息是GET /lol-summoner/v1/current-summoner获取近期对局列表是GET /lol-match-history/v1/products/lol/current-summoner/matches。这里有个坑我踩过lockfile 在客户端重启后会变化端口和密码都会更新。所以你的程序必须每次启动时重新读取不能缓存。另外客户端未运行时 lockfile 不存在程序要做好异常处理。2.2 SGP API 的认证与请求构造SGP API 比 LCU API 复杂一些因为它走的是服务器端需要处理 token 认证。通常的流程是先从 LCU API 获取当前的 access token然后用这个 token 去请求 SGP 的端点。SGP API 的 base URL 通常是https://sgp-{region}.pvp.net这样的格式region 根据你的服务器不同而变化。请求头里需要带上Authorization: Bearer {token}。我实测下来SGP API 的 token 有效期大概在几十分钟到一小时左右过期后会返回 401。处理方式有两种一是每次请求前检查 token 有效性失效则重新从 LCU 获取二是捕获 401 后自动刷新重试。第二种更稳妥因为 token 的实际过期时间并不完全固定。def request_sgp(endpoint, token, regioncn): url fhttps://sgp-{region}.pvp.net{endpoint} headers {Authorization: fBearer {token}} resp requests.get(url, headersheaders) if resp.status_code 401: token refresh_token_from_lcu() headers[Authorization] fBearer {token} resp requests.get(url, headersheaders) return resp.json()2.3 两个接口的数据覆盖范围对比不是所有数据两个接口都能拿到实际使用中需要根据需求选择。我整理了一个对比表数据类型LCU APISGP API推荐来源当前召唤师信息支持支持LCU更快近期对局列表20场内支持支持LCU历史对局详情超过20场不支持支持SGP对局内经济/伤害曲线部分支持支持SGP排位段位信息支持支持LCU英雄熟练度支持不支持LCU跨赛季数据不支持支持SGP这个表是我在实际调用中逐个验证过的。需要注意的是SGP API 的历史对局查询有分页限制每次最多返回20条需要循环请求。而且请求频率过高会触发限流建议每次请求之间加 200-500ms 的延迟。3. 数据抓取管道的搭建与优化3.1 从 lockfile 到结构化数据的完整链路整个数据抓取链路可以拆成四步凭证获取、接口调用、数据解析、本地存储。每一步都有需要注意的细节。凭证获取前面讲过了核心是实时读取 lockfile。接口调用阶段我建议用一个统一的请求封装把 LCU 和 SGP 的请求都走同一个入口方便统一处理超时、重试和日志。数据解析是最容易出问题的环节。LCU 和 SGP 返回的 JSON 结构在不同游戏版本之间可能会有微调比如字段名从gameId变成game_id或者嵌套层级变化。我的做法是写一层适配器把原始 JSON 映射到自定义的数据模型这样即使接口变了只需要改适配器不影响上层分析逻辑。本地存储我推荐用 SQLite。轻量、无需额外服务、Python 原生支持对于个人战绩数据量来说完全够用。建表的时候把对局 ID 设为主键避免重复插入。import sqlite3 def init_db(db_pathmatch_history.db): conn sqlite3.connect(db_path) conn.execute( CREATE TABLE IF NOT EXISTS matches ( game_id TEXT PRIMARY KEY, champion TEXT, kills INTEGER, deaths INTEGER, assists INTEGER, win INTEGER, duration INTEGER, timestamp INTEGER, raw_json TEXT ) ) conn.commit() return conn3.2 请求频率控制与反限流策略这是实战中最关键的部分。SGP API 对请求频率有隐性限制我测试下来如果每秒请求超过3-4次大概率会收到 429 状态码。一旦被限流可能需要等几分钟才能恢复。我的策略是第一控制并发数不要用多线程同时打多个请求串行加延迟反而更稳第二实现指数退避重试遇到 429 后等待时间逐次翻倍第三做本地缓存已经抓取过的对局数据不再重复请求。import time import random def fetch_with_backoff(url, headers, max_retries5): for attempt in range(max_retries): resp requests.get(url, headersheaders) if resp.status_code 200: return resp.json() elif resp.status_code 429: wait (2 ** attempt) random.uniform(0, 1) time.sleep(wait) else: resp.raise_for_status() raise Exception(Max retries exceeded)提示延迟时间不要设固定值加一点随机抖动可以避免多个请求同时恢复导致的二次限流。3.3 数据去重与增量更新增量更新是保证长期数据完整性的关键。我的做法是每次抓取前先查本地数据库里已有的最大时间戳然后只请求这个时间戳之后的对局。这样既避免了重复请求也减少了被限流的风险。去重逻辑放在插入阶段用INSERT OR IGNORE配合主键约束即使有重复数据也不会报错。另外我建议每次抓取后记录一个日志包括抓取时间、新增对局数、失败请求数方便后续排查问题。4. 数据分析层的实战应用4.1 用 pandas 做胜率与英雄池分析数据存到 SQLite 之后用 pandas 读取就是一行代码的事。我平时最常做的分析有这么几类第一分英雄胜率统计。按 champion 分组计算 win 的均值和场次快速找出自己的本命英雄和坑货英雄。import pandas as pd import sqlite3 conn sqlite3.connect(match_history.db) df pd.read_sql(SELECT * FROM matches, conn) champion_stats df.groupby(champion).agg( games(game_id, count), win_rate(win, mean), avg_kda(kills, lambda x: x.mean()) ).sort_values(games, ascendingFalse) print(champion_stats[champion_stats[games] 5])第二时间段胜率分析。把 timestamp 转成小时看看自己在哪个时间段打得最好。我发现自己晚上10点后的胜率明显低于下午这可能和疲劳度有关。第三KDA 与胜负的相关性。用 pandas 的 corr 函数算一下 kills、deaths、assists 和 win 的相关系数能看出哪个指标对胜负影响最大。我算下来 deaths 的负相关性最强少死比多杀更重要。4.2 对局经济曲线的可视化SGP API 能拿到对局内每分钟的经济数据这部分数据用 matplotlib 画出来非常直观。我通常会画三条线自己的经济、对位对手的经济、团队平均经济。三条线一对比就能看出对局是在哪个时间点崩的。import matplotlib.pyplot as plt def plot_gold_curve(my_gold, enemy_gold, team_avg): minutes range(1, len(my_gold) 1) plt.figure(figsize(12, 6)) plt.plot(minutes, my_gold, labelMy Gold, colorgold) plt.plot(minutes, enemy_gold, labelEnemy Gold, colorred) plt.plot(minutes, team_avg, labelTeam Avg, colorblue, linestyle--) plt.xlabel(Minutes) plt.ylabel(Gold) plt.legend() plt.title(Gold Curve Comparison) plt.grid(True, alpha0.3) plt.show()这个图我复盘的时候用得最多。有一次我发现自己前10分钟经济领先但15分钟后被反超回去看录像发现是中期团战决策出了问题不是对线问题。这种洞察光看战绩列表是看不出来的。4.3 队友与对手的表现画像如果你经常和固定队友开黑可以做一个队友表现画像。把每场对局中队友的 KDA、伤害占比、参团率提取出来按队友 ID 分组统计。这样能看出谁是你的稳定 carry 点谁需要你多帮。对手画像也有用。如果你发现某个英雄或某个 ID 的对手经常出现在你的对局里可以针对性研究他们的打法习惯。不过这个功能需要 SGP API 返回足够详细的对手数据部分服务器可能不支持。5. 常见故障与排查链路5.1 客户端更新后接口失效这是最常见的问题。每次游戏大版本更新LCU API 的端点路径或返回结构都可能变化。表现是程序报 404 或解析失败。排查步骤先用浏览器或 curl 直接请求 LCU 的端点看返回什么。如果 404说明路径变了需要去查最新的 API 文档或社区维护的端点列表。如果返回 200 但结构变了那就是解析层的问题需要更新适配器。我的经验是大版本更新后不要急着改代码先等一两天社区通常会有人整理出变更点。自己硬啃效率低。5.2 SGP token 过期导致的 401前面提过SGP token 有效期不固定。如果你发现程序运行一段时间后开始大量报 401基本就是 token 过期了。解决方案是加一个自动刷新机制。我的做法是在请求封装层捕获 401然后重新从 LCU 获取 token更新请求头后重试一次。如果重试还是 401说明 LCU 那边也有问题需要检查客户端是否还在运行。5.3 数据抓取不完整的排查思路有时候你会发现抓取的对局数量比实际打的少。可能的原因有三个一是分页没处理完SGP 每次只返回20条需要循环请求直到没有更多数据二是时间戳过滤条件写错了把部分对局排除了三是请求被限流部分请求失败了但没重试。排查的时候先看日志里的失败请求数再看数据库里的对局时间分布有没有断档。如果发现某段时间的数据缺失大概率是限流导致的。故障现象可能原因排查方法解决方案404 错误LCU 端点变更curl 直接请求验证更新端点路径401 错误SGP token 过期检查 token 时间戳自动刷新重试429 错误请求频率过高查看请求间隔加延迟指数退避数据缺失分页未处理完检查请求日志循环请求直到空解析失败JSON 结构变更打印原始返回更新适配器5.4 性能优化从分钟级到秒级最初我的抓取脚本跑一次要几分钟后来做了几个优化降到几秒。关键优化点一是用连接池复用 HTTP 连接避免每次请求都重新握手二是把数据库写入改成批量提交不要每条都 commit三是把解析逻辑里耗时的部分比如正则匹配换成更高效的字符串操作。session requests.Session() session.headers.update(auth_header) # 批量插入 def batch_insert(conn, matches): conn.executemany( INSERT OR IGNORE INTO matches VALUES (?,?,?,?,?,?,?,?,?), matches ) conn.commit()6. 我踩过的坑和实际使用建议6.1 lockfile 路径在不同系统上的差异Windows 上 lockfile 通常在游戏安装目录下但如果你用的是 WeGame 启动路径可能不一样。Mac 上路径又不同。我建议不要硬编码路径而是提供一个配置项让用户自己填或者做自动搜索。另外lockfile 的读取权限在某些系统上可能受限需要确保程序有足够的权限。我遇到过在 Windows 上因为权限问题读不到 lockfile 的情况后来把程序改成以普通用户权限运行就好了。6.2 不要频繁请求 SGP 的历史接口SGP 的历史对局接口是限流最严的。我一开始为了快速拉取整个赛季的数据写了个循环疯狂请求结果直接被限流了半小时。后来改成每次只拉最近20场增量更新就再也没被限流过。提示如果你需要一次性拉取大量历史数据建议分多次运行每次间隔几小时不要试图一次拉完。6.3 数据备份的重要性SQLite 数据库文件虽然稳定但也不是不会坏。我有一次因为程序异常退出数据库文件损坏了整个赛季的数据全没了。从那以后我养成了定期备份的习惯每次抓取后把数据库文件复制一份到另一个目录。备份策略很简单按日期命名保留最近7天的备份。这样即使出问题最多损失一天的数据。6.4 关于数据可视化的选型建议如果你只是自己看matplotlib 足够了。如果想做交互式的图表可以考虑 plotly 或 pyecharts。我用 plotly 做过一个段位变化的时间线图鼠标悬停能看到每场的具体数据复盘的时候很方便。但不要过度追求可视化效果。数据分析的核心是洞察不是图表好看。我见过有人花大量时间调图表样式结果数据本身没分析出什么东西。先把分析逻辑跑通再考虑美化。6.5 长期维护的注意事项游戏版本会持续更新接口也会持续变化。如果你打算长期用这个工具建议做好两件事一是把接口调用层和分析层解耦接口变了只改调用层二是关注社区动态LCU API 和 SGP API 的变更通常会在相关社区里有人讨论。我自己是每隔一两个月检查一次看看有没有接口变更导致的数据异常。平时用的时候如果发现数据不对先看日志再对照上面的排查表基本能定位到问题。最后分享一个我常用的调试技巧在请求封装层加一个开关打开后把所有请求的 URL、状态码、返回体大小打印出来。这样一旦出问题看一眼日志就知道是哪个环节挂了。这个习惯帮我省了很多排查时间。