Skip to content

18.4 Browser Use Demo:浏览器自动化

项目定位:基于 Playwright 的浏览器自动化参考实现,展示 Claude 的 Browser Tool API,提供比 Computer Use 更精准的 Web 交互能力。


1. 项目概述

1.1 Browser Use vs Computer Use

特性Browser UseComputer Use
定位方式DOM ref 引用屏幕坐标
可靠性高(结构化)中(视觉依赖)
速度较慢
适用范围仅浏览器全桌面
表单处理直接设置值模拟键盘
跨分辨率稳定需要缩放

1.2 核心优势

Browser Use 的关键创新是使用 DOM ref 替代坐标点击:

传统坐标方式:
Claude: "点击坐标 (512, 384)"
问题:屏幕尺寸变化时失效

DOM ref 方式:
Claude: "点击 ref='btn-submit'"
优势:无论屏幕尺寸如何,都能准确定位

1.3 技术栈

浏览器引擎:    Playwright + Chromium
Web 界面:      Streamlit
显示系统:      XVFB + NoVNC
容器化:        Docker + Docker Compose
AI 引擎:       Claude API (4.5 系列)

2. 快速开始

2.1 环境准备

bash
# 克隆仓库
git clone https://github.com/anthropics/claude-quickstarts.git
cd claude-quickstarts/browser-use-demo

# 配置环境变量
cp .env.example .env
# 编辑 .env,添加 ANTHROPIC_API_KEY

2.2 运行容器

bash
# 生产模式
docker-compose up --build

# 开发模式(文件变化自动同步)
docker-compose up --build --watch

2.3 访问界面

端口用途URL
8080Streamlit 主界面http://localhost:8080
6080NoVNC 浏览器视图http://localhost:6080
5900VNC 直连vnc://localhost:5900

3. 架构详解

3.1 系统架构

┌─────────────────────────────────────────────────────────────────┐
│                     Docker Container                            │
│                                                                  │
│  ┌───────────────────────────────────────────────────────────┐  │
│  │                  Streamlit Interface                       │  │
│  │                     (Port 8080)                            │  │
│  └───────────────────────────┬───────────────────────────────┘  │
│                              │                                   │
│                              ▼                                   │
│  ┌───────────────────────────────────────────────────────────┐  │
│  │              Claude API + Browser Tool                     │  │
│  │                                                            │  │
│  │  ┌─────────────┐         ┌─────────────┐                  │  │
│  │  │   loop.py   │ ◀─────▶ │ browser.py  │                  │  │
│  │  │ (Agent循环) │         │ (浏览器工具) │                  │  │
│  │  └─────────────┘         └──────┬──────┘                  │  │
│  └─────────────────────────────────┼─────────────────────────┘  │
│                                    │                             │
│                                    ▼                             │
│  ┌───────────────────────────────────────────────────────────┐  │
│  │              Playwright + Chromium                         │  │
│  │                                                            │  │
│  │  ┌─────────────────────────────────────────────────────┐  │  │
│  │  │                  Browser Instance                    │  │  │
│  │  │  • DOM 检查 (read_page)                             │  │  │
│  │  │  • 元素交互 (click, type)                           │  │  │
│  │  │  • 表单操作 (form_input)                            │  │  │
│  │  │  • 页面导航 (navigate)                              │  │  │
│  │  └─────────────────────────────────────────────────────┘  │  │
│  └───────────────────────────────────────────────────────────┘  │
│                              │                                   │
│                              ▼                                   │
│  ┌───────────────────────────────────────────────────────────┐  │
│  │              XVFB Virtual Display                          │  │
│  └───────────────────────────┬───────────────────────────────┘  │
│                              │                                   │
│                              ▼                                   │
│  ┌───────────────────────────────────────────────────────────┐  │
│  │              VNC / NoVNC Server                            │  │
│  │              (Ports 5900 / 6080)                           │  │
│  └───────────────────────────────────────────────────────────┘  │
└─────────────────────────────────────────────────────────────────┘

3.2 项目结构

browser-use-demo/
├── browser_use_demo/
│   ├── __init__.py
│   ├── streamlit.py          # Streamlit UI
│   ├── loop.py               # Agent 循环
│   └── tools/
│       ├── browser.py        # 浏览器工具(核心)
│       ├── browser_tool_utils/
│       │   ├── dom_extract.js    # DOM 提取
│       │   ├── element_find.js   # 元素定位
│       │   └── form_utils.js     # 表单操作
│       └── coordinate_scaling.py # 坐标缩放
├── docker-compose.yml
├── Dockerfile
├── .env.example
└── README.md

4. Browser Tool 详解

4.1 支持的动作

Web 专属动作(Browser Use 独有)

动作参数说明
navigateurl 或 "back"/"forward"导航到 URL 或历史操作
read_page获取 DOM 树和元素 ref
get_page_text提取页面所有文本
findtext搜索并高亮页面文本
form_inputref, value直接设置表单值
scroll_toref滚动到指定元素
execute_jscode执行 JavaScript

鼠标动作(支持 ref 或坐标)

动作参数说明
left_clickref 或 coordinate左键点击
right_clickref 或 coordinate右键点击
double_clickref 或 coordinate双击
hoverref 或 coordinate悬停
left_click_dragstart, end拖拽

键盘动作

动作参数说明
typetext输入文本
keykey_name按键(如 "Enter", "ctrl+a")
hold_keykey, duration长按按键

其他动作

动作参数说明
screenshot截取屏幕
scrolldirection, amount滚动页面
zoomx1, y1, x2, y2放大区域
waitduration等待

4.2 DOM ref 机制

javascript
// browser_tool_utils/dom_extract.js(简化版)

function extractDOM() {
  const elements = [];
  let refCounter = 0;

  function traverse(node) {
    if (node.nodeType === Node.ELEMENT_NODE) {
      const ref = `el-${refCounter++}`;
      node.setAttribute('data-claude-ref', ref);

      elements.push({
        ref: ref,
        tag: node.tagName.toLowerCase(),
        text: node.innerText?.slice(0, 100),
        attributes: {
          id: node.id,
          class: node.className,
          type: node.type,
          placeholder: node.placeholder,
        },
        rect: node.getBoundingClientRect(),
        isInteractive: isInteractive(node),
      });
    }

    for (const child of node.childNodes) {
      traverse(child);
    }
  }

  traverse(document.body);
  return elements;
}

function isInteractive(node) {
  const interactiveTags = ['A', 'BUTTON', 'INPUT', 'SELECT', 'TEXTAREA'];
  return (
    interactiveTags.includes(node.tagName) ||
    node.hasAttribute('onclick') ||
    node.hasAttribute('role')
  );
}

4.3 坐标缩放系统

浏览器视口与 Claude 处理的图像尺寸不同,需要自动缩放:

python
# browser_use_demo/tools/coordinate_scaling.py

class CoordinateScaler:
    # 浏览器视口:1920x1080 (16:9)
    BROWSER_WIDTH = 1920
    BROWSER_HEIGHT = 1080

    # Claude 处理分辨率:1456x819 (16:9)
    CLAUDE_WIDTH = 1456
    CLAUDE_HEIGHT = 819

    def __init__(self):
        self.scale_x = self.BROWSER_WIDTH / self.CLAUDE_WIDTH
        self.scale_y = self.BROWSER_HEIGHT / self.CLAUDE_HEIGHT

    def to_browser(self, x: int, y: int) -> tuple[int, int]:
        """将 Claude 坐标转换为浏览器坐标"""
        return (
            int(x * self.scale_x),
            int(y * self.scale_y)
        )

    def to_claude(self, x: int, y: int) -> tuple[int, int]:
        """将浏览器坐标转换为 Claude 坐标"""
        return (
            int(x / self.scale_x),
            int(y / self.scale_y)
        )

5. 核心实现

5.1 Browser Tool 类

python
# browser_use_demo/tools/browser.py(简化版)

from playwright.async_api import async_playwright

class BrowserTool:
    def __init__(self):
        self.browser = None
        self.page = None
        self.scaler = CoordinateScaler()

    async def initialize(self):
        self.playwright = await async_playwright().start()
        self.browser = await self.playwright.chromium.launch(
            headless=False,  # 需要可视化
            args=['--no-sandbox']
        )
        self.page = await self.browser.new_page()
        await self.page.set_viewport_size({
            'width': 1920,
            'height': 1080
        })

    async def execute(self, action: str, **params):
        match action:
            case 'navigate':
                return await self._navigate(params['text'])

            case 'read_page':
                return await self._read_page()

            case 'left_click':
                return await self._click(params.get('ref'), params.get('coordinate'))

            case 'form_input':
                return await self._form_input(params['ref'], params['value'])

            case 'screenshot':
                return await self._screenshot()

            case _:
                raise ValueError(f"Unknown action: {action}")

    async def _navigate(self, target: str):
        if target == 'back':
            await self.page.go_back()
        elif target == 'forward':
            await self.page.go_forward()
        else:
            await self.page.goto(target)
        return {"success": True, "url": self.page.url}

    async def _read_page(self):
        # 注入 DOM 提取脚本
        elements = await self.page.evaluate(DOM_EXTRACT_SCRIPT)
        return {
            "url": self.page.url,
            "title": await self.page.title(),
            "elements": elements
        }

    async def _click(self, ref: str = None, coordinate: tuple = None):
        if ref:
            # 使用 ref 定位
            element = await self.page.query_selector(f'[data-claude-ref="{ref}"]')
            if element:
                await element.click()
                return {"success": True, "method": "ref"}
        elif coordinate:
            # 使用坐标(已缩放)
            x, y = self.scaler.to_browser(*coordinate)
            await self.page.mouse.click(x, y)
            return {"success": True, "method": "coordinate"}

        return {"success": False, "error": "No target specified"}

    async def _form_input(self, ref: str, value: str):
        element = await self.page.query_selector(f'[data-claude-ref="{ref}"]')
        if element:
            await element.fill(value)
            return {"success": True}
        return {"success": False, "error": f"Element {ref} not found"}

    async def _screenshot(self):
        import base64
        screenshot = await self.page.screenshot()
        return {
            "type": "image",
            "media_type": "image/png",
            "data": base64.b64encode(screenshot).decode()
        }

5.2 工具参数定义

python
BROWSER_TOOL_INPUT_SCHEMA = {
    "type": "object",
    "properties": {
        "action": {
            "type": "string",
            "enum": [
                "navigate", "read_page", "get_page_text", "find",
                "form_input", "scroll_to", "execute_js",
                "left_click", "right_click", "double_click", "hover",
                "type", "key", "hold_key",
                "screenshot", "scroll", "zoom", "wait"
            ],
            "description": "要执行的浏览器动作"
        },
        "ref": {
            "type": "string",
            "description": "目标元素的 ref 标识符"
        },
        "coordinate": {
            "type": "array",
            "items": {"type": "number"},
            "description": "[x, y] 坐标"
        },
        "text": {
            "type": "string",
            "description": "文本内容(用于导航、搜索、输入等)"
        },
        "value": {
            "type": "string",
            "description": "表单值"
        }
    },
    "required": ["action"]
}

6. 使用示例

6.1 基础导航

用户:访问 news.ycombinator.com 并告诉我前3条新闻

Claude 执行流程:
1. navigate("https://news.ycombinator.com")
2. read_page() → 获取 DOM 结构
3. 分析页面内容
4. 返回前3条新闻标题和链接

6.2 搜索任务

用户:在 GitHub 上搜索 "playwright python"

Claude 执行流程:
1. navigate("https://github.com")
2. read_page() → 找到搜索框 ref
3. form_input(ref="search-input", value="playwright python")
4. key("Enter")
5. wait(2000)
6. read_page() → 获取搜索结果
7. 总结搜索结果

6.3 表单填写

用户:在演示表单中填写姓名和邮箱

Claude 执行流程:
1. navigate("https://example.com/form")
2. read_page() → 识别表单字段
3. form_input(ref="name-field", value="John Doe")
4. form_input(ref="email-field", value="john@example.com")
5. left_click(ref="submit-button")
6. 确认提交成功

7. 与 Computer Use 的对比

7.1 能力矩阵

能力Browser UseComputer Use
DOM 访问✅ 结构化❌ 仅视觉
元素定位✅ ref 引用❌ 坐标
表单填写✅ 直接设置❌ 模拟键盘
页面文本提取✅ 精确❌ OCR
JavaScript 执行
桌面应用
多应用切换

7.2 选择指南

使用 Browser Use 当:
✓ 任务仅涉及浏览器
✓ 需要可靠的表单处理
✓ 需要提取结构化数据
✓ 对速度有要求

使用 Computer Use 当:
✓ 需要操作桌面应用
✓ 需要跨应用工作流
✓ 浏览器自动化被检测/阻止
✓ 任务涉及非 Web 界面

8. 安全最佳实践

8.1 风险与缓解

风险缓解措施
凭证泄露不在浏览器中输入真实密码
恶意重定向限制允许访问的域名
CAPTCHA避免触发反自动化机制
提示注入警惕网页内容覆盖指令

8.2 域名白名单示例

python
ALLOWED_DOMAINS = [
    "github.com",
    "news.ycombinator.com",
    "wikipedia.org",
    # 添加你信任的域名
]

async def navigate(self, url: str):
    from urllib.parse import urlparse
    domain = urlparse(url).netloc

    if domain not in ALLOWED_DOMAINS:
        raise SecurityError(f"Domain {domain} not in allowlist")

    await self.page.goto(url)

9. 故障排除

问题可能原因解决方案
浏览器不可见NoVNC 连接问题刷新 6080 端口页面
操作失败反自动化检测尝试其他网站
CAPTCHA 触发频繁访问添加随机延迟
元素找不到页面未加载完增加 wait 时间
API 错误模型不支持使用 4.5 系列模型

10. 总结

Browser Use Demo 提供了一种更可靠的浏览器自动化方案:

方面评价
可靠性⭐⭐⭐⭐⭐ DOM ref 机制稳定可靠
易用性⭐⭐⭐⭐ 清晰的 API 设计
性能⭐⭐⭐⭐ 比 Computer Use 更快
安全性⭐⭐⭐ 需要遵循最佳实践

适用场景

  • Web 数据抓取和提取
  • 自动化测试探索
  • 表单自动填写
  • 网站监控

学习要点

  • DOM ref 元素定位机制
  • Playwright 浏览器自动化
  • 坐标缩放与映射
  • Web 安全最佳实践

下一节,我们将学习 Autonomous Coding,了解如何构建能够持续编码的自主 Agent。

基于 MIT 许可证发布。内容版权归作者所有。