Python+Charles抓包实战:小程序短剧m3u8下载与合并

发布时间:2026/9/20 19:01:39

Python+Charles抓包实战:小程序短剧m3u8下载与合并 小程序里的付费短剧本质上是把视频流切片后通过接口按集下发地址客户端拿到地址再去拉流播放。这个链路里最薄弱的环节不是加密算法而是地址下发这一步——只要能在请求响应里看到真实的媒体地址后面的下载就是个体力活。我用Python配合Charles把这套流程跑通了好几次中间踩的坑比想象中多尤其是证书和模拟器这两块网上大部分教程都语焉不详。下面把完整思路、代码和避坑点摊开讲适合有一定Python基础、想搞清楚抓包与媒体下载链路的同学参考。1. 先想清楚为什么是Charles加Python这套组合1.1 抓包工具那么多为什么偏偏选Charles抓包工具市面上常见的有Charles、Fiddler、Wireshark、tcpdump这几类。Wireshark和tcpdump偏底层抓的是网卡层面的原始流量面对HTTPS只能看到加密后的字节流除非你导入会话密钥否则解不出明文。Fiddler功能接近Charles但它在处理移动端HTTPS解密时的证书信任链路在部分安卓版本上比Charles更容易出问题而且界面和会话过滤的顺手程度我个人更偏向Charles。Charles的核心优势在于它是一个中间人代理MITM。它在你和目标服务器之间架一个代理客户端以为自己在跟服务器说话实际上是在跟Charles说话Charles再转发给服务器。因为Charles持有自己签发的根证书只要客户端信任了这个根证书Charles就能解密HTTPS明文。这就是为什么证书安装是整个流程里绕不开的一步。选Charles还有一个现实原因它对移动端的支持成熟能直接抓安卓模拟器或真机的流量会话列表里按域名过滤、按接口路径筛选都很方便导出会话也简单。对于找到短剧视频地址这个目标来说效率最高。1.2 Python在这套流程里扮演什么角色很多人以为抓包工具能直接下载视频其实Charles只负责看不负责拿。你从Charles里看到的是一个m3u8播放列表或者一个mp4直链真正把这些分片下载、合并、转码成完整文件需要自己写脚本。Python在这里的价值就是自动化和批量化自动请求接口拿到每一集的媒体地址省去手动一集集点自动下载m3u8里的所有ts分片按顺序合并处理请求头、Referer、User-Agent这些容易被忽略但缺了就403的字段批量重命名、归档方便后续管理。如果只有几集手动下载也行。但短剧动辄几十上百集手动操作不现实脚本化是必然选择。1.3 这套方案的适用边界需要说清楚的是这套方法适用于你自己有权访问、有权保存的内容。抓包和下载技术本身是中性的但用在什么地方决定了它的性质。本文讨论的是技术链路本身——如何理解一个App的媒体下发机制、如何用工具观察和分析网络请求、如何用脚本处理媒体分片。请把它用在合规的场景里比如分析自己开发的小程序、研究媒体分发架构、或者处理你有合法访问权限的内容。2. 环境搭建Charles与模拟器的正确打开方式2.1 Charles的安装与基础配置Charles官网下载安装包Windows和macOS都有对应版本。安装完成后第一次启动会要求授予网络权限这个必须允许否则抓不到任何流量。启动后进入Proxy菜单勾选Windows ProxymacOS上是macOS Proxy这样系统层面的流量才会走Charles。接下来是关键的SSL代理设置。进入Proxy - SSL Proxying Settings勾选Enable SSL Proxying然后在Include列表里添加你要抓的域名。这里有个新手常犯的错误直接填*:*想抓所有域名。理论上可行但会导致大量无关流量涌入会话列表瞬间被刷屏找目标接口非常痛苦。正确做法是先大致确定目标小程序用到的域名只添加这几个。怎么确定域名先不配SSL代理让Charles以明文方式跑一遍观察哪些域名在请求。小程序通常会有几个固定域名一个业务接口域名、一个媒体CDN域名、可能还有统计和日志域名。找到业务接口域名后再把它加进SSL代理列表。2.2 证书安装这一步90%的人会卡住Charles要解密HTTPS必须让客户端信任Charles的根证书。电脑端的证书安装相对简单Help - SSL Proxying - Install Charles Root Certificate然后按系统提示导入到受信任的根证书颁发机构。Windows上要注意导入时一定要手动选择存储位置为受信任的根证书颁发机构如果只是双击安装到个人存储区浏览器和系统仍然不信任抓包会显示unknown。移动端才是真正的坑区。安卓7.0以后系统不再信任用户安装的证书用于应用流量只信任系统级证书。这意味着你把Charles证书装到用户证书里浏览器能抓但小程序、App的流量依然抓不到Charles里显示一片unknown或者直接连接失败。解决办法有两个方向方向一使用安卓7.0以下的模拟器或设备。这是最省事的办法。雷电模拟器有多个安卓版本可选选安卓5.1或7.0以下的镜像用户证书就能生效。这也是为什么热词里charles雷电模拟器出现频率很高——大家都在用这个组合。方向二把证书装到系统证书目录。需要root权限把证书文件转成特定格式后推到/system/etc/security/cacerts/目录下。这个过程涉及格式转换和权限修改稍有不慎会搞坏系统。如果你只是想快速跑通流程方向一更实际。证书装好后在模拟器的WiFi设置里配置代理主机名填电脑的局域网IP端口填Charles的默认端口8888。注意不能用127.0.0.1因为模拟器里的localhost指向模拟器自己不是你的电脑。电脑IP用ipconfigWindows或ifconfigmacOS查。2.3 验证抓包是否成功配置完成后在模拟器里打开浏览器访问一个HTTPS网站比如随便一个新闻站。如果Charles里能看到明文的请求和响应说明证书和代理都配对了。如果看到的是unknown或者连接被拒绝回到上一步检查证书存储位置和代理设置。这一步验证非常重要不要跳过。很多人直接打开小程序就开始抓结果抓不到又回头排查浪费大量时间。先用浏览器验证链路通畅再进小程序。3. 定位短剧媒体地址从接口到m3u8的完整链路3.1 小程序请求的特征与过滤技巧小程序运行在微信或宿主App的容器里它的网络请求和普通网页请求有几个区别。第一User-Agent里通常带有MicroMessenger字样方便过滤。第二请求往往带有一堆自定义header比如token、设备标识、版本号。第三接口路径通常有规律比如/api/drama/、/v1/video/这类。在Charles里用Filter框输入关键词过滤。先试m3u8因为短剧几乎都用HLS协议媒体地址大概率是m3u8。如果没结果试video、play、episode这些词。找到可疑接口后右键选择Focus把它单独拎出来看。3.2 识别m3u8与mp4两种媒体形态短剧的媒体下发一般有两种形态形态一直接返回mp4地址。接口响应里直接给一个完整的mp4链接下载下来就是完整视频。这种最简单但较少见因为不利于防盗链和分片加密。形态二返回m3u8播放列表。这是主流。m3u8是一个文本文件里面列出了所有ts分片的地址。客户端拿到m3u8后按顺序请求每个ts分片边下边播。你要做的是把m3u8和所有ts都下载下来再合并成一个完整视频。m3u8的内容长这样#EXTM3U #EXT-X-VERSION:3 #EXT-X-TARGETDURATION:10 #EXT-X-MEDIA-SEQUENCE:0 #EXTINF:10.000000, segment_000.ts #EXTINF:10.000000, segment_001.ts ... #EXT-X-ENDLIST#EXTINF后面是分片时长下一行是分片文件名。如果文件名是相对路径需要和m3u8的URL拼接成完整地址。3.3 接口里的加密参数怎么处理有些小程序的接口会带签名参数比如sign、timestamp、nonce。这些参数通常由前端JS计算生成直接复制URL过一会儿就失效。遇到这种情况有两个思路思路一在Charles里直接复制完整的请求。右键接口 - Copy cURL Request把整个请求包括所有header和参数复制出来在Python里用requests库原样发送。这样签名参数是现成的只要不过期就能用。缺点是签名有时效过期后要重新复制。思路二分析签名算法。如果接口调用频繁手动复制不现实就需要逆向小程序的JS代码找到签名生成逻辑用Python复现。这涉及反编译小程序包、定位加密函数工作量较大。对于一次性下载任务思路一足够。我个人的经验是先用思路一快速跑通确认整个链路可行再考虑要不要做思路二的自动化。很多情况下短剧的媒体地址一旦拿到有效期比较长不需要频繁重新签名。4. Python下载脚本从m3u8到完整视频4.1 下载m3u8并解析分片列表先写一个函数负责下载m3u8文件并解析出所有ts分片的完整URL。import requests import os from urllib.parse import urljoin def fetch_m3u8(m3u8_url, headers, save_dir): resp requests.get(m3u8_url, headersheaders, timeout15) resp.raise_for_status() content resp.text # 保存原始m3u8方便排查 os.makedirs(save_dir, exist_okTrue) with open(os.path.join(save_dir, index.m3u8), w, encodingutf-8) as f: f.write(content) segments [] for line in content.splitlines(): line line.strip() if not line or line.startswith(#): continue # 相对路径转绝对路径 full_url urljoin(m3u8_url, line) segments.append(full_url) return segments这里用urljoin处理相对路径是关键。很多m3u8里的分片是相对路径直接拼字符串容易出错urljoin会自动处理各种边界情况。4.2 带重试的分片下载分片下载最容易遇到的问题是网络抖动导致某个分片失败。如果不做重试整个下载就断了。下面这个函数带重试机制import time def download_segment(url, headers, save_path, retries3): for attempt in range(retries): try: resp requests.get(url, headersheaders, timeout20, streamTrue) resp.raise_for_status() with open(save_path, wb) as f: for chunk in resp.iter_content(chunk_size8192): if chunk: f.write(chunk) return True except Exception as e: print(f分片下载失败 {url} 第{attempt1}次: {e}) time.sleep(1.5) return FalsestreamTrue配合iter_content是处理大文件的标准做法避免一次性把整个分片读进内存。分片通常几百KB到几MB虽然不大但养成流式下载的习惯没坏处。4.3 并发下载与顺序保证短剧一集可能有几十上百个分片串行下载太慢。用线程池并发下载能大幅提速但要注意分片的顺序不能乱合并时必须按m3u8里的顺序来。from concurrent.futures import ThreadPoolExecutor, as_completed def download_all_segments(segments, headers, save_dir, max_workers8): os.makedirs(save_dir, exist_okTrue) results {} def task(idx_url): idx, url idx_url save_path os.path.join(save_dir, f{idx:05d}.ts) ok download_segment(url, headers, save_path) return idx, save_path, ok with ThreadPoolExecutor(max_workersmax_workers) as executor: futures [executor.submit(task, (i, u)) for i, u in enumerate(segments)] for future in as_completed(futures): idx, path, ok future.result() results[idx] (path, ok) # 按索引排序返回有序列表 ordered [results[i] for i in range(len(segments))] return ordered用索引命名分片文件00000.ts、00001.ts是保证顺序的简单办法。合并时按文件名排序即可不依赖下载完成的顺序。并发数不要开太大。我试过开32个线程结果被CDN限流大量分片返回403。8到16个是比较稳妥的范围具体看目标服务器的容忍度。4.4 合并分片与格式转换所有ts分片下载完成后直接二进制拼接就能得到可播放的视频文件。ts格式本身支持拼接这是HLS协议的设计特性。def merge_segments(ordered_segments, output_path): with open(output_path, wb) as out: for path, ok in ordered_segments: if not ok: print(f警告分片缺失 {path}) continue with open(path, rb) as f: out.write(f.read()) print(f合并完成: {output_path})拼接出来的文件是ts容器格式大部分播放器能直接播。如果要用剪辑软件处理建议用ffmpeg转成mp4ffmpeg -i merged.ts -c copy -bsf:a aac_adtstoasc output.mp4-c copy表示不重新编码只做容器转换速度极快。-bsf:a aac_adtstoasc是处理音频流的比特流过滤器ts转mp4时经常需要不加可能导致音频异常。5. 那些教程不会告诉你的坑5.1 证书装了还是unknown的三种原因这是最高频的问题。证书明明装了Charles里还是unknown。按可能性排序原因一证书装到了用户存储区但目标App只信任系统证书。前面说过安卓7.0以上这是默认行为。换低版本模拟器或root后装系统证书。原因二代理配置的IP或端口不对。模拟器里配的代理必须是电脑的局域网IP不是127.0.0.1。端口要和Charles设置里的一致默认8888但如果你改过就要对应改。原因三目标App使用了证书固定SSL Pinning。有些App会在代码里硬编码只信任特定证书不信任系统证书链。这种情况下Charles的证书再正确也没用。应对方法涉及hook框架复杂度较高不在本文讨论范围。遇到这种建议换目标或放弃。排查顺序先用浏览器验证证书是否生效如果浏览器能抓而App不能基本就是原因一或原因三。5.2 分片下载403的Referer与User-Agent问题媒体CDN通常有防盗链机制检查请求头里的Referer和User-Agent。如果你下载分片时只带了URL没带这两个头服务器直接返回403。解决办法是从Charles里把请求头完整复制出来在Python里原样带上headers { User-Agent: Mozilla/5.0 (Linux; Android 7.1.2) ..., Referer: https://servicewechat.com/..., Accept: */*, Accept-Encoding: identity, }Accept-Encoding设成identity很重要。如果设成gziprequests会自动解压但ts是二进制流不应该被当作文本压缩处理。设成identity让服务器返回原始字节。5.3 m3u8里的加密分片怎么识别有些m3u8里会出现#EXT-X-KEY标签#EXT-X-KEY:METHODAES-128,URIkey.key,IV0x...这表示分片用AES-128加密了需要先下载key文件再用key解密每个分片。解密逻辑from Crypto.Cipher import AES def decrypt_segment(encrypted_data, key, iv): cipher AES.new(key, AES.MODE_CBC, iv) return cipher.decrypt(encrypted_data)key文件就是16字节的原始密钥IV在m3u8里以十六进制给出。如果m3u8里没有#EXT-X-KEY说明没加密直接下载即可。遇到加密的先确认自己有权处理再动手。5.4 模拟器网络模式导致的抓包失败雷电模拟器有桥接和NAT两种网络模式。NAT模式下模拟器通过电脑上网代理配置正确就能抓。桥接模式下模拟器相当于独立设备代理配置方式不同。如果按教程配了代理还是抓不到检查一下模拟器的网络模式。另外有些模拟器自带网络加速或代理功能会和Charles冲突。在模拟器设置里把这些功能关掉。6. 把流程串起来一个可复用的下载脚本骨架把前面的函数组装成一个完整脚本输入m3u8地址和请求头输出合并后的视频文件。def download_drama(m3u8_url, headers, output_dir, output_nameoutput.ts): os.makedirs(output_dir, exist_okTrue) print(步骤1: 下载并解析m3u8) segments fetch_m3u8(m3u8_url, headers, output_dir) print(f共 {len(segments)} 个分片) print(步骤2: 并发下载分片) ordered download_all_segments(segments, headers, output_dir) failed [p for p, ok in ordered if not ok] if failed: print(f有 {len(failed)} 个分片下载失败建议重跑) print(步骤3: 合并分片) output_path os.path.join(output_dir, output_name) merge_segments(ordered, output_path) return output_path调用时把从Charles复制的请求头填进去headers { User-Agent: 你的UA, Referer: 你的Referer, } download_drama( m3u8_url从Charles里复制的m3u8地址, headersheaders, output_dir./drama_ep01 )这个骨架可以根据需要扩展批量处理多集、自动读取接口返回的m3u8列表、下载完成后自动调ffmpeg转mp4等。7. 关于效率与稳定性的几点实操心得并发数不是越大越好。我最初图快开了32线程结果CDN直接限流一半分片403。后来降到8线程稳定跑完。如果目标服务器响应快可以试12到16但要做好失败重试。分片下载失败不要慌脚本里已经带了重试。如果重试后还有失败把失败的分片单独拎出来重新下载不用整个重跑。可以在脚本里加个逻辑只下载本地不存在的分片已下载的跳过。这样重跑成本很低。m3u8地址有时效性。有些接口返回的m3u8链接带token几小时后就失效。所以拿到地址后尽快下载不要拖。如果确实需要长期保存下载完的ts分片和合并后的视频才是可靠的原始链接不可靠。最后Charles的会话可以导出为.chls文件方便以后回看。如果某个接口的参数逻辑没搞明白导出会话慢慢分析比对着屏幕干看强。这套流程我跑过好几次从最初的证书卡壳到后来的批量下载中间踩的坑基本都在这了。核心就三件事证书配对、请求头带全、分片顺序别乱。把这三件事做好剩下的就是写脚本的体力活。
延伸阅读

更多相关文章

2026/9/20 19:01:39

基于Matlab的固体火箭发动机零维内弹道仿真实现与验证

简介:面向固体火箭发动机设计与仿真领域的Matlab模拟器压缩包,适合航天、机械及计算机仿真方向的研究者与工程师,用于在物理试验前完成点火、燃烧、推进等环节的虚拟验证。包内共7个文件,包含3个Matlab源程序覆盖主流程、发动机结…

2026/9/20 20:01:46

Java实现五子棋AI:Alpha-Beta剪枝实战与性能优化

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/20 20:01:46

Bootstrap图书商城前台模板实战:页面拆解与二次开发指南

简介:这是一份基于Bootstrap构建的图书商城前台页面模板,面向Web前端开发初学者和需要快速搭建商城界面的开发者,帮助解决从零编写页面样式与交互逻辑耗时的问题。整个模板共包含649个文件,压缩包约6.64MB,其中146个ht…

2026/9/20 20:01:46

TabPFN 快速上手指南:10分钟搞定表格数据的分类与回归

TabPFN 快速上手指南:10分钟搞定表格数据的分类与回归 【免费下载链接】TabPFN ⚡ TabPFN: Foundation Model for Tabular Data ⚡ 项目地址: https://gitcode.com/GitHub_Trending/ta/TabPFN 手里只有几百行样本、又要在一两天内交付模型,这是数…

2026/9/20 20:01:46

LibreChat自建指南:多模型AI聚合平台部署与踩坑实录

前阵子我把电脑里七个AI聊天客户端全部卸了,最后只留一个自建服务,就是LibreChat。如果你手上同时握着OpenAI、Claude、Gemini好几家的API Key,又不想在几个网页之间来回切,那这篇文章应该正对你的胃口。LibreChat本质是一个开源的…

2026/9/20 19:56:45

ADB自适应远光电子系统架构:感知、决策与执行全链路设计

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/20 0:04:49

GAMP 5 基于风险的计算机化系统验证:软件分类与审计追踪实践

简介:《A Risk-Based Approach to Compliant GxP Computerized Systems》即业内熟知的GAMP 5指南,面向制药企业质量与IT合规人员、验证工程师及计算机化系统管理者,用于解决GxP法规环境下系统合规性难以科学落地的问题。文档以风险管理为主线…

2026/9/20 0:04:49

安全托管MSSP实战:从静态防御到人机协同的攻防运营与应急响应

简介:这份PPT围绕互联网业务安全托管服务展开,面向企业安全负责人、IT运维人员及关注MSSP/MSS选型的读者,重点回应传统安全过度依赖人工、碎片化静态防御难以对抗产业化攻击等痛点。资源共1个pptx文件,包体约30.63MB,以…

2026/9/20 0:04:49

GAMP 5 基于风险的计算机化系统验证:软件分类与审计追踪实践

简介:《A Risk-Based Approach to Compliant GxP Computerized Systems》即业内熟知的GAMP 5指南,面向制药企业质量与IT合规人员、验证工程师及计算机化系统管理者,用于解决GxP法规环境下系统合规性难以科学落地的问题。文档以风险管理为主线…

2026/9/20 0:04:49

安全托管MSSP实战:从静态防御到人机协同的攻防运营与应急响应

简介:这份PPT围绕互联网业务安全托管服务展开,面向企业安全负责人、IT运维人员及关注MSSP/MSS选型的读者,重点回应传统安全过度依赖人工、碎片化静态防御难以对抗产业化攻击等痛点。资源共1个pptx文件,包体约30.63MB,以…

2026/9/20 4:54:47

USB Type-C PCB布局分区设计:电源、高速信号与PD协议全攻略

做硬件这行,Type-C接口算是典型的“看着简单,做起来全坑”的东西。光引脚就24个,高低速信号、电源、控制线全部塞在一个小小的连接器里,如果PCB布局不做规划,打样回来基本就是“插上没反应”、“高速掉线”、“静电一打…

2026/9/20 5:01:23

系统编程学习原型如何补齐稳定性边界

系统编程学习原型如何补齐稳定性边界预算有限时&#xff0c;我先优化明显多余的复制&#xff0c;而不是猜测性地换容器。用借用传递只读数据通常就能减少分配&#xff1a; fn parse(line: &str) -> Result<Item, Error> { /* ... */ }用基准确认热点确实在分配&am…

2026/9/20 5:09:33

雨花区哪家财务公司代理记账比较好?

在雨花区&#xff0c;企业处理财税事务常常面临诸多挑战&#xff0c;选择一家靠谱的财务公司至关重要。湖南巨勤财务管理咨询有限公司就是本地正规实体财税服务机构&#xff0c;深耕本地工商财税行业多年&#xff0c;熟悉当地工商局、税务局最新政策与申报流程。主营公司注册、…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
咨询二维码