所有文章

先向供应商索取数据源:导出文件、API、SFTP 投递或通过电子邮件发送的发票。这些方式中的任何一种都胜过自动化他们的网站。如果供应商没有这些,就在浏览器中自动化门户:登录一次并复用该会话,处理 2FA 验证码,按计划运行,将每个文件保存到你的存储中,并在运行失败时发出警报。请先查看门户的使用条款和你的合同;有些条款禁止自动化访问。

我们开发了 Browser Use,即下面的方案 3。关于 Playwright、Selenium、UiPath、Power Automate 和 GitHub Actions 的事实来自它们各自的文档和定价页面,核查于 2026 年 10 月 26 日,并在使用时附上链接。

自动化门户的三种方式

方案设置网站变更时会失效吗?2FA调度下载成本
1. Playwright(或 Selenium)脚本 + cron你用代码编写登录和下载步骤会。选择器一旦改变,运行就会失败,直到你修复它保存的会话在有效期内可跳过它: storageState 或使用 持久化上下文. TOTP 需要你自己的代码;认证指南中没有提供由你负责:cron、GitHub Actions、任务计划程序等待 下载事件, 然后 saveAs免费, Apache-2.0, 加上运行它的机器
2. RPA:UiPath、Power Automate在供应商的设计器中构建流程,设置一台机器人机器会,当其选择器不再匹配时。Power Automate 会尝试 回退选择器;UiPath Enterprise 列出了 自愈 UI 自动化UiPath 记录了 TOTP 代码示例UiPath 时间触发器;Power Automate:一个 重复 云流 触发桌面流在运行流程的机器上;Power Automate 的 文件和文件夹操作 移动它们UiPath: 免费社区版;Basic 版每月 25 美元起;无人值守 RPA 是附加项。 Power Automate: 每个机器人每月 150 美元,按年计费,用于无人值守运行
3. 带保存配置文件的浏览器智能体(Browser Use Cloud,我们的产品)一个用平实英语描述的任务、一个保存的配置文件,以及一个用于获取文件的简短脚本;如果登录可能过期,则使用 1Password它每次运行都会读取页面。如果你要求,它可以修复 保存的脚本. 检查其输出配置文件 cookie 在有效期内可跳过它,或者由人在实时视图中输入(文档);一个 1Password one-time-password 字段会输入 TOTP 代码(文档)公共 V4 API 中没有调度端点。从 cron 或 GitHub Actions 调用它会话下载 API, 链接有效期为 15 分钟模型 token + 每次运行 20%,加上每浏览器小时 0.02 美元和流量费用(定价)

选哪一个:

  • Playwright 如果门户很少变更且你有开发者。它每次运行不产生费用,并且每次都做同样的事。对于一个稳定的门户,它比我们的方案更合适。
  • RPA 如果你的公司已经在运行 UiPath 或 Power Automate,或者同一项工作还涉及 Windows 桌面应用。
  • 浏览器智能体 如果你有许多门户、布局会变化,或者没有人愿意维护选择器。你每次运行都要为模型 token 付费,而且其输出需要检查。

值得了解的 RPA 限制:

  • Power Automate 的无人值守运行需要 Process 许可证或按需付费;一个已授权的机器人一次运行一个桌面流(Microsoft).
  • UiPath 的无服务器云机器人每个任务在 15 分钟时停止(UiPath).

方案 1 的代码:Playwright

首次运行: LOGIN=1 node invoices.mjs, 在窗口中登录,按 Resume。后续运行复用 ./portal-profile 文件夹并以无头模式运行。Selenium 使用 Chrome 的 --user-data-dir (Selenium 文档).

// invoices.mjs
import { chromium } from "playwright";
 
const url = "https://portal.example.com/invoices";
const login = process.env.LOGIN === "1";
const context = await chromium.launchPersistentContext("./portal-profile", { headless: !login });
const page = await context.newPage();
await page.goto(url);
if (login) {
  await page.pause(); // log in by hand, then press Resume
  await page.goto(url);
}
if (page.url().includes("/login")) throw new Error("session expired: run again with LOGIN=1");
 
const links = page.getByRole("link", { name: "Download PDF" });
await links.first().waitFor(); // fails the run if no invoice links show up
for (const link of await links.all()) {
  const downloadPromise = page.waitForEvent("download"); // start waiting before the click
  await link.click();
  const download = await downloadPromise;
  await download.saveAs(`invoices/${download.suggestedFilename()}`);
}
await context.close(); // downloads are deleted when the context closes, so save first

方案 3 分五步:Browser Use Cloud

  1. 在配置文件中保存登录信息。 在 仪表板 > 配置文件, 下创建一个配置文件,然后在使用它的浏览器中登录一次;当该浏览器停止时,登录信息会保存到配置文件中(配置文件)。保留配置文件 ID。
  2. 将密码和 TOTP 放入 1Password。 一个专用保险库、一个具有读取权限的服务账户,在 Settings > Secrets 下连接(1Password 指南)。 one-time-password 字段会输入当前代码。仅在门户将你登出时需要。
  3. 编写运行并收集文件。 列出运行会话使用的浏览器(agentSessionId),然后列出每个浏览器的下载(下载 API):
# daily_invoices.py
import os, pathlib, time, requests
from browser_use_sdk.v4 import BrowserUse
 
API = "https://api.browser-use.com/api/v4"
KEY = {"X-Browser-Use-API-Key": os.environ["BROWSER_USE_API_KEY"]}
 
def op(alias, field):  # one 1Password field, typed only on the portal's host
    return {"alias": alias, "allowedDomains": ["portal.example.com"],
            "source": {"type": "onepassword", "integrationId": os.environ["OP_INTEGRATION_ID"],
                       "vaultId": os.environ["OP_VAULT_ID"], "itemId": os.environ["OP_ITEM_ID"],
                       "fieldId": field}}
 
def get(path, **params):
    r = requests.get(f"{API}{path}", headers=KEY, params=params, timeout=60)
    r.raise_for_status()
    return r.json()
 
def downloads(browser_id):  # 50 files per page; follow the cursor
    files, params = [], {"includeUrls": "true"}
    while True:
        page = get(f"/browsers/{browser_id}/downloads", **params)
        files += page["files"]
        if not page.get("hasMore"):
            return files
        params["cursor"] = page["nextCursor"]
 
with BrowserUse() as client:
    run = client.runs.create(
        "Open https://portal.example.com/invoices. If asked to log in, use portal_user, "
        "portal_pass and portal_otp. Download every invoice dated yesterday as PDF "
        "and report the invoice numbers.",
        browser_settings={"profileId": os.environ["PORTAL_PROFILE_ID"]},
        secret_bindings=[op("portal_user", "username"), op("portal_pass", "password"),
                         op("portal_otp", "one-time-password")],
    )
    done = client.runs.wait_for_completion(run.id)
 
if done.status.value != "completed":
    raise SystemExit(f"run failed: {done.error or done.status.value}")
 
browsers = get("/browsers", agentSessionId=run.session_id)["items"]
files, last = [], None
for _ in range(18):  # files can land after the run ends: wait until two polls agree (max 3 min)
    files = [f for b in browsers for f in downloads(b["id"])]
    if files and len(files) == last:
        break
    last = len(files)
    time.sleep(10)
if not files or any(f["size"] == 0 for f in files):  # drop if some days have no invoices
    raise SystemExit(f"no invoice files; agent said: {done.result}")
 
out = pathlib.Path("invoices")
out.mkdir(exist_ok=True)
for f in files:  # download links expire after 15 minutes
    r = requests.get(f["url"], timeout=120)
    r.raise_for_status()
    (out / f["path"]).write_bytes(r.content)
print(done.result)
  1. 将文件复制到你的存储中。 S3、SharePoint、Drive:在循环之后添加,或作为工作流步骤。将智能体报告的发票编号与文件放在一起,以便你进行比较。
  2. 安排调度。 公共 V4 API 参考中没有调度端点,所以触发器由你负责。在服务器上,用一行 crontab。在 GitHub Actions 上:
# .github/workflows/invoices.yml
name: Pull invoices
on:
  schedule:
    - cron: "17 6 * * *" # 06:17 UTC daily
  workflow_dispatch:
jobs:
  invoices:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: pip install browser-use-sdk requests
      - run: python daily_invoices.py
        env:
          BROWSER_USE_API_KEY: ${{ secrets.BROWSER_USE_API_KEY }}
          PORTAL_PROFILE_ID: ${{ secrets.PORTAL_PROFILE_ID }}
          OP_INTEGRATION_ID: ${{ secrets.OP_INTEGRATION_ID }}
          OP_VAULT_ID: ${{ secrets.OP_VAULT_ID }}
          OP_ITEM_ID: ${{ secrets.OP_ITEM_ID }}

GitHub 按 UTC 运行调度,除非你设置一个 timezone, 在繁忙时段(如整点)可能会延迟或丢弃它们,并且在公共仓库中 60 天无活动后会关闭它们(GitHub 文档).

当运行失败时

  • 会话过期。 运行停在登录页面。Playwright:再次登录(LOGIN=1);其文档说在过期时删除存储的状态(文档)。Browser Use:在使用该配置文件的浏览器中再次登录(文档).
  • 2FA 提示。 运行停在代码屏幕。TOTP:一个 1Password one-time-password 绑定会输入代码(文档)。发送到某人手机的推送或短信需要那个人:把实时视图交给他们(文档).
  • 布局变更。 选择器超时,或者智能体报告找不到发票。Playwright:修复选择器。智能体:阅读其报告并检查文件。
  • 空下载。 零个文件或一个 0 字节文件。让它使运行失败,如上面的脚本所做,并将文件数量与发票列表进行比较。
  • 没有人听说这件事。 以非零状态退出。GitHub 会通知最后编辑该 cron 行的人;为团队添加 Slack 或电子邮件步骤。计划被丢弃不会引发错误,因此还要在 24 小时内没有成功运行时发出警报。

更多

发布于 2026 年 10 月 26 日

继续阅读

所有文章

指南

/2026 年 10 月 26 日

如何用 LLM 从网站提取结构化数据(2026)

将众多网站转化为可信的 JSON:单一模式、干净的页面文本、验证、重试。Firecrawl、Crawl4AI、Browser Use 和 Stagehand 对比。

Gregor Zunic

指南

/2026 年 10 月 25 日

如何让你的编码智能体使用已登录的 Chrome(2026)

将你已登录的 Chrome 交给 Claude Code、Codex、Gemini CLI 或 Cursor:Claude in Chrome、Playwright MCP、Chrome DevTools MCP、Browser Harness 对比。

Gregor Zunic

指南

/2026 年 10 月 25 日

Web 智能体基准测试:WebArena、WebVoyager、Mind2Web 等(2026)

哪些基准测试在真实网站任务上测试 AI 智能体:WebArena、WebVoyager、Mind2Web、BrowseComp 等,按任务、环境和评分进行对比。

Gregor Zunic