☰
Charles抓包实战:从百词斩批量提取单词音频与例句
2026/9/29 10:36:20 网站建设 项目流程

我大概从去年开始被一件事折磨:想按自己的顺序背单词,不想用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完全够用。但我要抓的是App,App不会走浏览器代理,必须有一个独立的代理入口,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端口,只要电脑本身访问HTTP,Charles就能看到明文请求;但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, params=params, timeout=10) resp.raise_for_status() return resp.json() for word in WORD_LIST: try: data = fetch_word(word) # 保存完整JSON,方便后续校验 Path(f"data/{word}_raw.json").write_text( json.dumps(data, ensure_ascii=False, indent=2), encoding="utf-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, timeout=15) 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", []), start=1): 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), encoding="utf-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配置麻烦,反而觉得这些环节才是真正让人把工具用明白的部分。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询