ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Charles抓包实战:从百词斩批量提取单词音频与例句

Charles抓包实战:从百词斩批量提取单词音频与例句 我大概从去年开始被一件事折磨想按自己的顺序背单词不想用App内置的词书试过Anki、欧路、Excel全都不顺手。后来我想到一个更“程序员”的思路——直接把百词斩里的单词音频、翻译、例句音频、例句翻译整套拉下来做一份属于自己的语料库。要完成这件事最顺手的工具就是Charles。这篇记录就围绕“Charles抓包百词斩”展开从环境搭建、接口定位、批量下载到那些折腾到半夜的证书和unknown问题全都会过一遍。如果你是想批量整理语音语料的学习型开发者或者在Windows、手机、模拟器上折腾Charles的“配置困难户”这篇应该能帮你省下不少时间。1. 起因从手动复制到给App动一次“抓包手术”1.1 这个需求到底卡在哪手动整理单词发音和例句说起来简单做起来极其枯燥。一个单词点开详情要找到音标、中文释义、例句、例句翻译再逐个复制下载音频重命名文件整套下来少说一分钟。我要整理的不是十个八个而是几百上千个常用词这种工作量完全没法靠手点。换过几个词表网站要么发音不全要么只有英音没有美音要么例句老掉牙很少有App里那种“点击即发音”的体验。后来我意识到百词斩本身就是一套现成的、高质量的英语学习语料库单词音频、中英翻译、例句音频全都做了精细处理。问题只剩一个怎么把这些数据成批地拿到自己的电脑上。这就要靠Charles了。Charles本质上是一个HTTP/HTTPS调试代理跑在电脑上之后手机或电脑的流量都从它这儿过一遍。所有请求和响应都能被看见最关键的是它能解密HTTPS让你直接看到API返回的JSON。圈内人习惯叫它“青花瓷”因为它图标长得像一只蓝色的陶瓷茶壶。1.2 为什么选Charles而不是Fiddler或浏览器DevTools市面上能做抓包的工具不少但轮到这个场景Charles是最省事的。我拿它和几个常用方案做了对比工具跨平台HTTPS解密移动端抓包修改响应上手难度Charles好好方便支持Map Local/Map Remote中等Fiddler偏Windows好也能做支持脚本中等偏上Wireshark通用麻烦麻烦不适合高Chrome DevTools仅Web好只看浏览器有限低如果你只想抓网页请求DevTools完全够用。但我要抓的是AppApp不会走浏览器代理必须有一个独立的代理入口Charles在这里比Fiddler更直观界面也清晰。尤其是看JSON响应时Charles自带的树形和JSON查看器非常顺手不用像我以前用Fiddler还得再配一个JSON格式化插件。1.3 整体思路先看接口再写脚本最后批量跑整件事可以拆成三步先把环境搭好让Charles能解密百词斩App的HTTPS流量然后在App里查一个单词从抓包记录里找到返回音频和翻译的接口最后解析这个接口的请求参数和响应格式用Python脚本批量跑。前两步的工作量占比其实只有三成最耗时间的反而是环境配置里的那些“非技术坑”。因为百词斩的接口用的是HTTPS如果你没有安装和信任Charles的根证书抓包窗口里显示的全部是unknown什么都看不到。下文我会把Windows、安卓真机、iPhone、雷电模拟器的配置逐个讲清楚。2. 环境准备搭好抓包链路先踩平那些“配不通”的坑2.1 下载安装与基础设置Charles的下载直接用官网版本就行Windows和macOS我都用过界面几乎一样。启动后默认监听8888端口只要电脑本身访问HTTPCharles就能看到明文请求但HTTPS请求必须额外开启SSL Proxying否则所有的HTTPS流量统统显示为unknown。具体操作是打开菜单Proxy - Proxy Settings勾选Enable transparent HTTP proxying然后在SSL Proxying Settings里添加*:*。这里的*:*表示对所有域名和端口都做SSL解密。如果你想收敛一点也可以只添加百词斩的接口域名这个等你找到核心域名以后再加也不迟。我在装到Charles 4.6.2这个版本时遇到过Windows下勾选了SSL Proxying却依然unknown的情况原因不是版本问题而是证书没装好。所以别急着换版本先看证书。2.2 本机抓包的证书安装细节电脑本身的入口是Help - SSL Proxying - Install Charles Root Certificate。证书会弹出到Windows证书管理器这里最容易出错的是存储位置。默认可能被放到“当前用户”的“个人”目录Charles不认这个位置必须手动把证书移到“受信任的根证书颁发机构”下。具体路径弹出的证书窗口里点“安装证书”选择“本地计算机”然后选择“将所有的证书都放入下列存储”浏览选择“受信任的根证书颁发机构”。完成后重启Charles再抓本机HTTPS就不会再显示unknown了。macOS下其实也有类似的坑你需要双击证书在“信任”一栏把使用此证书时改为始终信任否则系统默认可能不信任Charles的根证书导致HTTP请求报错。2.3 真机抓包iPhone和Android代理配置手机抓包是另一套玩法。电脑和手机连同一个WiFi电脑上把Charles的代理服务开起来然后在手机的WiFi设置里找到手动代理填上电脑的局域网IP和8888端口。IP可以在Charles的Help - Local IP Address里看到不要填127.0.0.1那是手机自己。配好代理之后用手机浏览器访问chls.pro/ssl来下载并安装Charles根证书。iPhone用户需要特别注意光下载和安装描述文件还不够还要去系统设置的“通用 - 关于本机 - 证书信任设置”里把Charles根证书的开关打开。很多教程只讲到下载安装少了最后这一步就会导致抓到的HTTPS流量全是unknown。Android的证书安装方式类似但有一个更麻烦的分水岭Android 7.0及以上版本很多App默认不再信任用户安装的CA证书。百词斩在部分系统上会出现流量能抓到、但显示unknown的情况。这个时候要么换一台Android 6或更早的设备要么走模拟器加系统证书导入方案要么直接用iPhone抓最后一种是我实测最省心的。2.4 雷电模拟器的配置方案如果没有真机雷电模拟器也是可行的。理论上模拟器等同于一台Android设备在模拟器的WiFi设置里添加代理IP填你电脑在局域网里的地址端口还是8888。然后打开模拟器浏览器访问chls.pro/ssl下载证书并安装。不过模拟器的坑在于系统证书信任问题。普通用户证书在Android 7.0以上经常不生效。如果你遇到unknown可以试试把证书转换成系统证书。大致流程用openssl把Charles的PEM证书重命名成证书哈希.0这种格式然后通过模拟器的root权限把文件放到/system/etc/security/cacerts/目录。这种方式依赖模拟器是否允许root操作雷电模拟器默认带root开关操作起来还算方便。但我个人建议如果只是抓百词斩这种普通App直接用iPhone或者旧Android真机更省时间没必要在模拟器里死磕系统证书。3. 定位百词斩接口从一次普通查词逆向出完整数据流3.1 从查一个单词开始快速过滤请求环境配好之后打开手机上的百词斩搜索一个单词比如abandon。Charles的请求列表会瞬间刷十几条包含图片、统计、登录、词库、搜索建议等。如果一条条翻眼睛会瞎。第一步先用Charles底部的Filter输入框过滤关键词。我一般会先试“word”“dict”“search”这类的词匹配到结果后再逐个点开看响应内容。也可以在请求列表里直接按Command F全局搜索响应内容搜索audio或translation这样能更快地定位到真正包含单词详细数据的请求。我实际抓到的核心接口路径长得类似/api/dictionary/word/detail具体域名和服务端路径不同版本会有所差异不要拿我的路径死套请以你自己抓到的为准。找到后建议右键这个请求选择Save Response把这个JSON完整保存下来方便后面分析字段。3.2 识别请求参数与请求头点开这个核心接口请求Charles会展示完整的请求URL、Query参数、请求头、请求体。我发现里面除了一目了然的单词名之外还有平台、版本号、token之类的参数。token跟用户登录状态有关但不一定每次都强制要求你可以先用抓包里的原始请求直接重放看是否返回数据。更稳妥的做法是在Charles里选中这条请求先右键Repeat一次看是否能正常返回同样的JSON。如果可以说明这个接口没有绑定一次性签名后面写脚本模拟就能成功。如果返回错误再检查是否漏带了某个Header比如User-Agent或Authorization。有一个容易被忽略的点把请求头里的User-Agent和Referer原样保留。很多App接口会把浏览器UA或空Referer视为异常请求直接返回403。当时我踩过这个坑第一次用Python写请求时只带了单词参数结果被拒了后来把所有Header完整复制过去问题就消失了。3.3 分析返回JSON音频、翻译、例句到底在哪接口返回的JSON是一个典型的嵌套结构。我简化之后大概是这样的{ word: abandon, translation: v. 放弃抛弃, phonetic: { uk: əˈbændən, us: əˈbændən }, audio: { uk: https://media.example.com/audio/uk/abandon.mp3, us: https://media.example.com/audio/us/abandon.mp3 }, sentences: [ { en: He abandoned his car., zh: 他弃车而去。, audio: https://media.example.com/audio/sentence/100001.mp3 } ] }注意实际字段名可能完全不同但规律是一致的——单词翻译、英音美音地址、例句原文、例句翻译、例句音频都会被放在同一个JSON里。我用Charles的JSON视图直接展开后把字段名抄下来再对应到自己的脚本解析逻辑里。这里有一个经验音频URL不一定全是MP3格式也可能是带签名参数的动态地址有效期可能只有几小时甚至几百秒。如果你抓包后隔了一天再重放音频链接可能404这种情况必须重新抓包拿新链接。3.4 为什么老老实实看抓包记录而不是猜域名很多教程会直接告诉你“百词斩的接口是api.baicizhan.com/xxx”但真实场景里App版本一升级、A/B测试一开接口路径说变就变。我在做这个项目时试过直接用网上找到的旧接口结果返回的数据结构和现在完全对不上白折腾了一个晚上。所以不管别人怎么描述一定要自己抓一次包亲眼看到当前版本的真实请求。Charles的价值就在这里它不只是让你看到“发生了什么”而是给你一个“完整可复现的样本”。把这条样本请求保存下来后续脚本就参照它来写比自己猜要靠谱得多。4. 批量获取单词音频与例句用Python脚本把链路自动化4.1 两条路线导出HAR重新解析还是直接模拟请求确认接口可用之后接下来有两条路可以走。路线A用Charles手动查所有需要抓取的单词把整个会话导出成.har文件然后写脚本解析HAR里的JSON响应再下载音频。优点是简单不需要额外考虑签名、参数过期缺点是几百个词手动点也太累了而且会浪费大量时间在App操作上。路线B把抓包得到的请求参数、请求头、接口地址整理成模板用Python脚本直接对接口发起请求循环遍历单词表。优点是自动化几百个词几秒就能跑完缺点是如果接口有签名或限频策略需要额外处理。我实际采用的是路线B。原因很简单我的单词清单是固定的几百个跑脚本一次就能出结果。但在写脚本前我先用Charles手动查了5个单词通过HAR确认了字段结构确保接口解析逻辑没有偏差这才开始写批量代码。4.2 模拟请求的核心代码逻辑下面是我简化后的Python脚本核心部分。实际使用时把URL、参数名、字段名替换成你在Charles里抓到的真实值即可。import requests import time import json from pathlib import Path session requests.Session() session.headers.update({ User-Agent: Mozilla/5.0 ..., Referer: https://word.example.com/, token: 你的token可从Charles请求头中复制 }) WORD_LIST [abandon, ability, abnormal, aboard] def fetch_word(word: str) - dict: url https://api.example.com/study/dictionary/word/detail params { word: word, platform: android, version: 4.6.2 } resp session.get(url, paramsparams, timeout10) resp.raise_for_status() return resp.json() for word in WORD_LIST: try: data fetch_word(word) # 保存完整JSON方便后续校验 Path(fdata/{word}_raw.json).write_text( json.dumps(data, ensure_asciiFalse, indent2), encodingutf-8 ) except Exception as e: print(f[ERROR] {word}: {e}) time.sleep(0.5)注意几个细节请求间隔我用time.sleep(0.5)也就是每秒最多两个单词尽量不给对方服务器造成压力。session复用连接比每次新建requests.get更高效也能减小被风控的概率。如果你在保存JSON后发现某个单词返回的字段缺失尤其是audio或sentences为空最常见的原因是单词类型不同——比如一个词是动词短语可能没有例句音频。不要直接跳过打印出来单独处理。4.3 下载音频与整理命名规范拿到JSON后下一步就是把对应的MP3下载到本地并生成一份带翻译和例句的文本文件。我的命名规范是这样的每个单词一个目录音频文件名包含词义标签和位置。import requests def download_audio(url: str, save_path: Path): if not url: return resp requests.get(url, timeout15) resp.raise_for_status() save_path.write_bytes(resp.content) def save_text_data(word: str, data: dict, data_dir: Path): translation data.get(translation, ) phonetic data.get(phonetic, {}) lines [ f# {word}, f【翻译】{translation}, f【英音】{phonetic.get(uk, )}, f【美音】{phonetic.get(us, )}, ] for idx, sent in enumerate(data.get(sentences, []), start1): lines.append(f[例句{idx}]) lines.append(sent.get(en, )) lines.append(sent.get(zh, )) lines.append() (data_dir / f{word}_note.md).write_text( \n.join(lines), encodingutf-8 )音频下载时我遇到的坑是有些CDN会检查Referer直接requests.get(url)会返回403。这种情况可以先试一下不带Header的中立请求不行再在下载函数里加上从Charles复制来的Referer。还有例句音频的URL经常带签名时间戳如果批量脚本跑得太慢先下载的链接可能过期所以最好把“解析”和“下载”分成两个阶段每次只处理少量单词避免时间差导致大面积404。4.4 结果校验哪些单词容易缺失数据跑完一遍之后我建议不要立刻当作成品先做一次基本的文件完整性检查。比如MP3文件小于1KB的大概率是404错误页或者空文件需要重新下载。我当时的校验逻辑是扫描所有.mp3文件列出小于5KB的再对照JSON里的URL重新下载一次。有一个容易忽视的问题是单词大小写。百词斩对大小写敏感abandon能正常返回但Abandon可能返回的不是同一个词条。批量处理前最好统一做小写转换遇到专有名词如London再单独处理。另外很多单词并不只有一个词性比如book既可以作名词又可以作动词接口返回的翻译可能是一整段多词性释义。如果你只需要某一个词性的音频还是要在下载后人工抽检一下内容不要完全依赖字段名。5. 抓包过程中翻过的车完整排查链路和避坑指南5.1 证书安装成功了却还是unknown一个常见的排查顺序这个问题被问得最多热搜上全是“charles 证书安装过了 windows抓包还是unknown”。我总结了一个固定的排查顺序按这个走95%的问题都能解决。第一步确认SSL Proxying是否真的开启。很多人只装了证书忘了在Proxy - SSL Proxying Settings里勾选Enable SSL Proxying导致HTTPS全显示unknown。第二步确认证书是否被系统信任。Windows下要放在“受信任的根证书颁发机构”macOS下要设置为“始终信任”。第三步重启Charles。证书和SSL配置改动后最好把应用彻底退出再重新打开。第四步确认Windows防火墙没有拦截8888端口。尤其是Windows开启网络发现这类配置时偶尔会把Java进程的监听端口拦住。如果这些都检查完还是unknown可以换一个思路不要抓所有域名直接在SSL Proxying Settings里只添加百词斩的接口域名。限制范围反而能减少干扰也能确认是不是域名层面的兼容问题。5.2 iPhone无法下载Charles证书怎么绕过去iPhone用户会在下载证书那步遇到卡壳设置好代理后Safari访问chls.pro/ssl却打不开页面或者提示无法连接。我的处理方法是先把手机WiFi的代理暂时关掉直接访问chls.pro/ssl下载并安装描述文件后再重新打开代理。代理开启状态下部分网络环境会阻止访问这个特殊域名但关闭代理后下载就正常了。下载完描述文件后别忘了去“设置 - 通用 - 关于本机 - 证书信任设置”把Charles对应的根证书开关打开。这一步不做证书等于白装。如果你用是苹果电脑抓iPhone还需要注意Charles的Proxy Settings里勾选Allow Remote Access否则iPhone虽然能连上代理会一直提示Connection failed。这也是一个很隐蔽的坑。5.3 安卓7.0以上应用不认用户证书的三种破解思路如果使用安卓7.0以上真机会碰到一类更头大的问题系统设置里明明能看到Charles证书Charles的SSL Proxying也配置了但百词斩的HTTPS请求仍然显示unknown。这是因为App在构建时声明了networkSecurityConfig默认不信任用户的CA证书。关于这个问题我试过三种办法。第一种换用Android 6或更早的设备老系统对用户证书的限制较少能直接抓到明文。第二种用雷电模拟器等支持root的模拟器把Charles证书导入系统证书目录。第三种在已root的安卓设备上通过Magisk模块把证书变成系统证书。我个人最推荐第一种成本最低效果也最稳定如果你身边没有旧设备再考虑模拟器。5.4 代理失效、连不上、请求刷不出来还有一个高频问题手机或模拟器设置了代理后Charles收不到任何请求。这个不用改证书先回到最基础的排查上。手机和电脑是否在同一WiFi这是最容易被忽略的前提。手机代理的IP不能填127.0.0.1要填电脑的实际局域网IP。Charles是否开启了系统代理Windows端如果关闭了Proxy - Windows Proxy部分流量的转发也会有异常。模拟器场景更特殊。雷电模拟器默认使用NAT网络电脑的局域网IP在模拟器里未必能被访问。我当时的解决办法是在模拟器的网络设置里选“桥接模式”或者“直接连接物理网络”保证模拟器和电脑在同一广播域代理才生效。如果网络模式改不了用adb reverse把手机端口转发到电脑也可以只是配置过程更复杂这里不展开。5.5 抓包时误抓太多无关流量影响排查效率Charles一旦开启代理整个操作系统的所有HTTP/HTTPS流量都会被拦截包括软件更新、弹窗广告、系统遥测。请求列表每秒钟刷新好几条根本看不到百词斩的接口。建议在抓包期间把Charles的Filter固定在百词斩的域名关键词上同时开启Focus功能把非目标域名的请求置灰。这样既能保留流量上下文又不会干扰视线。另外手机端尽量关掉其他后台App减少无意义流量Charles看起来也清爽很多。6. 一点个人体会这套抓包流程还能迁移到哪6.1 不只是百词斩所有App都能用同一套思路做数据定制做完这个项目之后我最强烈的感受是Charles的价值不在“抓包”这两个字而在于它给了你一个观察数据的入口。任何App里看到的内容背后都是结构化数据用Charles就能把这份数据还原成文件再按自己的需求加工。我用同样的思路搭建过背单词的Anki卡片库把英语单词、音频、释义自动化填入模板也帮朋友把某新闻App的RSS全文抓下来做过离线阅读。不要被“抓包”这个词吓到它本质上就是一个代理调试工具合法、通用和浏览器F12是一样的底层逻辑。6.2 合规和版权是绕不开的话题最后必须说一句数据抓下来之后怎么用边界要清楚。像百词斩的音频、例句翻译版权都归其开发方和原著作权人所有把它用于个人学习、离线整理、本地检索完全没问题。但如果把抓下来的音频库打包分享或者做成付费内容分发就有版权风险了。我自己的习惯是抓下来的数据只存在本地绝不公开上传。平时调试脚本会控制请求频率跑批量数据时单线程加sleep不给对方服务器造成额外负担。抓包工具是中性技术使用的方式决定了它的正当性。6.3 如果只想要单词发音几个实测建议送给你最后分享几个实际操作中积累的小经验。如果你和我一样主要目标是单词发音那直接抓audio_us和audio_uk两个MP3链接就够了不用处理整个句子层面数据结构更简单出错率也更低。如果你需要例句音频尽量在一次抓包会话内把批量下载跑完因为例句链接的签名有效期比单词发音短。养成“抓完即处理”的习惯别把HAR文件存到下周再解析。还有下载过程中如果发现某个MP3打不开先别急着重试检查一下是不是网络代理没关干净Python脚本里的requests可能继承了系统代理导致下载失败。自从踩过这些坑后我再也不嫌Charles配置麻烦反而觉得这些环节才是真正让人把工具用明白的部分。
返回列表