Files
MediaCrawler/mac-agent-os-main/05_tools/07_matrix/scripts/data/schema.sql
T
butubb 2112c1a870
Deploy VitePress site to Pages / build (push) Canceled after 0s
Deploy VitePress site to Pages / Deploy (push) Canceled after 0s
feat(monitor): 抖音 Web 接口客户端 —— 绕开爬虫子进程,直接发 HTTP
移植自 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 带网关原话 / 正常返回)。
2026-10-10 17:12:25 +08:00

110 lines
5.6 KiB
SQL
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
-- anchor.db 数据库设计 v1.0
-- 锚点规则 + 原子操作 + 检测日志 + 校准
-- ── 1. 锚点规则表 ──
-- 每个状态由多条规则组成(同 state + logic_group = AND,不同 group = OR)
CREATE TABLE anchor_rules (
id INTEGER PRIMARY KEY AUTOINCREMENT,
state_name TEXT NOT NULL, -- HOME / PLAYER / SEARCH / DETAIL / POPUP
field TEXT NOT NULL, -- 检测字段: video_count, title, video_playing, card_count, url, has_search_input
operator TEXT NOT NULL, -- eq / neq / gt / gte / lt / lte / contains / not_contains / regex
value TEXT NOT NULL, -- 目标值(统一存字符串)
logic_group INTEGER DEFAULT 0, -- 0=AND组, 同组内AND, 不同组OR
active INTEGER DEFAULT 1, -- 1=启用, 0=停用
created_at TEXT DEFAULT (datetime('now','localtime')),
updated_at TEXT DEFAULT (datetime('now','localtime'))
);
-- ── 2. 原子操作表 ──
-- 每个原子操作 = 一个操作标识 + 执行方式 + 验证规则
CREATE TABLE atomic_ops (
id INTEGER PRIMARY KEY AUTOINCREMENT,
op_name TEXT NOT NULL UNIQUE, -- click_card, swipe_down, like, search, go_home, dismiss_popup
description TEXT, -- 操作描述
action_type TEXT DEFAULT 'locator', -- locator / keyboard / evaluate / mouse
target TEXT, -- CSS选择器 / 按键名 / JS代码
verify_rule TEXT, -- 验证方式: video_src_change / video_count_gt / title_change
retry_count INTEGER DEFAULT 3,
fallback_op TEXT, -- 失败时降级的操作名
created_at TEXT DEFAULT (datetime('now','localtime')),
updated_at TEXT DEFAULT (datetime('now','localtime'))
);
-- ── 3. 状态迁移表 ──
-- 定义合法的状态转换(状态机)
CREATE TABLE state_transitions (
id INTEGER PRIMARY KEY AUTOINCREMENT,
from_state TEXT NOT NULL, -- HOME, PLAYER, SEARCH
to_state TEXT NOT NULL, -- PLAYER, SEARCH, HOME
via_op TEXT NOT NULL, -- click_card, swipe_down, search, go_home
UNIQUE(from_state, to_state, via_op)
);
-- ── 4. 检测日志表 ──
-- 每次锚点检测的记录,用于成功率追踪
CREATE TABLE detection_logs (
id INTEGER PRIMARY KEY AUTOINCREMENT,
identity TEXT, -- douyin_01, douyin_02
expected_state TEXT, -- 操作前预期的状态
detected_state TEXT, -- 实际检测到的状态
match INTEGER, -- 1=匹配, 0=不匹配
anchor_snapshot TEXT, -- 锚点数据快照(JSON)
screenshot_path TEXT, -- 截图路径
timestamp TEXT DEFAULT (datetime('now','localtime'))
);
CREATE INDEX idx_detection_logs_match ON detection_logs(match, timestamp);
-- ── 5. 校准候选表 ──
-- AI或自动校准产生的新规则,对比验证后决定是否采纳
CREATE TABLE calibration_candidates (
id INTEGER PRIMARY KEY AUTOINCREMENT,
state_name TEXT, -- 哪个状态的规则
source TEXT, -- ai / manual / auto_recovery
old_rules TEXT, -- 旧规则JSON
new_rules TEXT, -- 建议的新规则JSON
test_count INTEGER DEFAULT 0, -- 测试次数
test_pass INTEGER DEFAULT 0, -- 测试通过次数
status TEXT DEFAULT 'pending', -- pending / accepted / rejected / testing
created_at TEXT DEFAULT (datetime('now','localtime'))
);
-- ── 初始数据:基于2026-05-06手动采集的锚点 ──
-- HOME 状态规则
INSERT INTO anchor_rules (state_name, field, operator, value, logic_group) VALUES
('HOME', 'video_count', 'eq', '1', 0),
('HOME', 'title', 'contains', '抖音精选', 0);
-- PLAYER 状态规则(全屏播放)
INSERT INTO anchor_rules (state_name, field, operator, value, logic_group) VALUES
('PLAYER', 'video_count', 'gte', '3', 0),
('PLAYER', 'video_playing', 'eq', 'true', 0);
-- SEARCH 状态规则
INSERT INTO anchor_rules (state_name, field, operator, value, logic_group) VALUES
('SEARCH', 'title', 'contains', '搜索', 0),
('SEARCH', 'title', 'not_contains', '精选', 0);
-- SEARCH_PLAYER(从搜索结果进入的播放页)
INSERT INTO anchor_rules (state_name, field, operator, value, logic_group) VALUES
('SEARCH_PLAYER', 'video_count', 'gte', '3', 0),
('SEARCH_PLAYER', 'video_playing', 'eq', 'true', 0),
('SEARCH_PLAYER', 'url', 'contains', '/search/', 0);
-- 原子操作
INSERT INTO atomic_ops (op_name, description, action_type, target, verify_rule, retry_count) VALUES
('click_card', '点击首页视频卡片', 'locator', '.discover-video-card-item', 'video_count_gte_3', 3),
('swipe_down', '下滑切换视频', 'keyboard', 'ArrowDown', 'video_src_change', 2),
('like', '点赞当前视频', 'locator', '[data-e2e="like-count"]', 'like_count_change', 1),
('search', '搜索关键词', 'evaluate', 'search_input', 'title_contains_搜索', 2),
('go_home', '回到首页', 'locator', 'https://www.douyin.com/', 'video_count_eq_1', 2),
('dismiss_popup','关闭弹窗', 'locator', 'button:text-is("我知道了")','popup_gone', 2);
-- 状态迁移
INSERT INTO state_transitions (from_state, to_state, via_op) VALUES
('HOME', 'PLAYER', 'click_card'),
('PLAYER', 'PLAYER', 'swipe_down'),
('PLAYER', 'HOME', 'go_home'),
('HOME', 'SEARCH', 'search'),
('SEARCH', 'SEARCH_PLAYER', 'click_card');