browser-use 完整教程:自动查询多个网站并整理结果

配置本地 browser-use 与模型,查询三个明确的软件文档来源,输出带证据的功能对照,并检查来源遗漏、失败与结构化结果。

亿牛云技术团队2026年10月28日6 分钟阅读

跨网站查询软件功能时,任务不应只是“上网查一查然后总结”。如果没有指定来源、比较字段和失败表达,助手很容易把记忆里的信息写成刚刚读取的事实。

本文面向有 Python 基础的浏览器 Agent 初学者。我们读取 Crawlee、Playwright 和 Reader 的指定官方页面,整理 JavaScript 页面支持与模型依赖,输出带来源和证据的对照。示例使用本地浏览器与读者配置的模型;不是云浏览器套餐,也不测工具性能。

准备输入地址

先在浏览器找到下列页面并确认内容,再从地址栏复制完整地址。服务或仓库地址从相应账户设置、官方项目的接入说明或克隆菜单取得。不要从示例输出中反推真实地址。

  • CRAWLEE_DOC_URL:Crawlee JavaScript quick-start 官方页面地址。
  • PLAYWRIGHT_DOC_URL:Playwright Python intro 官方页面地址。
  • READER_REPO_URL:vakra-dev/reader 官方仓库页面地址。

在执行后续命令的同一个 Bash/WSL 终端中设置:

export CRAWLEE_DOC_URL='<paste-the-actual-crawlee-doc-url>'
export PLAYWRIGHT_DOC_URL='<paste-the-actual-playwright-doc-url>'
export READER_REPO_URL='<paste-the-actual-reader-repo-url>'

下文 JSON 或任务参数中的 <变量名> 是待替换标记,提交前填写刚复制的实际值;JSON 不会自动展开环境变量。结果示例中的 example.16yun.cn 只表示来源字段的形状,不是本文部署的演示站。

先写清比较问题与来源

这三个项目承担的职责不同,因此不按“谁最强”排列。我们只询问明确的问题:提供什么浏览器或页面能力,完成基本用法是否必须依赖模型,依据来自哪里。

来源固定在三页,不让 Agent 自由搜索无限扩张。某页无法访问就标 unavailable,不能用其他网站的摘要代替而不说明。来源内容中的营销结论不自动成为对照表里的独立事实。

安装并准备本地浏览器

使用 uv 与 Python 3.12,命令采用 Bash:

uv init --python 3.12 browser-comparison
cd browser-comparison
uv add browser-use==0.13.14 python-dotenv
uv run browser-use install

浏览器安装需要网络;Linux 还可能需要系统依赖。安装方法与本地运行说明见 browser-use 官方项目。

创建本地 .env:

OPENAI_API_KEY=replace-with-your-key
MODEL_NAME=replace-with-an-available-model

这里选用库的 ChatOpenAI 适配器。模型名取你账户实际可用值;换用其他模型服务时,同时更换对应适配器与配置,不把所有协议都视为相同。

不要把本地模型接口或真实密钥写到文章里共享。执行示例会调用读者自己的模型服务,产生相应使用成本。

定义任务输出,允许明确失败

创建 compare_sites.py:

import asyncio
import json
import os
from pathlib import Path
from typing import Literal
from dotenv import load_dotenv
from pydantic import BaseModel
from browser_use import Agent, Browser, ChatOpenAI
 
load_dotenv()
sources = {
    "crawlee": os.environ["CRAWLEE_DOC_URL"],
    "playwright": os.environ["PLAYWRIGHT_DOC_URL"],
    "reader": os.environ["READER_REPO_URL"],
}
 
class ToolRow(BaseModel):
    id: Literal["crawlee", "playwright", "reader"]
    status: Literal["read", "unavailable"]
    javascript_support: str | None
    model_required_for_basic_use: bool | None
    evidence_quote: str | None
    source_url: str
 
class Comparison(BaseModel):
    rows: list[ToolRow]
 
async def main():
    browser = Browser(
        use_cloud=False,
        allowed_domains=["crawlee.dev", "playwright.dev", "github.com"],
    )
    agent = Agent(
        task=(
            "逐一打开以下官方来源,只依据对应页面回答,不使用记忆补缺:"
            + json.dumps(sources)
            + "。每个来源返回一条记录。说明 JavaScript 页面支持,"
              "以及基本用法是否必须依赖模型,附简短原文证据。"
              "source_url 使用给定来源。无法访问时 status=unavailable,"
              "其他未知字段为 null。不要注册、购买、登录或修改站点。"
        ),
        llm=ChatOpenAI(model=os.environ["MODEL_NAME"]),
        browser=browser,
        output_model_schema=Comparison,
        max_failures=3,
    )
    history = await agent.run(max_steps=24)
    history.save_to_file("history.json")
    raw = history.final_result()
    if not raw:
        raise RuntimeError("Agent 没有返回最终数据,请查看 history.json")
    result = Comparison.model_validate_json(raw)
    ids = [row.id for row in result.rows]
    if len(ids) != len(sources) or set(ids) != set(sources):
        raise ValueError("来源记录存在重复或遗漏")
    for row in result.rows:
        if row.source_url.rstrip("/") != sources[row.id].rstrip("/"):
            raise ValueError("结果来源不是指定页面")
        if row.status == "read" and not row.evidence_quote:
            raise ValueError("已读取记录缺少证据")
    Path("comparison.json").write_text(
        result.model_dump_json(indent=2), encoding="utf-8"
    )
    incomplete = not history.is_successful() or any(
        row.status != "read" for row in result.rows
    )
    print({"records": len(result.rows), "incomplete": incomplete})
    if incomplete:
        raise SystemExit(1)
 
asyncio.run(main())

运行:

uv run python compare_sites.py

本例按默认生命周期在 Agent.run 结束时清理浏览器。没有设置 keep_alive,也不额外创建长期会话。需要保留浏览器调试时,要另外明确会话所有者和结束方式,不能只加一个常驻开关就忘记回收。

为什么步骤上限与 Schema 都不够

max_steps 限制 Agent 动作轮次,不是严格总运行秒数,也不保证在上限内取得所有资料。步骤不足时查看 history,判断是页面打不开、反复导航还是任务描述不明确,不先无限提高上限。

Schema 检查 JSON 字段形状,不能证明 Agent 真正读过网页。脚本还核对来源集合、重复与原文证据,并保留历史。即便 Agent 自己标成功,仍要人工确认引用确实支持对应结论。

允许 unavailable 是为了表达部分失败。它与“这个工具不支持某功能”不同:无法访问页面不能成为产品能力的否定结论。

怎样检查并使用结果

comparison.json 每行对应一个指定来源,结构示例如下,具体值由页面证据决定:

{
  "id": "reader",
  "status": "unavailable",
  "javascript_support": null,
  "model_required_for_basic_use": null,
  "evidence_quote": null,
  "source_url": "https://example.16yun.cn/reader"
}

这条示例明确失败,不是模型返回记录。成功记录应有简短原文证据;把它与原页面比较,检查是否把“支持浏览器”误写成“必须使用浏览器”,或把可选模型能力写成基本用法必需。

整理表格时保留 status 和 source_url。不要为了表格整齐把 null 改成“否”;未知、无法访问和确定不支持是三种不同含义。

保存的 history 可能包含页面内容和动作,按本地任务材料管理,不当成可无条件公开的日志。本文只读取公开文档,也不需要动作录像。

遇到失败怎样重做

模型鉴权失败。 检查 env 是否被加载、适配器和模型名是否匹配。浏览器页面正常不代表模型服务正常。

本地浏览器启动失败。 检查安装命令与运行账户、浏览器二进制和 Linux 依赖。use_cloud=False 不会替你补一个有效的云浏览器账户。

allowed_domains 拦住跳转。 查看目标实际重定向地址。只有确定仍属于预期官方来源时才增加必要域名,不直接放开所有网站。

只返回两条来源。 根据 history 找出未处理项,将任务缩小到该来源后补查。合并时保留原来源 ID 与新的观测,不覆盖其他已核对记录。

引用看起来合理但原文没有。 该记录不能作为已证实信息。重新打开指定页面,缩小提取问题并核对材料;结构化输出成功不能关闭事实错误。

步骤都在重复。 检查页面是否卡在相同状态,任务是否同时要求搜索、比较、登录和导出。把本次任务保持在三个明确来源与两个问题上,再决定是否扩展。

这种写法让跨站 Agent 有明确的任务边界和失败去向。最终对照表的价值来自可回查的来源,而不是一段流畅但无法追溯的总结。

需要企业代理方案?

我们可根据目标站点、并发规模与稳定性目标提供定制方案。