移植自 mac-agent-os 的 mediacrawler_adapter:不起子进程、不开页面,用浏览器里那份
登录态直接调抖音 Web 接口。产物键名照抄 store/douyin,所以 ingest 那条链路一个字不用改。
**目前能用的(真环境实测,非推断)**:
profile/other : 200, 7075 字节 —— 博主主页指标(粉丝/获赞/作品数/昵称)
aweme/detail : 200, 45425 字节 —— 单条作品详情(含点赞/评论/收藏/分享)
**目前不能用的:作品列表 `aweme/post`。** 两个互相独立的原因:
1. 这个接口被抖音单独升级成了真校验:不带 x-tt-argus 回 403「Uifid Not Found」,
带上 dummy 值回 200 + **空 body**。也就是说「头在不在」骗得过,「真校验」过不了。
同一套头打 profile/other 和 aweme/detail 都是通的 —— 抖音是挑着接口加保护的,
挑中的恰好是「批量拉作品列表」这个最敏感的动作。
2. 改走页面截获也不行:CDP 浏览器打开博主主页会落到「验证码中间页」(当天大量探测的
代价,过几小时要重测)。
所以现在的边界是:**已知作品的指标刷新能做,自动发现新作品做不了**。
**排查中控住变量后得到的两条事实**(都写进注释了):
· `Accept` / `Accept-Language` / `Referer` 才是主页接口能返回真数据的原因 —— 只有
UA+client hints+Cookie 时是 200 但仅 121 字节的空壳,补上这三个头变 7074 字节。
(我先前猜的 sec-ch-ua 不是关键。)
· 因此 UA 与 client hints 必须**成套地取自同一个浏览器**,所以 BrowserIdentity 一次
从 CDP 取齐 cookie + UA + hints,而不是各自写死。
「200 + 空 body 必须当场报错」也是刻意写死的:放过去它会在下游变成「这个博主没作品」,
把一次失败伪装成一条正常结果 —— 爬虫那条路正是这么栽的,还被翻译成「账号被封」。
测试 +11:cookie 解析、请求头成套性(含 uifid 缺失/回退)、产物键名与 store 对齐、
以及 _get 的三条失败路径(空 body / 403 带网关原话 / 正常返回)。
5.5 KiB
title, tags, created, updated, nature, collected, collected_date
| title | tags | created | updated | nature | collected | collected_date | ||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Matrix 养号系统已知坑与解决方案 |
|
2026-05-29 | 2026-05-29 | method | true | 2026-06-09 |
浏览器引擎
Camoufox Profile 锁冲突
现象:TargetClosedError、页面无法操作、养号快速失败
根因:两个浏览器实例争用同一 profile 目录,.parentlock 锁文件被占用
场景:
- 抖音 daemon 模式浏览器保持运行,小红书复用同 identity_dir 启动新实例
- 上次进程异常退出,锁文件未清理
解决:
# 清理残留锁文件
find identities/ -name ".parentlock" -delete
预防:Phase 间使用 --no-daemon 确保浏览器正常关闭(conn.close())
Camoufox 启动失败
现象:浏览器进程无法启动或立即退出 排查:
- 检查
.user_data/目录锁文件(.parentlock) - 检查 config.yaml 语法
- 检查 Camoufox 二进制是否可用
Chrome 148+ Emulation API 失效
现象:Emulation.setDeviceMetricsOverride 报错或无效果
根因:Chrome 148 移除了该 CDP 命令
解决:改用 set_viewport_size 方法
macOS 系统
窗口激活限制
现象:点击操作不响应,元素未被触发 根因:macOS 限制后台进程操控前台窗口 解决:AppleScript 激活窗口 + 双击策略
tell application "System Events" to set frontmost of process "Firefox" to true
macOS 26.4 .so 签名问题
现象:ModuleNotFoundError 或 OSError: dlopen 加载 .so 失败
根因:系统升级后 Team ID 不匹配
解决:ad-hoc 签名
codesign --force --sign - /path/to/file.so
# 批量签名
find /path/to/venv -name "*.so" -exec codesign --force --sign - {} \;
Python stdout 缓冲
现象:后台运行日志文件为空或延迟写入 根因:Python 后台进程 stdout 默认全缓冲(非终端环境) 解决:
PYTHONUNBUFFERED=1 nohup python3 script.py > log.txt 2>&1 &
或检查账号级日志文件而非 phase 级日志。
XHS 特有
QR 检测墙卡死
现象:弹出大弹窗后无响应,养号停滞 根因:XHS 非常用登录检测,弹窗无自动超时 解决:JS 检测 QR 墙关键词 → 定位"返回首页"按钮 → L 型鼠标路径点击 区分:QR 墙 ≠ 未登录。QR 墙是频控触发,多刷会恢复。
黑屏
现象:瀑布流页面变黑,卡片不可见 根因:SPA 状态异常 解决:点击底部导航"发现" tab 刷新页面状态
AI-layout 布局差异
现象:搜索栏消失、首卡位置偏移 根因:指纹分辨率 + DPR 触发 A/B 测试 解决:
- 调整指纹(screen ≥ 1920x1080, DPR ≥ 1.25)
- 代码兼容(三重搜索降级、通用 href 匹配)
登录检测误判
现象:已登录但被判定为未登录 根因:先关闭弹窗再检测 → 弹窗已隐藏 → 无法检测登录提示 解决:先检测登录状态 → 再 dismiss_login_modal
抖音特有
Draft.js 输入不可靠
现象:评论内容无法输入或粘贴后消失 根因:Draft.js 只认系统级键盘事件 解决:pbcopy + Meta+V,不用 page.fill/type/keyboard.type
视频页评论区输入框在视口外
现象:评论发送失败,输入框不可交互 根因:输入框 y ~2212px,不在可视区域 解决:scrollIntoView → 等待 DraftEditor 懒加载 → click 容器触发激活
代码层面
identity_dir 路径构造
坑:仅用 identity_name 拼接路径可能错误(如 douyin_01_camo ≠ douyin_01)
正确做法:从 accounts.yaml 读取 identity_dir 字段
窗口位置读取
坑:config.yaml 中无窗口位置字段 正确做法:从 accounts.yaml 读取,运行结束不回写
argparse daemon 参数
坑:store_true + getattr(args, 'daemon', True) 当参数为 None 时返回 None 而非 True
正确做法:BooleanOptionalAction + args.daemon if args.daemon is not None else True
page.evaluate() 无限挂起
现象:养号运行到某一步突然停止,无错误日志,浏览器进程存活
根因:Playwright 的 page.evaluate() 在页面状态异常时无限挂起,无默认超时
解决:所有 page.evaluate() 调用加 asyncio.wait_for() 超时(8-10s)
影响范围:所有涉及 JS 调用的函数(dismiss_login_modal, get_note_cards, click_refresh_button 等)
注意:超时会捕获 asyncio.TimeoutError,返回 False/None,不会导致进程退出
搜索结果页无 FAB 刷新按钮
现象:搜索返回首页后 click_refresh_button() 一直在找按钮,日志显示"刷新瀑布流页面..."长时间无进展 根因:go_back_to_home() 后可能回到搜索页,搜索页没有右下角 FAB 刷新按钮 解决:click_refresh_button() 加 10s 超时 → 找不到按钮安全返回 False → 流程继续
诊断方法论
遇到异常时按以下流程处理:
- 环境因素:浏览器版本变化?网络波动?进程锁残留?文件被占用?
- 人为操作:用户手动关闭浏览器?拖拽窗口?修改配置?
- 确认代码问题:记录日志,确认后再改
- 读文档:查看项目规划/架构设计,不偏离已有设计
- 一次改一处:改完验证再继续
反面教材:Chrome 148 升级导致 CDP API 失效,反复修改 cdp_connector.py 6 次把好的改坏了。