关于 playwright
此技能通过 `playwright-core` 和 Chrome DevTools Protocol 自动化 Chromium,适用于以 `.mjs` 文件编写浏览器任务的场景,并提供页面交互、检查和监控模式。
主要功能
- 01通过 CDP 连接 Chromium
- 02截取屏幕并检查页面
- 03填写表单并点击元素
- 04提取文本、HTML 和标题
- 05监控控制台与网络响应
- 06将浏览器会话录制为视频
适用场景
- 01自动执行多页登录流程
- 02检查渲染页面和屏幕截图
- 03通过脚本收集页面内容
- 04排查浏览器请求和控制台错误
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ fellowship-dev/ dogfooded-skills --skill playwright安装后在 Claude Code 中输入/playwright确认已加载。
详细介绍
Playwright Web 自动化
通过 Chromium + playwright-core 和 CDP 实现浏览器自动化。无需 MCP 服务——脚本以 .mjs 文件的形式运行。
前置条件
# Install playwright-core (no browser download — uses existing Chromium)
npm install playwright-core
# Verify Chromium exists
which chromium || ls /Applications/Chromium.app/Contents/MacOS/Chromium
启动支持 CDP 的 Chromium
# Kill any existing debug instance
pkill -f "remote-debugging-port=9222" 2>/dev/null
# Launch headless (cron/CI) or headed (interactive)
chromium --remote-debugging-port=9222 \
--user-data-dir="/tmp/playwright-profile" \
--disable-blink-features=AutomationControlled \
--no-first-run --no-default-browser-check \
--headless=new \
"about:blank" > /tmp/chromium.log 2>&1 &
# macOS: use full path /Applications/Chromium.app/Contents/MacOS/Chromium
# Linux: chromium-browser or chromium
# Wait for CDP
sleep 3 && curl -sf http://localhost:9222/json/version > /dev/null && echo "CDP ready"
通过 Playwright 连接(ESM)
将脚本编写为 .mjs 文件:
// run: node script.mjs
import { chromium } from 'playwright-core';
const browser = await chromium.connectOverCDP('http://localhost:9222');
const ctx = browser.contexts()[0];
const page = ctx.pages()[0] || await ctx.newPage();
await page.goto('https://example.com', { waitUntil: 'domcontentloaded', timeout: 15000 });
await page.waitForTimeout(2000);
await page.screenshot({ path: '/tmp/screenshot.png' });
await browser.close(); // disconnects, doesn't kill Chromium
**重要:**始终使用 .mjs 扩展名——playwright-core 仅支持 ESM。
关键模式
截图与检查
await page.screenshot({ path: '/tmp/screen.png' });
// Read /tmp/screen.png in Claude to see it visually
填写表单并点击
// Inspect inputs first
const inputs = await page.evaluate(() =>
Array.from(document.querySelectorAll('input')).map(i => ({
name: i.name, type: i.type, placeholder: i.placeholder
}))
);
console.log(JSON.stringify(inputs, null, 2));
await page.fill('input[name="email"]', '[email protected]');
await page.click('button:has-text("Submit")');
await page.waitForTimeout(2000);
提取页面内容
const text = await page.evaluate(() => document.body.innerText);
const html = await page.content();
const title = await page.title();
等待导航
await Promise.all([
page.waitForNavigation({ waitUntil: 'networkidle' }),
page.click('button:has-text("Login")')
]);
SPA / 客户端渲染的数据
SPA 可能无法在无头模式下完全完成水合。如果有可用的数据端点,请使用:
const data = await page.evaluate(async () => {
const res = await fetch('/api/data', { credentials: 'include' });
return res.json();
});
多页面流程
await page.goto('https://app.example.com/login');
await page.fill('#email', '[email protected]');
await page.fill('#password', 'password');
await page.click('button[type="submit"]');
await page.waitForNavigation({ waitUntil: 'networkidle' });
await page.screenshot({ path: '/tmp/after-login.png' });
// Continue to next page
await page.click('a:has-text("Dashboard")');
await page.waitForLoadState('networkidle');
await page.screenshot({ path: '/tmp/dashboard.png' });
录制视频
// Must create a NEW context for video recording
const ctx = await browser.newContext({
recordVideo: { dir: '/tmp/videos/', size: { width: 1280, height: 720 } }
});
const page = await ctx.newPage();
await page.goto('https://example.com');
// ... do things ...
await ctx.close(); // video saved to /tmp/videos/*.webm
控制台与网络监控
page.on('console', msg => console.log(`[${msg.type()}] ${msg.text()}`));
page.on('response', res => {
if (res.status() >= 400) console.log(`[${res.status()}] ${res.url()}`);
});
选择器(按优先顺序使用)
data-testid="foo"—page.click('[data-testid="foo"]')- 角色 —
page.getByRole('button', { name: 'Submit' }) - 文本 —
page.click('button:has-text("Submit")') - CSS —
page.click('.submit-btn')(不可靠,最后再用)
清理
pkill -f "remote-debugging-port=9222"
故障排除
| 问题 | 解决方案 |
|---|---|
| CDP 无响应 | 多等待一会儿,检查 curl http://localhost:9222/json/version |
| 端口 9222 已被占用 | 运行 pkill -f "remote-debugging-port=9222",然后重新启动 |
| ESM 导入错误 | 使用 .mjs 扩展名,而非 .js |
| 页面未加载 | 增加超时时间,使用 waitUntil: 'domcontentloaded' |
| 截图为空白 | 捕获前添加 page.waitForTimeout(2000) |
| CSS 未加载 | 使用 waitUntil: 'networkidle',或等待某个可见元素 |
| 表单提交失败 | 检查元素是否可见:await el.isVisible() |
评论
支持 Markdown