10 个平台的 web 端全部用 TypeScript 移植自 cv-cat 的开源项目,请求与上游逐字节对拍。 统一的命令语法与 JSON 输出、按 平台 × 端 × 账号 隔离的登录态、给 AI Agent 用的技能与 llms.txt。
8.4 KiB
输出与错误处理
catbus 的输出同时面向人和程序(脚本、Agent):stdout 只输出 JSON,日志、提示、进度、二维码一律在 stderr。唯一的例外是 --help,它把帮助文字写到 stdout。
规范见 AGENTS.md 第 6 节。
信封
默认 -o json 时,stdout 恰好输出一个信封,成功和失败都是:
{
"ok": true,
"platform": "bilibili",
"endpoint": "web",
"resource": "item",
"action": "search",
"account": "default",
"data": [ { "id": "BV1...", "kind": "video", "...": "..." } ],
"page": { "cursor": "2", "has_more": true },
"error": null
}
| 字段 | 说明 |
|---|---|
ok |
是否成功 |
platform / endpoint / resource / action |
执行的命令,平台用规范 id(别名也会归一成规范 id) |
account |
使用的账号名 |
data |
结果:单个对象、数组,失败时为 null |
page |
分页命令为 {cursor, has_more},其他命令为 null |
error |
失败时为 {code, message, hint, detail},成功时为 null |
- stdout 是终端时缩进输出,重定向到文件或管道时输出紧凑的一行。
- 全局命令(
platforms、doctor、auth list、config、version)的platform、endpoint、account为 null,resource是命令名,action是子命令(没有时为 null)。
json 与 jsonl
-o jsonl 时 stdout 每行一个 data 条目:
- 列表命令一行一条,翻页时边翻边输出,适合大批量导出;
- 非列表命令只有一行;
- 结束时在 stderr 写一行摘要信封,
data为 null,带page和error,用来取下一页游标或错误信息。
catbus xhs user items <user-url> --all -o jsonl > items.jsonl 2> run.log
tail -n 1 run.log | jq '{ok, page, error}'
live listen、msg listen 这类长连接命令总是输出 jsonl(一行一个事件),直到 Ctrl-C 或 --duration 到期,退出码 0。
--raw
归一化会丢掉平台私有字段。需要原始数据时加 --raw:每个归一化对象换成平台返回的原始对象,信封结构不变。
catbus bilibili item get BV1xx411c7mD --raw | jq '.data | keys'
原始对象的结构随平台和接口变化,不保证稳定;写脚本时优先用归一化字段。
归一化类型
同一种对象在所有平台上结构相同。常用类型:
| 类型 | 字段 |
|---|---|
| UserRef | id name url |
| User | id name handle avatar url bio stats{followers following items likes} |
| Item | id kind url title text author:UserRef created_at cover media:Media[] stats{views likes comments collects shares} price:Price|null status|null |
| Media | id type url width height duration |
| Price | amount currency(amount 以元等主单位计) |
| Comment | id item_id parent_id author:UserRef text created_at stats{likes replies} |
| Live | id url title status host:UserRef cover stats{viewers} |
| Event | type time user:UserRef text gift{name count}|null |
| Conversation | id peer:UserRef unread last_message updated_at |
| Message | id conversation_id from:UserRef type text media:Media[] created_at |
| Notice | id type user:UserRef target{id url}|null text created_at |
| AuthStatus | logged_in user:UserRef|null method expires_at |
| Account | platform endpoint account current user:UserRef|null method updated_at |
| File | path type url size |
完整的类型(Folder、Category、Keyword、Topic、Poi、Gift、Rank、Subtitle、Danmaku、Order、Coupon……)、枚举值和每条命令的输出类型见 AGENTS.md 6.2、6.3。每条命令的输出类型也写在 --help 的「输出」一行里。
约定:
- ID 一律是字符串(包括 64 位整数 ID,不会丢精度)。
- 计数是整数或 null,「1.2万」会换算成 12000。
- 时间是带时区的 ISO 8601。
- 字段总是存在,取不到时为 null 或
[]。有些字段因为上游接口不返回而恒为 null,例如抖音作品的title、抖音和小红书列表的stats.views,各平台说明的「已知限制」里有完整列表。 - 所有对象都带
id和url,可以直接作为下一条命令的参数。
写操作的返回:成对动作(like/unlike 等)、delete、live send 等返回 {id}(目标对象的 id);item publish 返回 Item;comment add 返回 Comment;msg send 返回 Message;item download 返回 File[]。
分页与游标
列表命令默认只取一页:
| 选项 | 说明 |
|---|---|
| (不加) | 只取一页 |
--limit N |
自动翻页,直到取满 N 条 |
--cursor <c> |
从上次返回的 page.cursor 继续 |
--all |
一直翻到没有更多 |
- 游标是不透明字符串,原样传回即可,不要自己解析或构造。各平台的游标格式不同(页码、offset、时间戳、base64……)。
--limit截在一页中间时,返回的游标会带#skip=N后缀:续翻时 catbus 会重取这一页、跳过已输出的 N 条,剩下的不会丢。has_more为 false 时已经到底。服务端返回和上次相同的游标时,catbus 会停止翻页并在 stderr 警告,--all不会死循环。- 翻页间隔用平台的默认值,不需要自己加 sleep。
分批导出、断点续翻:
cursor=""
while :; do
out=$(catbus bilibili user items <user> --limit 100 ${cursor:+--cursor "$cursor"})
echo "$out" | jq -c '.data[]' >> items.jsonl
[ "$(echo "$out" | jq -r '.page.has_more')" = "true" ] || break
cursor=$(echo "$out" | jq -r '.page.cursor')
sleep 60
done
退出码
| 退出码 | code | 含义 | 建议 |
|---|---|---|---|
| 0 | — | 成功 | |
| 1 | ERROR |
未分类错误 | 看 message;可以带 -v 重跑看调试日志 |
| 2 | USAGE |
参数或选项错误 | 看 hint 和 --help |
| 2 | UNSUPPORTED |
平台 / 端没有这个命令,或不支持这个取值 | hint 会给出规范词、可选值或 -e <端> |
| 2 | CONFIRM_REQUIRED |
危险操作未确认 | 非交互环境加 -y |
| 3 | AUTH_REQUIRED |
未登录、账号不存在,或平台返回登录墙 | 执行 hint 里的登录命令 |
| 3 | AUTH_EXPIRED |
登录态失效 | 重新登录 |
| 4 | NOT_IMPLEMENTED |
规划中的能力(矩阵里的 ○)或端(app / pc) | 目前不支持,不必重试 |
| 5 | RISK_CONTROL |
验证码或风控,detail.kind 为 captcha / rate_limit / blocked |
停一停,不要连续重试,见 faq.md |
| 6 | NETWORK |
网络 / 代理错误 | 检查网络和代理设置 |
| 7 | UPSTREAM |
平台返回业务错误 | 原始错误码在 detail 里 |
命令无法执行时的判断顺序:参数和选项先校验(写错的先报 USAGE);然后是规划中的端或能力(NOT_IMPLEMENTED);平台没有的概念或不在词表里的命令报 UNSUPPORTED。用了平台原生叫法(如 note、video)时,hint 会给出规范词:
{"ok":false,"platform":"xhs","endpoint":"web","resource":"note","action":"get","account":null,"data":null,"page":null,
"error":{"code":"UNSUPPORTED","message":"xhs (web) 不支持 note get","hint":"用规范词:catbus xhs item get","detail":null}}
在脚本里处理错误
按退出码分支:
out=$(catbus xhs item get "$url")
case $? in
0) echo "$out" | jq '.data.title' ;;
3) echo "需要登录:$(echo "$out" | jq -r '.error.hint')" >&2; exit 1 ;;
5) echo "被风控了,稍后再试" >&2; exit 1 ;;
*) echo "$out" | jq -r '.error | "\(.code): \(.message)"' >&2; exit 1 ;;
esac
只取需要的字段:
# 搜索结果转成 TSV
catbus bilibili item search 猫 --limit 50 | jq -r '.data[] | [.id, .title, .author.name, .stats.views] | @tsv'
# 失败时打印错误码和建议
catbus douyin item get "$url" | jq -r 'if .ok then .data.url else "\(.error.code) \(.error.hint // "")" end'
# jsonl 逐行处理
catbus xhs comment list "$url" --all -o jsonl | jq -r '.text'
# 列出某个平台已实现的命令
catbus platforms kuaishou | jq -r '.data.commands[] | select(.status=="implemented") | "\(.resource) \(.action)"'
日志与提示
-v在 stderr 输出调试日志,cookie、token、Authorization、密码等字段已打码。贴到 issue 之前请再检查一遍。-q只保留错误,不输出提示和进度。- 提示、警告以
[catbus]开头,不会混进 stdout。