尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

Python爬取王者荣耀盒子APP数据:从抓包到数据分析实战

Python爬取王者荣耀盒子APP数据:从抓包到数据分析实战 1. 项目概述与核心价值最近在分析手游用户行为时经常需要获取《王者荣耀》这类热门游戏的对战数据。官方API要么不开放要么限制严格而像“王者荣耀盒子”这类第三方聚合APP就成了一个非常宝贵的数据源。它整合了玩家的战绩、英雄使用情况、出装偏好等大量信息。这次要聊的就是如何用Python把这些数据从APP里“拿”出来构建我们自己的数据分析基础。听起来像是爬虫但和爬网页不太一样。APP的数据交互通常走的是HTTP/HTTPS接口数据格式也更规整大多是JSON。核心思路就是模拟APP的请求去跟它的服务器“对话”。这不仅能用于《王者荣耀盒子》其方法论也适用于分析其他手游的聚合平台数据比如看玩家常用英雄、胜率趋势、装备搭配等对于游戏运营分析、内容创作甚至是个人技术复盘都很有价值。无论你是数据分析师、游戏爱好者还是想学习移动端数据抓取的开发者这套流程都能给你提供一个清晰的实战路径。2. 核心思路与技术选型解析2.1 逆向工程从APP到可爬取的接口直接爬取APP的界面是不现实的效率极低。现代APP几乎都采用前后端分离架构界面前端由APP渲染数据后端通过API接口提供。因此我们的核心目标是找到并理解这些API接口。为什么选择抓包分析这是最直接、最可靠的方法。我们需要一个“中间人”来监听手机或模拟器与“王者荣耀盒子”服务器之间的所有网络通信。通过分析这些通信我们可以找到数据请求的URL接口地址是什么。请求方法GET/POST数据是如何索要或提交的。请求头Headers服务器如何识别合法的客户端请求这里通常包含设备信息、用户令牌Token、签名Sign等关键身份验证参数。请求参数Params/Body查询特定数据时需要附带的条件比如玩家的游戏ID、查询的场次范围等。响应数据格式服务器返回的数据结构通常是JSON便于我们解析。工具链选择与理由抓包工具Charles 或 FiddlerCharles功能强大对HTTPS流量解密支持好界面直观。它需要你在电脑和手机上安装并信任其CA证书以实现HTTPS流量的解密。这是后续分析的基础。Fiddler老牌抓包工具在Windows平台上有优势。原理与Charles类似。为什么不选Mitmproxy虽然它更程序员友好且可脚本化但初期学习和配置成本略高于Charles。对于首次进行APP抓包的同学Charles的图形化界面更利于观察和理解流量。开发环境Python 3.7丰富的库生态是我们的底气。核心库包括requests用于发送HTTP请求模拟APP的行为。它比标准库的urllib更简洁高效。json用于解析服务器返回的JSON格式数据。pandas非必须但强烈推荐。用于将爬取到的结构化数据如每场对战英雄、KDA、装备列表进行清洗、整理和存储到CSV或数据库方便后续分析。运行环境安卓模拟器 或 真机模拟器如夜神、MuMu推荐新手使用。在电脑上设置代理和安装证书非常方便且不影响真机使用。模拟器的设备信息相对固定有助于稳定复现请求。真机数据更真实但需要手机和电脑在同一Wi-Fi下并在手机上配置代理和安装证书步骤稍多。注意抓包和分析行为应仅用于个人学习与技术研究。务必尊重目标APP的服务条款避免高频请求对服务器造成压力严禁将获取的数据用于商业牟利或侵犯他人隐私。2.2 关键难点签名Sign与令牌Token的应对策略在分析“王者荣耀盒子”这类APP时你会很快发现它的接口绝不是简单的GET /api/data?uid123。为了反爬和确保安全服务器会要求请求中包含加密的签名Sign和代表用户身份的令牌Token。Token令牌通常是用户登录后服务器颁发的一个字符串在一段时间内代表该用户身份。它可能放在请求头如Authorization: Bearer xxxx或Cookie里。对于不需要登录就能查看的公开数据如某个大神的主页战绩可能不需要Token或者使用一个通用的访客Token。我们的策略先尝试抓取无需登录的公开页面流量观察请求头中是否存在类似token、auth的字段。如果需要登录才能获取目标数据则需模拟登录流程获取Token但这通常涉及更复杂的加密难度大增。初期建议从公开数据入手。Sign签名这是最大的挑战。为了防止请求被篡改和重放客户端APP会将请求参数、时间戳、可能还有一个固定的密钥Secret按特定算法常见如MD5, HMAC-SHA256生成一个唯一的签名sign附在请求中。服务器收到后用同样的算法和密钥再计算一遍如果一致则认为请求合法。我们的策略静态分析如果APP未加固或加固较弱可以尝试使用反编译工具如JADX查看Android源码搜索关键词sign、md5、encrypt寻找签名生成逻辑。动态调试更高级的方法通过Xposed、Frida等框架Hook关键函数直接打印出计算签名时的输入和输出。模拟还原如果算法不复杂例如只是对参数1值1参数2值2key固定值的字符串做MD5我们可以用Python完全复现这个计算过程。备用方案——直接复用如果算法过于复杂涉及设备指纹、so库加密等一个取巧但可能不稳定的办法是在抓包时截获一个有效的sign值短时间内在其过期前在Python请求中直接复用这个sign。但这只适用于短期、小规模的测试。实操心得不要一开始就试图攻克最难的签名。先从最简单的、不带签名的接口如果存在开始比如APP的启动配置、公告信息等。逐步理解整个请求的构造过程最后再集中精力解决签名问题。3. 实战步骤从抓包到Python脚本3.1 环境搭建与抓包配置假设我们使用Charles 夜神模拟器的组合。安装与配置Charles在电脑安装Charles启动后在Proxy - Proxy Settings中设置代理端口例如8888。在Help - SSL Proxying - Install Charles Root Certificate安装电脑的根证书。在Proxy - SSL Proxying Settings中添加一个规则Host为*Port为443这样Charles才能解密所有HTTPS流量。配置模拟器代理启动夜神模拟器打开系统设置 - WLAN长按当前网络选择“修改网络”。设置代理为“手动”代理服务器主机名填写你电脑的IP地址在Charles的Help - Local IP Address查看端口填写Charles的端口如8888。保存后在模拟器的浏览器中访问chls.pro/ssl下载并安装Charles的CA证书。在安卓系统中可能需要去“设置 - 安全 - 加密与凭据”中从存储设备安装证书。抓取“王者荣耀盒子”流量确保Charles正在录制红色圆点图标是开启状态。在模拟器中打开“王者荣耀盒子”APP进行你想要爬取数据的操作例如进入“战绩”页面查看“英雄”榜单刷新“国服”列表等。此时Charles左侧的Structure窗口会不断出现主机名如api.xxxbox.com。找到疑似数据接口的请求通常路径包含api、data、get等关键词响应内容为JSON格式。3.2 关键接口分析与参数解构以一个假设的查询玩家近期战绩的接口为例我们在Charles中可能看到如下请求GET https://api.wzrybox.com/v1/user/battle/list?userId123456789page1size10t1640995200000signa1b2c3d4e5f67890 Headers: User-Agent: Mozilla/5.0 (Linux; Android 11; ...) ... (王者荣耀盒子的特定UA) token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9... app-version: 6.2.1 device-id: 862400043912357我们需要在Charles中仔细查看这个请求的每一个部分Query Params (URL参数):userId: 目标玩家的唯一ID。这个ID如何获取可能从APP的个人主页URL或另一个搜索接口获得。pagesize: 分页参数控制获取第几页、每页多少条记录。t: 时间戳很可能是当前时间的毫秒数。用于防止请求被缓存和重放。sign: 签名由其他参数密钥计算得出。Headers (请求头):User-Agent: 模拟APP的关键标识。直接复制抓包到的完整字符串。token: 用户身份凭证。对于公开数据这个token可能是固定的或可省略。app-versiondevice-id: 设备信息服务器用于风控。必须与抓包时的一致。响应体点击Charles中的该请求查看Response通常是JSON格式。我们需要解析它的结构例如{ code: 200, message: success, data: { list: [ { battleId: 10001, heroName: 孙悟空, kill: 12, death: 3, assist: 8, win: true, equipment: [无尽战刃, 宗师之力, ...], battleTime: 2023-01-01 20:30:00 } // ... 更多对战记录 ], total: 150 } }我们的目标就是解析这个data.list里的每一个字段。3.3 Python爬虫脚本编写基于以上分析我们可以开始编写Python脚本。这里假设我们已通过某种方式解决了sign的生成问题例如发现它是对userIdpagesizetsecret的MD5值。import requests import hashlib import time import json import pandas as pd from typing import List, Dict class WZRYBoxSpider: def __init__(self): # 基础URL从抓包中获取 self.base_url https://api.wzrybox.com/v1 # 从抓包中复制的请求头 self.headers { User-Agent: Mozilla/5.0 (Linux; Android 11; ...) ..., app-version: 6.2.1, device-id: 862400043912357, # token: 你的token, # 如果需要则加上 } # 假设的密钥需要通过逆向分析获得 self.secret your_secret_key_from_app def generate_sign(self, params: Dict) - str: 模拟签名生成算法示例为MD5 # 1. 将参数按Key排序并拼接成字符串 sorted_params .join([f{k}{params[k]} for k in sorted(params.keys())]) # 2. 拼接密钥 sign_string sorted_params key self.secret # 3. 计算MD5具体算法需根据逆向结果调整 m hashlib.md5() m.update(sign_string.encode(utf-8)) return m.hexdigest() def get_user_battles(self, user_id: str, page: int 1, size: int 10) - Dict: 获取用户对战列表 endpoint /user/battle/list url self.base_url endpoint # 构造请求参数 params { userId: user_id, page: page, size: size, t: int(time.time() * 1000) # 当前毫秒时间戳 } # 生成签名并加入参数 params[sign] self.generate_sign(params) try: response requests.get(url, paramsparams, headersself.headers, timeout10) response.raise_for_status() # 检查HTTP错误 data response.json() if data.get(code) 200: return data.get(data, {}) else: print(f请求失败: {data.get(message)}) return {} except requests.exceptions.RequestException as e: print(f网络请求异常: {e}) return {} except json.JSONDecodeError as e: print(fJSON解析异常: {e}) return {} def parse_battle_data(self, battle_list: List[Dict]) - pd.DataFrame: 解析单场对战数据并转换为DataFrame records [] for battle in battle_list: record { 对战ID: battle.get(battleId), 使用英雄: battle.get(heroName), 击杀: battle.get(kill, 0), 死亡: battle.get(death, 0), 助攻: battle.get(assist, 0), 是否胜利: 是 if battle.get(win) else 否, KDA: round((battle.get(kill, 0) battle.get(assist, 0)) / max(battle.get(death, 1), 1), 2), # 计算KDA 装备: |.join(battle.get(equipment, [])), # 装备列表用|分隔 对战时间: battle.get(battleTime) } records.append(record) return pd.DataFrame(records) if __name__ __main__: spider WZRYBoxSpider() target_user_id 123456789 # 替换为目标用户ID all_battles [] # 示例爬取前3页数据 for page in range(1, 4): print(f正在爬取第 {page} 页...) data spider.get_user_battles(target_user_id, pagepage, size10) battle_list data.get(list, []) if not battle_list: print(f第 {page} 页无数据停止爬取。) break all_battles.extend(battle_list) time.sleep(2) # 礼貌性延迟避免请求过快 # 解析并保存数据 if all_battles: df spider.parse_battle_data(all_battles) print(f共爬取到 {len(df)} 条对战记录。) print(df.head()) # 预览前几条 # 保存到CSV文件 df.to_csv(fwzry_battles_{target_user_id}.csv, indexFalse, encodingutf-8-sig) print(数据已保存至CSV文件。) else: print(未爬取到任何数据。)脚本要点解析类封装将功能封装成类结构更清晰便于管理配置和状态。签名生成generate_sign方法模拟了签名过程。这是最需要你根据实际逆向结果修改的部分。算法可能是HMAC-SHA256或者参数拼接顺序不同。错误处理使用try-except捕获网络和解析异常并使用response.raise_for_status()检查HTTP状态码使脚本更健壮。数据解析与存储使用pandas将JSON数据转换为结构化的DataFrame并轻松导出为CSV方便后续用Excel、Python或BI工具进行分析。延迟策略在循环中加入了time.sleep(2)这是一个基本的反反爬措施避免短时间内发送大量请求被服务器封禁IP。4. 进阶技巧与深度优化4.1 处理动态Token与Session维持如果目标数据需要登录流程会复杂一个数量级。模拟登录你需要抓取登录接口通常是POST请求。分析其请求体除了用户名密码很可能还有图形验证码、滑动验证码或者对密码进行了非对称加密RSA。处理验证码可能需要接入打码平台而密码加密则需要从APP源码中找到公钥和加密方式。维持会话登录成功后服务器会返回一个token并可能在响应头中设置Set-Cookie。后续所有请求都需要携带这个token在headers里和Cookie。在Python中使用requests.Session()对象可以自动管理Cookies非常方便。session requests.Session() # 先POST登录session会自动保存登录后的cookie login_resp session.post(login_url, datalogin_data, headersheaders) # 后续请求直接使用同一个session对象 data_resp session.get(data_url, paramsparams)4.2 应对反爬机制策略“王者荣耀盒子”的服务器很可能有反爬措施。请求频率限制除了在代码中加随机延迟time.sleep(random.uniform(1, 3))更优的做法是尊重robots.txt如果有并尽量在访问低谷期运行脚本。IP封禁如果请求过于频繁你的服务器IP或家庭宽带IP可能会被临时封禁。解决方案是使用代理IP池。可以购买付费代理服务或者在云服务器上搭建代理。proxies { http: http://your-proxy-ip:port, https: http://your-proxy-ip:port, } response requests.get(url, proxiesproxies, headersheaders)请求头校验服务器会检查User-Agent、Referer、Origin等头。务必从抓包中完整复制并注意有些头如X-Requested-With可能也是必需的。参数完整性校验除了sign一些看似无关的参数如channel、platform如果缺失请求也可能失败。务必保证抓包看到的参数一个不少地模拟。4.3 数据清洗与结构化存储爬取到的原始JSON数据往往需要清洗才能用于分析。缺失值处理某些场次数据可能缺少“助攻”字段在解析时使用.get(assist, 0)提供默认值。数据格式转换时间字符串2023-01-01 20:30:00可以转换为Python的datetime对象便于进行时间序列分析。df[对战时间] pd.to_datetime(df[对战时间]) df[星期几] df[对战时间].dt.day_name() # 新增列分析玩家在周几更活跃多维度存储对于大量数据CSV可能不是最佳选择。可以考虑SQLite数据库轻量级适合桌面级应用。使用sqlite3库可以方便地按玩家ID、英雄、时间建立索引查询效率高。MongoDB数据库如果数据嵌套层次深如一场对战有10个玩家的详细数据文档型数据库MongoDB的JSON式存储更有优势。5. 常见问题与排查实录在实际操作中你几乎一定会遇到下面这些问题。Q1: Charles抓不到“王者荣耀盒子”的HTTPS流量全是unknown。排查证书未正确安装或信任。确保在模拟器/手机的系统证书目录而不仅是用户目录安装了Charles的CA证书。安卓7.0以上对用户安装的证书限制更严可能需要将APP移到可调试的目录或者使用已Root的模拟器。备用方案如果Charles始终无法解密可以尝试使用Packet Capture等手机端抓包APP它通常能绕过系统证书限制。然后在电脑上分析其导出的抓包文件。Q2: 用Python复现的请求返回code: 403或签名错误。排查这是最核心的问题。99%的原因在于sign生成错误。检查参数对比Python脚本中的请求参数和Charles中看到的原始请求参数确保完全一致包括大小写、下划线。特别注意时间戳t的值它必须是当前时刻的毫秒数且与服务器时间不能相差太大。检查签名算法这是最难的部分。使用JADX反编译APK全局搜索sign、md5、hmac、encode等关键词。找到疑似方法后重点看参数的拼接顺序、是否进行了URL编码、密钥secret是什么。可以尝试将抓包到的原始参数和sign值与你Python算法计算出的值进行对比调试。检查请求头确保所有必要的headers都已添加特别是User-Agent、app-version、device-id必须和抓包时的一模一样。Q3: 爬了几页数据后突然返回空数据或code: 429请求过多。排查触发了服务器的频率限制。立即措施大幅增加请求间隔例如time.sleep(random.uniform(5, 10))并更换代理IP。长期策略设计更友好的爬取节奏模拟真人操作如先访问主页再点开详情避免规律性的、高并发的请求。Q4: 如何找到某个特定玩家的userId方法通常APP内都有一个搜索功能。抓取搜索玩家这个动作的接口。例如在搜索框输入玩家游戏名“百星鲁班”会触发一个搜索请求其响应里就包含了该玩家的userId、区服等信息。先爬这个搜索接口拿到ID再爬战绩接口。Q5: 数据爬下来了但想分析“哪个英雄在逆风局出某件装备胜率更高”该怎么入手思路这属于数据分析范畴。用pandas可以轻松实现。定义“逆风局”例如前10分钟经济落后超过2000。筛选出所有逆风局的记录。针对特定英雄如“孙悟空”再筛选出他出装了“贤者的庇护”的记录。计算这部分记录的胜率。# 假设df是包含所有对战记录的DataFrame df[是否逆风] df[前10分钟经济差] -2000 monkey_king_df df[df[使用英雄] 孙悟空] comeback_item_df monkey_king_df[monkey_king_df[装备].str.contains(贤者的庇护)] win_rate comeback_item_df[是否胜利].mean() print(f孙悟空在逆风局出贤者的庇护的胜率为{win_rate:.2%})爬取APP数据是一个系统工程结合了网络协议分析、逆向工程和编程实践。整个过程最耗时的往往不是写代码而是前期的抓包、分析和破解签名机制。保持耐心从简单接口入手逐步深入每一次成功破解都会带来巨大的成就感。最后再次强调技术用于学习获取的数据请妥善使用遵守相关法律法规和平台规则。
返回列表