PLAN_comment_completeness.md 13 KB

评论完整度提升方案(签名能力 / 多源交叉)

状态:方案待评审,尚未实施。 本文档只做设计与论证,不改动 tiktok_stats.js。 立项背景:7552202593888603410 官方显示评论 18,900,tikwm 实得顶级评论 9,931(52%), 天花板卡在 cursor ≈ 10369。计数 4 项(点赞/评论/分享/收藏)已无精度问题,本文只解决评论覆盖率。 编制日期:2026-09-25


一、先把问题问对:那堵墙在哪一层

在讨论"怎么突破"之前,必须先确认 10369 是什么性质。三种假设,对应完全不同的投入:

假设 墙的真实位置 突破手段 成本
H1 tikwm 自己的实现上限(硬编码 / 自身会话) 换源、多源并行 极低
H2 TikTok 对单个匿名会话的读取深度限制 换 session / 换 ttwid / 换 IP 中
H3 官方接口缺签名(X-Bogus + X-Gnarly)被拒 自建签名服务 高

关键旁证(已公开可查,非推测)

  • H2 成立的证据:Apify 的 stanvanrooy6/tiktok-comments-scraper 在 FAQ 中明确描述 ——「TikTok caps how deep one anonymous session can read; the scraper measures that wall and rotates sessions to keep reading」,并给出 2026 年 9 月的实测:两个匿名会话收集 25,133 条 唯一评论(1100 万评论量的视频,30 分钟内),单个会话放慢节奏则读过了 37,000 条。 → 说明「匿名会话深度墙」真实存在,且可以通过轮换会话穿透。
  • H3 成立的证据:TikTok 侧签名已演进为 X-Bogus + X-Gnarly 双签名(X-Gnarly 为新增), 且 TikTok 使用 ttwid + msToken 设备级指纹 + TLS 指纹校验,纯 Python 客户端被秒级拦截。
  • 对 H1 的怀疑:tikwm 能返回 total: 18752,说明它知道总数,却只给到 10369。 这个数字既不等于 18752,也不是整数关口,形态上更像上游会话的读取深度或其自身的软截断。

结论:不先做诊断就投入签名或真机方案,有 50% 以上的概率是在解决一个不存在的问题。


二、L0 诊断(半天,零成本,必做第一步)

四个实验,全部可在现有基础设施上完成(云机 + NekoBox 多节点 + 本机),不改主脚本, 用 _probe/ 下的一次性脚本跑。

实验 0-1:cap 是固定阈值还是随视频变化

取 4 个评论量差异明显的视频(≈500 / 5,000 / 19,000 / 50,000+),各自跑到无数据,记录 cap。

  • 若 cap 全部落在 ≈10369 附近 → 固定阈值(指向 H1 或某个全局参数)
  • 若 cap 随视频规模成比例浮动 → 另有机制,需重新定位

这是信息量最大的一个实验。 一个固定数字意味着可以绕;一个比例意味着要换思路。

实验 0-2:cap 是否与客户端出口 IP 相关

同一视频、同一 cursor 区间,分别用云机节点 A / 节点 B / 本机出口各跑一遍,比较 cap。

  • cap 随出口变化 → 墙绑定出站会话 → 换节点/换 session 可推进(直接支持 L1 与 L3)
  • cap 完全不变 → 与客户端无关,墙在 tikwm 内部或上游

实验 0-3:换镜像源看各自 cap

同一视频分别打 tikwm 与至少一个独立源,记录各自 cap 与返回条目集合。

  • cap 或集合不同 → 多源并集立竿见影(L1 直接可行,成本 1~2 天)
  • 完全一致 → 共享同一上游墙,多源只是浪费钱

实验 0-4:10369 处返回体的形态

抓取 cursor=10300~10400 区间的原始响应,逐条看返回体。

返回形态 含义
code:0 + comments:[] 上游确实没给 → 真墙
code:-1 / 错误码 是限流,不是墙 → 节流/换节点即可
hasMore:true 但空 与已知的「假 false」同族的边界行为

交付物:一张四行的诊断结论表。这张表决定后面投 L1 / L2 / L3 中的哪一条。


三、方案层级

L1|多源交叉补齐(低成本 · 1~2 天 · 仅当 0-3 显示 cap 不同时有效)

思路:不追求单源完整,用多个来源做并集,以 cid 去重。

候选源:

源 接入方式 备注
tikwm 已在用 免费、无 key、当前主力
evil0ctal/douyin_tiktok_download_api 自建 Docker 开源,实现 X-Bogus + A_Bogus,需浏览器 cookie
tokfluence / captapi / parse.bot REST + key 付费,接口干净,有 cursor 分页
Apify Actor 平台调用 见 L4

必须做的量化:并集收敛曲线——每并入一个新源后,新增唯一条目数。当增量 < 1% 时停止, 这就是实际操作上限。没有这条曲线,就永远说不清"还差多少"。

L1 的诚实边界:如果 L0 显示各源共享同一上游墙,L1 的收益接近 0。别跳过诊断直接做 L1。


L2|自建签名服务,直连官方接口(中成本 · 3~7 天 · 对应 H3)

首选基座:carcabot/tiktok-signature(开源、Docker、headless Chromium 注入 TikTok 官方 SDK)

POST http://<host>:8080/signature
{ "url": "https://www.tiktok.com/api/comment/list/?aweme_id=...&cursor=0&count=20" }

→ { "signed_url": "...&X-Bogus=...&X-Gnarly=...",
    "x-bogus": "...", "x-gnarly": "...",
    "device-id": "...", "cookies": "ttwid=...; tt_chain_token=...",
    "navigator": { "user_agent": "...", "platform": "...", ... } }

为什么用浏览器注入而不是纯算法实现:签名与浏览器环境(UA / platform / 屏幕尺寸)强绑定, 且 TikTok 有 TLS 指纹校验。用 TikTok 自己的 JS 生成签名,等价于"借官方 SDK 签名", 在 schema 更新时只需重启容器,而纯 Python 复刻每次更新都要重写算法。

实施要点:

  1. 部署位置:云机(走 NekoBox 出口,IP 与请求目标地区一致)或本机 Docker。
  2. 必须使用响应里返回的 user_agent——UA 不匹配签名直接失效(文档明确警告)。
  3. 拿到 signed_url 后由我们自己发请求(/signature 模式),便于自己控制节流与重试; 浏览器代发的 /fetch 模式只在签名失效时兜底。
  4. 容器会自动刷新会话(每 500 次签名或 30 分钟)。
  5. 复用现有节流器:把"取签名"也计入令牌桶,避免签名服务成为新的限流点。

运维代价(必须提前接受):TikTok 每 2~4 个月更新一次签名 schema, 失效模式是静默的空结果(不是硬报错)。需挂一个每天自检的探针(能否取到非空评论首页)。


L3|云机真机路线(高保真 · 3~5 天 · 利用你的独有资产)

这是本方案里性价比最高但最被低估的一条,因为你的环境有别人没有的东西: root 云机 + 真实 TikTok App + 已登录态 + 可换节点的 NekoBox。 TikTok 的防线(ttwid 设备指纹、TLS 指纹、msToken 行为序列)全部是针对"伪造浏览器"设计的, 而真机+真 App 天然携带全部合法指纹。

L3-A|WebView 自动滚动抓包(★ 推荐)

在 Auto.js 中创建 WebView 加载视频页,注入 JS 同时 hook XMLHttpRequest 与 window.fetch, 捕获 /api/comment/list/ 的响应体;用注入脚本自动滚动评论区触发分页加载。

  • 不是无障碍权限——WebView 是应用内组件,全程 API 驱动,符合你最初"不用无障碍"的约束
  • 复用现有 Auto.js 基础设施与推送流程(/sdcard/Download/ + isolate 目录)
  • 签名由 TikTok 自己的 JS 生成,我们完全不碰算法,schema 更新对它无感
  • 代价:速度受渲染限制(比纯 API 慢),且需要处理滚动到底与懒加载等待

这条路线本质是"用真实浏览器去抓"。业界结论一致:在 TikTok 上, 真实 Chrome 会话的拦截率远低于 Playwright/Python 方案——而云机上的真机 WebView 更彻底。

L3-B|mitmproxy + Frida 反 SSL pinning

抓 App 的真实评论请求,提取其签名头,在外部重放。

  • 优点:能拿到 App 端的完整签名与 ttwid
  • 代价:需过 SSL pinning,且 App 请求参数与 web 端不同,重放需逐字段对齐

L3-C|Frida hook App 网络层

直接调用 App 内部函数让它自己去请求,我们只收结果。

  • 保真度最高(连风控评分都继承),但实现与维护成本最高,不建议首选

L4|付费现成源(最快 · 当天可用)

stanvanrooy6/tiktok-comments-scraper(Apify):$0.40 / 1,000 条,无需登录、无 cookie。

  • 它的实测能力正好覆盖你的缺口:单视频 25,133 / 37,000+ 条,远超 9,931
  • 10,000 条 ≈ $4;这是一次性补全本项目缺口的最低成本
  • 代价:数据流向第三方、按量计费、依赖供应商存续
  • 适合:一次性/少量视频需要高完整度,或用它作为 L1 的"交叉校验源"验证我们的抓取是否有遗漏

四、决策矩阵

方案 成本 预期完整度 失效风险 长期维护 数据安全
L1 多源交叉 1~2 天 取决 0-3,可能 0% 也可能 80~95% 低 多源各自失效 中(触第三方)
L2 自建签名 3~7 天 高(受官方接口深度限制) 高(2~4 月一换) 需常驻运维 高(数据不过第三方)
L3-A WebView 3~5 天 高 低(真机真指纹) 低(算法无感) 高
L3-B/C Frida 7~15 天 最高 中 中 高
L4 付费源 当天 高(供应商已解决) 低 无 低(数据出境)

五、推荐路径

L0 诊断(半天,零成本)
   │
   ├─ 0-3 显示各源 cap 不同 ──────► L1 多源交叉(1~2 天,先拿便宜收益)
   │
   ├─ cap 随出口 IP 变化 ─────────► L3-A WebView 为主 + L1 交叉校验
   │
   ├─ 各源 cap 相同且与 IP 无关 ──► 墙在上游
   │                                   ├─ 少量/一次性 ──► L4(当天,$4 量级)
   │                                   └─ 长期大量 ─────► L3-A(3~5 天)
   │
   └─ 官方接口在真机/真签下能深翻 ─► L2 自建签名(3~7 天,接受长期运维)

一句话:L0 没做完之前,不要写一行 L2/L3 的代码。

理由很直接——如果 0-3 发现 cap 是 per-mirror 的,那么 L1 用 1~2 天就能拿到大部分收益, 而 L2 要 3~7 天并且终身运维。先在便宜的地方找答案。

我的推荐倾向:即使 L0 显示墙在上游,长期方案也优先 L3-A 而非 L2。 L2 的签名算法是持续对抗(每 2~4 个月重置一次,且静默失败、难排查), 而 L3-A 让 TikTok 自己的代码去签名——把维护成本转移给了对手方。 你手上那台 root 云机 + 真 App 的组合,正是这个路线成立的前提。


六、先定义"完整",再谈完整度(重要)

现在说"完整"缺一个可验收的定义。三个必须先澄清的事实:

  1. 18,900 是展示值,不是可达值。 各家的说明一致:显示评论数包含已删除与已隐藏的评论, 即使小视频,实际可取行数也略低于显示值。100% 在数学上就不存在。
  2. 按热度排序的分布极度倾斜。 前 10,000 条评论承载的互动量通常已占绝大部分。 如果你的用途是舆情/内容分析,"覆盖前 1 万条"和"覆盖 1.89 万条"的结论差异可能远小于直觉。
  3. 验收应该用收敛曲线,而不是单一数字。 建议指标:
    • 并集收敛:新增源带来的增量 < 1% 时认定为操作上限
    • 排名覆盖率:对比 L4 付费源的结果,量化我们漏掉了哪些(是尾部长尾,还是随机分布)

建议先把"最小可接受覆盖率"定下来(比如 85%)。定完之后,很可能 L1 就够, 不需要 L2/L3 的重投入。这一步花 10 分钟,可能省下 5 天。


七、风险与合规

风险 说明 缓解
会话农场加剧封禁 频繁轮换 session 会被判为扫描器 用住宅/移动节点,按信号轮换而非定时
L2 静默失效 schema 更新后返回空结果而非报错 每日探针自检
L4 数据出境 评论含用户名等标识 按 GDPR/《个保法》口径评估
条款风险 TikTok ToS 禁止自动化访问 与法律合规性是两个独立问题,需分别评估
单一镜像施压 tikwm 是免费服务 沿用现有节流,多源分摊压力

八、待你决策的事项

  1. 是否先执行 L0 诊断?(建议:是。半天,零成本,且能砍掉一半错误投入)
  2. "最小可接受覆盖率"定多少?(决定要不要上 L2/L3)
  3. 是长期批量还是少量一次性?(决定 L4 付费源的性价比)
  4. L3-A 是否可接受"速度慢于纯 API"?(换来的是近乎免疫的稳定性)

回答这 4 个问题,就可以直接进入实施。