
摘要:
炉石传说盒子不仅是记牌工具,更在本地悄悄沉淀了大量结构化对局数据。本文从开发者视角出发,定位盒子的本地存储目录,用 Python 解析其中的 JSON 战绩文件、配置文件与日志记录,并对结构化后的数据做清洗、聚合与可视化,最终输出一份完全自定义的个人胜率分析看板。适合想用代码读懂自己游戏数据的玩家。
炉石传说盒子内置了战绩统计功能:总胜率、各职业胜率、近期对局列表,一目了然。但用过一段时间就会发现三个问题——
第一,数据不互通。盒子里的对局记录无法导出,想和天梯插件(如 HDT)的数据做交叉分析,或者把数据扔进 Excel 做透视表,都无从下手。
第二,维度过粗。官方面板只给你「过去 N 场的胜率」,但如果你想知道「周五晚上的快攻卡组胜率是不是比周一低」「某张新卡替换前后胜率变化多少」,内置统计就回答不了。
第三,历史数据有边界。盒子滚动保留的记录数量有限,有些对局会被覆盖,长期趋势分析天然受限。
解决办法很直接:找到盒子的本地数据目录,用 Python 把这些文件读出来,清洗、入库,然后想怎么算就怎么算。
炉石传说盒子(网易版)的核心数据存储在 Windows 用户目录下的 AppData 路径中:
数据目录:
%APPDATA%\HSAng\
在资源管理器地址栏直接输入上述路径即可打开。该目录下值得关注的文件和子目录:
hsa.config:盒子的主配置文件,纯文本键值对格式,记录游戏路径、窗口设置等参数。*.json:卡组配置与用户数据,JSON 格式存储,盒子的许多功能模块依赖这里的结构化数据。Logs\ 目录:盒子自身运行日志,以及被同步过来的游戏客户端通讯日志,记录每一场对局的详细事件流。此外,如果你同时使用国际版的 Hearthstone Deck Tracker(HDT),它的数据在 %APPDATA%\HearthstoneDeckTracker\ 下,包含 History.xml(历史记录)、DeckStats.xml(卡组统计)等 XML 文件,也是不错的数据源。
下面按文件类型分别处理,代码均在 Python 3.9+ 环境下可运行。
hsa.config)配置文件通常是键值对形式,可用标准库直接解析:
import os
def parse_hsa_config():
config_path = os.path.join(os.getenv('APPDATA'), 'HSAng', 'hsa.config')
config = {}
try:
with open(config_path, 'r', encoding='utf-8') as f:
for line in f:
line = line.strip()
if line and not line.startswith('#') and '=' in line:
key, _, value = line.partition('=')
config[key.strip()] = value.strip()
except FileNotFoundError:
print(f"配置文件未找到: {config_path}")
return config
cfg = parse_hsa_config()
print(cfg)盒子本地缓存的卡组信息和对局记录很可能以 JSON 格式落盘。遍历目录找出所有 JSON 文件并逐个解析:
import json
import glob
def load_all_json(data_dir):
records = []
for filepath in glob.glob(os.path.join(data_dir, '*.json')):
try:
with open(filepath, 'r', encoding='utf-8') as f:
data = json.load(f)
if isinstance(data, list):
records.extend(data)
elif isinstance(data, dict):
records.append(data)
except (json.JSONDecodeError, UnicodeDecodeError):
print(f"跳过无法解析的文件: {filepath}")
return records
base_dir = os.path.join(os.getenv('APPDATA'), 'HSAng')
all_records = load_all_json(base_dir)
print(f"共加载 {len(all_records)} 条记录")拿到数据后,可以用 Pandas 快速做结构化:
import pandas as pd
df = pd.DataFrame(all_records)
# 根据实际字段做筛选与重命名,以下为常见字段示例
if not df.empty:
df = df.rename(columns={
'hero': '使用职业',
'opponent': '对手职业',
'result': '胜负',
'timestamp': '对局时间'
})
print(df[['对局时间', '使用职业', '对手职业', '胜负']].head(10))POWER.log)游戏客户端的 POWER.log 记录了每场对局完整的卡牌流转事件(ZONE_CHANGE、PLAY_CARD 等),盒子正是通过监听这类日志来实现实时记牌。我们可以用正则表达式提取关键事件:
import re
from datetime import datetime
def parse_power_log(log_path):
events = []
zone_change_pattern = re.compile(
r'(?P<timestamp>\d{2}:\d{2}:\d{2}\.\d+)\s+'
r'ZoneChangeList\.ProcessChanges\(\)\s*-\s*'
r'id=(?P<card_id>\d+).*'
r'from\s*(?P<from_zone>\w+)\s*->\s*(?P<to_zone>\w+)'
)
try:
with open(log_path, 'r', encoding='utf-8', errors='ignore') as f:
for line in f:
m = zone_change_pattern.search(line)
if m:
events.append(m.groupdict())
except FileNotFoundError:
pass
return events
log_dir = os.path.join(os.getenv('LOCALAPPDATA'), 'Blizzard', 'Hearthstone', 'Logs')
power_log = os.path.join(log_dir, 'Power.log')
zone_events = parse_power_log(power_log)
print(f"解析到 {len(zone_events)} 条卡牌转移事件")注意:日志解析依赖游戏客户端启用了详细日志输出。如果文件夹中找不到
Power.log,需在战网客户端设置中追加启动参数-log以开启日志记录。
原始数据拿到后,距离可分析还有一步:清洗。主要做三件事——
datetime 对象。df['对局时间'] = pd.to_datetime(df['对局时间'], unit='ms', errors='coerce')
df = df.dropna(subset=['胜负'])
df = df[df['胜负'].isin(['win', 'lose'])]
profession_map = {
1: '德鲁伊', 2: '猎人', 3: '法师', 4: '圣骑士',
5: '牧师', 6: '潜行者', 7: '萨满', 8: '术士',
9: '战士', 10: '恶魔猎手', 11: '死亡骑士'
}
df['使用职业名'] = df['使用职业'].map(profession_map)
df['对手职业名'] = df['对手职业'].map(profession_map)数据入库后,用 Matplotlib 生成几张关键图表。
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif'] = ['SimHei'] # 中文显示
stats = df.groupby('使用职业名')['胜负'].apply(
lambda x: (x == 'win').sum() / len(x)
).sort_values(ascending=False)
plt.figure(figsize=(10, 5))
stats.plot(kind='bar', color='#4CAF50')
plt.title('各职业胜率对比')
plt.ylabel('胜率')
plt.xticks(rotation=45)
plt.tight_layout()
plt.savefig('winrate_by_class.png', dpi=150)df['日期'] = df['对局时间'].dt.date
pivot = df.pivot_table(
values='胜负', index='使用职业名', columns='日期',
aggfunc=lambda x: (x == 'win').sum() / len(x)
)
plt.figure(figsize=(12, 6))
plt.imshow(pivot, cmap='RdYlGn', aspect='auto')
plt.colorbar(label='胜率')
plt.xticks(range(len(pivot.columns)), pivot.columns, rotation=45)
plt.yticks(range(len(pivot.index)), pivot.index)
plt.title('每日各职业胜率热力图')
plt.tight_layout()
plt.savefig('heatmap.png', dpi=150)几行代码,就拿到了一份完全自定义的分析看板,维度想要多细就多细。
以上只是基础框架,几个值得继续挖的方向:
Power.log 的卡牌转移事件,可以精确追踪每一张关键卡的上手率、打出时机和胜率贡献,远比盒子面板的「卡组胜率」细。最后提醒一句:本文所有操作仅读取本地文件,不涉及修改游戏客户端或网络数据包,符合暴雪与网易的用户协议。数据分析的边界是读本地、不篡改——守住这条线,炉石传说盒子就是你手上最好的个人教练。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。