跳到正文

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。

继续阅读

相关文章

产品更新

11 分钟

CrawlForge MCP v5.2.0: Shopify Product Data Without Parsing HTML

v5.2.0 adds a shopify-product template that reads the store's own JSON instead of its markup, rebuilds the Amazon template against live pages after it passed six tests while returning nulls, and fixes price monitoring that never fired.

产品更新

9 分钟

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

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