# 抓取后交互

> 通过 prompt 或运行代码与获取的页面交互。

import QuickstartPython from "/snippets/zh/v2/interact/quickstart/python.mdx";
import QuickstartJS from "/snippets/zh/v2/interact/quickstart/js.mdx";
import QuickstartCURL from "/snippets/zh/v2/interact/quickstart/curl.mdx";
import QuickstartCLI from "/snippets/zh/v2/interact/quickstart/cli.mdx";
import ExecNodePython from "/snippets/zh/v2/interact/execute-node/python.mdx";
import ExecNodeJS from "/snippets/zh/v2/interact/execute-node/js.mdx";
import ExecNodeCURL from "/snippets/zh/v2/interact/execute-node/curl.mdx";
import ExecNodeCLI from "/snippets/zh/v2/interact/execute-node/cli.mdx";
import ExecPythonPython from "/snippets/zh/v2/interact/execute-python/python.mdx";
import ExecPythonJS from "/snippets/zh/v2/interact/execute-python/js.mdx";
import ExecPythonCURL from "/snippets/zh/v2/interact/execute-python/curl.mdx";
import ExecPythonCLI from "/snippets/zh/v2/interact/execute-python/cli.mdx";
import ExecBashPython from "/snippets/zh/v2/interact/execute-bash/python.mdx";
import ExecBashJS from "/snippets/zh/v2/interact/execute-bash/js.mdx";
import ExecBashCURL from "/snippets/zh/v2/interact/execute-bash/curl.mdx";
import ExecBashCLI from "/snippets/zh/v2/interact/execute-bash/cli.mdx";
import PromptPython from "/snippets/zh/v2/interact/prompt/python.mdx";
import PromptJS from "/snippets/zh/v2/interact/prompt/js.mdx";
import PromptCURL from "/snippets/zh/v2/interact/prompt/curl.mdx";
import PromptCLI from "/snippets/zh/v2/interact/prompt/cli.mdx";
import PromptFormPython from "/snippets/zh/v2/interact/prompt/python-form.mdx";
import PromptFormJS from "/snippets/zh/v2/interact/prompt/js-form.mdx";
import PromptFormCURL from "/snippets/zh/v2/interact/prompt/curl-form.mdx";
import PromptFormCLI from "/snippets/zh/v2/interact/prompt/cli-form.mdx";
import PromptNavPython from "/snippets/zh/v2/interact/prompt/python-navigate.mdx";
import PromptNavJS from "/snippets/zh/v2/interact/prompt/js-navigate.mdx";
import PromptNavCURL from "/snippets/zh/v2/interact/prompt/curl-navigate.mdx";
import PromptNavCLI from "/snippets/zh/v2/interact/prompt/cli-navigate.mdx";
import PromptOutput from "/snippets/zh/v2/interact/response/prompt-output.mdx";
import ResponseOutput from "/snippets/zh/v2/interact/response/output.mdx";
import StopPython from "/snippets/zh/v2/interact/stop/python.mdx";
import StopJS from "/snippets/zh/v2/interact/stop/js.mdx";
import StopCURL from "/snippets/zh/v2/interact/stop/curl.mdx";
import StopCLI from "/snippets/zh/v2/interact/stop/cli.mdx";
import ProfilePython from "/snippets/zh/v2/interact/profile/python.mdx";
import ProfileJS from "/snippets/zh/v2/interact/profile/js.mdx";
import ProfileCURL from "/snippets/zh/v2/interact/profile/curl.mdx";
import ProfileCLI from "/snippets/zh/v2/interact/profile/cli.mdx";
import InteractFeedbackCTA from "/snippets/zh/interact-feedback-cta.mdx";

抓取页面以获取干净的数据，然后调用 `/interact` 在该页面中开始执行 actions：点击按钮、填写表单、提取动态内容，或进一步深入导航。只需描述你想做什么；如果需要完全控制，也可以编写代码。

<InteractFeedbackCTA src="docs-interact" />

<div id="choose-the-right-interaction-model">
  ## 选择合适的交互模型
</div>

| 需求                       | 使用               | 权威文档                                                                                                                                                                                                                                                                                                     | SDK 方法 (Node)                                                        |
| ------------------------ | ---------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------- |
| 无需先抓取，直接启动独立的浏览器会话       | 浏览器沙箱 / 独立 交互 会话 | [Browser Sandbox](/zh/features/browser), [Create Browser Session](/zh/api-reference/endpoint/browser-create), [Execute Browser Code](/zh/api-reference/endpoint/browser-execute), [List Browser Sessions](/zh/api-reference/endpoint/browser-list), [Delete Browser Session](/zh/api-reference/endpoint/browser-delete) | `browser()`, `browserExecute()`, `listBrowsers()`, `deleteBrowser()` |
| 使用 `scrapeId` 基于抓取结果继续交互 | 抓取后交互            | [Execute Interact](/zh/api-reference/endpoint/scrape-execute), [Stop Interact](/zh/api-reference/endpoint/scrape-browser-delete)                                                                                                                                                                               | `interact()`, `stopInteraction()`                                    |

如果工作流以 `POST /v2/scrape` 开始，且响应中包含 `data.metadata.scrapeId`，请使用绑定到抓取的 交互。当你需要具有独立生命周期的独立会话时，请使用 浏览器沙箱。Python SDK 使用对应的 snake&#95;case 方法 (`browser()`、`browser_execute()`、`list_browsers()`、`delete_browser()`、`interact()`、`stop_interaction()`) 。

<CardGroup cols={3}>
  <Card title="AI prompts" icon="wand-magic-sparkles">
    描述你希望在页面中执行的操作
  </Card>

  <Card title="代码执行" icon="code">
    通过代码安全地与 playwright、agent-browser 交互
  </Card>

  <Card title="实时视图" icon="eye">
    通过可嵌入的流实时观看或与浏览器交互
  </Card>
</CardGroup>

<div id="how-it-works">
  ## 工作原理
</div>

1. 使用 `POST /v2/scrape` **抓取**一个 URL。响应会在 `data.metadata.scrapeId` 中返回 `scrapeId`。如果你想持久保存浏览器状态，请在此请求中传入 `profile`。
2. 调用 `POST /v2/scrape/{scrapeId}/interact`，并传入 `prompt` 或 Playwright `code` 进行**交互**。此处不要传入 `profile`；交互会话会继承抓取任务中的 `profile`。
3. 完成后，使用 `DELETE /v2/scrape/{scrapeId}/interact` **停止**该会话。对于可写的 `profile`，会话停止时会保存更改。

<div id="quick-start">
  ## 快速开始
</div>

抓取页面、与其交互，然后停止会话：

<CodeGroup>
  <QuickstartPython />

  <QuickstartJS />

  <QuickstartCURL />

  <QuickstartCLI />
</CodeGroup>

<ResponseOutput />

<div id="interact-via-prompting">
  ## 通过 prompt 交互
</div>

这是与页面交互的最简单方式。用自然语言描述你的需求，它会自动点击、输入、滚动并提取数据。

<CodeGroup>
  <PromptPython />

  <PromptJS />

  <PromptCURL />

  <PromptCLI />
</CodeGroup>

响应中包含一个 `output` 字段，其中包含代理的答案：

<PromptOutput />

<div id="keep-prompts-small-and-focused">
  ### 保持 prompt 简短且聚焦
</div>

当每个 prompt 都是**单一且明确的任务**时，效果最好。不要一次性要求代理完成复杂的多步骤工作流，而应将其拆分为单独的交互调用。每次调用都会复用同一个浏览器会话，因此状态会在调用之间延续。

<div id="running-code">
  ## 运行代码
</div>

若要实现完全控制，你可以直接在浏览器沙箱中执行代码。`page` 变量 (一个 Playwright Page 对象) 可在 Node.js 和 Python 中使用。Bash 模式已预装 [agent-browser](https://github.com/vercel-labs/agent-browser)。你还可以在当前会话中截取屏幕截图：在 Node.js 中使用 `(await page.screenshot()).toString("base64")`，在 Python 中使用 `await page.screenshot(path="/tmp/screenshot.png")`，或在 Bash 中使用 `agent-browser screenshot`。

<div id="nodejs-playwright">
  ### Node.js (Playwright)
</div>

默认语言。可直接编写 Playwright 代码。`page` 已连接到浏览器。

<CodeGroup>
  <ExecNodePython />

  <ExecNodeJS />

  <ExecNodeCURL />

  <ExecNodeCLI />
</CodeGroup>

<div id="python">
  ### Python
</div>

将 `language` 设置为 `"python"`，以使用 Playwright 的 Python API。

<CodeGroup>
  <ExecPythonPython />

  <ExecPythonJS />

  <ExecPythonCURL />

  <ExecPythonCLI />
</CodeGroup>

<div id="bash-agent-browser">
  ### Bash (agent-browser)
</div>

[agent-browser](https://github.com/vercel-labs/agent-browser) 是一个预装在沙箱中的 CLI 工具，提供 60 多个命令。它会提供带有元素引用 (`@e1`、`@e2` 等) 的辅助功能树，非常适合由 LLM 驱动的自动化。

<CodeGroup>
  <ExecBashPython />

  <ExecBashJS />

  <ExecBashCURL />

  <ExecBashCLI />
</CodeGroup>

常见的 agent-browser 命令：

| 命令                        | 描述                |
| ------------------------- | ----------------- |
| `snapshot`                | 带元素引用的完整辅助功能树     |
| `snapshot -i`             | 仅显示可交互元素          |
| `click @e1`               | 通过引用点击元素          |
| `fill @e1 "text"`         | 清空字段并输入文本         |
| `type @e1 "text"`         | 不清空直接输入           |
| `press Enter`             | 按下键盘按键            |
| `scroll down 500`         | 向下滚动 500 像素       |
| `get text @e1`            | 获取文本内容            |
| `get url`                 | 获取当前 URL          |
| `wait @e1`                | 等待元素出现            |
| `wait --load networkidle` | 等待网络空闲            |
| `find text "X" click`     | 按文本查找元素并点击        |
| `screenshot`              | 对当前页面进行截图         |
| `eval "js code"`          | 在页面中运行 JavaScript |

<div id="live-view">
  ## 实时视图
</div>

每个交互响应都会返回一个 `liveViewUrl`，你可以将其嵌入页面中，以实时查看浏览器画面。适用于调试、演示或构建基于浏览器的 UI。

```json Response
{
  "success": true,
  "cdpUrl": "wss://browser.firecrawl.dev/...",
  "liveViewUrl": "https://liveview.firecrawl.dev/...",
  "interactiveLiveViewUrl": "https://liveview.firecrawl.dev/...",
  "stdout": "",
  "result": "...",
  "exitCode": 0
}
```

```html
<iframe src="LIVE_VIEW_URL" width="100%" height="600" />
```

<div id="interactive-live-view">
  ### 交互式实时视图
</div>

响应还包含一个 `interactiveLiveViewUrl`。与仅可查看的标准实时视图不同，交互式实时视图允许用户通过嵌入式流直接点击、输入，并与浏览器会话交互。这对于构建面向用户的浏览器 UI 很有帮助，例如登录流程，或需要终端用户控制浏览器的引导式工作流。

```html
<iframe src="INTERACTIVE_LIVE_VIEW_URL" width="100%" height="600" />
```

<div id="cdp-url">
  ### CDP URL
</div>

每个交互响应也会返回一个 `cdpUrl`：即该浏览器会话的原始 Chrome DevTools Protocol (CDP) WebSocket URL。你可以用它从 Playwright、Puppeteer 或任何 CDP 客户端直接连接到实时会话，并通过自己的代码控制浏览器。

```js
import { chromium } from "playwright";

const browser = await chromium.connectOverCDP(cdpUrl);
const context = browser.contexts()[0];
const page = context.pages()[0];
```

<div id="session-lifecycle">
  ## 会话生命周期
</div>

<div id="creation">
  ### 创建
</div>

首次调用 `POST /v2/scrape/{scrapeId}/interact` 会延续抓取会话并启动交互。

<div id="reuse">
  ### 复用
</div>

对同一个 `scrapeId` 的后续 interact 调用会复用现有会话。浏览器会保持打开状态，并在调用之间保留其状态，因此你可以将多个交互串联起来：

<CodeGroup>
  ```python Python
  # 第一次调用：点击一个标签页
  app.interact(scrape_id, code="await page.click('#tab-2')")

  # 第二次调用：该标签页仍保持选中状态，提取其内容
  result = app.interact(scrape_id, code="await page.$eval('#tab-2-content', el => el.textContent)")
  print(result.result)
  ```

  ```js Node
  // 第一次调用：点击一个标签页
  await app.interact(scrapeId, { code: "await page.click('#tab-2')" });

  // 第二次调用：该标签页仍保持选中状态，提取其内容
  const result = await app.interact(scrapeId, {
    code: "await page.$eval('#tab-2-content', el => el.textContent)",
  });
  console.log(result.result);
  ```

  ```bash CLI
  # 第一次调用：点击一个标签页
  firecrawl interact -c "await page.click('#tab-2')"

  # 第二次调用：该标签页仍保持选中状态，提取其内容
  firecrawl interact -c "await page.\$eval('#tab-2-content', el => el.textContent)"
  ```
</CodeGroup>

<div id="cleanup">
  ### 清理
</div>

完成后请显式停止会话：

<CodeGroup>
  <StopPython />

  <StopJS />

  <StopCURL />

  <StopCLI />
</CodeGroup>

会话也会根据 TTL (默认值：10 分钟) 或无活动超时时间 (默认值：5 分钟) 自动过期。

<Warning>
  请务必在使用完毕后停止会话，以避免不必要的计费。额度按秒折算。最低收费为一个浏览器分钟。使用 `prompt` 的会话按每浏览器分钟 7 额度计费；不使用 prompt 的会话按每浏览器分钟 2 额度计费。详情请参见[计费](/zh/billing#credit-costs-per-endpoint)。
</Warning>

<div id="persistent-profiles-with-scrape-interact">
  ## 使用 抓取 + 交互 的持久化配置文件
</div>

默认情况下，每个 scrape + 交互 会话都会从全新的浏览器状态开始。使用 `profile`，你可以在多次抓取之间保存并复用浏览器状态 (cookies、localStorage、会话) 。这对于保持登录状态和保留偏好设置非常有用。

在初始 `POST /v2/scrape` 请求中传入 `profile` 对象。不要在 `POST /v2/scrape/{scrapeId}/interact` 中传入 `profile`；交互 会话会复用抓取任务的浏览器会话和 `profile` 设置。使用 `DELETE /v2/scrape/{scrapeId}/interact` 停止 交互 会话，以便保存对可写配置文件所做的更改。

```bash cURL
curl -X POST "https://api.firecrawl.dev/v2/scrape" \
  -H "Authorization: Bearer fc-YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://example.com",
    "formats": ["markdown"],
    "profile": {
      "name": "my-profile",
      "saveChanges": true
    }
  }'

curl -X POST "https://api.firecrawl.dev/v2/scrape/SCRAPE_ID/interact" \
  -H "Authorization: Bearer fc-YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "prompt": "Click the login button"
  }'

curl -X DELETE "https://api.firecrawl.dev/v2/scrape/SCRAPE_ID/interact" \
  -H "Authorization: Bearer fc-YOUR_API_KEY"
```

配置文件的生命周期如下：

1. 使用 `profile.name` 和 `saveChanges: true` 创建抓取。
2. 针对返回的 `scrapeId` 运行 prompt 或代码交互。
3. 停止会话以保存 cookies、localStorage 和其他浏览器状态。
4. 稍后使用相同的 `profile.name` 启动新的抓取。当你只想读取现有状态而不将更改写回时，使用 `saveChanges: false`。

<CodeGroup>
  <ProfilePython />

  <ProfileJS />

  <ProfileCURL />

  <ProfileCLI />
</CodeGroup>

| 参数            | 默认值    | 描述                                                                               |
| ------------- | ------ | -------------------------------------------------------------------------------- |
| `name`        | None   | 持久化配置文件的名称。名称相同的抓取会共享浏览器状态。                                                      |
| `saveChanges` | `true` | 当为 `true` 时，交互 会话停止后会将浏览器状态保存回该配置文件。设为 `false` 可在不写入的情况下加载现有数据，这在你需要多个并发读取方时很有用。 |

<Note>
  同一时间只能有一个会话保存到某个配置文件。如果另一个会话已在保存，你将收到 `409` 错误。你仍然可以使用 `saveChanges: false` 打开同一个配置文件，或稍后重试。
</Note>

浏览器状态会在 交互 会话停止时保存。完成后请务必停止该会话，以便该配置文件可以被复用。

<div id="validate-persistence">
  ### 验证持久化
</div>

你可以在一个会话中写入 `localStorage` 值并停止该会话，然后在第二个使用相同配置文件的会话中读取该值，以此测试持久化，而无需依赖真实的登录流程。

```bash cURL
# 会话 1：写入浏览器状态并保存
RESPONSE=$(curl -s -X POST "https://api.firecrawl.dev/v2/scrape" \
  -H "Authorization: Bearer $FIRECRAWL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://example.com",
    "formats": ["markdown"],
    "profile": { "name": "profile-validation", "saveChanges": true }
  }')

SCRAPE_ID=$(echo "$RESPONSE" | jq -r ".data.metadata.scrapeId")

curl -s -X POST "https://api.firecrawl.dev/v2/scrape/$SCRAPE_ID/interact" \
  -H "Authorization: Bearer $FIRECRAWL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "code": "await page.evaluate(() => { localStorage.setItem(\"firecrawlProfileCheck\", \"saved\"); document.cookie = \"firecrawl_profile_check=saved; path=/; max-age=3600\"; return localStorage.getItem(\"firecrawlProfileCheck\"); });"
  }'

curl -s -X DELETE "https://api.firecrawl.dev/v2/scrape/$SCRAPE_ID/interact" \
  -H "Authorization: Bearer $FIRECRAWL_API_KEY"

# 会话 2：以只读模式加载相同配置文件并验证值
RESPONSE=$(curl -s -X POST "https://api.firecrawl.dev/v2/scrape" \
  -H "Authorization: Bearer $FIRECRAWL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://example.com",
    "formats": ["markdown"],
    "profile": { "name": "profile-validation", "saveChanges": false }
  }')

SCRAPE_ID=$(echo "$RESPONSE" | jq -r ".data.metadata.scrapeId")

curl -s -X POST "https://api.firecrawl.dev/v2/scrape/$SCRAPE_ID/interact" \
  -H "Authorization: Bearer $FIRECRAWL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "code": "await page.evaluate(() => ({ localStorage: localStorage.getItem(\"firecrawlProfileCheck\"), cookie: document.cookie.includes(\"firecrawl_profile_check=saved\") }));"
  }'

curl -s -X DELETE "https://api.firecrawl.dev/v2/scrape/$SCRAPE_ID/interact" \
  -H "Authorization: Bearer $FIRECRAWL_API_KEY"
```

第二个交互响应应显示 `localStorage` 为 `"saved"`，`cookie` 为 `true`。

<Info>
  通过 API 创建的 Profiles 可能暂时还不会显示在 Dashboard &gt; Interact &gt; Profiles 中。Dashboard 目前尚未提供通过 API 创建的持久化 Profiles 的完整列表。
</Info>

<div id="when-to-use-what">
  ## 何时使用什么
</div>

| 使用场景         | 推荐                         | 原因                  |
| ------------ | -------------------------- | ------------------- |
| 网页搜索         | [Search](/zh/features/search) | 专用搜索端点              |
| 从 URL 获取干净内容 | [Scrape](/zh/features/scrape) | 一次 API 调用，无需会话      |
| 在页面上点击、输入、导航 | **交互** (prompt)            | 只需用英文描述即可           |
| 提取交互后的数据     | **交互** (prompt)            | 无需选择器               |
| 复杂的抓取逻辑      | **交互** (code)              | 完整的 Playwright 控制能力 |

<Info>
  **交互 与 浏览器沙箱**：交互构建在与 [浏览器沙箱](/zh/features/browser) 相同的基础设施之上，但针对最常见的使用模式提供了更好的界面：先抓取页面，再进一步深入。当你需要一个不绑定到特定抓取任务的独立浏览器会话时，浏览器沙箱更合适。
</Info>

<div id="pricing">
  ## 定价
</div>

* **仅代码** (无 `prompt`): 每个会话分钟 2 个额度
* **使用 AI prompts**: 每个会话分钟 7 个额度
* **抓取**: 单独计费 (每次抓取 1 个额度，外加任何特定格式的费用) 。

<div id="api-reference">
  ## API 参考
</div>

* [执行交互](/zh/api-reference/endpoint/scrape-execute): `POST /v2/scrape/{scrapeId}/interact`
* [停止交互](/zh/api-reference/endpoint/scrape-browser-delete): `DELETE /v2/scrape/{scrapeId}/interact`

<div id="request-body-post">
  ### 请求体 (POST)
</div>

| 字段         | 类型       | 默认值      | 描述                                                                  |
| ---------- | -------- | -------- | ------------------------------------------------------------------- |
| `prompt`   | `string` | None     | 提供给 AI 代理的自然语言任务。若未设置 `code`，则此项必填。最多 10,000 个字符。                   |
| `code`     | `string` | None     | 要执行的代码 (Node.js、Python 或 Bash) 。若未设置 `prompt`，则此项必填。最多 100,000 个字符。 |
| `language` | `string` | `"node"` | `"node"`、`"python"` 或 `"bash"`。仅在使用 `code` 时生效。                     |
| `timeout`  | `number` | `30`     | 超时时间，单位为秒 (1–300) 。                                                 |
| `origin`   | `string` | None     | 用于活动追踪的调用方标识符。                                                      |

<div id="response">
  ### 响应
</div>

| 字段                       | 描述                                                                                               |
| ------------------------ | ------------------------------------------------------------------------------------------------ |
| `success`                | 如果执行已完成且未出现错误，则为 `true`                                                                          |
| `cdpUrl`                 | 浏览器会话的原始 Chrome DevTools Protocol (CDP) WebSocket URL。可直接使用 Playwright、Puppeteer 或任何 CDP 客户端进行连接 |
| `liveViewUrl`            | 浏览器会话的只读实时视图 URL                                                                                 |
| `interactiveLiveViewUrl` | 交互式实时视图 URL (查看者可控制浏览器)                                                                          |
| `output`                 | 代理对你的 `prompt` 给出的自然语言回答。仅在使用 `prompt` 时返回。                                                      |
| `stdout`                 | 代码执行的标准输出                                                                                        |
| `result`                 | 沙箱的原始返回值。对于 `code`：最后一个求值的表达式。对于 `prompt`：代理用于生成 `output` 的原始页面快照。                         |
| `stderr`                 | 标准错误输出                                                                                           |
| `exitCode`               | 退出码 (`0` = 成功)                                                                                   |
| `killed`                 | 如果执行因超时而终止，则为 `true`                                                                             |

***

有反馈或需要帮助？请发送邮件至 [help@firecrawl.com](mailto:help@firecrawl.com)，或通过 [Discord](https://discord.gg/firecrawl) 联系我们。
