ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

用Python做BP数据分析:从NIP对阵WBG复盘谈起

2026/8/31 17:38:48 拓冰建站 浏览量
用Python做BP数据分析:从NIP对阵WBG复盘谈起 最近 LPL 常规赛里 NIP 2:1 拿下 WBG赛后 NIP 打野 guwon 的采访很有意思一边说“本来以为会轻松拿下”一边承认“WBG 的 BP 有备而来”。这两句话放在一起其实点出了一个非常值得用技术手段去验证的问题一支队伍的 BP 到底是不是“有备而来”是赛前功课做足了还是临场手感好普通观众看比赛记住的往往是选手操作和团战结果。但如果你要做战队分析、内容复盘或者想用小规模数据验证赛前判断纯肉眼不够用。这篇文章就用 NIP 对 WBG 这场系列赛做引子讲一套可以复用的 BP 数据分析工作流数据从哪来、怎么清洗、怎么算优先级、怎么批量出图。整个方案用 Python 就能跑不需要服务器普通笔记本足够。1. 核心能力速览能力项说明项目定位赛事 BP 数据分析与复盘工具不是爬虫脚本单点而是完整的分析工作流主要功能比赛数据抓取、BP 字段解析、Ban/Pick 优先级计算、红蓝方胜率对比、批量赛果归档输入数据公开赛事网站导出的比赛记录或手工整理的赛后 BP 数据表技术栈Python 3.8、pandas、requests、BeautifulSoup、matplotlib、openpyxl运行环境Windows / macOS / Linux 均可CPU 即可运行显存需求无纯数据分析任务不涉及 GPU启动方式Jupyter Notebook 分步执行或命令行脚本一键生成报告接口 API不依赖第三方付费 API对公网赛事页面做低频请求即可也可以完全离线分析批量任务支持整轮比赛批量导入输出统一格式的 BP 分析表适合场景战队赛后复盘、自媒体内容制作、赛前 BP 预判验证、电竞数据教学这套东西的价值不在于“预测比赛输赢”而是把“BP 有没有备而来”这种主观判断尽量转成可量化的指标英雄优先级、前三手选择倾向、红蓝方胜率差、关键 Counter 关系。2. 适用场景与使用边界先说清楚这篇文章是数据分析方法教学不是菠菜工具也不保证预测准确率。它适合三类人。第一类是电竞自媒体作者。赛后想快速产出“第三局 BP 解析”内容靠手工截屏和口述太慢。用脚本把这一轮的 BP 数据拉下来生成热力图和统计表直接作为视频或图文素材。第二类是战队分析师或业余战队教练。虽然 LPL 正赛队伍有自己的专业数据系统但对业余赛事、高校联赛、平台杯赛来说自己维护一套轻量 BP 分析流程完全够用。第三类是 LOL 数据爱好者。想验证“蓝色方前两手到底抢什么”“某个版本热门英雄的优先级是否被高估”这类问题这套工作流能给你一个可复现的答案。使用边界也要明确赛事数据网站的页面结构和接口可能调整脚本需要定期维护抓取频率不要过高避免给目标站点造成压力如果涉及选手个人信息、肖像、赛事版权素材只用于个人学习研究不要做商业传播。涉及任何版权敏感内容时遵守平台规则和赛事方授权要求。另外要提醒一句比赛复盘存在明显的“事后归因”偏差。BP 结果好未必是策略好也可能是选手英雄池恰好克制。数据分析只能提供参考维度不能替代教练组的赛训判断。3. 环境准备与前置条件先说硬件这类任务真的不挑设备。只要电脑能装 Python4G 内存都够跑。最吃资源的是批量出图环节几百场比赛的 BP 数据做热力图内存占用也不会超过 1G主要是 CPU 计算。没有 GPU 要求没有显存要求也没有 50 系显卡适配问题。软件层面建议准备软件版本建议用途Python3.8 及以上主开发语言pandas1.3 及以上数据处理requests2.x抓取公开页面数据BeautifulSoup44.x解析 HTMLmatplotlib3.5 及以上生成可视化图表openpyxl3.x导出 Excel 报告Jupyter Notebook可选分步调试更方便安装命令pip install pandas requests beautifulsoup4 matplotlib openpyxl jupyter如果是在国内网络环境建议配置镜像源避免下载超时。pip install -i https://pypi.tuna.tsinghua.edu.cn/simple pandas requests beautifulsoup4 matplotlib openpyxl jupyter动手之前先建目录把后续要用的文件归类好lol_bp_analysis/ ├── data/ │ ├── raw/ # 原始数据 │ └── processed/ # 清洗后数据 ├── output/ │ └── figures/ # 生成的图表 ├── scripts/ │ ├── fetch.py # 数据获取 │ ├── clean.py # 数据清洗 │ └── report.py # 生成报告 └── notebook/ └── bp_analysis.ipynb4. 赛事数据获取BP 分析的第一步是拿到比赛原始数据。有两条路一条是从公开赛事数据站点抓取另一条是手工整理。这里重点讲公开数据源的方法但注意站点结构随时可能变化下面的代码是通用思路不要把选择器写死当成万能方案。抓取数据时需要用到的核心字段包括比赛 ID、系列赛双方战队、红蓝方归属、每一局的 Ban 列表、Pick 列表、比赛结果。这里用 requests 加 BeautifulSoup 做一次低频请求示例。目标站点的 HTML 结构需要你按实际情况调整。import requests from bs4 import BeautifulSoup def fetch_bp_page(url): headers { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0 Safari/537.36 } try: resp requests.get(url, headersheaders, timeout10) resp.raise_for_status() # 部分站点是中文或 UTF-8 编码这里做统一处理 resp.encoding resp.apparent_encoding return resp.text except requests.exceptions.RequestException as e: print(f请求失败: {e}) return None def parse_bp_table(html): soup BeautifulSoup(html, html.parser) # 注意这里的选择器是示例必须按目标站点实际结构调整 table soup.find(table, class_bp-table) if not table: print(未找到 BP 表格请检查站点结构) return [] rows [] for tr in table.find_all(tr): cells [td.get_text(stripTrue) for td in tr.find_all([td, th])] if cells: rows.append(cells) return rows if __name__ __main__: # 示例 URL实际请替换为可访问的公开赛事页面 url https://example.com/match/12345 html fetch_bp_page(url) if html: bp_data parse_bp_table(html) for row in bp_data: print(row)这里强调一个原则不要对同一个站点做高频并发请求也不要拿这种脚本去爬全量历史数据然后对外分发。个人学习用途低频访问即可。如果站点有 API 接口优先用接口比解析 HTML 稳定得多。如果找不到合适的公开数据源手工整理也不丢人。做一个 CSV每一行记录一场比赛的 BP 细节字段如下match_id,team,side,game_num,ban1,ban2,ban3,ban4,ban5,pick1,pick2,pick3,pick4,pick5,result M001,NIP,blue,1,Azir,Elise,Jayce,Rakan,Poppy,Vi,Orianna,Lucian,Nautilus,Karma,WIN M001,WBG,red,1,Pikachu,Rumble,Kaisa,etc,...手工整理的工作量很大但数据质量可控适合单场重点复盘。如果只是做 NIP 对 WBG 这一轮比赛的 BP 分析手工整理三局数据也只需要半小时左右。5. BP 数据字段与清洗逻辑拿到原始数据后不能直接分析。需要先明确字段含义。字段含义示例match_id比赛唯一标识NIP_WBG_2025SPRINGteam战队名NIPside阵营blue / redgame_num局数1、2、3ban1 - ban5五个禁选英雄Azir、Elise ...pick1 - pick5五个选用英雄Vi、Orianna ...result本局结果WIN / LOSS数据清洗主要做三件事。英雄名称标准化。同一个英雄在不同数据源可能有不同写法比如“悟空”和“Wukong”、“皇子”和“Jarvan IV”。必须先统一成英文名或者中文官方名否则统计会出现重复项。import pandas as pd def normalize_hero_name(name): hero_map { 悟空: Wukong, 猴子: Wukong, 皇子: Jarvan IV, 嘉文四世: Jarvan IV, 瑞尔: Rell, } return hero_map.get(name, name) def clean_bp_data(df): ban_cols [ban1, ban2, ban3, ban4, ban5] pick_cols [pick1, pick2, pick3, pick4, pick5] for col in ban_cols pick_cols: df[col] df[col].apply(normalize_hero_name) return df补全缺失值。有些比赛数据源可能只记录前三个 Ban因为部分赛制是前两手 Ban 再选。要根据赛制补成标准六 Ban 结构或者统一只分析前三个 Ban。新增阵营与胜负标记。分析红蓝方胜率前必须确保每一行都有 side 和 result 字段。统计脚本里直接对缺失字段报错不要静默跳过否则最终结果可能被污染。def validate_bp_data(df): required [match_id, team, side, game_num, result] for col in required: if col not in df.columns: raise ValueError(f缺少必需字段: {col}) assert set(df[side].unique()).issubset({blue, red}), 阵营字段非法 assert set(df[result].unique()).issubset({WIN, LOSS}), 结果字段非法 return df清洗完成后把处理好的数据另存一份。def save_processed(df, pathdata/processed/bp_clean.csv): df.to_csv(path, indexFalse, encodingutf-8-sig) print(f已保存清洗后数据: {path})6. BP 分析维度数据准备好正式进入“验证 BP 是否有备而来”的分析环节。这里的核心思路是计算英雄优先级指数。一个英雄的 BP 优先级不能只看 Ban 率或 Pick 率要把两者合成一个指数。常用公式优先级指数 Ban率 * 0.6 Pick率 * 0.4权重可以按版本调整。如果某个版本禁位压力很大Ban 率权重应该更高如果是强调抢英雄的版本Pick 率权重应该更高。从 NIP 和 WBG 这场比赛的材料看guwon 赛后提到“BP 有备而来”意思是 WBG 可能在特定英雄上做了针对性设计。用优先级指数就能发现WBG 在前三手是否高频 Ban 掉 NIP 打野位强势英雄或者是否自己抢下某一手关键英雄。代码实现def calculate_priority(df): ban_cols [ban1, ban2, ban3, ban4, ban5] pick_cols [pick1, pick2, pick3, pick4, pick5] hero_stats {} all_heroes set() for col in ban_cols pick_cols: all_heroes.update(df[col].dropna().unique()) total_games df[game_num].nunique() for hero in all_heroes: ban_count 0 pick_count 0 win_count 0 for _, row in df.iterrows(): if hero in row[ban_cols].values: ban_count 1 if hero in row[pick_cols].values: pick_count 1 result row[result] if hero in row[pick_cols].values and result WIN: win_count 1 ban_rate ban_count / total_games pick_rate pick_count / total_games win_rate pick_count 0 and win_count / pick_count or 0 priority ban_rate * 0.6 pick_rate * 0.4 hero_stats[hero] { ban_count: ban_count, pick_count: pick_count, ban_rate: ban_rate, pick_rate: pick_rate, win_rate: win_rate, priority: priority } return pd.DataFrame(hero_stats).T.sort_values(priority, ascendingFalse)除了英雄优先级还可以拆四个维度看 BP 策略。红蓝方胜率对比。LPL 很多版本里蓝色方胜率会更高因为先手抢英雄有信息优势。统计红色方和蓝色方在样本比赛里的胜率能判断某个队伍是否特别擅长红色方后手 Counter。前三手选择倾向。前三手通常决定了阵容核心思路是保上中野节奏还是打下路对线还是先拿野区强势英雄。统计每支队伍前三手最常出的英雄组合能看出战术体系。关键局 BP 变化。比如第二局输了第三局 BP 做了哪些调整。重点看 Ban 位变化和 Pick 顺序调整。guwon 说“本来以为会轻松拿下”说明第一局可能 NIP 比较顺利而“WBG 的 BP 有备而来”说明后续局 WBG 可能调整了 BP 策略抢到了一些关键英雄。Counter 关系验证。两个位置的英雄存在明显相克关系。用样本数据统计“当 A 英雄被 Pick 后对方下一手是否立刻 Pick B 英雄”的次数可以判断对手是否在临场做针对性反制。7. 可视化复盘数据算出来之后出图才是内容生产者最关心的环节。这里给三张图BP 优先级 Top10、红蓝方胜率对比、战队 Ban/Pick 热力图。BP 优先级条形图import matplotlib.pyplot as plt import pandas as pd plt.rcParams[font.sans-serif] [SimHei, Microsoft YaHei] plt.rcParams[axes.unicode_minus] False def plot_priority(df_priority, top_n10): top df_priority.head(top_n) fig, ax plt.subplots(figsize(10, 6)) ax.barh(top.index, top[priority], color#1E90FF) ax.set_xlabel(优先级指数) ax.set_title(BP 优先级 Top10) ax.invert_yaxis() plt.tight_layout() plt.savefig(output/figures/priority_top10.png, dpi150) plt.show()红蓝方胜率堆叠图def plot_side_winrate(df): side_stats df.groupby(side)[result].apply( lambda x: (x WIN).sum() / len(x) ).reset_index() side_stats.columns [side, winrate] fig, ax plt.subplots(figsize(6, 6)) colors [#D32F2F, #1976D2] ax.pie( side_stats[winrate], labelsside_stats[side], autopct%.1f%%, colorscolors ) ax.set_title(红蓝方胜率占比) plt.savefig(output/figures/side_winrate.png, dpi150) plt.show()战队 BP 热力图。这张图做出来最有视频封面感。横轴是英雄纵轴是战队颜色深浅表示该战队对这个英雄的 Ban/Pick 偏好import numpy as np def plot_team_hero_heatmap(df, teams[NIP, WBG], top_heroesNone): if top_heroes is None: top_heroes [Azir, Elise, Vi, Orianna, Lucian, Rakan, Poppy] matrix np.zeros((len(teams), len(top_heroes))) for i, team in enumerate(teams): team_df df[df[team] team] for j, hero in enumerate(top_heroes): count 0 for _, row in team_df.iterrows(): if hero in row[[ban1, ban2, ban3, ban4, ban5]].values: count 1 if hero in row[[pick1, pick2, pick3, pick4, pick5]].values: count 1 matrix[i, j] count fig, ax plt.subplots(figsize(12, 4)) im ax.imshow(matrix, cmapYlOrRd, aspectauto) ax.set_xticks(range(len(top_heroes))) ax.set_xticklabels(top_heroes, rotation45, haright) ax.set_yticks(range(len(teams))) ax.set_yticklabels(teams) for i in range(len(teams)): for j in range(len(top_heroes)): ax.text(j, i, int(matrix[i, j]), hacenter, vacenter, colorblack) ax.set_title(战队 BP 关注度热力图) plt.colorbar(im) plt.tight_layout() plt.savefig(output/figures/team_bp_heatmap.png, dpi150) plt.show()出图的时候注意中文字体设置。Linux 服务器上通常没有 SimHei 和 Microsoft YaHei需要装中文字体否则图里中文会变成方框。展示图片时优先输出 SVG 或高 DPI PNG方便后续剪视频时放大裁切。8. 批量分析与报告导出单场分析只是热身。做赛事复盘往往要拉整个常规赛的数据或者连续分析某支队伍最近五场。批量任务的设计思路是读入多场比赛数据循环计算汇总输出。import os import pandas as pd def batch_process(raw_dirdata/raw, output_pathdata/processed/all_matches.csv): all_files [f for f in os.listdir(raw_dir) if f.endswith(.csv)] df_list [] for f in all_files: file_path os.path.join(raw_dir, f) df pd.read_csv(file_path) df_list.append(df) if not df_list: print(raw 目录下没有 CSV 文件) return None combined pd.concat(df_list, ignore_indexTrue) combined.to_csv(output_path, indexFalse, encodingutf-8-sig) print(f已合并 {len(all_files)} 个文件共 {len(combined)} 行) return combined报告导出用 Excel 多 Sheet 结构第一个 Sheet 放比赛总览第二个 Sheet 放英雄优先级排名第三个 Sheet 放红蓝方胜率第四个 Sheet 放逐局 BP 明细。这样给战队看或者自己复盘时一个文件就够。def export_excel(df, priority_df, filenameoutput/bp_report.xlsx): with pd.ExcelWriter(filename, engineopenpyxl) as writer: df.to_excel(writer, sheet_name比赛总览, indexFalse) priority_df.to_excel(writer, sheet_name英雄优先级) side_stats df.groupby(side)[result].apply( lambda x: (x WIN).sum() ).reset_index() side_stats.to_excel(writer, sheet_name红蓝方胜率, indexFalse) print(f报告已导出: {filename})批量任务要特别注意失败重试和日志记录。如果某场比赛的原始数据格式有问题不能让它卡住整批任务要记录到失败列表最后统一处理。fail_log [] for file in all_files: try: clean_bp_data(pd.read_csv(file)) except Exception as e: fail_log.append({file: file, error: str(e)}) if fail_log: pd.DataFrame(fail_log).to_csv(output/fail_log.csv, indexFalse)接口调用方面这套工作流本身不涉及对外 API但如果要接公众号或者微博自动发布脚本批量导出的 CSV 和 Excel 文件可以直接被上层调度系统读取。简单来说它输出的数据天然就是机器可读的。9. 资源占用与性能观察虽然这个项目不需要 GPU但也需要关注性能否则处理大样本时会慢。显存占用零。整个流程依赖 CPU 和内存所以 50 系显卡、核显、老显卡都不影响。内存占用单场 BP 数据量很小一场三局比赛也就几十行。瓶颈通常出在“把所有历史比赛读进内存”这一步。如果分析三个赛季的数据原始 CSV 可能有几万行pandas 读入后内存占用通常在 500M 到 1G 之间普通笔记本可以接受。计算耗时英雄优先级计算里用了两层循环如果样本达到几万行计算时间会明显上升。建议引入 numpy 或 pandas 的向量化操作来重写统计逻辑。def calculate_priority_vectorized(df): hero_list [] for col in [ban1, ban2, ban3, ban4, ban5]: hero_list.append(df[col]) ban_stack pd.concat(hero_list, axis1) all_heroes pd.unique(ban_stack.values.ravel()) hero_rows [] for hero in all_heroes: ban_count (ban_stack hero).sum().sum() hero_rows.append({hero: hero, ban_count: ban_count}) return pd.DataFrame(hero_rows)出图性能不是问题matplotlib 单张图渲染时间在 1 秒以内批量出几十张图也只是几秒的事。如果发现程序越来越慢优先检查是否在循环里反复读 CSV或者反复创建 DataFrame。正确做法是一次性读入全部数据分析完成后统一释放。还可以增加进程内缓存但这类项目规模不需要上 Redis简单的变量缓存就够。10. 常见问题与排查方法问题现象可能原因排查方式解决方案抓取页面返回 403User-Agent 被识别或被站点封禁查看响应状态码和页面内容更换 User-Agent降低请求频率等待几分钟再试解析不到 BP 表格站点 HTML 结构调整选择器失效在浏览器里查看元素确认 class 名更新 BeautifulSoup 选择器优先改用官方 API中文显示为方框系统缺少中文字体运行 fc-list 查看已安装字体安装 Noto Sans CJK 或 wqy-microhei英雄名称统计重复同一英雄名称不统一打印 pd.unique() 查看全部英雄名完善 normalize_hero_name 映射表计算优先级时报 KeyError脏数据里有空值或非法字段检查原始 CSV在 validate_bp_data 里提前拦截空值批量处理中单场比赛报错某文件格式不规范查看 fail_log单独处理失败文件修复后重新合并Excel 打开后中文乱码CSV 编码问题用文本编辑器查看文件编码保存时使用 utf-8-sig 编码多次运行结果不一致数据源页面内容更新或抓取不全对比两次抓取的行数确保在数据源数据稳定后再抓取必要时缓存原始页面图片模糊DPI 设置过低打开图片属性查看分辨率保存时增大 dpi 参数建议 300最容易踩的坑还是数据源变动的坑。今天能跑的解析代码下周可能失效。解决思路是把原始 HTML 存到本地做缓存解析代码改动后再重新跑一遍历史数据保证分析结果可复现。11. 最佳实践与使用建议先说这套流程在当前比赛版本下怎么用最舒服。第一次跑通不要贪多先选单独一个系列赛比如 NIP 对 WBG 三局手工整理或者抓取下来完成“清洗 — 优先级计算 — 出图”全流程确认数据质量没问题再扩大到整个赛区。数据管理上建议统一文件命名规则例如LPL_2025_Spring_W1D1.csv月份和轮次写清楚。原始数据、清洗后数据、图表、Excel 报告分目录存放避免三个月之后找不到原始文件。批量分析时必须加日志。每一场比赛在合并前记录文件名、行数、成功状态。宁可多写一条日志也不要等全部跑完才发现某一步有数据污染。输出图表时应该统一风格。战队颜色建议直接用官方配色NIP 主色偏黑金WBG 主色偏粉白。颜色统一后不同的复盘视频和图文作品放在一起更专业。 matplotlib 样式可以在一个配置文件里统一定义。接口这块如果后续要接入自动发布平台建议把分析结果转换成 JSON 格式而不是直接处理 CSV。JSON 便于程序间传输字段语义也更清楚。def export_json_summary(priority_df, output_pathoutput/bp_summary.json): summary priority_df.head(10).to_dict(orientrecords) import json with open(output_path, w, encodingutf-8) as f: json.dump(summary, f, ensure_asciiFalse, indent2) print(fJSON 摘要已导出: {output_path})关于合规再强调一次赛事数据和选手数据只用于个人学习、非商业内容制作和战队内部复盘。如果要对外发布包含选手肖像、比赛录像截图或赛事独家数据的视频和文章需要确认是否符合赛事方和平台的使用规范。涉及比赛录像的二次创作也要注意版权边界。12. 总结与下一步从 guwon 那句“WBG 的 BP 有备而来”入手这篇文章把 BP 复盘这件事变成了一个可执行的数据分析流程公开数据抓取、字段清洗、优先级指数计算、批量出图、Excel 报表导出。整套方法不挑机器不依赖 GPU普通笔记本跑起来很轻松。最值得先验证的功能是英雄优先级指数和红蓝方胜率对比。前者能直接把“BP 有备而来”这种话转化成英雄数据排名后者能看出队伍在选边上的真实优劣势。最容易踩的坑是数据源结构和英雄名称不统一提前做好清洗规则能省很多时间。下一步可以做的事很多给这个流程加一个置信区间分析用历史数据判断某个英雄的高优先级是短期版本红利还是长期强势也可以接一个简单的网页爬虫调度每周自动拉取赛果生成周报还可以把输出图表整合到视频剪辑流程里直接作为 B 站或抖音的内容素材。如果你手里正好有 NIP 或 WBG 最近几场的赛后数据建议直接打开 Jupyter Notebook 跑一遍上面的脚本。数据量不用大三场比赛就能看出这套流程和纯肉眼复盘的区别。