OA0
OA0 是一个探索 AI 的社区
现在注册
已注册用户请  登录
OA0  ›  技能包  ›  browse:创建与部署浏览器自动化功能的完整指南

browse:创建与部署浏览器自动化功能的完整指南

 
  blueprint ·  2026-02-01 23:01:18 · 22 次点击  · 0 条评论  

名称: browse
描述: 使用 stagehand CLI 创建和部署浏览器自动化功能的完整指南
主页: https://browserbase.com
元数据: {"moltbot":{"emoji":"🌐","requires":{"bins":["stagehand"],"env":["BROWSERBASE_API_KEY","BROWSERBASE_PROJECT_ID"]},"primaryEnv":"BROWSERBASE_API_KEY"}}


浏览器自动化与功能技能

使用 stagehand CLI 创建和部署浏览器自动化功能的完整指南。

使用场景

  • 用户希望自动化网站任务
  • 用户需要从网站抓取数据
  • 用户想要创建 Browserbase 函数
  • 用户希望将自动化部署为定时任务或通过 Webhook 触发

前提条件

设置凭据

stagehand fn auth status  # 检查是否已配置
stagehand fn auth login   # 如果需要 - 从 https://browserbase.com/settings 获取凭据

完整工作流

步骤 1:交互式探索网站

启动本地浏览器会话以了解网站结构:

stagehand session create --local
stagehand goto https://example.com
stagehand snapshot                    # 获取带引用的 DOM 结构
stagehand screenshot -o page.png      # 可视化检查

手动测试交互:

stagehand click @0-5
stagehand fill @0-6 "value"
stagehand eval "document.querySelector('.price').textContent"
stagehand session end  # 探索完成后结束会话

步骤 2:初始化函数项目

stagehand fn init my-automation
cd my-automation

创建以下文件:
- package.json - 依赖项
- .env - 凭据(来自 ~/.stagehand/config.json
- index.ts - 函数模板
- tsconfig.json - TypeScript 配置

步骤 3:⚠️ 立即修复 package.json

严重错误stagehand fn init 生成的 package.json 不完整,会导致部署失败并显示“未构建任何函数”。

必需修复 - 在进行任何其他操作之前更新 package.json

{
  "name": "my-automation",
  "version": "1.0.0",
  "description": "我的自动化描述",
  "main": "index.js",
  "type": "module",
  "packageManager": "pnpm@10.14.0",
  "scripts": {
    "dev": "pnpm bb dev index.ts",
    "publish": "pnpm bb publish index.ts"
  },
  "dependencies": {
    "@browserbasehq/sdk-functions": "^0.0.5",
    "playwright-core": "^1.58.0"
  },
  "devDependencies": {
    "@types/node": "^25.0.10",
    "typescript": "^5.9.3"
  }
}

与生成文件的关键更改:
- ✅ 添加 descriptionmain 字段
- ✅ 添加 packageManager 字段
- ✅ 将 "latest" 更改为固定版本,如 "^0.0.5"
- ✅ 添加包含 TypeScript 和类型定义的 devDependencies

然后安装:

pnpm install

步骤 4:编写自动化代码

编辑 index.ts

import { defineFn } from "@browserbasehq/sdk-functions";
import { chromium } from "playwright-core";

defineFn("my-automation", async (context) => {
  const { session, params } = context;
  console.log("连接到浏览器会话:", session.id);

  const browser = await chromium.connectOverCDP(session.connectUrl);
  const page = browser.contexts()[0]!.pages()[0]!;

  // 在此处编写自动化逻辑
  await page.goto("https://example.com");
  await page.waitForLoadState("domcontentloaded");

  // 提取数据
  const data = await page.evaluate(() => {
    // 复杂的数据提取逻辑
    return Array.from(document.querySelectorAll('.item')).map(el => ({
      title: el.querySelector('.title')?.textContent,
      value: el.querySelector('.value')?.textContent,
    }));
  });

  // 返回结果(必须是 JSON 可序列化的)
  return {
    success: true,
    count: data.length,
    data,
    timestamp: new Date().toISOString(),
  };
});

关键概念:
- context.session - 浏览器会话信息(id, connectUrl)
- context.params - 调用时传入的输入参数
- 返回 JSON 可序列化的数据
- 最长执行时间为 15 分钟

步骤 5:本地测试

启动开发服务器:

pnpm bb dev index.ts

服务器运行在 http://127.0.0.1:14113

使用 curl 调用:

curl -X POST http://127.0.0.1:14113/v1/functions/my-automation/invoke \
  -H "Content-Type: application/json" \
  -d '{"params": {"url": "https://example.com"}}'

开发服务器会在文件更改时自动重新加载。检查终端以查看日志。

步骤 6:部署到 Browserbase

pnpm bb publish index.ts
# 或:stagehand fn publish index.ts

预期输出:

✓ 构建成功完成
构建 ID:xxx-xxx-xxx
函数 ID:yyy-yyy-yyy  ← 保存此 ID!

如果看到“未构建任何函数” → 你的 package.json 不完整(参见步骤 3)。

步骤 7:测试生产环境

stagehand fn invoke <function-id> -p '{"param": "value"}'

或通过 API:

curl -X POST https://api.browserbase.com/v1/functions/<function-id>/invoke \
  -H "Content-Type: application/json" \
  -H "x-bb-api-key: $BROWSERBASE_API_KEY" \
  -d '{"params": {}}'

完整工作示例:Hacker News 抓取器

import { defineFn } from "@browserbasehq/sdk-functions";
import { chromium } from "playwright-core";

defineFn("hn-scraper", async (context) => {
  const { session } = context;
  console.log("连接到浏览器会话:", session.id);

  const browser = await chromium.connectOverCDP(session.connectUrl);
  const page = browser.contexts()[0]!.pages()[0]!;

  await page.goto("https://news.ycombinator.com");
  await page.waitForLoadState("domcontentloaded");

  // 提取前 10 条故事
  const stories = await page.evaluate(() => {
    const storyRows = Array.from(document.querySelectorAll('.athing')).slice(0, 10);

    return storyRows.map((row) => {
      const titleLine = row.querySelector('.titleline a');
      const subtext = row.nextElementSibling?.querySelector('.subtext');
      const commentsLink = Array.from(subtext?.querySelectorAll('a') || []).pop();

      return {
        rank: row.querySelector('.rank')?.textContent?.replace('.', '') || '',
        title: titleLine?.textContent || '',
        url: titleLine?.getAttribute('href') || '',
        points: subtext?.querySelector('.score')?.textContent?.replace(' points', '') || '0',
        author: subtext?.querySelector('.hnuser')?.textContent || '',
        time: subtext?.querySelector('.age')?.textContent || '',
        comments: commentsLink?.textContent?.replace(/\u00a0comments?/, '').trim() || '0',
        id: row.id,
      };
    });
  });

  return {
    success: true,
    count: stories.length,
    stories,
    timestamp: new Date().toISOString(),
  };
});

常用模式

参数化抓取

defineFn("scrape", async (context) => {
  const { session, params } = context;
  const { url, selector } = params;  // 从调用中接收参数

  const browser = await chromium.connectOverCDP(session.connectUrl);
  const page = browser.contexts()[0]!.pages()[0]!;

  await page.goto(url);
  const data = await page.$$eval(selector, els =>
    els.map(el => el.textContent)
  );

  return { url, data };
});

身份验证

defineFn("auth-action", async (context) => {
  const { session, params } = context;
  const { username, password } = params;

  const browser = await chromium.connectOverCDP(session.connectUrl);
  const page = browser.contexts()[0]!.pages()[0]!;

  await page.goto("https://example.com/login");
  await page.fill('input[name="email"]', username);
  await page.fill('input[name="password"]', password);
  await page.click('button[type="submit"]');
  await page.waitForURL("**/dashboard");

  const data = await page.textContent('.user-data');
  return { success: true, data };
});

多页面工作流

defineFn("multi-page", async (context) => {
  const { session, params } = context;
  const browser = await chromium.connectOverCDP(session.connectUrl);
  const page = browser.contexts()[0]!.pages()[0]!;

  const results = [];
  for (const url of params.urls) {
    await page.goto(url);
    await page.waitForLoadState("domcontentloaded");

    const title = await page.title();
    results.push({ url, title });
  }

  return { results };
});

故障排除

🔴 “未构建任何函数。请检查你的入口点和函数导出。”

这是最常见的错误!

原因: stagehand fn init 生成的 package.json 不完整。

修复方法:
1. 更新 package.json(参见上面的步骤 3)
2. 添加所有必需字段:descriptionmainpackageManager
3. 将 "latest" 更改为固定版本,如 "^0.0.5"
4. 添加包含 TypeScript 和类型定义的 devDependencies 部分
5. 运行 pnpm install
6. 再次尝试部署

快速检查: 将你的 package.json 与代码库中的 bitcoin-functions/package.json 进行比较。

本地开发服务器无法启动

# 检查凭据
stagehand fn auth status

# 如果需要,重新登录
stagehand fn auth login

# 全局安装 SDK
pnpm add -g @browserbasehq/sdk-functions

函数在本地工作但部署失败

常见原因:
1. 缺少 devDependencies(TypeScript 无法编译)
2. 使用 "latest" 而不是固定版本
3. package.json 中缺少必需字段

解决方案: 按照步骤 3 的描述修复 package.json。

无法从页面提取数据

  1. 截图:stagehand screenshot -o debug.png
  2. 获取快照:stagehand snapshot
  3. 使用 page.evaluate() 记录 DOM 中的内容
  4. 检查选择器是否与实际 HTML 结构匹配

“调用超时”

  • 函数最长执行时间为 15 分钟
  • 使用特定的等待而不是长时间的睡眠
  • 检查页面是否实际在加载

最佳实践

  1. 立即修复 package.json - 在 stagehand fn init 之后
  2. 首先交互式探索 - 使用本地浏览器会话了解网站
  3. 手动测试 - 在编写代码前验证每个步骤是否有效
  4. 本地测试 - 在部署前使用开发服务器
  5. 返回有意义的数据 - 包含时间戳、计数、URL
  6. 优雅地处理错误 - 在风险操作周围使用 try/catch
  7. 使用特定的选择器 - 优先使用数据属性而非 CSS 类
  8. 添加日志记录 - console.log() 有助于调试已部署的函数
  9. 验证参数 - 在使用前检查 params
  10. 设置合理的超时 - 不要无限等待

快速检查清单

  • [ ] 使用 stagehand session create --local 探索网站
  • [ ] 手动测试交互
  • [ ] 创建项目:stagehand fn init <name>
  • [ ] 立即修复 package.json(步骤 3)
  • [ ] 运行 pnpm install
  • [ ] 在 index.ts 中编写自动化代码
  • [ ] 本地测试:pnpm bb dev index.ts
  • [ ] 使用 curl 验证
  • [ ] 部署:pnpm bb publish index.ts
  • [ ] 测试生产环境:stagehand fn invoke <function-id>
  • [ ] 保存函数 ID

需要修复的代码(针对维护者)

文件: /src/commands/functions.ts
行号: 146-158
函数: initFunction()

将当前的 packageJson 对象替换为:

const packageJson = {
  name,
  version: '1.0.0',
  description: `${name} function`,
  main: 'index.js',
  type: 'module',
  packageManager: 'pnpm@10.14.0',
  scripts: {
    dev: 'pnpm bb dev index.ts',
    publish: 'pnpm bb publish index.ts',
  },
  dependencies: {
    '@browserbasehq/sdk-functions': '^0.0.5',
    'playwright-core': '^1.58.0',
  },
  devDependencies: {
    '@types/node': '^25.0.10',
    'typescript': '^5.9.3',
  },
};

这将为所有新项目消除“未构建任何函数”的错误。

22 次点击  ∙  0 人收藏  
登录后收藏  
0 条回复
关于 ·  帮助 ·  PING ·  隐私 ·  条款   
OA0 - Omni AI 0 一个探索 AI 的社区
沪ICP备2024103595号-2
耗时 18 ms
Developed with Cursor