mcpField
P
playwright

playwright

github.com/fellowship-dev开发3178 次安装3 周前更新
代码仓库

关于 playwright

此技能通过 `playwright-core` 和 Chrome DevTools Protocol 自动化 Chromium,适用于以 `.mjs` 文件编写浏览器任务的场景,并提供页面交互、检查和监控模式。

主要功能

  1. 01通过 CDP 连接 Chromium
  2. 02截取屏幕并检查页面
  3. 03填写表单并点击元素
  4. 04提取文本、HTML 和标题
  5. 05监控控制台与网络响应
  6. 06将浏览器会话录制为视频

适用场景

  1. 01自动执行多页登录流程
  2. 02检查渲染页面和屏幕截图
  3. 03通过脚本收集页面内容
  4. 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()}`);
});

选择器(按优先顺序使用)

  1. data-testid="foo" — page.click('[data-testid="foo"]')
  2. 角色 — page.getByRole('button', { name: 'Submit' })
  3. 文本 — page.click('button:has-text("Submit")')
  4. 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