ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

移动端数据采集实战:Fiddler抓包与今日头条接口还原

移动端数据采集实战:Fiddler抓包与今日头条接口还原

1. 移动端数据采集的底层逻辑与方案选型

1.1 为什么移动端采集比网页采集更棘手

做过网页采集的人第一次转向移动端时,往往会有一个错觉:不就是换个请求地址吗?实际上,移动端的数据采集难度比网页端高出一个量级。网页端你打开开发者工具,所有请求一目了然,请求头、参数、响应体全部透明。但移动端App是一个封装好的黑盒,你无法直接看到它发出了哪些请求、带了什么参数、走了什么协议。

更麻烦的是,移动端App普遍采用加密传输。以资讯类App为例,客户端会对请求参数做签名处理,常见的有MD5签名、HMAC签名、AES加密等。你就算抓到了请求,直接复制到代码里重放,大概率会返回“签名校验失败”或者“参数错误”。这就意味着,移动端采集的核心难点不在于写代码,而在于搞清楚请求的构造规则。

我刚开始接触移动端采集时,踩过最大的坑就是以为抓包工具能直接看到明文参数。实际上,很多App的关键参数在抓包工具里显示的是加密后的字符串,你需要逆向分析或者通过其他手段还原。这个过程需要耐心,也需要对HTTP协议、加密算法有基本的理解。

1.2 抓包工具的选择:为什么是Fiddler

市面上抓包工具不少,Charles、Wireshark、mitmproxy、Fiddler各有各的适用场景。我选Fiddler作为主力工具,主要基于以下几个考量。

第一,Fiddler对HTTP/HTTPS的解密支持非常成熟。它本质上是一个中间人代理,客户端把请求发给Fiddler,Fiddler再转发给服务器,服务器返回的数据也经过Fiddler再回到客户端。只要在客户端安装了Fiddler的根证书,HTTPS流量就能被解密查看。这个机制虽然简单,但Fiddler在证书管理和会话展示上做得非常顺手。

第二,Fiddler的过滤和搜索功能很强。移动端App的请求量非常大,一个页面可能触发几十个请求,其中大部分是图片、日志、埋点等无关请求。Fiddler支持按域名、按响应类型、按状态码过滤,能快速定位到你需要的那几个接口。

第三,Fiddler支持自定义脚本。它内置了JScript.NET脚本引擎,你可以写脚本自动修改请求或响应,比如批量替换请求头、自动解密某个字段。这个功能在调试阶段非常实用。

当然,Fiddler也不是没有缺点。它只支持Windows平台(虽然有Linux和Mac的替代方案,但体验不如Windows原生),而且界面相对老旧。但就移动端抓包这个场景来说,Fiddler的稳定性和功能完整度依然是第一梯队的。

1.3 整体技术路线拆解

整个采集方案可以拆成四个阶段:环境搭建、抓包分析、请求模拟、数据解析与存储。

环境搭建阶段需要解决三个问题:Fiddler的安装与配置、移动端设备的代理设置、HTTPS证书的安装与信任。这三个环节任何一个出问题,都会导致抓不到包或者抓到乱码。

抓包分析阶段的核心任务是找到目标接口。你需要反复操作App,观察Fiddler会话列表的变化,通过对比不同操作下的请求差异,定位到承载目标数据的那个接口。这个阶段最考验耐心,因为App的请求命名往往没有规律,你需要靠响应内容来判断。

请求模拟阶段是把抓到的请求翻译成Python代码。这里的关键是还原请求头、请求参数和签名逻辑。请求头里的User-Agent、Cookie、Referer等字段往往有校验,参数里的签名更是必须还原。我通常会先用requests库直接重放抓到的请求,确认能拿到数据,再逐步精简参数,找到最小必要集合。

数据解析与存储阶段相对简单,主要是JSON解析和数据库写入。但要注意的是,移动端接口返回的数据结构往往比网页端更复杂,嵌套层级更深,需要仔细梳理字段关系。

2. Fiddler抓包环境搭建与配置细节

2.1 Fiddler的安装与基础配置

Fiddler的安装没什么好说的,官网下载安装包,一路下一步就行。但安装完成后的配置才是关键,很多人卡在这一步。

首先打开Fiddler,进入Tools > Options,在HTTPS选项卡里勾选“Capture HTTPS CONNECTs”和“Decrypt HTTPS traffic”。这两个选项是解密HTTPS流量的前提。勾选后Fiddler会提示你安装根证书,点击“Yes”即可。安装完成后,你可以在Windows的证书管理器里看到Fiddler的根证书,说明安装成功。

接下来是Connections选项卡。默认情况下Fiddler监听的是8888端口,这个端口你可以改成其他值,但没必要。关键是勾选“Allow remote computers to connect”,这个选项允许局域网内其他设备把Fiddler作为代理。不勾选的话,手机连上来也抓不到包。

还有一个细节:Fiddler默认只捕获来自浏览器的流量,对于非浏览器进程的流量需要额外配置。在Tools > Options > Connections里,有一个“Act as system proxy on startup”选项,勾选后Fiddler会自动设置系统代理。但移动端抓包不依赖这个,手机是手动设置代理的。

配置完成后,建议重启一次Fiddler,确保所有设置生效。我遇到过好几次配置没生效的情况,重启后就好了。

2.2 移动端代理设置与证书安装

移动端设置代理的步骤因系统而异,但核心逻辑是一样的:让手机的HTTP/HTTPS流量经过Fiddler所在的电脑。

以Android为例,进入WiFi设置,长按当前连接的WiFi,选择“修改网络”,在高级选项里把代理设为“手动”,主机名填电脑的局域网IP,端口填8888。这里有个坑:电脑的IP必须是局域网IP,不能是127.0.0.1。你可以在电脑上打开命令行,输入ipconfig查看局域网IP,通常是192.168.x.x或10.x.x.x。

设置完代理后,用手机浏览器访问一个HTTP网站,比如http://www.example.com,如果Fiddler里能看到这个请求,说明代理设置成功。如果看不到,检查防火墙是否拦截了8888端口,或者手机和电脑是否在同一个局域网。

接下来是安装证书。在手机浏览器里访问http://电脑IP:8888,Fiddler会返回一个页面,点击“FiddlerRoot certificate”下载证书。下载完成后,在手机的设置里找到“安全”或“加密与凭据”,选择“从存储设备安装证书”,找到刚才下载的文件安装。

这里有个关键点:Android 7.0及以上版本,App默认不信任用户安装的证书。也就是说,你安装了Fiddler证书,但App的HTTPS流量依然无法解密。解决办法有两个:一是把证书安装到系统证书区(需要root权限),二是用Xposed模块或VirtualApp等工具绕过证书校验。如果目标App没有做证书固定(SSL Pinning),用第一种方法就行;如果做了证书固定,就需要更复杂的处理。

iOS的证书安装相对简单,下载证书后在“设置 > 通用 > 关于本机 > 证书信任设置”里手动信任即可。但iOS对代理的管控更严格,某些App会检测代理设置,发现代理后直接拒绝请求。

2.3 抓包环境验证与常见问题排查

环境搭好后,不要急着去抓目标App,先用一个简单的场景验证整个链路是否通畅。我通常会用手机浏览器访问一个HTTPS网站,比如百度,然后在Fiddler里看能不能解密出明文内容。如果能,说明证书安装正确;如果不能,说明证书没被信任。

验证通过后,再打开目标App。这时候你会看到Fiddler里涌出大量请求。不要慌,先按域名过滤,找到目标App的主域名。然后逐个点击请求,看响应内容里有没有你需要的数据。

常见问题有几个:一是Fiddler里显示“Tunnel to”,说明HTTPS解密没生效,检查证书是否安装正确;二是请求显示红色感叹号,说明连接失败,可能是代理设置有问题;三是响应内容乱码,说明编码格式不对,在Fiddler的Response面板里切换编码即可。

还有一个容易被忽略的问题:Fiddler的会话列表默认只保留最近的请求,如果请求太多,旧的会被冲掉。你可以在File > Capture Traffic里调整缓冲区大小,或者用Filters过滤掉无关请求。

3. 今日头条App接口分析与请求还原

3.1 定位目标接口的实操方法

打开今日头条App,在Fiddler里观察请求。你会发现请求量非常大,有图片、有日志、有埋点、有广告,真正承载内容的接口往往藏在其中。

我的方法是:先清空Fiddler会话列表,然后在App里做一个明确的操作,比如刷新首页,观察新增的请求。通常首页内容接口的响应体比较大,返回的是JSON格式,里面包含文章标题、摘要、图片链接等字段。你可以按响应大小排序,快速找到大响应体的请求。

找到疑似接口后,不要急着写代码,先在Fiddler里仔细分析这个请求。看它的请求方法(GET还是POST)、请求URL、请求头、请求参数、响应结构。今日头条的接口通常是GET请求,参数里包含设备信息、用户信息、时间戳、签名等。

这里有个技巧:在Fiddler里右键点击请求,选择“Copy > Just URL”,可以把完整URL复制出来。然后把这个URL粘贴到浏览器的无痕模式里访问,看能不能拿到数据。如果能,说明这个接口没有严格的签名校验,直接模拟请求即可;如果不能,说明有签名或Cookie校验,需要进一步分析。

3.2 请求头与关键参数拆解

今日头条的请求头里有几个关键字段需要特别注意。

User-Agent字段标识了客户端类型和版本。今日头条的UA通常包含“NewsArticle”或“news_article”等关键词,后面跟着版本号。这个字段必须和抓包时一致,否则服务器可能返回不同的数据格式。

Cookie字段里包含了用户标识和会话信息。今日头条的Cookie里通常有“sessionid”、“sid_tt”等字段,这些是用户登录后的凭证。如果你不登录,有些接口也能访问,但返回的数据可能不完整。

请求参数里最麻烦的是签名参数。今日头条的接口通常有一个“_signature”参数,这个参数是通过特定算法生成的,算法逻辑在App的so文件里。如果你直接复制抓到的签名,短时间内能用,但签名有时效性,过期后就失效了。

我的做法是:先不纠结签名,用抓到的完整URL和请求头直接重放,看能拿到多少数据。如果签名过期导致失败,再考虑逆向分析签名算法。对于学习目的来说,抓包后短时间内重放通常能成功,足够用来练习数据解析和存储。

3.3 用Python还原请求的完整过程

假设我们已经抓到了一个可用的请求,现在用Python来还原它。首先安装requests库:

pip install requests

然后写一个最简单的重放脚本:

import requests url = "抓到的完整URL" headers = { "User-Agent": "抓到的UA", "Cookie": "抓到的Cookie", # 其他请求头 } response = requests.get(url, headers=headers) print(response.status_code) print(response.text[:500])

运行后如果返回200且内容正常,说明请求还原成功。如果返回403或签名错误,说明有额外的校验。

接下来是精简参数。把URL里的参数逐个删除,看哪些是必需的,哪些是可选的。通常设备信息、时间戳、签名是必需的,其他参数可以省略。精简后的URL更简洁,也更容易维护。

如果签名是必需的且有时效性,你需要分析签名算法。今日头条的签名算法涉及多个参数的拼接和加密,逆向难度较高。对于学习目的,我建议先用抓到的签名做短时间内的数据采集,或者寻找其他不需要签名的接口。今日头条有一些接口(如搜索接口)的签名校验相对宽松,适合练手。

4. 数据解析、存储与采集策略优化

4.1 JSON响应结构解析与字段提取

今日头条的接口返回的是JSON格式,结构通常是这样:

{ "data": [ { "title": "文章标题", "abstract": "文章摘要", "article_url": "文章链接", "publish_time": "发布时间", "source": "来源", "image_list": [...] } ], "has_more": true, "next_page": "..." }

解析的时候用response.json()直接转成字典,然后逐层提取。注意有些字段可能是None,提取时要加判断。图片链接通常在image_list里,是一个数组,每个元素包含url字段。

我习惯把提取逻辑封装成函数,传入JSON对象,返回一个扁平化的字典。这样后续存储时更方便。

def parse_article(item): return { "title": item.get("title", ""), "abstract": item.get("abstract", ""), "url": item.get("article_url", ""), "publish_time": item.get("publish_time", ""), "source": item.get("source", ""), "images": ",".join([img.get("url", "") for img in item.get("image_list", [])]) }

4.2 数据存储方案:从CSV到数据库

小规模采集用CSV就够了,简单直接,用pandas的to_csv方法一行搞定。但CSV有几个问题:不支持并发写入、字段多了容易乱、查询不方便。

数据量大了之后,建议用SQLite。SQLite是文件型数据库,不需要额外安装服务,Python内置支持。建表的时候把字段类型定义好,插入的时候用executemany批量插入,效率比逐条插入高很多。

import sqlite3 conn = sqlite3.connect("toutiao.db") cursor = conn.cursor() cursor.execute(""" CREATE TABLE IF NOT EXISTS articles ( id INTEGER PRIMARY KEY AUTOINCREMENT, title TEXT, abstract TEXT, url TEXT UNIQUE, publish_time TEXT, source TEXT, images TEXT ) """) cursor.executemany(""" INSERT OR IGNORE INTO articles (title, abstract, url, publish_time, source, images) VALUES (?, ?, ?, ?, ?, ?) """, data_list) conn.commit()

注意url字段加了UNIQUE约束,配合INSERT OR IGNORE可以自动去重。这个技巧在采集场景里非常实用,避免重复数据。

4.3 采集频率控制与反爬应对

今日头条对频繁请求有风控机制。如果你短时间内发送大量请求,服务器会返回验证码或者直接封IP。控制采集频率是必须的。

我的做法是:每次请求后随机sleep 1到3秒,模拟人工操作的节奏。如果发现返回状态码异常(如403、429),立即停止采集,等待几分钟后再试。

另外,请求头里的User-Agent和Cookie要定期更换。如果你有多个账号,可以轮换使用。没有多个账号的话,至少把User-Agent换成不同机型的,降低被识别的概率。

还有一个细节:今日头条的接口返回的next_page参数是分页的关键。每次请求后从响应里提取next_page,作为下一次请求的参数。如果next_page为空,说明没有更多数据了,停止采集。

5. 常见问题排查与实操避坑指南

5.1 抓包工具与证书类问题

问题一:Fiddler抓不到手机的请求。排查步骤:确认手机和电脑在同一局域网;确认Fiddler的“Allow remote computers to connect”已勾选;确认手机代理设置正确;确认电脑防火墙没有拦截8888端口。如果都不行,试试关闭电脑的防火墙再测试。

问题二:HTTPS请求显示“Tunnel to”,无法解密。这说明证书没被信任。Android 7.0以上需要把证书安装到系统证书区,或者用VirtualApp等工具绕过。iOS需要在“证书信任设置”里手动开启信任。

问题三:App检测到代理,拒绝请求。有些App会检测系统代理设置,发现代理后直接返回错误。解决办法是用透明代理或者用iptables做流量转发,但这需要root权限。对于学习目的,可以换一个检测不严格的App练手。

5.2 请求模拟与签名类问题

问题四:重放请求返回“签名错误”。签名有时效性,抓到的签名可能已经过期。解决办法是尽快重放,或者分析签名算法。如果签名算法太复杂,可以寻找不需要签名的接口。

问题五:请求返回的数据和App里看到的不一样。可能是请求头里的某个字段缺失,导致服务器返回了不同的数据版本。仔细对比抓包时的请求头和代码里的请求头,确保完全一致。

问题六:Cookie过期导致请求失败。Cookie里的sessionid有时效性,过期后需要重新抓包获取新的Cookie。建议把Cookie存在配置文件里,方便更新。

5.3 数据解析与存储类问题

问题七:JSON解析报错。有些接口返回的不是标准JSON,可能包含多余字符或者编码问题。用response.text查看原始内容,确认格式后再解析。如果是编码问题,设置response.encoding = 'utf-8'。

问题八:数据重复插入。用INSERT OR IGNORE配合UNIQUE约束可以自动去重。如果url字段没有UNIQUE约束,可以在插入前先查询是否存在。

问题九:采集速度太慢。单线程采集确实慢,但多线程容易触发风控。折中方案是用异步IO(aiohttp),在控制频率的前提下提高并发。但要注意,异步请求也要加sleep,否则和单线程没区别。

5.4 实操避坑速查表

问题类型具体表现排查方向解决方案
抓包失败Fiddler无请求代理设置、防火墙检查局域网、端口、代理配置
证书问题Tunnel to证书未信任安装证书到系统区或绕过校验
签名错误403/签名校验失败签名过期或算法不对尽快重放或逆向签名算法
数据不一致返回内容与App不同请求头缺失对比请求头,补全字段
Cookie过期登录态失效sessionid过期重新抓包更新Cookie
解析报错JSONDecodeError返回非标准JSON检查原始内容,处理编码
数据重复同一条数据多次入库无去重机制加UNIQUE约束,用INSERT OR IGNORE
采集被限429/验证码频率过高加随机sleep,更换UA和Cookie

这张表是我在实际操作中反复踩坑后总结的,基本上覆盖了移动端采集80%的问题。遇到新问题时,先对照这张表排查,能省不少时间。

6. 采集策略的进阶思考与经验沉淀

6.1 从单接口到多接口的扩展思路

掌握了单个接口的采集方法后,你可以把思路扩展到多个接口。今日头条的App里有很多不同类型的接口:首页推荐、频道列表、搜索、评论、用户主页等。每个接口的请求参数和签名逻辑可能不同,但抓包分析的方法是一样的。

我的建议是先从最简单的接口入手,比如搜索接口。搜索接口的参数少,签名校验相对宽松,适合练手。等熟悉了整个流程,再去攻克首页推荐这种签名复杂的接口。

多接口采集时,建议把每个接口的请求逻辑封装成独立的函数,统一用同一个请求会话(requests.Session)来管理Cookie和连接池。这样代码结构清晰,也方便维护。

6.2 数据采集的边界与合规意识

做数据采集这些年,我越来越意识到边界感的重要性。技术本身没有对错,但使用技术的方式有边界。采集公开数据用于学习研究,和采集用户隐私数据用于商业目的,性质完全不同。

我的原则是:只采集公开可见的内容,不碰用户隐私数据;控制采集频率,不影响目标服务的正常运行;不将采集的数据用于商业用途或二次分发。这些原则不是法律要求,而是作为一个技术从业者的基本自律。

另外,采集到的数据要妥善保管,不要随意分享或上传到公开平台。尤其是包含用户信息的数据,一旦泄露,后果很严重。

6.3 工具链的持续优化方向

Fiddler加requests的组合适合入门和中小规模采集。如果你要长期做数据采集,可以考虑以下几个优化方向。

一是用mitmproxy替代Fiddler。mitmproxy是命令行工具,支持Python脚本扩展,可以自动化处理请求和响应。它更适合集成到自动化流程里,比如自动保存抓到的请求、自动重放、自动解析。

二是用Scrapy框架替代手写requests。Scrapy提供了完整的采集框架,包括调度器、下载器、管道、中间件等组件。对于大规模采集,Scrapy的效率和可维护性远超手写脚本。

三是用代理池解决IP限制问题。如果你需要大量采集,单个IP肯定不够用。代理池可以轮换IP,降低被封的风险。但代理池的搭建和维护成本不低,需要权衡投入产出比。

四是把数据存储从SQLite升级到PostgreSQL或MongoDB。SQLite适合单机小规模,数据量大了之后查询和写入都会变慢。PostgreSQL适合结构化数据,MongoDB适合半结构化数据,根据你的数据类型选择。

这些优化方向不是必须的,而是根据实际需求逐步引入的。我见过很多人一上来就搭复杂的架构,结果采集量根本没到那个级别,白白浪费精力。先把最简单的方案跑通,遇到瓶颈再优化,这才是务实的做法。

6.4 我个人在实际操作中的几点体会

最后分享几点个人体会,都是踩坑踩出来的。

第一,抓包前先想清楚要什么数据。不要打开Fiddler就开始乱抓,先明确目标:你要采集的是文章列表、评论、还是用户信息?目标明确了,才能在海量请求里快速定位。

第二,请求还原不要追求一步到位。先用完整请求跑通,再逐步精简。我见过有人一开始就想构造最小请求,结果调了半天调不通,浪费时间。

第三,签名问题不要死磕。如果签名算法太复杂,逆向成本太高,就换个思路:找不需要签名的接口,或者用抓到的签名做短时间采集。学习阶段,跑通流程比攻克算法更重要。

第四,数据解析要留容错。接口返回的字段可能缺失、类型可能变化,解析代码要加try-except和默认值。我吃过亏,一个字段为None导致整个解析崩溃,采集了几千条数据全丢了。

第五,采集频率宁慢勿快。快了一时爽,被封火葬场。控制好频率,细水长流,比一次性采完然后被封IP划算得多。

这些体会没有什么高深的技术含量,但都是实打实的经验。技术可以学,经验只能攒。希望这些内容能帮你少走点弯路。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进