跳到正文

CrawlForge Team工程团队

阅读时长 8 分钟

CrawlForge MCP v6.6.0:保持打开的浏览器会话

智能体从未见过它即将点击的那个页面。scrape_with_actions 却仍要求它一次性写出最多 20 个动作 — 在 #user 里输入、点击 .btn-primary、等待 1000 毫秒 — 然后在调用返回时关闭浏览器。第 3 步猜错,链条就停在那里,结果返回 success: false,而那 5 credits 已经花掉了。

CrawlForge MCP v6.6.0 修的不是猜测,而是这个循环。新的 browser_session 工具 — 我们的第 31 个 — 让一个真实的浏览器页面在多次工具调用之间保持存活,于是智能体可以打开页面、看一眼、针对看到的内容动手、再看一眼、然后读取结果。「看」才是关键的那一步,它有个名字:snapshot。

目录

本次发布了什么

v6.6.0 就是一个工具加一个原语:

  • browser_session — 单个工具,带一个 operation 枚举:open、snapshot、act、read、screenshot、close、list。页面、它的 cookies 和登录状态会在多次调用之间存活,直到会话过期。
  • snapshot — 实时页面的无障碍风格树,其中每个可交互元素都带一个稳定的 ref(@e1、@e2)。任何动作的 selector 都可以用 ref 代替 CSS 选择器。
  • 工具数量从 30 增加到 31,两个接口都有:MCP 与 REST API。
  • 同时发布的还有 crawlforge-browser-sessions 智能体 skill 和 crawlforge browser CLI 命令。

没有任何重命名,现有工具的形状和价格都没变,因此这是一次可直接替换的升级。

为什么一次性链条是盲目的

scrape_with_actions 是个好工具,但有一个结构性限制:动作数组必须在任何东西加载之前写好。智能体挑 #login-email、input[name="email"] 还是 .form-field:first-child,靠的是对登录表单通常长什么样的记忆,而不是这个登录表单。

其中一个猜测落空时,ActionExecutor 会中止链条 — continueOnError 默认为 false — 并且工具返回一个带 success: false 和错误信息的结果,而不是抛出异常。计费跟随调用而非结果,所以这 5 credits 花在了一条只走到第三个动作的链条上。

问题不在成本,而在于智能体的下一步只能再猜一次,掌握的信息并不比第一次多。一次性工具没法告诉它页面里有什么,因为结果到手时浏览器已经不在了。

Snapshot:智能体先看后动

snapshot 遍历实时 DOM,为每个有意义的节点输出一行缩进文本,参照无障碍树的模型给出角色、可访问名称,并为可交互节点给出 ref:

Text
[document] "Sign in"
  @e1 [textbox] "Email"
  @e2 [textbox] "Password"
  @e3 [button] "Sign in"
  [link] "Forgot password?"

有两点让它真正有用而不只是好看。结构性节点(标题、地标、表单)作为上下文出现,但不会拿到 ref,因为它们不是操作目标。而每个 ref 背后都有遍历过程中打在元素上的 data-cf-ref 属性,所以 @e1 会解析成普通的 [data-cf-ref="e1"] 选择器,天然适配现有的每一条动作路径 — 包括接收原始选择器字符串的 stealth 浏览器拟人行为代码。

我们自己写了这套遍历,而没有包装 Playwright 的 ariaSnapshot()(它输出的 YAML 完全没有元素 ref),也没有用 page._snapshotForAI()(私有 API,我们不会依赖)。

interactive_only 默认为 true,max_nodes 默认为 200,因此一个有 4000 个元素的应用页面返回的是智能体真的读得完的列表。

完整的会话循环

下面是一次登录加一次读取,使用 crawlforge-sdk 包。五次调用,一个浏览器页面,一套 cookies。

Typescript
// npm install crawlforge-sdk
import { CrawlForge } from 'crawlforge-sdk';

const client = new CrawlForge({ apiKey: process.env.CRAWLFORGE_API_KEY });

// 1. Open the session (3 credits). One API key may hold one session at a time.
const opened = await client.browserSession({
  operation: 'open',
  url: 'https://app.example.com/login',
  ttl: 600
});

const { sessionId } = opened.data as { sessionId: string };

try {
  // 2. Look before acting (1 credit). Every interactive element comes back
  //    with a stable ref, so the next call targets what is actually there.
  const seen = await client.browserSession({ operation: 'snapshot', session_id: sessionId });
  console.log((seen.data as { snapshot: { tree: string } }).snapshot.tree);

  // 3. Act on those refs in a separate call (1 credit) — same page, same cookies.
  await client.browserSession({
    operation: 'act',
    session_id: sessionId,
    actions: [
      { type: 'type', selector: '@e1', text: 'user@example.com' },
      { type: 'type', selector: '@e2', text: 'secret123' },
      { type: 'click', selector: '@e3' },
      { type: 'wait', duration: 1000 }
    ]
  });

  // 4. Read the page the login landed on (2 credits) — the live DOM with the
  //    session's cookies, not a fresh fetch of the URL.
  const page = await client.browserSession({
    operation: 'read',
    session_id: sessionId,
    formats: ['markdown']
  });
  const { title, content } = page.data as { title: string; content: { markdown: string } };
  console.log(title, content.markdown.length);
} finally {
  // 5. Close it rather than waiting for the TTL (1 credit).
  await client.browserSession({ operation: 'close', session_id: sessionId });
}

第 4 步值得多说一句。read 是从会话正握着的那个页面里提取内容,而不是重新请求它的 URL — 重新请求会缺少 cookies,也不包含会话已经点过的一切,而那正是拥有一个会话的全部意义。格式有 markdown、html、text 和 json,一次调用可以同时要多个。

ref 失败时会大声报错,绝不悄无声息

导航会让 ref 失效。这不是注意事项,而是设计:一个跨页面存活下来的 ref 会指向新文档里恰好排第三的元素,而智能体会在毫不知情的情况下点下去。

因此 ref 表存放在以页面为键的 WeakMap 里,并在每次主框架导航时清空。对失效的 ref 动手,你拿到的是原因和解法,而不是一团迷雾:

Text
Stale element ref @e3: the page navigated since the last snapshot —
take a new snapshot before acting on refs.

未知 ref 同样直白:"Unknown element ref @e9: the current snapshot has 4 refs (@e1-@e4) — take a new snapshot." 智能体看到这两种消息都能自行处理。悄无声息地点错元素,正是我们花设计预算去避免的那种失败。

snapshot 也进了 scrape_with_actions

想先看一眼,并不一定需要会话。snapshot 同时作为动作类型进入了 scrape_with_actions,所以一次性链条也可以先观察,再针对自己的 snapshot 产出的 ref 动手,全都在同一次 5 credits 的调用里:

Json
{
  "url": "https://news.ycombinator.com/login",
  "actions": [
    { "type": "snapshot" },
    { "type": "type", "selector": "@e1", "text": "reader" },
    { "type": "click", "selector": "@e3" }
  ]
}

这覆盖了常见情形:智能体需要看页面,但整个流程仍然装得进一次调用。当装不下时再动用会话 — 后面的决策取决于前一步返回了什么,或者登录状态必须跨多次读取保持。

一次会话的成本

browser_session 按操作计费,因为一次会话就是多次调用,统一定价会让每个便宜的操作都按上限收费:

操作Credits作用
open3启动浏览器上下文并导航
read2把实时 DOM 提取为你要的格式
snapshot1对已打开页面注入一次遍历
act1对已打开页面执行最多 20 个动作
screenshot1PNG 或 JPEG,整页或单个元素
close1释放页面及其上下文
list1你打开的会话及其计时器

上面那个登录流程是 8 credits:3 + 1 + 1 + 2 + 1。加上登录后导航所需要的那次重新 snapshot 就是 9。同样的事情用 scrape_with_actions 是 5 — 而面对一个从未见过的表单,这 5 个更可能白花。

工具参考把 browser_session 标为统一的 3 credits。那是 open 的价格,而且是上限而非费率:REST 路由在还读不到请求体时先预留 3,调用成功后再扣除该操作的真实成本。你付的永远不会超过公布的数字,通常还更少。credits 与其他工具共用同一份额度 — 见套餐与 credits 包。

限制,说清楚

一次会话占用的是真实基础设施,所以限制也是实打实的:

  • 它会过期。 ttl 默认从 open 起算 600 秒(范围 30-3600),activity_ttl 默认自上次使用起 300 秒(范围 10-3600),以先到者为准。用完就关,别等它自己过期。
  • 托管 API 上一次只能开一个。 一个 REST API key 只能持有一个会话;stdio 和自托管安装保留默认的三个。这是算术而非谨慎:一个客户开三个会话就会占满整个托管会话容量。
  • 托管接口上没有 executeJavaScript。 在我们基础设施的浏览器里跑任意脚本,和在你自己笔记本上跑同一段脚本是两回事,所以远程传输上一律拒绝。改用 click、type、select 和 press,或者通过 stdio 在本地运行 MCP server。
  • 每次导航都会重新过闸。 长期会话是一个可重复的导航原语,因此会话内的每次 navigate 都会重新经过 SSRF 防护、主机黑名单和 robots.txt 检查。开一个会话并不等于买到一次免检跳转。
  • 登录状态不跨会话保留。 登录只在同一个会话内有效。没有从一个会话带到下一个会话的已保存配置文件。

从 CLI 跑一次会话

CLI 每次调用跑完整的一个会话 — open、snapshot、你的步骤、read、close — 因为会话活在打开它的那个进程里,而 CLI 进程在命令结束时就结束了:

Bash
# steps.json: [{"operation":"act","actions":[{"type":"click","selector":"@e2"}]}]
crawlforge browser https://news.ycombinator.com \
  --steps steps.json \
  --read --format markdown

当会话必须比打开它的那次调用活得更久时,请用 MCP 工具或 REST API。并不存在返回一个 id 的 crawlforge browser open:那个 id 背后的页面会在进程退出时死掉。

什么时候用哪个

scrape_with_actionsbrowser_session
浏览器生命周期一次调用跨调用,直到 TTL
选择器事先写好,未曾见过来自你读过的 snapshot 的 ref
价格统一 5开启 3,之后每次 1-2
选错选择器的代价整次 5 credits 的调用那次 act 的 1 credit
登录每次调用重来付一次,由会话保持
适合可以事先写下来的流程必须边看边走的流程

两者都不取代 scrape(2 credits):对于无需交互就能渲染的页面,它仍然是正确答案。而 browser_session 最适合目标是应用而非文档的团队:登录后的仪表盘、多步向导、点四次才出现的筛选结果。

如何升级

Bash
npm install -g crawlforge-mcp-server@latest
crawlforge --version   # 6.6.0

如果你的 MCP 客户端用 npx 启动服务器,下次重启就会用上 v6.6.0。现有工具的 schema、输出形状和费用都没有变化。完整的版本历史见changelog。

想要一个先看后点的智能体?免费开始,赠送 1,000 credits — 足够 125 次完整的登录加读取会话 — 并阅读 browser_session API 参考,了解每个操作、参数和响应字段。

亲自试一试——无需注册

在 Playground 中探索全部 31 个 CrawlForge 抓取与提取工具,然后免费开始,获取 1,000 credits。

1,000 免费 credits • 一次性 • 无需信用卡

标签

  • release
  • v6.6.0
  • browser_session
  • browser automation
  • MCP
  • web scraping
  • changelog

关于作者

CrawlForge Team

工程团队

我们正在打造功能最全面的 Web 抓取 MCP server。我们开发的工具帮助开发者为 AI 应用提取、分析和转换 Web 数据。

邮件订阅

及时获取最新洞察

将教程、产品更新与 Web 抓取技巧直接发送到你的收件箱。

拒绝垃圾邮件,随时可取消订阅。

FAQ

常见问题

01CrawlForge MCP 里的 browser_session 是什么?

browser_session 是 CrawlForge MCP 的第 31 个工具,随 v6.6.0 发布。它让一个真实的浏览器页面在多次工具调用之间保持存活,而不是在调用返回时关闭,并由一个 operation 枚举驱动:open、snapshot、act、read、screenshot、close 和 list。页面的 cookies、登录状态和元素 ref 都会在调用之间保留,因此智能体可以把观察页面、针对所见动手、读取结果拆成三个独立步骤,而不是一条盲目的链条。

02browser_session 和 scrape_with_actions 有什么不同?

scrape_with_actions 是一次性的:它接收最多 20 个在页面加载之前就选好的动作,调用返回时浏览器随即关闭。browser_session 把这个循环倒过来 — 打开、snapshot、针对 snapshot 返回的 ref 动手、再 snapshot — 所以选择器是在智能体真正看过的页面上选的。实际差别在于猜错的代价:一个错误的选择器会让整条 5 credits 的 scrape_with_actions 链条作废,而在会话中只损失那次 act 调用的 1 credit。

03一次浏览器会话要花多少 credits?

计费按操作计算:open 3 credits,read 2,snapshot、act、screenshot、close 和 list 各 1。一次登录加读取的流程 — open、snapshot、act、read、close — 是 8 credits;加上登录后导航所需要的那次重新 snapshot 就是 9。公布的 3 credits 统一价是 open 的价格,也是 REST API 在读取请求体之前预留的上限;调用成功后扣除的是该操作的真实成本。

04CrawlForge 的浏览器会话能保持打开多久?

两个计时器同时运行,先到者生效。ttl 从会话打开时起算,默认 600 秒,可设置在 30 到 3600 秒之间;activity_ttl 从上一次操作起算,默认 300 秒,范围 10 到 3600 秒。托管的 REST API key 同一时间只能持有一个会话,而 stdio 和自托管安装允许三个,因此明确关闭会话而不是等它过期,值得那 1 credit。

05登录状态可以在不同会话之间复用吗?

不能。登录只在一个会话内部有效 — 这正是保持页面打开的意义 — 但没有任何东西会从一个会话带到下一个会话。没有已保存的浏览器配置文件,关闭会话或让它过期都会丢弃其 cookies。如果工作流需要一个已认证的页面,请在将要读取它的那个会话内登录,并让会话保持打开以完成后续读取。

06可以在托管的浏览器会话里执行 JavaScript 吗?

不可以。executeJavaScript 动作在通过远程传输提供的浏览器会话中一律被拒绝,包括托管的 CrawlForge REST API,因为脚本会运行在 CrawlForge 基础设施的浏览器里,而不是你的机器上。就交互而言,click、type、select 和 press 动作覆盖了同样的场景。如果确实需要页面内脚本执行,请通过 stdio 在本地运行 MCP server。

继续阅读

相关文章

产品更新

9 分钟

CrawlForge MCP v5.1.0:无需 API 也能搜索 Reddit

reddit.com 挡住了我们手上的每一种抓取方式 — 所以 v5.1.0 推出第 28 个工具 reddit_search:通过社区归档搜索帖子和评论、读取完整讨论串。无需 Reddit API key,无需任何凭证,每次调用 5 credits。