seed-browser · git:20261105.bf47097 · 2026-11-05 · sha256 e3fa9acdbc2d3f1e

seed-browser git:20261105.bf47097A

Immutable. This exact content never changes and is served at /api/v1/blob/e3fa9acdbc2d3f1e.

---
name: seed-browser
description: seed-browser(命令行工具):借用户已登录的浏览器上网。读网页、搜索和取数据(Google、B 站、YouTube、Reddit、HN、微博、知乎、小红书、雪球、东方财富、Yahoo Finance、arXiv、维基百科等 50 多个网站有现成函数)、跨多个网站检索调研、点击填表、上传下载、截图、看接口请求。只要任务要用到网页内容或网站操作(尤其是要登录的网站),就用它。
---

# seed-browser:借用户的浏览器上网

`seed-browser` 是命令行工具,后台 daemon 会自动启动。浏览器的登录状态、打开的标签、网络记录都存在 daemon 里,所以每条命令跑完就退出也不会丢东西。

## 0. 先选对工具

| 要做的事 | 用什么 |
|---|---|
| 看一个网页讲了什么 | `seed-browser read <网址>` |
| 长文章 / 长评论区里只找和问题相关的部分 | `seed-browser read <网址> --grep '关键词1\|关键词2'` |
| 搜索引擎 | `seed-browser call google.com search …`(被拦自动换必应 / DuckDuckGo);中文内容加一路 `baidu.com` |
| 在某个网站上搜索、取列表、取评论 | 先 `seed-browser lib list` 看有没有现成函数;有就 `seed-browser call <域名> <函数> …`(网站目录见第 4 节) |
| 视频讲了什么 | `youtube.com transcript` / `bilibili.com subtitles` 取字幕,B 站还有 `summary` |
| 股票、公告、资金流、期货外汇、美股期权 | `xueqiu.com` / `eastmoney.com` / `sina.com.cn` / `finance.yahoo.com` / `barchart.com`,怎么选见第 4 节 |
| 加密货币行情、市值榜、趋势币 | `binance.com` / `coingecko.com` |
| 论文、审稿、AI 模型和数据集 | `arxiv.org` / `semanticscholar.org` / `openreview.net` / `huggingface.co` |
| 百科词条、网页的历史存档 | `wikipedia.org` / `archive.org`(Wayback) |
| 依赖包详情、下载量、CVE / 开源漏洞 | `npmjs.com` / `pypi.org` / `crates.io` / `nvd.nist.gov` / `osv.dev` |
| 英文新闻、Newsletter、新产品发布 | `bbc.com` / `substack.com` / `producthunt.com` / `lobste.rs` |
| 中国政府和部委的政策文件 | `gov.cn`(搜索、最新列表、全文) |
| 跨好几个网站调研一个问题 | `seed-browser run -f 脚本.js`,分步做,见第 7 节 |
| 一次做一串事、批量处理、拼接几个网站的数据 | `seed-browser run`(写 JS,全局有 `seedBrowser` 对象) |
| 在页面上点按钮、填表、上传、下载 | `seed-browser snapshot -i` / `seed-browser find` 找元素,然后 `seed-browser click` / `seed-browser fill` |
| 想知道网站的数据来自哪个接口 | `seed-browser net log --api -t <标签>`,然后 `seed-browser net show <id>` |

**开工时先跑这两条**:

```bash
seed-browser browser list     # 有没有连上浏览器;有多个时问用户用哪个,之后都加 --browser <名字>
seed-browser lib list         # 有哪些网站的现成函数;[要登录] / [登录更好] 标出哪些站要用户先登录
```

任务要用到标了 `[要登录]` 的网站时,开工前就告诉用户“这几个站需要你在浏览器里登录”,别等报错了才说。

`browser list` 为空:先等几秒再试(插件连上要一点时间)。还是空就请用户在 `chrome://extensions` 里加载仓库的 `extension/` 目录。用户不想用自己的浏览器时,用 `seed-browser browser launch [名字]` 启动一个专用浏览器(无头加 `--headless`,但无头浏览器没有登录状态,很多网站会拦)。

## 1. 守则

- **不打扰用户**:自己要用的标签在后台开(`seed-browser tab open <url> --bg --keep` 或 `seedBrowser.open(url)`)。不要关闭或跳转用户自己的标签,用完自己开的标签要关掉(`seed-browser tab close t5`,或在 `seed-browser run` 里 `await seedBrowser.cleanup()`)。
- **每条页面命令都带 `-t <标签>`**,不要依赖“当前标签”,它可能被别的任务改掉。
- **省上下文**:大块数据写进文件,只把摘要打印出来看(一行一条、长文本截断)。`seed-browser run` 的结果超过 2 万字会自动写到 `~/.seed-browser/out/`,只打印路径和摘要。
- **先读后操作**:查信息用 `read` / 函数库;要操作时再 `snapshot -i` 或 `find`;截图放到最后。
- **遇到登录、验证码不要硬闯**:`seed-browser tab activate <标签>` 把标签切到前台,请用户处理完再继续。

## 2. 错误码怎么处理

报错的格式是 `✗ [CODE] 说明`,下一行 `→` 后面是建议。

| code | 什么情况 | 你该怎么做 |
|---|---|---|
| `NEED_LOGIN` | 没登录、登录过期;403 也可能是被网站拦了 | 告诉用户去浏览器里登录这个网站;用的是无头 / 专用浏览器的话,换用户日常的浏览器 |
| `BLOCKED` | 验证码、风控、请求太频繁 | 停一停再试;有验证码就请用户在浏览器里处理(错误提示里有标签编号) |
| `EMPTY` | 正常执行了,但没有结果 | 换关键词、放宽条件 |
| `NOT_FOUND` | 帖子、用户、视频不存在 | 检查参数 |
| `CHANGED` | 网站改版了,函数解析不出来 | 用 `seed-browser read` 或 `seed-browser run` 现场取数据,再修函数库里的那个函数 |
| `AMBIGUOUS` | 目标元素匹配到多个 | 照着列出的候选写得更具体,或者用 snapshot 编号 |
| `STALE_REF` / `UNKNOWN_REF` | 编号失效(页面跳转了) | 重新 `seed-browser snapshot -i` |
| `COVERED` | 元素被弹层挡住了 | 先关掉弹层(`seed-browser press Escape`,或点关闭按钮) |
| `NO_LIB` / `NO_FUNCTION` | 没有这个函数库 / 函数 | `seed-browser lib list [域名]` 看看有什么 |

## 3. 读网页

```bash
seed-browser read <网址>           # 后台开标签,读完关掉;--keep 保留(输出里有标签编号)
seed-browser read -t t5            # 读已经打开的标签
seed-browser read <网址> -b        # 只看概要和分段,最省 token
seed-browser read -t t5 -s comments   # 展开某一段(分段 id 见输出末尾)
seed-browser read -t t5 --offset 6000 # 内容被截断时接着读;--limit 50 列表多取几项;--budget 20000 放宽字数
seed-browser read <网址> --scroll 3   # 先往下滚 3 屏再读(懒加载的评论、无限滚动的列表)
seed-browser read <网址> --grep '断货|限购'      # 只看命中的段落(前后各带一段);-C 0 不带上下文,-C 2 多带点
seed-browser read <网址> -s comments --grep AMD # 只在评论区里找
```

- **长文章只关心其中几件事时用 `--grep`**,比读全文省很多:它在整篇内容里按段落找(一整段不换行的中文网页按句子找),相邻命中合并成一块。关键词按正则、不分大小写。每块前面的 `〔@1304〕` 是它在全文里的位置,`seed-browser read --offset 1304` 从那里接着读。列表页上 `--grep` 是按条目过滤。脚本里:`(await seedBrowser.read(url, { grep: /断货|限购/ })).matches` → `[{ offset, text, hits }]`。
- `via: lib:<域名>` 表示用的是函数库里的专用读法(最准),`readability` / `list` / `outline` 是通用提取。内容不对时可以用 `--via readability|list|outline` 手动指定。
- **输出末尾如果列出了“这个网站有函数库 …”**,要批量取数据时就用那些函数,别自己去解析页面。
- 出现 💡 提示页面里有 `window.__INITIAL_STATE__` 这类数据时,用 `seed-browser eval "window.__INITIAL_STATE__.xxx" -t t5` 直接拿结构化数据。

## 4. 函数库:seed-browser call

### 有哪些网站

函数名只是大概,参数、例子和坑以 `seed-browser lib list <域名>` 为准。标 🔑 的要用户在浏览器里登录;(🔑)是登录后更全 / 不容易被限流。

| 类别 | 域名 | 能取什么 |
|---|---|---|
| 搜索引擎 | `google.com` | 网页搜索(被拦自动换必应 / DDG)、`suggest` 联想词、`news` Google 新闻、`trends` 热搜 |
| | `bing.com` `duckduckgo.com` `baidu.com` | 网页搜索;`baidu.com hot` 百度热搜(实时 / 财经 / 民生 / 影视 / 小说 / 汽车 / 游戏) |
| 技术社区 | `news.ycombinator.com` | 搜索、`stories` 首页 / 最新 / Ask / Show 榜、评论(按网页上的排名顺序)、用户 |
| | `reddit.com`(🔑) | 搜索、`posts` 版块帖子 / 热门、评论(会展开“加载更多”)、用户及其帖子 / 评论、版块信息 |
| | `github.com`(🔑) `stackoverflow.com` `v2ex.com` `linux.do` 🔑 `medium.com`(🔑) | 搜索、帖子 / 问题 / issue / 仓库、热门 / trending |
| | `x.com` 🔑 | 搜推文、推文详情和回复、用户 |
| 学术 / AI | `arxiv.org` | 搜论文、论文摘要和 PDF 链接、作者的全部论文、分类最新;`semanticscholar.org` 搜论文、引用和参考文献、相关论文推荐;`openreview.net` 搜投稿、审稿意见和评分、作者;`huggingface.co`(🔑 登录看私有)搜模型 / 数据集 / Spaces、模型详情、每日 / 每周热门榜 |
| 参考 / 存档 | `wikipedia.org` | 搜词条、摘要、正文(按小节)、每日 / 每月最多浏览、随机词条;`archive.org` 查 Wayback 历史快照和快照里的正文、搜存档资料和文件列表 |
| 包和漏洞 | `npmjs.com` `pypi.org` `crates.io` | 搜索、包详情(版本、依赖、仓库)、下载量(按日 / 周);`nvd.nist.gov` 查 CVE 详情和按关键词 / 严重度 / 时间搜漏洞;`osv.dev` 按包名(指定生态、版本)查已知漏洞和漏洞详情 |
| 币圈 | `binance.com` | 实时价格、全市场行情、成交额排行、涨跌幅榜、K 线、盘口、成交流水、合约资金费率 |
| | `coingecko.com` | 市值榜(可按分类过滤)、币详情、历史价格、趋势币、全球行情、分类、交易所、衍生品 |
| 视频 | `youtube.com`(🔑) | 搜索、频道 / 频道全部视频、视频详情、`comments` 评论(翻页、按最新、楼中楼)、`transcript` 字幕(可机翻)、播放列表;🔑 订阅 / 历史 / 稍后观看 |
| | `bilibili.com`(🔑) | 搜索、排行 / 热门、视频详情、评论、`subtitles` 字幕、`summary` 官方 AI 总结、UP 主及其视频 / 动态、下载、收藏夹、🔑 历史、关注 |
| | `douyin.com`(🔑) | 热榜、搜索、视频详情、评论、用户视频 |
| 中文资讯 / 社交 | `weibo.com` 🔑 | 热搜(不用登录)、搜索、微博正文、评论(带楼中楼)、用户及其微博 |
| | `zhihu.com` 🔑 `xiaohongshu.com` 🔑 `toutiao.com`(🔑) `weixin.qq.com` `36kr.com` | 搜索、热榜、问答 / 笔记 / 文章正文、评论 |
| 外文新闻 / Newsletter | `reuters.com`(🔑) `bloomberg.com`(🔑) | 搜索、栏目列表、文章正文 |
| | `bbc.com` | 各栏目头条(RSS,含 BBC 中文)、搜索、文章正文 |
| | `substack.com` | 分类热榜(文章和热门 Notes)、搜文章和 Newsletter、某个 Newsletter 的最新文章、正文 |
| | `lobste.rs` | 热门 / 最新 / 活跃榜、按标签和域名看帖子、搜帖子、评论 |
| | `producthunt.com` | 今日发布、日 / 周 / 月 / 年排行榜、最新、分类佳作、搜产品、产品详情 |
| 中国政策 | `gov.cn` | 国务院文件、部门文件、政策解读的最新列表(可按日期和标题过滤)、全文搜索(国务院文件 / 公报 / 全部)、正文和附件链接 |
| A 股 / 国内财经 | `xueqiu.com`(🔑) | 股票代码、行情、K 线、财务指标、公司资料、热股;讨论帖、帖子评论 |
| | `eastmoney.com` | 7x24 快讯、公告列表和正文、龙虎榜和营业部席位、个股 / 板块资金流、北向成交、板块排行和成分股、涨跌排行(含 ETF / 可转债)、十大股东、人气榜 |
| | `sina.com.cn` | 滚动新闻、7x24 直播、实时报价(**期货、外汇、全球指数**、港美股)、涨跌排行(东方财富被限流时用它) |
| | `10jqka.com.cn` `tdx.com.cn` | 同花顺 / 通达信热股榜、热门板块;通达信还有 ETF / 可转债 / 期货 / 港美股人气榜 |
| 美股 | `finance.yahoo.com` | 行情(含盘前盘后)、K 线、公司资料和估值、财报科目、期权链、新闻、热门、涨跌榜 |
| | `barchart.com` | 期权:IV / IV Rank / Put-Call 比、带希腊值的期权链、到期日、异常期权成交 |
| AI | `aistudio.google.com` 🔑 | 向 Gemini 提问 |

```bash
seed-browser lib list bilibili.com     # 站点笔记 + 每个函数的签名、说明、例子、命令行写法(第一次用某个网站先看这个)
seed-browser call google.com search "sqlite production" --limit 20 --time year
seed-browser call google.com news "deno cloudflare" --days 7
seed-browser call bilibili.com comments BV1GJ411x7h7 --limit 50 -o csv > 评论.csv
seed-browser call bilibili.com subtitles BV1GJ411x7h7 --text      # 视频内容先看字幕 / summary,比看简介准
seed-browser call youtube.com transcript dQw4w9WgXcQ --translate zh-Hans --text
seed-browser call youtube.com videos '@doctorx2023' --limit 200    # PowerShell 里 @ 开头的参数要加引号
seed-browser call reddit.com comments "https://www.reddit.com/r/xxx/comments/abc/" --limit 300
seed-browser call eastmoney.com notices 600519 --type finance      # 公告;正文用 notice <id>
seed-browser call sina.com.cn quote "hf_GC,hf_CL,fx_susdcny,DINIW"  # 纽约金、原油、美元人民币、美元指数
seed-browser call finance.yahoo.com profile NVDA                   # 公司资料 + 估值 + 分析师目标价
seed-browser call binance.com top --limit 10                       # 币安成交额前十;coingecko.com top 是市值榜
seed-browser call gov.cn search 消费 --type state --sort time      # 国务院文件全文搜;正文用 gov.cn article <网址>
```

- **搜索引擎**:`google.com search` 自带排队(同一个进程里的搜索一个接一个发,并行写 `Promise.allSettled` 也没事)、被人机验证拦住时等一会儿重试一次,还不行就**自动改用必应、再不行用 DuckDuckGo**(结果里 `engine` 字段标明来源,stderr 会打一行 ⚠),之后 10 分钟内直接走兜底。只要 Google 的结果就传 `--fallback false`;指定兜底顺序用 `--fallback baidu,bing`。`suggest` / `news` / `trends` 走公开接口,不会被拦。
- **视频内容**:先用字幕(`youtube.com transcript`、`bilibili.com subtitles`)或 B 站 `summary`,比读简介、看评论猜要准得多。YouTube 列表类(`videos`、`playlist`)走内部翻页接口,几秒拉完几百条,不要自己滚动抓 DOM。
- **金融数据怎么选**:A 股行情 / K 线 / 财务用 `xueqiu.com`;快讯、公告、龙虎榜、资金流、板块、股东用 `eastmoney.com`;期货、外汇、全球指数用 `sina.com.cn quote`;美股用 `finance.yahoo.com`,期权希腊值用 `barchart.com`;币圈实时行情和盘口用 `binance.com`,市值榜和趋势币用 `coingecko.com`。东方财富的行情列表接口(`rank` `boards` `moneyflowRank`)请求多了会被临时封 IP,报 `BLOCKED` 时按提示换新浪 / 同花顺 / 通达信。
- 位置参数按函数签名的顺序传,`--名字 值` 合成最后一个对象参数;`--full` 这种是开关。函数自己有 `tab` 参数时(如 `youtube.com videos --tab streams`),`--tab` 交给函数,指定标签用 `-t`。
- 输出被管道或程序接走时默认是 JSONL;给人看用 `-o table` / `-o yaml`;要解析就用 `-o json`。stderr 里“字段 xx 在全部记录里都是 undefined”多半只是这批数据没有这个字段,不用管。
- **管道**:第一个参数写 `-` 就从 stdin 一行一条地读,每条执行一次。JSON 记录默认取它的 `url` 字段(`--field mid` 指定别的字段);`--concurrency 3` 并发;某条失败只在 stderr 打 `✗`,其余照常。

```bash
seed-browser call bilibili.com search 电影解说 --limit 5 | seed-browser call bilibili.com comments - --limit 3
seed-browser call bilibili.com rank 动画 --limit 5 | seed-browser call bilibili.com user - --field mid
seed-browser call news.ycombinator.com search "bun in production" -o jsonl | seed-browser call news.ycombinator.com comments - --limit 50
seed-browser call sina.com.cn search 黄金 --limit 3 | seed-browser call sina.com.cn quote - --field symbol
```
## 5. seed-browser run:写 JS 串起来

代码在 Node 里执行(ES 模块):可以用顶层 `await` 和 `import fs from 'node:fs'`,`return` 的值会打印出来。只有一个表达式时可以不写 `return`。

```bash
seed-browser run 'await seedBrowser.tabs()'
seed-browser run -f step1.js 0,3,7     # 多出来的参数在 seedBrowser.args 里
```

```js
// 标签
const tab = await seedBrowser.open(url)                 // 后台新开
const tab = await seedBrowser.tab('t5')                 // 按编号拿回之前开的标签(上次 seed-browser run 开的也行)
const tab = await seedBrowser.tab('reddit.com')         // 复用已打开的、网址匹配的标签(带登录状态),没有就后台新开
await seedBrowser.cleanup()                             // 关掉这次运行自己开的标签

// 取数据
await seedBrowser.read(url, { budget: 4000 })           // → { title, content, … },读完自动关标签
await seedBrowser.read(url, { grep: /断货|限购/ })        // 只要命中的段落 → .matches = [{ offset, text, hits }]
await seedBrowser.lib('reddit.com').search('x', { limit: 20 })
await fetch('https://api.github.com/...')      // 公开接口直接用 Node 的 fetch,不用开标签
await tab.fetch(url)                           // 在页面里发请求,带着登录状态,返回 JSON
await tab.eval(() => document.title)           // 在页面里跑 JS(函数会被序列化,用不了外面的变量)
await tab.eval(n => [...document.querySelectorAll('h3')].slice(0, n).map(h => h.innerText), 10)  // 参数从后面传

// 接口带签名、自己发不了请求时:让页面自己翻页,接住每一页的返回
for await (const res of tab.collect('/api/search', { more: () => tab.scroll() })) { … }

// 操作(和命令行一一对应)
await tab.click(目标)  tab.fill(目标, 文字, { submit: true })  tab.fill({ 目标: 值, … })
tab.press('Enter')  tab.type(文字)  tab.select(目标, 选项)  tab.upload(目标, 文件)  tab.scroll()
tab.snapshot({ interactive: true })  tab.find(文字)  tab.waitFor({ text })  tab.waitFor({ url: /issues\/\d+/ })  tab.shot({ save })  tab.close()
seedBrowser.log(...)  seedBrowser.sleep(ms)   // 日志走 stderr,不混进结果
```

- 先试 `tab.fetch`:很多看起来带签名的接口,在页面里带着 cookie 直接请求也能用。不行再用 `collect`。
- JS 变量在进程退出后就没了:要留的结果写文件;标签留着,下次用 `seedBrowser.tab('t5')` 拿回来。

## 6. 操作页面

目标元素有四种写法,所有点击、填写类命令都接受:

```bash
seed-browser click e15 -t t5                                      # snapshot 编号(临时操作用)
seed-browser click "#main button.submit" -t t5                    # CSS 选择器
seed-browser click "getByRole('button', { name: '提交' })" -t t5  # 按角色 + 名字(写脚本时推荐)
seed-browser fill "getByLabel('邮箱')" a@b.com -t t5              # 还有 getByText('下一步')、getByPlaceholder('搜索')
```

```bash
seed-browser snapshot -i -t t5              # 可操作的元素 + 编号;不加 -i 会连文字一起显示
seed-browser find "加入购物车" -t t5         # 只返回匹配的那几行和编号,比整页 snapshot 省很多
seed-browser click <目标> -t t5             # --double --right --middle --modifiers Shift --force(被挡住也点)
seed-browser fill <目标> "内容" --submit -t t5
seed-browser fill e3=张三 e5=13800000000 e7=true -t t5    # 一次填多个:勾选框填 true/false,下拉框填选项文字
seed-browser select <目标> 北京 -t t5 / check <目标> / uncheck <目标> / press Enter / type "文字"
seed-browser upload <目标> ./a.png -t t5    # 目标可以是“点了会弹选文件窗口”的按钮
seed-browser click <目标> --download --save ./out -t t5   # 等下载完成,返回文件路径
seed-browser scroll [down|bottom|<目标>] -t t5           # 返回 atBottom
seed-browser wait --text 加载完成 -t t5     # 还有 --gone / --selector / --url / --fn / --idle
seed-browser wait --url '/issues\/\d+/' -t t5   # --url:子串 /issues/、通配 '*github.com/*/issues/*'、斜杠包起来的正则(net log / collect 的匹配也一样)
seed-browser mouse click 320 240 -t t5      # canvas、地图这类:坐标从 seed-browser shot --marks 的截图上看
seed-browser shot --marks -t t5             # 截图,在图上标出编号
```

- 每次操作都返回 `changes`:`navigated`(页面跳转了,编号作废,要重新 snapshot)、`newTabs`(开了新标签,在后台,用 `-t <新编号>` 操作它)、`dialogs`(弹窗已自动确认)、`note`。
- 加 `--snap` 操作完顺带返回新的 snapshot,省一次调用。
- 页面局部重绘后编号会自动按“角色 + 名字”重新定位(结果里会注明);页面跳转后要重新 snapshot。

## 7. 跨站检索的最佳实践

跨几个网站调研一个问题(“X 能不能用于生产环境”“大家怎么评价 Y”“Z 方案现在什么状况”)时,**不要一个网站一个网站地手动 `read`**。按下面的步骤做,每一步是一次 `seed-browser run`:

1. **广撒网**:几个站并行搜索。中英文网站各用对应语言的关键词。把结果规整成同一种结构 `{ site, title, url, heat, time }`,写进 `research/posts.json`。只返回“每个来源成功/失败 + 一行一条、带序号的摘要”。
2. **挑着深挖**:看摘要按标题挑出真正相关的几条(搜索结果里总会混进跑题的帖子)。用序号作参数,拉评论或正文,写进 `research/threads.json`。文章拆成段落,关心特定问题时在脚本顶部设 `KEY`(正则),文章用 `seedBrowser.read(url, { grep })` 只取命中段落、评论也按它过滤。
3. **一手资料**:官方文档用 `seedBrowser.read`,项目数据用公开接口(GitHub API 等)。
4. **写结论**:每个说法后面带来源链接;分清是谁的经验、哪篇文章、官方怎么写的;矛盾的说法都列出来,写清楚各自的前提。
5. **存下来**:现场摸通的网站写成 `~/.seed-browser/lib/<域名>.js` 里的函数,下次直接用(见第 8 节)。

几个要点:

- 用 `Promise.allSettled` 并行。一个站失败(`NEED_LOGIN` / `BLOCKED`)不影响其它站,把失败原因报出来就行。多个 Google 查询也可以并行写(函数库里会排队、被拦会自动换引擎);中文问题可以加一路 `baidu.com`。
- PowerShell 里序号参数要加引号:`seed-browser run -f step2.js "0,31,60"`(不加会被拆成数组)。
- 保留各站自己的相关度顺序,不要按评论数排序(会把跑题的热帖排到前面)。
- 拉详情时限制并发(同时 3 个左右),结束时 `await seedBrowser.cleanup()`。
- 中间结果都写文件,所以每一步都可以单独重跑。

**可以直接复制的脚本**:本 skill 目录下的 `examples/cross-site-research/`(`step1.js` 搜索汇总、`step2.js` 拉详情、`step3.js` 一手资料,`README.md` 是说明)。三个脚本都在真实浏览器里跑通过(HN + Reddit + B 站 + Google)。复制到工作目录,改脚本顶部的关键词就能用:

```bash
seed-browser run -f step1.js              # → 来源状态 + 带序号的摘要
seed-browser run -f step2.js 0,31,60,80   # → 挑中的几条的评论 / 正文
seed-browser run -f step3.js
```

## 8. 把摸通的网站存成函数库

```
第一次遇到 → seed-browser read <网址> 看一眼
不够用     → seed-browser run 里试:tab.eval 看页面数据、seed-browser net log --api -t t5 找接口、tab.fetch 调一下
试通了     → 改成函数,存进 ~/.seed-browser/lib/<域名>.js
写完 / 修完 → seed-browser lib test <域名> [函数],把 @example 真跑一遍验证
```

```js
// ~/.seed-browser/lib/example.com.js
/* 站点笔记:接口在哪、字段什么意思、有什么坑(seed-browser lib list 时显示;下次来修先看它)
 * @login required 没登录会怎样(required 必须登录 / optional 登录更好 / 不写就是不用登录;函数说明里也能写,覆盖这里) */

/** 一行说明
 *  @example search('关键词', { limit: 20 }) */
export async function search(q, { limit = 20 } = {}) {
  const tab = await seedBrowser.tab('example.com')
  const j = await tab.fetch(`https://example.com/api/search?q=${encodeURIComponent(q)}`)
  if (!Array.isArray(j.items)) throw new SeedBrowserError('CHANGED', '搜索接口的返回结构变了', '去修 search')
  if (!j.items.length) throw new SeedBrowserError('EMPTY', `没有搜到 ${q}`, '换个关键词')
  return j.items.slice(0, limit).map(x => ({ title: x.title, url: x.url }))   // 返回数组,每项带 url
}

/** 可选:有这个函数时 seed-browser read 会优先用它;处理不了的页面返回 null */
export async function read(tab) { … }
```

- 参数类型看默认值(`limit = 20` 是数字,`full = false` 是开关),不用另外声明;全局有 `seedBrowser` 和 `SeedBrowserError`,不用 import。
- 标了 `@login` 的函数要自己认出“没登录”(返回 200 但 JSON 里带错误码、跳到登录页、只给前几条)并抛 `NEED_LOGIN`,框架只认得 401 / 403。
- `@example` 会被 `seed-browser lib test` 真的执行:要写真实、能跑通的参数;需要文件就加 `@test file 文件名 内容`,不能自动跑(发帖、删东西)就加 `@test skip 原因`。`seed-browser lib test` 只有“真坏了”才算失败,要登录 / 被拦 / 例子过期单独列出。
- 函数库里定位元素用 CSS 或 `getByRole(...)`,不要用编号(页面一变编号就变);自己开的标签在 `finally` 里关掉。
- 动手之前先看看 [OpenCLI](https://github.com/jackwener/opencli) 的 `clis/<站点>/` 有没有现成实现:它的 `page.goto` / `page.evaluate` 对应 `tab.goto` / `tab.eval`。
- 详细写法见仓库里的 `docs/lib.md`。想录下一次操作、自动分析数据来自哪个接口:`seed-browser trace start --goal "…"` … `seed-browser trace stop`,见 `docs/trace.md`。