截获法 ·「谁已经做到了」(没有接口也能做)
> 提炼自真实任务:一个「每一步直白地说都不行」的数据搬运任务(下载被签名拦 / 上传无 API / 列表抓 0 条),每一步实际都跑通了。本技能是那次经验的完整沉淀。
一句话
别问「我能不能做到」,问「谁已经做到了」。
接口不存在 ≠ 能力不存在。 只要系统里有任何一方在事实上已经完成了这件事,它就是可行的——你不必重新实现那件事,只需截获它手里的产物,并借它的身份去执行。
判据:什么时候才是真的「不行」
| 情形 | 结论 | 动作 |
|---|---|---|
| 系统里有任何主体已经在事实上做到(浏览器正在播、网页能点按钮、App 已下载、DOM 已渲染、系统已解密) | 可行 | 换通道 → 用本方法 |
| 没有任何主体持有它(别人的私聊记录、需要对方私钥、物理上不存在) | 真不行 | 别硬来,直接说 |
关键分野:「我选的这条路不通」≠「这件事做不到」。
把「不行」翻译成一个具体的阻断点:403?没有 API?要签名?要登录态?要验证码?你会发现阻断点几乎总是通道级的,而不是能力级的——通道可以换,能力不能凭空造。
五步操作
- 拆「不行」的真身——把「做不到」翻译成具体阻断点(落在哪一层、卡在哪个响应码)。
- 找已经做到的主体——在同一系统里扫一遍:谁在事实上已经完成了这件事?(GUI 真人用户 / 浏览器渲染进程 / App / 系统服务 / 另一个已登录的客户端)
- 列出它手上必须有什么——它为了做到这件事,必然握着哪些凭证?(真实 URL / cookie / token / DOM 节点 / 内存对象 / 网络响应体 / 解密后的数据)
- 借它的身份执行——复用它的上下文:同一个浏览器 profile、同一个已登录 session、同一个进程。不要另起一套无身份的请求(那正是被 403 的原因)。
- 用最终结果验证——不信「看起来对」的代理信号,看最终产物成不成立。
核心分野:重做 vs 截获
| 做法 | 代价 | |
|---|---|---|
| 重做 | 自己补上缺失那一环(实现签名算法、逆向内部 API) | 成本高、易被风控、常常真做不到 |
| 截获 | 让已拥有能力的主体去跑,我只拿结果 | 低成本、稳 |
实例:视频站要 X-Bogus 签名,自己实现签名=重做(近乎不可行);让浏览器去播、从网络记录里拿 CDN 地址=截获(几行代码)。
L2 深挖:「谁」是可以机械枚举的,不需要灵感
第一层方法有个隐患:它靠临场灵光一现——那就不可靠、不可复用。再挖一层:「谁已经做到了」根本不是一个灵感问题,而是一个可枚举的搜索问题。
> 任何「做不到」都能拆成 (目标产物)×(可能已持有它的主体)×(可截获的位置)。这三者的笛卡尔积是有限且可枚举的——顺着清单扫一遍就知道哪个格子里有货。
枚举矩阵(卡住就顺着扫,别凭空想)
| 主体(谁可能已做到) | 可截获的位置 | 手法 |
|---|---|---|
| 浏览器渲染进程 | DOM、网络响应、window 全局变量、localStorage / IndexedDB | capability_probe.py 四层扫描 |
| 已登录的 GUI 客户端 | 它发出的请求、它的本地缓存库 | mitmproxy 抓包 / 读它的 SQLite · LevelDB |
| 网络层 | 明文流量 | 本机代理(Charles / mitmproxy) |
| 系统服务 / OS | 剪贴板、钥匙串、辅助功能树、截图、通知 | osascript / Accessibility / screencapture |
| 手机 App | 抓包、备份文件 | mitmproxy + 证书 / idevicebackup |
| 本地缓存 | 临时文件、下载目录、系统缓存目录 | find + mtime 过滤 |
| 另一个入口 | 网页版 vs App 版 vs 小程序 vs 开放平台 | 换 endpoint 重试 |
| 第三方轮子 | 别人已逆向好的开源实现 | GitHub / yt-dlp 生态里搜 |
7 步自动流程
| 步 | 动作 | 自动? |
|---|---|---|
| S1 | 定义产物:把"想做的事"翻译成"要拿到哪个具体东西" | 人工 |
| S2 | 定位阻断点:在哪一步失败、返回什么 | 人工 |
| S3 | 枚举主体:跑上面那张矩阵,圈出候选 | 半自动(有清单) |
| S4 | 反推关键词:产物若存在,必然包含什么字符串 | 人工 |
| S5 | 自动探测:capability_probe.py(扫 4 个截获面) |
✅ 自动 |
| S6 | 自动取物:加 --save-dir,借浏览器身份把命中资源拉下来 |
✅ 自动 |
| S7 | 验证:看最终产物成立不成立——不信代理信号 | 人工 / 半自动 |
S3 的清单化是关键:可枚举,就不必依赖灵感。
本次实例(视频站收藏 → 网盘转存)
| 卡点 | 官方通道说「不行」 | 谁已经做到了 | 截获了什么 | 借谁的身份 |
|---|---|---|---|---|
| 下载视频 | yt-dlp 403(要签名) | 浏览器正在播这个视频 | 网络请求记录里的真实 CDN URL | 浏览器自身上下文(page.request) |
| 上传到网盘 | 没有开放上传 API | 网页端用户能点「上传」按钮 | 上传按钮 + 文件选择器 | 已登录的网盘浏览器 profile |
| 判断登录态 | cookie 名 sessionid 存在(假阳性) |
页面真实渲染 | DOM 文本「未登录」 | — |
| 采集收藏列表 | 脚本抓 0 条 | 页面已渲染 76 条 | DOM 里的真卡片(img[alt] 非空) |
已登录的视频站 profile |
第 3、4 行是反向用法:不是「绕过去」,而是「别被看起来行的假信号骗了」——判据同样要取已经真实发生的那个东西(页面渲染结果),而不是代理变量(cookie 名)。
代价与边界(什么时候不该用)
- 稳定性差:依赖 GUI 结构,页面一改版就崩 → 必须加结构校验 + 失败显形,别静默失败。
- 慢:要起浏览器、要等渲染。
- 风控风险:高频自动化会触发平台风控 → 控制频率、加随机延时、别并发猛刷。
- 合规:只用于自己的数据 / 已授权内容。
顺序(与相邻方法的关系)
- 官方 API / 正常通道——有就用,最稳
- 截获法(本方法)——换通道,我自己就能做
- 换执行者——换个模型 / 工具链视角重试,截获法试不出来才用
- 才允许标记「需人工」,且必须附:试过哪些路径、各自返回什么
自动化工具(S5 / S6 的落地)
随包附带 capability_probe.py:自动扫 4 个截获面(网络 / DOM / 内存 / 存储),并可选借浏览器身份自动取物(自带 cookie + Referer,这是绕开 403 的关键)。
python3 capability_probe.py <URL> --want "关键词" --profile <已登录profile目录> \
[--click --scroll N] [--save-dir DIR --save-max N --min-bytes N] [--out 报告.md]
实测(正是 yt-dlp 403 拿不到的那个东西):
python3 capability_probe.py "https://www.douyin.com/video/7694927486508862747" \
--want "douyinvod" --profile douyin --click --wait 12 \
--save-dir ./probe --save-max 2 --min-bytes 102400
# → 命中 6 处,自动保存 16.87 MiB + 14.50 MiB 两个真实视频
已踩过的坑(已写进脚本,别再犯):
- 启动前清
SingletonLock/Cookie/Socket——不清会Mach rendezvous failed content_length不能用来预过滤——206 分段响应的块大小只有 1KB 出头,用它过滤会误杀真视频;只按实际拉取后的大小判断- DOM 命中的 URL 必须保留完整值——截断 400 字符后地址已断,拿不到真货
- 同名文件会互相覆盖(实测 45 MiB 被 18 MiB 冲掉)→ 自动加序号
口诀
接口没有,不等于做不到;找谁已经做到了,截他手里的东西,用他的身份去跑。
而"谁"是可以枚举的——不靠灵感,靠清单。