# 抓取后交互 (/zh/features/interact)

<!-- agent-signals: reading_time_min: 16 · est_tokens: 9852 · updated: 2026-07-31 -->

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

<div className="firecrawl-cta-box">
  <div style="{ display: &#x22;flex&#x22;, alignItems: &#x22;flex-start&#x22;, gap: &#x22;8px&#x22;, marginBottom: &#x22;8px&#x22; }">
    <Icon icon="<svg xmlns=&#x22;http://www.w3.org/2000/svg&#x22; viewBox=&#x22;0 0 24 24&#x22; fill=&#x22;none&#x22;><path d=&#x22;M8 7L16 7&#x22; stroke=&#x22;currentColor&#x22; stroke-linecap=&#x22;round&#x22; stroke-linejoin=&#x22;round&#x22; stroke-width=&#x22;1.5&#x22;/><path d=&#x22;M8 11L12 11&#x22; stroke=&#x22;currentColor&#x22; stroke-linecap=&#x22;round&#x22; stroke-linejoin=&#x22;round&#x22; stroke-width=&#x22;1.5&#x22;/><path d=&#x22;M13 21.5V21C13 18.1716 13 16.7574 13.8787 15.8787C14.7574 15 16.1716 15 19 15H19.5M20 13.3431V10C20 6.22876 20 4.34315 18.8284 3.17157C17.6569 2 15.7712 2 12 2C8.22877 2 6.34315 2 5.17157 3.17157C4 4.34314 4 6.22876 4 10L4 14.5442C4 17.7892 4 19.4117 4.88607 20.5107C5.06508 20.7327 5.26731 20.9349 5.48933 21.1139C6.58831 22 8.21082 22 11.4558 22C12.1614 22 12.5141 22 12.8372 21.886C12.9044 21.8623 12.9702 21.835 13.0345 21.8043C13.3436 21.6564 13.593 21.407 14.0919 20.9081L18.8284 16.1716C19.4065 15.5935 19.6955 15.3045 19.8478 14.9369C20 14.5694 20 14.1606 20 13.3431Z&#x22; stroke=&#x22;currentColor&#x22; stroke-linecap=&#x22;round&#x22; stroke-linejoin=&#x22;round&#x22; stroke-width=&#x22;1.5&#x22;/></svg>" color="#ff4d00" size="22" />

    <div className="firecrawl-cta-title" style="{ margin: 0 }">
      <span style="{ color: &#x22;#ff4d00&#x22; }">
        悬赏：5,000 额度奖励
      </span>

      <span style="{ fontWeight: 400 }">
        ，征集对 /interact 的优质反馈
      </span>
    </div>
  </div>

  <p className="firecrawl-cta-description">
    要获得奖励资格，请与我们的 Firecrawl Feedback Assistant 完成一次高质量访谈 (分享经过思考的具体用例等) 。访谈仅需几分钟，随时可以停止，对人类和代理都很友好 (只需将链接粘贴到您的代理框架中！) 。从未使用过 /interact？您的看法同样有价值。
  </p>

  <a href="&#x22;https://www.firecrawl.dev/survey/ogu30?src=&#x22; + (props.src || &#x22;docs-interact&#x22;)" className="firecrawl-cta-btn-primary firecrawl-cta-btn-inline">
    开始访谈
  </a>

  <p className="firecrawl-cta-description" style="{ fontSize: &#x22;12px&#x22;, fontStyle: &#x22;italic&#x22;, margin: &#x22;12px 0 0 0&#x22; }">
    请填写您的 email，才有资格获得奖励。我们会在每周末审核访谈质量。
  </p>
</div>

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

| 需求                       | 使用               | 权威文档                                                                                                                                                                                                                                                                                                                    |
| ------------------------ | ---------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 无需先抓取，直接启动独立的浏览器会话       | 浏览器沙箱 / 独立 交互 会话 | [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) |
| 使用 `scrapeId` 基于抓取结果继续交互 | 抓取后交互            | [Execute Interact](/zh/api-reference/endpoint/scrape-execute), [Stop Interact](/zh/api-reference/endpoint/scrape-browser-delete)                                                                                                                                                                                        |

如果工作流以 `POST /v2/scrape` 开始，且响应中包含 `data.metadata.scrapeId`，请使用绑定到抓取的 交互。当你需要具有独立生命周期的独立会话时，请使用 浏览器沙箱。

<CardGroup cols="3">
  <Card title="AI prompts" icon="<svg xmlns=&#x22;http://www.w3.org/2000/svg&#x22; viewBox=&#x22;0 0 24 24&#x22; fill=&#x22;none&#x22;><path d=&#x22;M8 7L16 7&#x22; stroke=&#x22;currentColor&#x22; stroke-linecap=&#x22;round&#x22; stroke-linejoin=&#x22;round&#x22; stroke-width=&#x22;1.5&#x22;/><path d=&#x22;M8 11L12 11&#x22; stroke=&#x22;currentColor&#x22; stroke-linecap=&#x22;round&#x22; stroke-linejoin=&#x22;round&#x22; stroke-width=&#x22;1.5&#x22;/><path d=&#x22;M13 21.5V21C13 18.1716 13 16.7574 13.8787 15.8787C14.7574 15 16.1716 15 19 15H19.5M20 13.3431V10C20 6.22876 20 4.34315 18.8284 3.17157C17.6569 2 15.7712 2 12 2C8.22877 2 6.34315 2 5.17157 3.17157C4 4.34314 4 6.22876 4 10L4 14.5442C4 17.7892 4 19.4117 4.88607 20.5107C5.06508 20.7327 5.26731 20.9349 5.48933 21.1139C6.58831 22 8.21082 22 11.4558 22C12.1614 22 12.5141 22 12.8372 21.886C12.9044 21.8623 12.9702 21.835 13.0345 21.8043C13.3436 21.6564 13.593 21.407 14.0919 20.9081L18.8284 16.1716C19.4065 15.5935 19.6955 15.3045 19.8478 14.9369C20 14.5694 20 14.1606 20 13.3431Z&#x22; stroke=&#x22;currentColor&#x22; stroke-linecap=&#x22;round&#x22; stroke-linejoin=&#x22;round&#x22; stroke-width=&#x22;1.5&#x22;/></svg>">
    描述你希望在页面中执行的操作
  </Card>

  <Card title="代码执行" icon="<svg xmlns=&#x22;http://www.w3.org/2000/svg&#x22; viewBox=&#x22;0 0 24 24&#x22; fill=&#x22;none&#x22;><path d=&#x22;M16 6.99998L19.0664 9.64296C20.3554 10.7541 21 11.3096 21 12C21 12.6903 20.3555 13.2459 19.0664 14.357L16 17&#x22; stroke=&#x22;currentColor&#x22; stroke-linecap=&#x22;round&#x22; stroke-linejoin=&#x22;round&#x22; stroke-width=&#x22;1.5&#x22;/><path d=&#x22;M8 6.99998L4.93365 9.64296C3.64455 10.7541 3 11.3096 3 12C3 12.6903 3.64455 13.2459 4.93365 14.357L8 17&#x22; stroke=&#x22;currentColor&#x22; stroke-linecap=&#x22;round&#x22; stroke-linejoin=&#x22;round&#x22; stroke-width=&#x22;1.5&#x22;/></svg>">
    通过代码安全地与 playwright、agent-browser 交互
  </Card>

  <Card title="实时视图" icon="<svg xmlns=&#x22;http://www.w3.org/2000/svg&#x22; viewBox=&#x22;0 0 24 24&#x22; fill=&#x22;none&#x22;><path d=&#x22;M2 8C2 8 6.47715 3 12 3C17.5228 3 22 8 22 8&#x22; stroke=&#x22;currentColor&#x22; stroke-linecap=&#x22;round&#x22; stroke-width=&#x22;1.5&#x22;/><path d=&#x22;M21.544 13.045C21.848 13.4713 22 13.6845 22 14C22 14.3155 21.848 14.5287 21.544 14.955C20.1779 16.8706 16.6892 21 12 21C7.31078 21 3.8221 16.8706 2.45604 14.955C2.15201 14.5287 2 14.3155 2 14C2 13.6845 2.15201 13.4713 2.45604 13.045C3.8221 11.1294 7.31078 7 12 7C16.6892 7 20.1779 11.1294 21.544 13.045Z&#x22; stroke=&#x22;currentColor&#x22; stroke-width=&#x22;1.5&#x22;/><path d=&#x22;M15 14C15 12.3431 13.6569 11 12 11C10.3431 11 9 12.3431 9 14C9 15.6569 10.3431 17 12 17C13.6569 17 15 15.6569 15 14Z&#x22; stroke=&#x22;currentColor&#x22; stroke-width=&#x22;1.5&#x22;/></svg>">
    通过可嵌入的流实时观看或与浏览器交互
  </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>
  <CodeBlockTabs defaultValue="Python" groupId="cli+curl+node+python">
    <CodeBlockTabsList>
      <CodeBlockTabsTrigger value="Python">
        Python
      </CodeBlockTabsTrigger>

      <CodeBlockTabsTrigger value="Node">
        Node
      </CodeBlockTabsTrigger>

      <CodeBlockTabsTrigger value="cURL">
        cURL
      </CodeBlockTabsTrigger>

      <CodeBlockTabsTrigger value="CLI">
        CLI
      </CodeBlockTabsTrigger>
    </CodeBlockTabsList>

    <CodeBlockTab value="Python">
      ```python  
      from firecrawl import Firecrawl

      app = Firecrawl(
        # 无需 API 密钥即可开始使用 — 添加一个以获得更高限流：
        # api_key="fc-YOUR-API-KEY",
      )

      # 1. 抓取 Amazon 首页
      result = app.scrape("https://www.amazon.com", formats=["markdown"])
      scrape_id = result.metadata.scrape_id

      # 2. 交互 — 搜索商品并获取价格
      app.interact(scrape_id, prompt="Search for iPhone 16 Pro Max")
      response = app.interact(scrape_id, prompt="Click on the first result and tell me the price")
      print(response.output)

      # 3. 停止会话
      app.stop_interaction(scrape_id)
      ```
    </CodeBlockTab>

    <CodeBlockTab value="Node">
      ```js  
      import { Firecrawl } from 'firecrawl';

      const app = new Firecrawl({
        // 无需 API 密钥即可开始使用 — 添加一个以获得更高的限流上限：
        // apiKey: 'fc-YOUR-API-KEY',
      });

      // 1. Scrape Amazon's homepage
      const result = await app.scrape('https://www.amazon.com', { formats: ['markdown'] });
      const scrapeId = result.metadata?.scrapeId;

      // 2. Interact — search for a product and get its price
      await app.interact(scrapeId, { prompt: 'Search for iPhone 16 Pro Max' });
      const response = await app.interact(scrapeId, { prompt: 'Click on the first result and tell me the price' });
      console.log(response.output);

      // 3. Stop the session
      await app.stopInteraction(scrapeId);
      ```
    </CodeBlockTab>

    <CodeBlockTab value="cURL">
      ```bash  
      # 1. 抓取 Amazon 首页
      # 无需 API 密钥即可开始 — 添加 -H "Authorization: Bearer $FIRECRAWL_API_KEY" 以获得更高限流：
      RESPONSE=$(curl -s -X POST "https://api.firecrawl.dev/v2/scrape" \
        -H "Content-Type: application/json" \
        -d '{"url": "https://www.amazon.com", "formats": ["markdown"]}')

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

      # 2. 交互 — 搜索商品并获取价格
      curl -s -X POST "https://api.firecrawl.dev/v2/scrape/$SCRAPE_ID/interact" \
        -H "Content-Type: application/json" \
        -d '{"prompt": "Search for iPhone 16 Pro Max"}'

      curl -s -X POST "https://api.firecrawl.dev/v2/scrape/$SCRAPE_ID/interact" \
        -H "Content-Type: application/json" \
        -d '{"prompt": "Click on the first result and tell me the price"}'

      # 3. 停止会话
      curl -s -X DELETE "https://api.firecrawl.dev/v2/scrape/$SCRAPE_ID/interact"
      ```
    </CodeBlockTab>

    <CodeBlockTab value="CLI">
      ```bash  
      # 1. 抓取 Amazon 首页（scrape ID 自动保存）
      firecrawl scrape https://www.amazon.com

      # 2. 交互 — 搜索商品并获取价格
      firecrawl interact "Search for iPhone 16 Pro Max"
      firecrawl interact "Click on the first result and tell me the price"

      # 3. 停止会话
      firecrawl interact stop
      ```
    </CodeBlockTab>
  </CodeBlockTabs>
</CodeGroup>

```json title="Response"
{
  "success": true,
  "cdpUrl": "wss://browser.firecrawl.dev/...",
  "liveViewUrl": "https://liveview.firecrawl.dev/...",
  "interactiveLiveViewUrl": "https://liveview.firecrawl.dev/...",
  "output": "The iPhone 16 Pro Max (256GB) is priced at $1,199.00.",
  "exitCode": 0,
  "killed": false
}
```

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

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

<CodeGroup>
  <CodeBlockTabs defaultValue="Python" groupId="cli+curl+node+python">
    <CodeBlockTabsList>
      <CodeBlockTabsTrigger value="Python">
        Python
      </CodeBlockTabsTrigger>

      <CodeBlockTabsTrigger value="Node">
        Node
      </CodeBlockTabsTrigger>

      <CodeBlockTabsTrigger value="cURL">
        cURL
      </CodeBlockTabsTrigger>

      <CodeBlockTabsTrigger value="CLI">
        CLI
      </CodeBlockTabsTrigger>
    </CodeBlockTabsList>

    <CodeBlockTab value="Python">
      ```python  
      response = app.interact(scrape_id, prompt="What are the customer reviews saying about battery life?")
      print(response.output)
      ```
    </CodeBlockTab>

    <CodeBlockTab value="Node">
      ```js  
      const response = await app.interact(scrapeId, {
        prompt: 'What are the customer reviews saying about battery life?',
      });
      console.log(response.output);
      ```
    </CodeBlockTab>

    <CodeBlockTab value="cURL">
      ```bash  
      # 无需 API 密钥即可开始使用 — 添加 -H "Authorization: Bearer $FIRECRAWL_API_KEY" 以获得更高的限流配额：
      curl -s -X POST "https://api.firecrawl.dev/v2/scrape/$SCRAPE_ID/interact" \
        -H "Content-Type: application/json" \
        -d '{
          "prompt": "What are the customer reviews saying about battery life?"
        }'
      ```
    </CodeBlockTab>

    <CodeBlockTab value="CLI">
      ```bash  
      firecrawl interact "What are the customer reviews saying about battery life?"
      ```
    </CodeBlockTab>
  </CodeBlockTabs>
</CodeGroup>

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

```json title="Response"
{
  "success": true,
  "cdpUrl": "wss://browser.firecrawl.dev/...",
  "liveViewUrl": "https://liveview.firecrawl.dev/...",
  "interactiveLiveViewUrl": "https://liveview.firecrawl.dev/...",
  "output": "Customers are generally positive about battery life. Most reviewers report 8-10 hours of use on a single charge. A few noted it drains faster with heavy multitasking.",
  "stdout": "...",
  "result": "...",
  "stderr": "",
  "exitCode": 0,
  "killed": false
}
```

<div id="keep-prompts-small-and-focused">
  ### 保持 prompt 简短且聚焦 [#保持-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) [#nodejs-playwright]
</div>

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

<CodeGroup>
  <CodeBlockTabs defaultValue="Python" groupId="cli+curl+node+python">
    <CodeBlockTabsList>
      <CodeBlockTabsTrigger value="Python">
        Python
      </CodeBlockTabsTrigger>

      <CodeBlockTabsTrigger value="Node">
        Node
      </CodeBlockTabsTrigger>

      <CodeBlockTabsTrigger value="cURL">
        cURL
      </CodeBlockTabsTrigger>

      <CodeBlockTabsTrigger value="CLI">
        CLI
      </CodeBlockTabsTrigger>
    </CodeBlockTabsList>

    <CodeBlockTab value="Python">
      ```python  
      response = app.interact(scrape_id, code="""
      // 点击按钮并等待页面导航
      await page.click('#next-page');
      await page.waitForLoadState('networkidle');

      // 从新页面提取内容
      const title = await page.title();
      const content = await page.$eval('.article-body', el => el.textContent);
      JSON.stringify({ title, content });
      """)
      print(response.result)
      ```
    </CodeBlockTab>

    <CodeBlockTab value="Node">
      ```js  
      const response = await app.interact(scrapeId, {
        code: `
          // 点击按钮并等待页面导航
          await page.click('#next-page');
          await page.waitForLoadState('networkidle');

          // 从新页面提取内容
          const title = await page.title();
          const content = await page.$eval('.article-body', el => el.textContent);
          JSON.stringify({ title, content });
        `,
      });
      console.log(response.result);
      ```
    </CodeBlockTab>

    <CodeBlockTab value="cURL">
      ```bash  
      # 无需 API 密钥即可开始使用 — 添加 -H "Authorization: Bearer $FIRECRAWL_API_KEY" 以获得更高的限流配额：
      curl -s -X POST "https://api.firecrawl.dev/v2/scrape/$SCRAPE_ID/interact" \
        -H "Content-Type: application/json" \
        -d '{
          "code": "await page.click(\"#next-page\"); await page.waitForLoadState(\"networkidle\"); const title = await page.title(); JSON.stringify({ title });",
          "language": "node",
          "timeout": 30
        }'
      ```
    </CodeBlockTab>

    <CodeBlockTab value="CLI">
      ```bash  
      # 自动使用最近一次的 scrape
      firecrawl interact -c "
        await page.click('#next-page');
        await page.waitForLoadState('networkidle');
        const title = await page.title();
        const content = await page.\$eval('.article-body', el => el.textContent);
        JSON.stringify({ title, content });
      "

      # 或显式传入 scrape ID
      # firecrawl interact <scrape-id> -c "await page.title()"
      ```
    </CodeBlockTab>
  </CodeBlockTabs>
</CodeGroup>

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

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

<CodeGroup>
  <CodeBlockTabs defaultValue="Python" groupId="cli+curl+node+python">
    <CodeBlockTabsList>
      <CodeBlockTabsTrigger value="Python">
        Python
      </CodeBlockTabsTrigger>

      <CodeBlockTabsTrigger value="Node">
        Node
      </CodeBlockTabsTrigger>

      <CodeBlockTabsTrigger value="cURL">
        cURL
      </CodeBlockTabsTrigger>

      <CodeBlockTabsTrigger value="CLI">
        CLI
      </CodeBlockTabsTrigger>
    </CodeBlockTabsList>

    <CodeBlockTab value="Python">
      ```python  
      response = app.interact(
          scrape_id,
          code="""
      import json

      await page.click('#load-more')
      await page.wait_for_load_state('networkidle')

      items = await page.query_selector_all('.item')
      data = []
      for item in items:
          text = await item.text_content()
          data.append(text.strip())

      print(json.dumps(data))
      """,
          language="python",
      )
      print(response.stdout)
      ```
    </CodeBlockTab>

    <CodeBlockTab value="Node">
      ```js  
      const response = await app.interact(scrapeId, {
        code: `
      import json

      await page.click('#load-more')
      await page.wait_for_load_state('networkidle')

      items = await page.query_selector_all('.item')
      data = []
      for item in items:
          text = await item.text_content()
          data.append(text.strip())

      print(json.dumps(data))
      `,
        language: 'python',
      });
      console.log(response.stdout);
      ```
    </CodeBlockTab>

    <CodeBlockTab value="cURL">
      ```bash  
      # 无需 API 密钥即可开始使用 — 添加 -H "Authorization: Bearer $FIRECRAWL_API_KEY" 以获得更高的限流配额：
      curl -s -X POST "https://api.firecrawl.dev/v2/scrape/$SCRAPE_ID/interact" \
        -H "Content-Type: application/json" \
        -d '{
          "code": "import json\nawait page.click(\"#load-more\")\nawait page.wait_for_load_state(\"networkidle\")\nitems = await page.query_selector_all(\".item\")\ndata = [await i.text_content() for i in items]\nprint(json.dumps(data))",
          "language": "python"
        }'
      ```
    </CodeBlockTab>

    <CodeBlockTab value="CLI">
      ```bash  
      firecrawl interact --python -c "
      import json
      await page.click('#load-more')
      await page.wait_for_load_state('networkidle')
      items = await page.query_selector_all('.item')
      data = [await i.text_content() for i in items]
      print(json.dumps(data))
      "
      ```
    </CodeBlockTab>
  </CodeBlockTabs>
</CodeGroup>

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

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

<CodeGroup>
  <CodeBlockTabs defaultValue="Python" groupId="cli+curl+node+python">
    <CodeBlockTabsList>
      <CodeBlockTabsTrigger value="Python">
        Python
      </CodeBlockTabsTrigger>

      <CodeBlockTabsTrigger value="Node">
        Node
      </CodeBlockTabsTrigger>

      <CodeBlockTabsTrigger value="cURL">
        cURL
      </CodeBlockTabsTrigger>

      <CodeBlockTabsTrigger value="CLI">
        CLI
      </CodeBlockTabsTrigger>
    </CodeBlockTabsList>

    <CodeBlockTab value="Python">
      ```python  
      # 拍摄快照以查看交互元素
      snapshot = app.interact(
          scrape_id,
          code="agent-browser snapshot -i",
          language="bash",
      )
      print(snapshot.stdout)
      # 输出：
      # [document]
      #   @e1 [input type="text"] "Search..."
      #   @e2 [button] "Search"
      #   @e3 [link] "About"

      # 使用 @refs 与元素交互
      app.interact(
          scrape_id,
          code='agent-browser fill @e1 "firecrawl" && agent-browser click @e2',
          language="bash",
      )
      ```
    </CodeBlockTab>

    <CodeBlockTab value="Node">
      ```js  
      // 拍摄快照以查看可交互元素
      const snapshot = await app.interact(scrapeId, {
        code: 'agent-browser snapshot -i',
        language: 'bash',
      });
      console.log(snapshot.stdout);
      // 输出：
      // [document]
      //   @e1 [input type="text"] "Search..."
      //   @e2 [button] "Search"
      //   @e3 [link] "About"

      // 使用 @refs 与元素进行交互
      await app.interact(scrapeId, {
        code: 'agent-browser fill @e1 "firecrawl" && agent-browser click @e2',
        language: 'bash',
      });
      ```
    </CodeBlockTab>

    <CodeBlockTab value="cURL">
      ```bash  
      # 拍摄快照以查看交互元素
      # 无需 API 密钥即可开始使用 — 添加 -H "Authorization: Bearer $FIRECRAWL_API_KEY" 以获得更高限流：
      curl -s -X POST "https://api.firecrawl.dev/v2/scrape/$SCRAPE_ID/interact" \
        -H "Content-Type: application/json" \
        -d '{"code": "agent-browser snapshot -i", "language": "bash"}'

      # Interact with elements using @refs
      curl -s -X POST "https://api.firecrawl.dev/v2/scrape/$SCRAPE_ID/interact" \
        -H "Content-Type: application/json" \
        -d '{"code": "agent-browser fill @e1 \"firecrawl\" && agent-browser click @e2", "language": "bash"}'
      ```
    </CodeBlockTab>

    <CodeBlockTab value="CLI">
      ```bash  
      # 拍摄快照以查看交互元素
      firecrawl interact --bash -c "agent-browser snapshot -i"

      # 使用 @refs 与元素交互
      firecrawl interact --bash -c 'agent-browser fill @e1 "firecrawl" && agent-browser click @e2'
      ```
    </CodeBlockTab>
  </CodeBlockTabs>
</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 title="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 [#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>
  <CodeBlockTabs defaultValue="Python" groupId="cli+node+python">
    <CodeBlockTabsList>
      <CodeBlockTabsTrigger value="Python">
        Python
      </CodeBlockTabsTrigger>

      <CodeBlockTabsTrigger value="Node">
        Node
      </CodeBlockTabsTrigger>

      <CodeBlockTabsTrigger value="CLI">
        CLI
      </CodeBlockTabsTrigger>
    </CodeBlockTabsList>

    <CodeBlockTab value="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)
      ```
    </CodeBlockTab>

    <CodeBlockTab value="Node">
      ```js  
      // 第一次调用：点击一个标签页
      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);
      ```
    </CodeBlockTab>

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

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

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

完成后请显式停止会话：

<CodeGroup>
  <CodeBlockTabs defaultValue="Python" groupId="cli+curl+node+python">
    <CodeBlockTabsList>
      <CodeBlockTabsTrigger value="Python">
        Python
      </CodeBlockTabsTrigger>

      <CodeBlockTabsTrigger value="Node">
        Node
      </CodeBlockTabsTrigger>

      <CodeBlockTabsTrigger value="cURL">
        cURL
      </CodeBlockTabsTrigger>

      <CodeBlockTabsTrigger value="CLI">
        CLI
      </CodeBlockTabsTrigger>
    </CodeBlockTabsList>

    <CodeBlockTab value="Python">
      ```python  
      app.stop_interaction(scrape_id)
      ```
    </CodeBlockTab>

    <CodeBlockTab value="Node">
      ```js  
      await app.stopInteraction(scrapeId);
      ```
    </CodeBlockTab>

    <CodeBlockTab value="cURL">
      ```bash  
      # 无需 API 密钥即可开始使用 — 添加 -H "Authorization: Bearer $FIRECRAWL_API_KEY" 以获得更高的限流配额：
      curl -s -X DELETE "https://api.firecrawl.dev/v2/scrape/$SCRAPE_ID/interact"
      ```
    </CodeBlockTab>

    <CodeBlockTab value="CLI">
      ```bash  
      # 停止最后一个 scrape 会话
      firecrawl interact stop

      # 或通过 ID 停止特定会话
      # firecrawl interact stop <scrape-id>
      ```
    </CodeBlockTab>
  </CodeBlockTabs>
</CodeGroup>

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

<Warning>
  请务必在使用完毕后停止会话，以避免不必要的计费。额度按秒折算。
</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 title="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>
  <CodeBlockTabs defaultValue="Python" groupId="cli+curl+node+python">
    <CodeBlockTabsList>
      <CodeBlockTabsTrigger value="Python">
        Python
      </CodeBlockTabsTrigger>

      <CodeBlockTabsTrigger value="Node">
        Node
      </CodeBlockTabsTrigger>

      <CodeBlockTabsTrigger value="cURL">
        cURL
      </CodeBlockTabsTrigger>

      <CodeBlockTabsTrigger value="CLI">
        CLI
      </CodeBlockTabsTrigger>
    </CodeBlockTabsList>

    <CodeBlockTab value="Python">
      ```python  
      from firecrawl import Firecrawl

      app = Firecrawl(
        # 无需 API 密钥即可开始使用——添加一个以获得更高的限流配额：
        # api_key="fc-YOUR-API-KEY",
      )

      # Session 1: Scrape with a profile, log in, then stop (state is saved)
      result = app.scrape(
          "https://app.example.com/login",
          formats=["markdown"],
          profile={"name": "my-app", "save_changes": True},
      )
      scrape_id = result.metadata.scrape_id

      app.interact(scrape_id, prompt="Fill in user@example.com and password, then click Login")
      app.stop_interaction(scrape_id)

      # Session 2: Scrape with the same profile in read-only mode - already logged in
      result = app.scrape(
          "https://app.example.com/dashboard",
          formats=["markdown"],
          profile={"name": "my-app", "save_changes": False},
      )
      scrape_id = result.metadata.scrape_id

      response = app.interact(scrape_id, prompt="Extract the dashboard data")
      print(response.output)
      app.stop_interaction(scrape_id)
      ```
    </CodeBlockTab>

    <CodeBlockTab value="Node">
      ```js  
      import { Firecrawl } from 'firecrawl';

      const app = new Firecrawl({
        // 无需 API 密钥即可开始使用——添加一个以获得更高的限流配额：
        // apiKey: 'fc-YOUR-API-KEY',
      });

      // Session 1: Scrape with a profile, log in, then stop (state is saved)
      const result1 = await app.scrape('https://app.example.com/login', {
        formats: ['markdown'],
        profile: { name: 'my-app', saveChanges: true },
      });
      const scrapeId1 = result1.metadata?.scrapeId;

      await app.interact(scrapeId1, { prompt: 'Fill in user@example.com and password, then click Login' });
      await app.stopInteraction(scrapeId1);

      // 会话 2：使用相同的配置文件以只读模式进行抓取——已登录
      const result2 = await app.scrape('https://app.example.com/dashboard', {
        formats: ['markdown'],
        profile: { name: 'my-app', saveChanges: false },
      });
      const scrapeId2 = result2.metadata?.scrapeId;

      const response = await app.interact(scrapeId2, { prompt: 'Extract the dashboard data' });
      console.log(response.output);
      await app.stopInteraction(scrapeId2);
      ```
    </CodeBlockTab>

    <CodeBlockTab value="cURL">
      ```bash  
      # Session 1: Scrape with a profile
      # 无需 API 密钥即可开始 — 添加 -H "Authorization: Bearer $FIRECRAWL_API_KEY" 以获得更高限流：
      RESPONSE=$(curl -s -X POST "https://api.firecrawl.dev/v2/scrape" \
        -H "Content-Type: application/json" \
        -d '{
          "url": "https://app.example.com/login",
          "formats": ["markdown"],
          "profile": { "name": "my-app", "saveChanges": true }
        }')

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

      # Log in via interact
      curl -s -X POST "https://api.firecrawl.dev/v2/scrape/$SCRAPE_ID/interact" \
        -H "Content-Type: application/json" \
        -d '{"prompt": "Fill in user@example.com and password, then click Login"}'

      # 停止 — 状态已保存到配置文件
      curl -s -X DELETE "https://api.firecrawl.dev/v2/scrape/$SCRAPE_ID/interact"

      # 会话 2：以只读模式使用相同配置文件再次抓取 — 已登录
      RESPONSE=$(curl -s -X POST "https://api.firecrawl.dev/v2/scrape" \
        -H "Content-Type: application/json" \
        -d '{
          "url": "https://app.example.com/dashboard",
          "formats": ["markdown"],
          "profile": { "name": "my-app", "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 "Content-Type: application/json" \
        -d '{"prompt": "Extract the dashboard data"}'
      ```
    </CodeBlockTab>

    <CodeBlockTab value="CLI">
      ```bash  
      # 会话 1：使用配置文件 scrape，登录，然后停止（状态已保存）
      firecrawl scrape https://app.example.com/login --profile my-app
      firecrawl interact "Fill in user@example.com and password, then click Login"
      firecrawl interact stop

      # 会话 2：使用相同配置文件 scrape —— 已登录
      firecrawl scrape https://app.example.com/dashboard --profile my-app
      firecrawl interact "Extract the dashboard data"
      firecrawl interact stop

      # 只读：加载配置文件状态，不将 changes 写回保存
      firecrawl scrape https://app.example.com/dashboard --profile my-app --no-save-changes
      ```
    </CodeBlockTab>
  </CodeBlockTabs>
</CodeGroup>

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

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

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

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

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

```bash title="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 > Interact > 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 参考 [#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) [#请求体-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) 联系我们。
