微软开源的跨浏览器自动化框架,单套 API 驱动 Chromium/Firefox/WebKit。已从测试框架进化为”AI Agent 的浏览器操作系统”,是当前 Claude + 浏览器自动化事实标准底座。

94.3k stars | TypeScript(官方支持 JS/TS、Python、Java、.NET)| Apache-2.0 | 最新版本 v1.62.0(2026-07)| https://github.com/microsoft/playwright

为什么选 Playwright 做页面监控

对比 Selenium/Puppeteer 的核心优势:

  • Auto-waiting:所有操作内置可操作性等待(可见、稳定、可接收事件),不需要手写 sleep/WebDriverWait,对 GitLab 这类重 SPA 渲染的页面尤其重要
  • Web-first assertions:expect(locator).to_have_text(...) 自动轮询重试直到超时
  • storageState:cookie/localStorage/IndexedDB 一次导出、反复注入,登录态复用是监控场景的命门
  • 三件套调试:codegen 录制生成代码、trace viewer 回放排障、UI mode 交互式运行
  • Agent 生态:官方维护 playwright-mcp 与 playwright-cli,Claude 可直接驱动浏览器

安装

# Node.js
npm init -y && npm i -D playwright @playwright/test
npx playwright install chromium          # 只装 chromium 即可,监控不需要三个浏览器
 
# Python
pip install playwright
playwright install chromium

核心模型:Browser → Context → Page

Browser(进程,昂贵)
 └─ BrowserContext(隔离会话,≈隐身窗口,轻量)← storageState 挂在这一层
     └─ Page(标签页)

监控脚本的典型骨架:一个 Browser + 一个带 storageState 的 Context + N 个 Page 并发跑多个监控项,跑完 close。Context 是隔离单位,不同监控目标互不污染。

选择器优先级

官方推荐顺序(抗重构能力递减):

  1. get_by_role("button", name="Merge") — 语义优先,首选
  2. get_by_label() / get_by_placeholder() — 表单
  3. get_by_text("Pipeline failed") — 文本监控常用
  4. locator("css=.gl-badge") — 兜底;GitLab 的 class 名带 hash 时改用 data-testid

避免 XPath 和层级 CSS,GitLab 前端重构频繁会碎。

登录态保持(GitLab 监控的关键)

GitLab 有 2FA/SSO 时脚本模拟登录不现实,标准做法是人工登录一次 + 导出 storageState:

# codegen 打开登录页,人工完成登录(含 2FA),关闭后状态存入 gitlab-auth.json
python -m playwright codegen --save-storage=gitlab-auth.json \
  https://gitlab.example.com/users/sign_in

之后所有监控脚本注入该状态:

context = browser.new_context(storage_state="gitlab-auth.json")

注意事项:

  • gitlab-auth.json 等价于你的会话凭证,进 .gitignore,不要入库
  • GitLab session 有有效期(默认数天~数周),监控脚本要检测”被踢回登录页”(URL 含 sign_in)并告警提醒你重新录一次
  • 每次跑完可 context.storage_state(path=...) 回写刷新,延长状态寿命
  • 如果只监控数据而非视觉页面,优先考虑 GitLab REST API + PAT,比浏览器稳得多;浏览器方案留给”页面长什么样”的监控

监控实战示例:GitLab MR/Pipeline 页面巡检

# monitor_gitlab.py — Python sync API 版(监控脚本用 sync 即可,无需 async)
import json, pathlib, hashlib
from playwright.sync_api import sync_playwright
 
URL = "https://gitlab.example.com/dashboard/merge_requests"
AUTH = "gitlab-auth.json"
LAST = pathlib.Path("last-snapshot.txt")
 
with sync_playwright() as p:
    browser = p.chromium.launch(headless=True)
    ctx = browser.new_context(storage_state=AUTH)
    page = ctx.new_page()
    page.goto(URL, wait_until="domcontentloaded")
 
    # GitLab 是 SPA,等关键元素渲染出来再取数
    page.wait_for_selector('[data-testid="merge-request-row"], .merge-request',
                           timeout=15_000)
 
    # 登录态失效检测
    if "sign_in" in page.url:
        raise RuntimeError("GitLab 登录态失效,需重新 codegen 录制")
 
    # 方案 1:结构化文本快照(轻量,适合内容变化检测)
    snapshot = page.locator("main").inner_text()
 
    # 方案 2:整页截图(适合视觉回归/交给 Claude 判断)
    page.screenshot(path="shots/mr-dashboard.png", full_page=True)
 
    # 方案 3:ARIA 快照(1.57+,语义化 DOM 结构,喂给 LLM token 效率高)
    aria = page.locator("main").aria_snapshot()
 
    ctx.storage_state(path=AUTH)   # 回写刷新登录态
    browser.close()
 
# 变化检测:hash 对比,变了才告警
digest = hashlib.sha256(snapshot.encode()).hexdigest()
if LAST.exists() and LAST.read_text() != digest:
    print("CHANGED")   # 这里接告警:飞书 webhook / 调 Claude 分析 diff
LAST.write_text(digest)

变化检测三种手段按成本排序:

手段适合缺点
文本/元素计数 hash 对比MR 列表、badge 数字、报错文案时间戳类噪音需过滤
截图像素对比(pixelmatch/PIL)布局/样式回归抗噪差,字体渲染微差就误报
截图/ARIA 快照交给 Claude 语义判断”这个页面有没有异常”类模糊判断有 API 成本,适合变化后二次分析

与 Claude 集成的三种架构

架构 A:Claude 直接驱动浏览器(探索期/自愈型) Claude Code 挂 playwright-mcp,自然语言指挥巡检;token 敏感的高吞吐场景换 playwright-cli(CLI + SKILLS,不加载完整 accessibility tree)。适合”让 Claude 帮我看看这个页面怎么了”,不适合 7×24 无人值守。

// .mcp.json
{ "mcpServers": { "playwright": { "command": "npx", "args": ["@playwright/mcp@latest"] } } }

架构 B:脚本巡检 + Claude 分析(推荐的生产形态) cron/launchd 定时跑上面的 Playwright 脚本做确定性检测;仅在”检测到变化/异常”时把截图 + ARIA 快照 + diff 发给 Claude API 做语义判断(是否真异常、严重程度、一句话摘要),结论推飞书。LLM 只在需要时介入,成本可控、链路可靠。

# 变化发生时:
import anthropic
client = anthropic.Anthropic()
msg = client.messages.create(
    model="claude-sonnet-5",
    max_tokens=1024,
    messages=[{"role": "user", "content": [
        {"type": "image", "source": {"type": "base64", "media_type": "image/png",
                                      "data": png_b64}},
        {"type": "text", "text": "这是 GitLab MR 看板截图,与上次相比变化如下:...,"
                                  "判断是否有需要关注的异常并给一句话结论"}]}])

架构 C:Claude Agent SDK 全托管 用 Agent SDK 把 Playwright 包成 tool,让 Agent 自主决策巡检策略。灵活但不可预测性高,只建议用在低频、允许人工兜底的场景。

调试工具链

python -m playwright codegen <url>     # 录制操作自动生成代码
npx playwright show-trace trace.zip    # trace 回放:逐步看 DOM 快照/网络/console

脚本里开 trace 与视频,排障效率数量级提升:

ctx.tracing.start(screenshots=True, snapshots=True, sources=True)
# ... 跑监控 ...
ctx.tracing.stop(path="trace.zip")

部署与调度

  • headless:launch(headless=True) 默认;Linux 服务器需系统依赖 playwright install-deps chromium
  • Docker:mcr.microsoft.com/playwright/python:v1.62.0-jammy(Node 版同前缀),镜像内浏览器已装好
  • 调度:单机 cron/launchd 足够;量大再上 K8s CronJob。每次跑都是独立进程 + 新建 Context,天然无状态
  • 防误报:page.goto 用 wait_until="domcontentloaded" + 显式 wait_for_selector,不要依赖 networkidle(GitLab 有长轮询/websocket,networkidle 经常等不到)

常见坑

  • 不要手写 sleep:auto-waiting 覆盖绝大多数场景;真要等待用 wait_for_selector/expect(...).to_be_visible()
  • iframe:GitLab 个别嵌入内容在 iframe 里,需 page.frame_locator("iframe.xxx") 再取子元素
  • locator 是惰性的:page.locator(...) 只是查询描述,取值/动作时才执行;所以取数前确保页面已稳定
  • 多标签/弹窗:ctx.expect_popup() 捕获新窗口
  • 代理:launch(proxy={"server": "http://127.0.0.1:7070"}),内网 GitLab 直连即可
  • 版本绑定:Playwright 版本与浏览器版本强绑定,升级库后必须重跑 playwright install

相关页面

  • playwright-mcp — 官方 MCP Server,让 Claude 通过 accessibility 快照驱动浏览器
  • playwright-cli — 官方 CLI + SKILLS,token 效率更高的 Agent 浏览器方案
  • puppeteer — Google 系替代方案,只支持 Chrome 系
  • browser-use — LLM 驱动的更高层浏览器 Agent 框架