ants-move
v0.0.7
Published
An open-source CLI for collecting and moving data between systems.
Maintainers
Readme
ants-move
ants-move 是一个开源 TypeScript CLI,用于在系统之间搬运数据。命令被设计成小型 worker:采集器把数据拿进来,自动化命令可以把数据写入创作者后台等系统。当前采集器支持 36 氪、今日头条、闲鱼(goofish)、Hacker News、GitHub,以及主流科技 / AI 媒体(WIRED、MIT Technology Review、TechCrunch、The Verge、Ars Technica、Engadget、IEEE Spectrum、BBC Technology、Google AI / DeepMind / NVIDIA / OpenAI 博客、Bloomberg 标题流)。头条还支持创作者后台登录,以及文章 / 微头条的草稿与发布。闲鱼搜索需要已登录的浏览器会话。
安装与环境
依赖:
- Node.js 22 或更高
- npm
PATH中可用的curl(36 氪与科技 / AI 媒体采集)- 通过 Playwright 安装的 Chromium(头条采集)
全局安装包,并安装头条采集用的浏览器:
npm install -g ants-move
npx [email protected] install chromium包会安装两个等价可执行文件:ants 与短别名 amv。
输出约定
成功结果写 stdout,结构化错误写 stderr。默认输出 JSON。列表 / 搜索命令支持 --format table 或 -t 表格输出。文章详情命令仅支持 JSON。
成功 JSON 信封:{ "ok": true, "data": ... }。错误信封:{ "ok": false, "error": ... },并以非零退出码返回。
36 氪命令
按数字 ID 拉取文章:
ants 36kr article <article-id> [--format json]从 AI 或 technology 频道拉取 1~20 页资讯:
ants 36kr list <AI|technology> [--pages 1..20] [--format json|table] [-t]采集方式:
- 文章:从
www.36kr.com拉 HTML(不用裸域36kr.com)并解析window.initialState。裸域常被火山引擎安全检测页拦截且没有正文数据;遇到时返回KR36_PARSE_ERROR并提示安全检测。 - 列表:只走官方 gateway 流式接口(
gateway.36kr.com,首页pageEvent: 0,后续页带pageCallback),不再依赖频道页 HTML。
分页串行,单次调用最多 20 页。每个 curl 响应与映射结果限制 20 MB,每页最多 30 条,映射列表限制 5 MB,因此一次列表最多保留 600 条,且不会跨页累积过大字段。
闲鱼(goofish)命令
这些命令使用从 www.goofish.com 逆向的非官方 MTOP 搜索接口,不是闲鱼开放平台官方 API。账号限流、登录验证与风控仍然有效。请用个人/测试账号,保持低频,不要接到高 QPS 路径。CLI 不会绕过验证码或风控。
登录
登录仍用有界面浏览器扫码(一次性)。会话以 Playwright storageState 保存(权限 0600),默认路径 ~/.config/ants-move/xianyu/default.json,可用 --state 覆盖。纯 API 搜索复用该文件中的 Cookie(尤其 _m_h5_tk)。
ants xianyu auth login [--browser chrome|msedge|chromium] [--state <path>] [--timeout-ms <ms>]
ants xianyu auth status [--browser chrome|msedge|chromium] [--state <path>] [--headed]
ants xianyu auth logout [--state <path>]搜索
搜索需要已保存的登录态。默认传输为纯 API(--transport api):用 _m_h5_tk 计算 H5 MTOP sign,HTTP POST mtop.taobao.idlemtopsearch.pc.search。可用 --transport browser 回退到 Playwright 页面拦截。默认 JSON;--format table 或 -t 输出表格。
ants xianyu search <keyword> \
[--pages 1..5] \
[--transport api|browser] \
[--sort default|price_asc|price_desc|newest|oldest|distance|credit] \
[--brand <名称>] [--brand-vid <vid>] \
[--min-price <n>] [--max-price <n>] \
[--publish-days 1|3|7|14] \
[--quick-filter personal,free_postage,new,appraise,...] \
[--personal] [--free-postage] [--new] [--appraise] \
[--province <省>] [--city <市>] [--area <区>] \
[--exclude-multi-places] \
[--lat <n> --lng <n> --distance <米>] \
[--format json|table] [-t] \
[--browser chrome|msedge|chromium] \
[--state <path>] \
[--headed]示例:
ants xianyu auth login
ants xianyu search 单反 --pages 1
ants xianyu search 单反 --brand 佳能
ants xianyu search 单反 --brand Canon --min-price 500 --max-price 2000 --sort price_asc --personal
ants xianyu search 单反 --province 广东 --city 深圳 --publish-days 7 -t
ants xianyu search 单反 --transport browser --headed实现要点:
- api(默认):从
storageState读 Cookie,按md5(token&t&appKey&data)签名后 POSTh5api.m.goofish.com,映射resultList。搜索本身不启动浏览器。 - browser:在已登录会话中打开搜索页,用 Playwright 捕获同一 MTOP 响应。高级筛选始终走 MTOP body(API 字段),即使指定了
--transport browser。 - PC 高级筛选(逆向 MTOP 字段):
- 排序 →
sortField/sortValue - 价格 / 发布时间 / 快捷筛选 →
propValueStr.searchFilter,例如priceRange:500,2000;publishDays:7;quickFilter:filterPersonal; - 地区 →
extraFilterValue的divisionList(可选排除多地卖家) - 距离 →
gps/customGps/customDistance(米) - 任意筛选时
fromFilter: true - 快捷筛选对应 UI:个人闲置、包邮、全新、验货宝、超赞鱼小铺、严选、转卖、验号担保
- 排序 →
- 映射字段:
itemId、title、price/priceNumber,可选area/picUrl/userNick,以及商品链接。meta.transport标明路径。 - 分页最多 5 页;结果 ≤ 5 MB;同 state 单飞锁;token 轮换最多再试 1 次。
- 品牌(
--brand/--brand-vid):一条命令内部两次 MTOP——(1) 从cpvNavigatorDo解析品牌 facet 并解析名称→pid/vid,(2) 搜索时写入searchFilter的pid:vid;。结果meta.brand回传解析结果;名称不存在返回XIANYU_BRAND_NOT_FOUND并列出可选品牌。 - 仍未单独做 CLI 的:成色/功能状态等其它 CPV tab(机制相同,可后续复用)。
头条命令与验证限制
按数字 ID 或文章 / group / 旧版 URL 拉取文章:
ants toutiao article <article-id-or-url> [--format json]从科技频道或支持的搜索关键词拉取 1~5 页:
ants toutiao list <tech|AI|光刻机|芯片|半导体> [--pages 1..5] [--format json|table] [-t]按 token 或 /c/user/token/<token>/ 主页 URL 拉取作者动态:
ants toutiao author <token-or-url> [--pages 1..5] [--with-content] [--format json|table] [-t]Feed 采集最多保持请求页数的解析结果活跃,最多启动两倍数量的有界解析尝试以替换畸形响应,并最多接受 5 个成功响应。每个 feed body 在 JSON 解析前限制 5 MB,解析后最多 100 条原始条目。
文章抽取对标题、正文、段落分别限制 1 MB。搜索回退最多串行检查 300 个有界链接,并拒绝大于 1 MB 的 body。--with-content 在同一浏览器会话中串行拉详情,最多 100 篇唯一文章,单次调用最多保留 10 MB 结果。头条可能要求交互式验证;验证导致搜索不可用时返回 TOUTIAO_VERIFICATION_REQUIRED。CLI 不会尝试绕过站点验证。
头条创作者登录与发布
这些命令用 Playwright 驱动非官方创作者后台 mp.toutiao.com,不是官方头条开放 API。账号限流、验证码、风控策略仍然有效。请先用测试账号。CLI 永不绕过验证码或风控。
首次用有界面浏览器扫码登录。会话以 Playwright storageState 保存(权限 0600),默认路径 ~/.config/ants-move/toutiao/default.json,可用 --state 覆盖。
登录 / 发布默认使用本机已安装系统浏览器(--browser chrome),而不是 Playwright 自带的 Chromium for Testing。支持:
chrome(默认):本机 Google Chromemsedge:Microsoft Edgechromium:Playwright 管理的 Chromium for Testing
也可设置 ANTS_TOUTIAO_BROWSER=chrome|msedge|chromium。
ants toutiao auth login [--browser chrome|msedge|chromium] [--state <path>] [--timeout-ms <ms>]
ants toutiao auth status [--browser chrome|msedge|chromium] [--state <path>]
ants toutiao auth logout [--state <path>]创建内容默认存草稿。正式发布必须显式 --strategy publish。省略 --strategy 永远等于 draft。--dry-run 只校验入参与登录态是否存在,不写后台:
ants toutiao publish article \
--title <title> \
--content <text> | --content-file <path> \
--images <path,path,path,...> \
[--cover <path>] \
[--covers <path,path,...>] \
[--keywords <csv>] \
[--category <name>] \
[--location <名称>] \
[--claim <name>] \
[--first-publish] \
[--strategy draft|publish] \
[--browser chrome|msedge|chromium] \
[--cdp <url>] \
[--state <path>] \
[--dry-run] \
[--headed]
ants toutiao publish micro \
--content <text> | --content-file <path> \
--images <path,path,...> \
[--topic <name>] \
[--location <名称>] \
[--claim <name>] \
[--first-publish] \
[--strategy draft|publish] \
[--browser chrome|msedge|chromium] \
[--cdp <url>] \
[--state <path>] \
[--dry-run] \
[--headed]文章图片规则
--images必填,至少 3 张本地图(最多 20)。- 图片会嵌在正文段落之间,不只是封面。
- 主路径:把图片写入系统剪贴板,再在折叠到文末、无选区的光标处粘贴,避免覆盖已有文字。
- 每次插入后校验此前段落指纹仍在;若粘贴会覆盖正文,命令以
TOUTIAO_UI_CHANGED失败。 - 兜底路径:创作者后台工具栏插图抽屉 → 本地上传 → 确定。
- 封面 / 主图(
--cover/--covers):可选。未指定时用第一张正文图作单图。封面上传为尽力而为;封面控件失败时仍可保存带正文图的草稿,并可能回退到「无封面」。
微头条图片规则
--images必填,至少 2 张本地图(最多 9)。- 通过微头条工具栏「图片」→「本地上传」→「确定」上传。
其他发布选项
| 选项 | 对应后台控件 |
|------|----------------|
| --topic <name> | 微头条创作话题(工具栏或 #话题# 文本兜底) |
| --location <名称> | 添加位置 / 城市(尽力而为,失败不阻断发布) |
| --claim <name> | 作品声明勾选项,例如 个人观点,仅供参考 |
| --first-publish | 头条首发(正文至少 100 字) |
| --keywords <csv> | 文章关键词(后台有该字段时) |
| --category <name> | 文章分类(后台有该字段时) |
| --headed | 自动化时显示浏览器窗口 |
成功判定与边界
草稿 / 发布成功仅以创作者保存 API 返回真实 id 为准:
- 文章:
POST /mp/agw/article/publish返回pgc_id - 微头条:
POST /mp/agw/draft/save_ugc_draft返回gid
仅有 UI toast 不算成功。保存响应 waiter 在表单填写完成后才 arm(保存 / 发布点击之前),避免把输入过程中的自动保存误判为成功。
边界:单次调用一篇文章或一条微头条;正文最多 1 MB;文章标题 2~30 字;文章正文图 3~20 张;微头条图 2~9 张;单图最多 10 MB。同一 auth 状态文件跨进程单飞加锁,并发持有者收到 TOUTIAO_LOCK_HELD。保存 / 发布不会自动重试。
示例
# 文章草稿:3 张正文插图(段落嵌入 + 可选封面)
ants toutiao publish article --headed \
--title "示例标题" \
--content $'第一段内容。\n\n第二段内容。\n\n第三段内容,字数足够时可加 --first-publish。' \
--images ./a.png,./b.png,./c.png \
--claim "个人观点,仅供参考" \
--first-publish
# 微头条草稿:2 张图 + 话题
ants toutiao publish micro --headed \
--content "微头条正文……" \
--images ./a.png,./b.png \
--topic 科技真实托管浏览器配置(推荐用于交互登录 / 发布)
用真实 Chrome/Edge + 专用自动化配置目录(不是你日常 Chrome 配置)时,先启动可调试浏览器:
ants toutiao browser start [--browser chrome|msedge] [--port 9222] [--profile <path>]
ants toutiao browser status
ants toutiao browser stop默认:
- 配置目录:
~/.config/ants-move/toutiao/chrome-profile - CDP:
http://127.0.0.1:9222 - 元数据:
~/.config/ants-move/toutiao/browser.json
再通过 CDP 在该真实浏览器中登录 / 发布:
# 在托管 Chrome 窗口中按需扫码
ants toutiao auth login --cdp http://127.0.0.1:9222
# 后续发布可用同一 CDP(若 browser start 仍在运行,也可省略 --cdp)
ants toutiao publish article --title "标题2到30字" --content "正文" --cdp http://127.0.0.1:9222若 ants toutiao browser start 已在运行,auth / publish 在省略 --cdp 时会自动探测 CDP URL。
这是 Playwright connectOverCDP 连接本机已安装的 Chrome/Edge 进程,不会复用 ~/Library/Application Support/Google/Chrome 下的日常个人配置。
Hacker News 命令与别名
从各 Firebase 列表拉取 1~100 条:
ants hn top [--limit 1..100] [--format json|table] [-t]
ants hn new [--limit 1..100] [--format json|table] [-t]
ants hn best [--limit 1..100] [--format json|table] [-t]通过 Algolia 搜索,按相关度或时间排序:
ants hn search <query> [--limit 1..100] [--sort relevance|date] [--format json|table] [-t]hackernews 是 hn 的别名,例如 ants hackernews top 等同于 ants hn top。amv 可执行文件接受上述全部命令,例如 amv hackernews search typescript。
Hacker News 详情请求最多 8 并发。ID 列表与搜索响应限制 5 MB,每条 Firebase story 详情限制 256 KB。
GitHub 命令
GitHub Trending
拉取 GitHub 全语言 Trending 页上展示的全部仓库。时间周期默认 daily:
ants github trending
ants github trending --since daily
ants github trending --since weekly
ants github trending --since monthly默认 JSON。可用 --format table 或 -t 输出表格:
ants github trending --since weekly --format table
ants github trending --since monthly -t采集器对官方服务端渲染 HTML 发一次 GitHub 请求,不使用第三方 Trending API 或 Playwright。请求超时 30 秒,HTML 限制 5 MB。解析最多接受 100 个仓库,每个仓库最多保留 10 个展示贡献者。命令无语言过滤、结果上限、自动重试或缓存;成功但 0 行的解析会当作页面结构错误拒绝,而不是返回误导性的空榜。
GitHub README
拉取公开仓库默认分支上的首选 README:
ants github readme https://github.com/owner/repository
ants github readme https://github.com/owner/repository.git返回 JSON,包含原始 UTF-8 README 内容以及 name、path、SHA、字节大小、HTML URL、download URL、规范仓库 URL 与 API 源 URL。使用文档化的 api.github.com/repos/{owner}/{repo}/readme 端点,一次有界 GitHub API 请求,超时 30 秒,响应限制 5 MB。不爬仓库页、不探测 Raw URL、不重试、不回退其他请求。不读取 GITHUB_TOKEN。
仅支持公开仓库。未认证 REST 客户端共享源 IP 时,GitHub 通常限制为每小时 60 次请求。额度用尽返回 GITHUB_RATE_LIMITED,且不重试。
科技 / AI 媒体采集
这些采集器用公开 RSS/Atom 拉列表,用公开文章 HTML 拉正文。请求走 curl,并带稳定的浏览器伪装头(Chrome Desktop UA、Accept、Accept-Language、Sec-Fetch-*、Referer/Origin)。不是各媒体官方开放 API。请保持个人低频使用;CLI 不会绕过付费墙、验证码或安全挑战页。
统一形态:
ants <source> list <channel> [--limit 1..50] [--format json|table] [-t]
ants <source> article <id-or-url> [--format json]--limit 默认 20,上限 50。列表 JSON 在 feed 提供时包含 title、summary、authorName、publishTime、url 与图片字段。正文 JSON 包含 title、summary、author、publishTime、content.paragraphs、coverImage、images[].url。拿不到正文时硬失败(MEDIA_PARSE_ERROR),不会静默返回半截内容。
| 命令 | 来源 | 频道 | 正文 |
|------|------|------|------|
| wired | WIRED | AI、technology、business、science、security、all | 支持 |
| mtr | MIT Technology Review | AI、technology | 支持 |
| techcrunch | TechCrunch | AI、technology | 支持 |
| verge | The Verge | AI、technology | 支持 |
| ars | Ars Technica | AI、technology | 支持 |
| engadget | Engadget | AI(关键词过滤)、technology | 支持 |
| ieee | IEEE Spectrum | AI(关键词过滤)、technology | 支持 |
| bbc | BBC Technology | AI(关键词过滤)、technology | 支持 |
| google-ai | Google AI Blog | AI、technology | 支持 |
| deepmind | Google DeepMind Blog | AI、technology | 支持 |
| nvidia | NVIDIA Blog | AI、technology | 支持 |
| openai | OpenAI Blog | AI、technology | 支持 |
| bloomberg | Bloomberg Technology | AI(关键词过滤)、technology | 仅列表 |
示例:
ants wired list AI
ants wired list AI --limit 10 -t
ants wired article ai-newsrooms-are-breaking-news-now-haha-im-in-danger
ants wired article https://www.wired.com/story/example-slug/
ants mtr list AI
ants techcrunch list AI --limit 15
ants verge list technology
ants bloomberg list technology
ants bloomberg list AIBloomberg 仅提供 Technology 公开 RSS 的标题与摘要。文章 HTML 有付费墙 / 反爬;没有 bloomberg article 子命令。
每次 list 一次有界 feed 请求;每次 article 一次有界 HTML 请求。响应体经 curl 缓冲限制(默认 8 MB)。无后台轮询、缓存或多页扇出。
高并发使用警告
单进程内资源使用有界,但不同进程中的相同命令没有全局去重或限流。在 Q 次并发调用下,上游工作量可接近:36 氪列表 20Q 请求;Hacker News 列表 201Q 请求;头条作者采集约 105Q 次浏览器导航及页面子资源;闲鱼搜索约 Q 次浏览器会话(含页面子资源与 MTOP);GitHub Trending 或 README 各 Q 次请求;科技 / AI 媒体 list 约 Q 次 curl(list+article 约 2Q)。README 采集还会短暂保留有界 API 响应、解析 JSON、Base64 文本与解码内容;共享源 IP 的匿名调用通常共享 GitHub 每小时 60 次限制,限流失败不会重试或回退。
每次调用保留的数据也有界:36 氪列表 5 MB;Hacker News 约 200 条候选详情共约 50 MB(过滤前);头条作者文章结果 10 MB;闲鱼搜索结果 5 MB;GitHub HTML 响应 5 MB(Cheerio 解析前)。头条在同一浏览器会话中最多还可持有 5 个活跃的 5 MB feed 缓冲;浏览器进程开销、必要子资源与 HTML 解析对象开销不计入这些 payload 上限。
不要把 CLI 直接挂在高 QPS 请求路径上。在线服务必须使用外部有界队列与共享限流器,在多进程 / 多实例间施加背压。本项目没有分布式锁、共享缓存、重试队列或多实例单飞机制。
开发与校验
安装依赖并运行确定性检查:
npm install
npm run check
npm test
npm run coverage
npm run build
npm pack --json测试使用注入的网络 / 浏览器边界,不会访问真实网站。
许可证
基于 MIT License。
