微软开源的跨浏览器自动化框架,单套 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 是隔离单位,不同监控目标互不污染。
选择器优先级
官方推荐顺序(抗重构能力递减):
get_by_role("button", name="Merge")— 语义优先,首选get_by_label()/get_by_placeholder()— 表单get_by_text("Pipeline failed")— 文本监控常用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 框架