跳到正文
Browser Use·· 4 小時前AI 評分64

如何自動登入沒有 API 的網站並每日下載發票

Automate a Website With No API: Log In and Download Invoices Daily

AI 導讀

Browser Use 的指南説明,沒有 API 的供應商網站可用瀏覽器自動登入並定期下載發票,並比較三種自動化方案。指南建議先向供應商索取匯出檔、API、SFTP 或電郵發票;若需自動化,應重用登入工作階段、處理 2FA、安排定時執行、保存檔案並在執行失敗時通知。

正文

First ask the supplier for a feed: an export, an API, an SFTP drop or invoices by email. Any of those beats automating their website. If they have none, automate the portal in a browser: log in once and reuse that session, handle the 2FA code, run it on a schedule, save each file to your storage, and alert when a run fails. Check the portal's terms of use and your contract first; some forbid automated access.

We make Browser Use, option 3 below. Facts about Playwright, Selenium, UiPath, Power Automate and GitHub Actions come from their own docs and pricing pages, checked October 26, 2026, and linked where used.

Three ways to automate the portal

OptionSetupBreaks when the site changes?2FASchedulingDownloadsCost
1. Playwright (or Selenium) script + cronYou write the login and download steps in codeYes. A changed selector fails the run until you fix itA saved session skips it while valid: storageState or a persistent context. TOTP needs your own code; the auth guide has noneYours: cron, GitHub Actions, Task SchedulerWait for the download event, then saveAsFree, Apache-2.0, plus the machine it runs on
2. RPA: UiPath, Power AutomateBuild a flow in the vendor's designer, set up a robot machineYes, when its selectors stop matching. Power Automate tries fallback selectors; UiPath Enterprise lists self-healing UI automationUiPath documents a TOTP code sampleUiPath time triggers; Power Automate: a Recurrence cloud flow triggers the desktop flowOn the machine running the flow; Power Automate's files and folders actions move themUiPath: free Community; Basic from $25/month; unattended RPA is an add-on. Power Automate: $150 per bot per month, billed yearly, for unattended runs
3. Browser agent with a saved profile (Browser Use Cloud, ours)A plain-English task, a saved profile, and a short script to fetch the files; 1Password if the login can expireIt reads the page each run. If you ask it to, it can repair a saved script. Check its outputProfile cookies skip it while valid, or a person enters it in the live view (docs); a 1Password one-time-password field types the TOTP code (docs)No schedule endpoint in the public V4 API. Call it from cron or GitHub ActionsSession downloads API, links valid 15 minutesModel tokens + 20% every run, plus $0.02 per browser-hour and traffic (pricing)

Which one:

  • Playwright if the portal rarely changes and you have a developer. It costs nothing per run and does the same thing every time. For one stable portal it is the better fit than ours.
  • RPA if your company already runs UiPath or Power Automate, or the same job also touches Windows desktop apps.
  • A browser agent if you have many portals, layouts change, or nobody wants to maintain selectors. You pay for model tokens on every run and its output needs checking.

RPA limits worth knowing:

  • Power Automate's unattended runs need a Process license or pay-as-you-go; one licensed bot runs one desktop flow at a time (Microsoft).
  • UiPath's serverless cloud robots stop each job at 15 minutes (UiPath).

Option 1 in code: Playwright

First run: LOGIN=1 node invoices.mjs, log in in the window, press Resume. Later runs reuse the ./portal-profile folder and run headless. Selenium works the same way with Chrome's --user-data-dir (Selenium docs).

// 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

Option 3 in five steps: Browser Use Cloud

  1. Save the login in a profile. Create a profile under Dashboard > Profiles, then log in once in a browser that uses it; the login is saved to the profile when that browser stops (profiles). Keep the profile ID.
  2. Put the password and TOTP in 1Password. A dedicated vault, a service account with read access, connected under Settings > Secrets (1Password guide). The one-time-password field types the current code. Needed only when the portal logs you out.
  3. Write the run and collect the files. List the browsers the run's session used (agentSessionId), then each browser's downloads (downloads 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. Copy the files to your storage. S3, SharePoint, Drive: add it after the loop, or as a workflow step. Keep the agent's reported invoice numbers next to the files so you can compare them.
  2. Schedule it. The public V4 API reference has no schedule endpoint, so the trigger is yours. On a server, a crontab line. On 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 runs schedules in UTC unless you set a timezone, can delay or drop them at busy times such as the top of the hour, and turns them off in public repos after 60 days without activity (GitHub docs).

When a run fails

  • Session expired. The run lands on the login page. Playwright: log in again (LOGIN=1); its docs say to delete stored state when it expires (docs). Browser Use: log in again in a browser that uses the profile (docs).
  • 2FA prompt. The run stops at a code screen. TOTP: a 1Password one-time-password binding types the code (docs). A push or SMS to someone's phone needs that person: hand them the live view (docs).
  • Layout change. A selector times out, or the agent reports it can't find the invoices. Playwright: fix the selector. Agent: read its report and check the files.
  • Empty download. Zero files or a 0-byte file. Fail the run on it, as the scripts above do, and compare the file count with the invoice list.
  • Nobody hears about it. Exit non-zero. GitHub notifies whoever last edited the cron line; add a Slack or email step for a team. A dropped schedule raises no error, so also alert when no run succeeded in 24 hours.

More

來源:Browser Use · browser-use.com