mcpField
P
playwright-local

playwright-local

github.com/jezweb开发1.1k505 次安装3 周前更新
代码仓库

关于 playwright-local

使用 Playwright 进行本地浏览器自动化的技能,适用于抓取、测试和浏览器工作流。内容包括 Node.js 与 Python 配置、隐身设置、会话管理、调试和问题排查。

主要功能

  1. 01Node.js 与 Python 配置指南
  2. 02Chromium、Firefox 和 WebKit 用法
  3. 03隐身模式配置指南
  4. 04Cookie 与会话持久化
  5. 05检查控制台、页面错误和请求
  6. 06常见浏览器问题排查

适用场景

  1. 01使用本地浏览器自动化抓取页面
  2. 02测试登录与身份验证流程
  3. 03排查浏览器测试失败
  4. 04检查页面错误和网络活动

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/jezweb/claude-skills --skill playwright-local

安装后在 Claude Code 中输入/playwright-local确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

README.md
.claude-plugin/
plugin.json
assets/
example-template.txt
references/
common-blocks.md
selector-strategies.md
stealth-techniques.md
scripts/
install-browsers.sh
templates/
authenticated-session.ts
basic-scrape.ts
infinite-scroll.ts
pdf-generation.ts
screenshot-capture.ts
stealth-mode.ts

详细介绍

Playwright 本地浏览器自动化

状态:已可用于生产 ✅ 上次更新:2026-01-21 依赖项:Node.js 20+(不再支持 Node.js 18)或 Python 3.9+ 最新版本:[email protected]、[email protected]、[email protected] 浏览器版本:Chromium 143.0.7499.4 | Firefox 144.0.2 | WebKit 26.0

⚠️ v1.57 重大变更:Playwright 现在使用 Chrome for Testing 构建版本,而不是 Chromium。这能提供更真实的浏览器行为,但会更改浏览器图标和标题栏。


快速开始(5 分钟)

1. 安装 Playwright

Node.js:

npm install -D playwright
npx playwright install chromium

Python:

pip install playwright
playwright install chromium

为什么这很重要:

  • playwright install 会下载浏览器二进制文件(Chromium 约 400MB)
  • 只安装需要的浏览器:chromium、firefox 或 webkit
  • 二进制文件存储在 ~/.cache/ms-playwright/
2. 抓取基本页面
import { chromium } from 'playwright';

const browser = await chromium.launch({ headless: true });
const page = await browser.newPage();

await page.goto('https://example.com', { waitUntil: 'networkidle' });
const title = await page.title();
const content = await page.textContent('body');

await browser.close();
console.log({ title, content });

重要:

  • 始终使用 await browser.close() 关闭浏览器,以避免产生僵尸进程
  • 对动态内容(SPA)使用 waitUntil: 'networkidle'
  • 默认超时时间为 30 秒;如有需要,可使用 timeout: 60000 进行调整
3. 在本地测试
# Node.js
npx tsx scrape.ts

# Python
python scrape.py

为什么选择 Playwright 本地运行,而不是 Cloudflare Browser Rendering

功能Playwright 本地运行Cloudflare Browser Rendering
IP 地址家庭宽带(你的 ISP)数据中心(容易被检测)
隐身插件完全支持不可用
速率限制无免费套餐每天 2,000 个请求
费用免费(使用你的 CPU)免费套餐用完后,每 10,000 个请求 $5
浏览器控制支持所有 Playwright 功能API 有限
并发量受你的硬件限制受账户限制
会话持久化可完全控制 Cookie 和存储会话管理有限
适用场景受机器人保护的网站、身份验证流程简单抓取、无服务器环境

何时使用 Cloudflare:无服务器环境、简单抓取、大规模使用时更节省成本
何时使用本地运行:需要绕过反机器人措施、需要家庭宽带 IP、复杂自动化


隐身设置的 7 个步骤

⚠️ 2025 年的现实情况:隐身插件可以很好地应对基础反机器人措施,但高级检测系统(Cloudflare Bot Management、PerimeterX、DataDome)已显著演进。目前的检测手段包括:

  • 行为分析(鼠标轨迹、滚动时机、击键动态)
  • TLS 指纹识别(JA3/JA4 特征)
  • Canvas 和 WebGL 指纹识别
  • HTTP/2 指纹识别

建议:

  • 隐身插件是一个不错的起点,但并非完整解决方案
  • 结合真实的用户行为模拟(使用 steps 选项)
  • 对于防护严密的网站,可考虑使用住宅代理
  • “今天有效的方法,明天未必有效”——请定期测试
  • 对于高级场景,可研究 nodriver 或 undetected-chromedriver 等替代方案
步骤 1:安装隐身插件(Node.js)
npm install playwright-extra playwright-stealth

兼容 puppeteer-extra:

npm install puppeteer-extra puppeteer-extra-plugin-stealth
步骤 2:配置隐身模式

playwright-extra:

import { chromium } from 'playwright-extra';
import stealth from 'puppeteer-extra-plugin-stealth';

chromium.use(stealth());

const browser = await chromium.launch({
  headless: true,
  args: [
    '--disable-blink-features=AutomationControlled',
    '--no-sandbox',
    '--disable-setuid-sandbox',
  ],
});

const context = await browser.newContext({
  userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
  viewport: { width: 1920, height: 1080 },
  locale: 'en-US',
  timezoneId: 'America/New_York',
});

要点:

  • --disable-blink-features=AutomationControlled 会移除 navigator.webdriver 标志
  • 随机化视口尺寸,以避免被识别为指纹
  • 使用户代理与浏览器版本匹配(上面的示例使用 Chrome 120)
步骤 3:屏蔽 WebDriver 检测
await page.addInitScript(() => {
  // Remove webdriver property
  Object.defineProperty(navigator, 'webdriver', {
    get: () => undefined,
  });

  // Mock plugins
  Object.defineProperty(navigator, 'plugins', {
    get: () => [1, 2, 3, 4, 5],
  });

  // Mock languages
  Object.defineProperty(navigator, 'languages', {
    get: () => ['en-US', 'en'],
  });

  // Consistent permissions
  const originalQuery = window.navigator.permissions.query;
  window.navigator.permissions.query = (parameters) => (
    parameters.name === 'notifications' ?
      Promise.resolve({ state: Notification.permission }) :
      originalQuery(parameters)
  );
});
步骤 4:模拟真人鼠标移动
// Simulate human cursor movement
async function humanClick(page, selector) {
  const element = await page.locator(selector);
  const box = await element.boundingBox();

  if (box) {
    // Move to random point within element
    const x = box.x + box.width * Math.random();
    const y = box.y + box.height * Math.random();

    await page.mouse.move(x, y, { steps: 10 });
    await page.mouse.click(x, y, { delay: 100 });
  }
}
步骤 5:轮换用户代理
const userAgents = [
  'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
  'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
  'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
];

const randomUA = userAgents[Math.floor(Math.random() * userAgents.length)];

const context = await browser.newContext({
  userAgent: randomUA,
});
步骤 6:Cookie 和会话持久化
import { chromium } from 'playwright';
import fs from 'fs/promises';

// Save session
const context = await browser.newContext();
const page = await context.newPage();

// ... perform login ...

const cookies = await context.cookies();
await fs.writeFile('session.json', JSON.stringify(cookies, null, 2));
await context.close();

// Restore session
const savedCookies = JSON.parse(await fs.readFile('session.json', 'utf-8'));
const newContext = await browser.newContext();
await newContext.addCookies(savedCookies);
步骤 7:验证隐身效果

在以下网址测试你的设置:https://bot.sannysoft.com/

const page = await context.newPage();
await page.goto('https://bot.sannysoft.com/', { waitUntil: 'networkidle' });
await page.screenshot({ path: 'stealth-test.png', fullPage: true });

检查内容:

  • navigator.webdriver 应为 undefined(而不是 false)
  • 应能检测到 Chrome
  • 应已填充插件信息
  • 页面上不应有红色警示

重要规则

务必做到

✅ 对单页应用(React、Vue、Angular)使用 waitUntil: 'networkidle' ✅ 使用 await browser.close() 关闭浏览器,防止内存泄漏 ✅ 将自动化操作包裹在 try/catch/finally 块中 ✅ 为不稳定的网站设置明确的超时时间 ✅ 出错时保存截图以便调试 ✅ 与元素交互前使用 page.waitForSelector() ✅ 高流量抓取时轮换用户代理 ✅ 先使用 headless: false 测试,再切换到 headless: true

绝不要做

❌ 不等待元素就使用 page.click()(先调用 waitForSelector) ❌ 依赖固定的 setTimeout() 等待(使用 waitForSelector、waitForLoadState) ❌ 抓取时不限制速率(在请求之间添加延迟) ❌ 所有请求都使用相同的用户代理(轮换用户代理) ❌ 忽略导航错误(捕获错误并通过退避重试) ❌ 未先测试有头模式就运行无头模式(视觉调试可以发现问题) ❌ 将凭据存储在代码中(使用环境变量)


调试方法(v1.56+)

Playwright v1.56 引入了无需设置事件监听器即可捕获调试信息的新方法:

控制台消息
import { test, expect } from '@playwright/test';

test('capture console output', async ({ page }) => {
  await page.goto('https://example.com');

  // Get all recent console messages
  const messages = page.consoleMessages();

  // Filter by type
  const errors = messages.filter(m => m.type() === 'error');
  const logs = messages.filter(m => m.type() === 'log');

  console.log('Console errors:', errors.map(m => m.text()));
});
页面错误
test('check for JavaScript errors', async ({ page }) => {
  await page.goto('https://example.com');

  // Get all page errors (uncaught exceptions)
  const errors = page.pageErrors();

  // Fail test if any errors occurred
  expect(errors).toHaveLength(0);
});
网络请求
test('inspect API calls', async ({ page }) => {
  await page.goto('https://example.com');

  // Get all recent network requests
  const requests = page.requests();

  // Filter for API calls
  const apiCalls = requests.filter(r => r.url().includes('/api/'));
  console.log('API calls made:', apiCalls.length);

  // Check for failed requests
  const failed = requests.filter(r => r.failure());
  expect(failed).toHaveLength(0);
});

适用场景:调试测试失败、验证是否存在控制台错误、审查网络活动。


高级鼠标控制(v1.57+)

steps 选项可精细控制鼠标移动,适用于:

  • 让行为更像真人,以应对反机器人检测
  • 测试带平滑动画的拖放操作
  • 调试视觉交互
带步骤的点击
// Move to element in 10 intermediate steps (smoother, more human-like)
await page.locator('button.submit').click({ steps: 10 });

// Fast click (fewer steps)
await page.locator('button.cancel').click({ steps: 2 });
带步骤的拖动
const source = page.locator('#draggable');
const target = page.locator('#dropzone');

// Smooth drag animation (20 steps)
await source.dragTo(target, { steps: 20 });

// Quick drag (5 steps)
await source.dragTo(target, { steps: 5 });

反检测优势:许多机器人检测系统会检查瞬时鼠标移动。使用 steps: 10 或更高值可模拟真实的人类鼠标行为。


已知问题预防

此技能可预防 10 个有记录的问题:

问题 #1:目标已关闭错误

错误:Protocol error (Target.sendMessageToTarget): Target closed. 来源:https://github.com/microsoft/playwright/issues/2938 发生原因:操作完成前页面已关闭,或浏览器崩溃 预防方法:

try {
  await page.goto(url, { timeout: 30000 });
} catch (error) {
  if (error.message.includes('Target closed')) {
    console.log('Browser crashed, restarting...');
    await browser.close();
    browser = await chromium.launch();
  }
}
问题 #2:未找到元素

错误:TimeoutError: waiting for selector "button" failed: timeout 30000ms exceeded 来源:https://playwright.dev/docs/actionability 发生原因:元素不存在、选择器错误,或页面尚未加载 预防方法:

// Use waitForSelector with explicit timeout
const button = await page.waitForSelector('button.submit', {
  state: 'visible',
  timeout: 10000,
});
await button.click();

// Or use locator with auto-wait
await page.locator('button.submit').click();
问题 #3:导航超时

错误:TimeoutError: page.goto: Timeout 30000ms exceeded. 来源:https://playwright.dev/docs/navigations 发生原因:页面加载缓慢、加载指示器无限转动,或被防火墙拦截 预防方法:

try {
  await page.goto(url, {
    waitUntil: 'domcontentloaded', // Less strict than networkidle
    timeout: 60000, // Increase for slow sites
  });
} catch (error) {
  if (error.name === 'TimeoutError') {
    console.log('Navigation timeout, checking if page loaded...');
    const title = await page.title();
    if (title) {
      console.log('Page loaded despite timeout');
    }
  }
}
问题 #4:框架已分离错误

错误:Error: Execution context was destroyed, most likely because of a navigation. 来源:https://github.com/microsoft/playwright/issues/3934 发生原因:单页应用导航重新渲染了元素 预防方法:

// Re-query element after navigation
async function safeClick(page, selector) {
  await page.waitForSelector(selector);
  await page.click(selector);
  await page.waitForLoadState('networkidle');
}
问题 #5:机器人检测(403/验证码)

错误:页面返回 403 或显示验证码 来源:https://github.com/berstend/puppeteer-extra/tree/master/packages/puppeteer-extra-plugin-stealth 发生原因:网站检测到 navigator.webdriver、数据中心 IP,或指纹不匹配 预防方法:使用隐身模式(上述步骤 2-7)+ 住宅 IP

问题 #6:文件下载未完成

错误:下载已开始但始终未完成 来源:https://playwright.dev/docs/downloads 发生原因:未等待下载事件,或文件流未关闭 预防方法:

const [download] = await Promise.all([
  page.waitForEvent('download'),
  page.click('a.download-link'),
]);

const path = await download.path();
await download.saveAs('./downloads/' + download.suggestedFilename());
问题 #7:无限滚动未加载更多内容

错误:滚动到底部,但没有加载新内容 来源:https://playwright.dev/docs/input#scrolling 发生原因:滚动事件未正确触发,或滚动速度过快 预防方法:

let previousHeight = 0;
while (true) {
  const currentHeight = await page.evaluate(() => document.body.scrollHeight);

  if (currentHeight === previousHeight) {
    break; // No more content
  }

  await page.evaluate(() => window.scrollTo(0, document.body.scrollHeight));
  await page.waitForTimeout(2000); // Wait for new content to load
  previousHeight = currentHeight;
}
问题 #8:WebSocket 连接失败

错误:WebSocket connection to 'ws://...' failed 来源:https://playwright.dev/docs/api/class-browser 发生原因:在受限环境中启动浏览器时未使用 --no-sandbox 预防措施:

const browser = await chromium.launch({
  args: ['--no-sandbox', '--disable-setuid-sandbox'],
});
问题 #9:page.pause() 在无头模式下会禁用超时

错误:存在 page.pause() 时,CI 中的测试会无限期挂起 来源:GitHub 问题 #38754 发生原因:page.pause() 在无头模式下会被忽略,但会禁用测试超时,导致后续失败的断言永远挂起 预防措施:

// Conditional debugging - only pause in local development
if (!process.env.CI && !process.env.HEADLESS) {
  await page.pause();
}

// Or use environment variable
const shouldPause = process.env.DEBUG_MODE === 'true';
if (shouldPause) {
  await page.pause();
}

影响:高 — 可能导致 CI 流水线因断言失败而无限期挂起

问题 #10:权限提示会阻碍 CI 中的扩展测试

错误:测试浏览器扩展时,测试会卡在权限提示上 来源:GitHub 问题 #38670 发生原因:launchPersistentContext 搭配扩展时会显示无法关闭的权限提示(clipboard-read/write、local-network-access),而且无法自动授予权限 预防措施:

// Don't use persistent context for extensions in CI
// Use regular context instead
const context = await browser.newContext({
  permissions: ['clipboard-read', 'clipboard-write']
});

// For extensions, pre-grant permissions where possible
const context = await browser.newContext({
  permissions: ['notifications', 'geolocation']
});

影响:高 — 会阻碍 CI/CD 环境中的自动化扩展测试


配置文件参考

playwright.config.ts(完整示例)
import { defineConfig, devices } from '@playwright/test';

export default defineConfig({
  testDir: './tests',
  timeout: 30000,
  expect: {
    timeout: 5000,
  },
  fullyParallel: true,
  forbidOnly: !!process.env.CI,
  retries: process.env.CI ? 2 : 0,
  workers: process.env.CI ? 1 : undefined,
  reporter: 'html',

  use: {
    baseURL: 'http://localhost:3000',
    trace: 'on-first-retry',
    screenshot: 'only-on-failure',
    video: 'retain-on-failure',

    // Anti-detection settings
    userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
    viewport: { width: 1920, height: 1080 },
    locale: 'en-US',
    timezoneId: 'America/New_York',
  },

  projects: [
    {
      name: 'chromium',
      use: { ...devices['Desktop Chrome'] },
    },
    {
      name: 'stealth',
      use: {
        ...devices['Desktop Chrome'],
        launchOptions: {
          args: [
            '--disable-blink-features=AutomationControlled',
            '--no-sandbox',
          ],
        },
      },
    },
  ],
});

这些设置的作用:

  • trace: 'on-first-retry' - 捕获完整跟踪信息,以便调试失败的测试
  • screenshot: 'only-on-failure' - 节省磁盘空间
  • viewport: { width: 1920, height: 1080 } - 常见的桌面分辨率
  • --disable-blink-features=AutomationControlled - 移除 webdriver 标志
动态 Web 服务配置(v1.57+)

使用正则表达式等待 Web 服务输出,然后再启动测试:

import { defineConfig } from '@playwright/test';

export default defineConfig({
  webServer: {
    command: 'npm run dev',
    // Wait for server to print port
    wait: {
      stdout: '/Server running on port (?<SERVER_PORT>\\d+)/'
    },
  },
  use: {
    // Use captured port in tests
    baseURL: `http://localhost:${process.env.SERVER_PORT ?? 3000}`
  }
});

优势:

  • 处理开发服务器(Vite、Next.js 开发模式)分配的动态端口
  • 无需进行 HTTP 就绪检查
  • 命名捕获组会成为环境变量
  • 适用于只记录就绪消息的服务

适用场景:

  • 使用随机端口的开发服务器
  • 没有 HTTP 端点的服务
  • 使用端口映射的容器化环境

常见模式

模式 1:抓取已通过身份验证的会话
import { chromium } from 'playwright';
import fs from 'fs/promises';

async function scrapeWithAuth() {
  const browser = await chromium.launch({ headless: false });
  const context = await browser.newContext();
  const page = await context.newPage();

  // Login
  await page.goto('https://example.com/login');
  await page.fill('input[name="email"]', process.env.EMAIL);
  await page.fill('input[name="password"]', process.env.PASSWORD);
  await page.click('button[type="submit"]');
  await page.waitForURL('**/dashboard', { timeout: 10000 });

  // Save session
  const cookies = await context.cookies();
  await fs.writeFile('session.json', JSON.stringify(cookies));

  // Navigate to protected page
  await page.goto('https://example.com/protected-data');
  const data = await page.locator('.data-table').textContent();

  await browser.close();
  return data;
}

适用场景:需要登录的网站、抓取用户专属内容

模式 2:带去重功能的无限滚动
async function scrapeInfiniteScroll(page, selector) {
  const items = new Set();
  let previousCount = 0;
  let noChangeCount = 0;

  while (noChangeCount < 3) {
    const elements = await page.locator(selector).all();

    for (const el of elements) {
      const text = await el.textContent();
      items.add(text);
    }

    if (items.size === previousCount) {
      noChangeCount++;
    } else {
      noChangeCount = 0;
    }

    previousCount = items.size;

    await page.evaluate(() => window.scrollTo(0, document.body.scrollHeight));
    await page.waitForTimeout(1500);
  }

  return Array.from(items);
}

适用场景:Twitter 信息流、商品列表、支持无限滚动的新闻网站

模式 3:多标签页编排
async function scrapeMultipleTabs(urls) {
  const browser = await chromium.launch();
  const context = await browser.newContext();

  const results = await Promise.all(
    urls.map(async (url) => {
      const page = await context.newPage();
      await page.goto(url);
      const title = await page.title();
      await page.close();
      return { url, title };
    })
  );

  await browser.close();
  return results;
}

适用场景:并发抓取多个页面、比价购物

模式 4:截取整页屏幕截图
async function captureFullPage(url, outputPath) {
  const browser = await chromium.launch();
  const page = await browser.newPage({
    viewport: { width: 1920, height: 1080 },
  });

  await page.goto(url, { waitUntil: 'networkidle' });

  await page.screenshot({
    path: outputPath,
    fullPage: true,
    type: 'png',
  });

  await browser.close();
}

适用场景:视觉回归测试、页面存档、文档编制

模式 5:生成 PDF
async function generatePDF(url, outputPath) {
  const browser = await chromium.launch();
  const page = await browser.newPage();

  await page.goto(url, { waitUntil: 'networkidle' });

  await page.pdf({
    path: outputPath,
    format: 'A4',
    printBackground: true,
    margin: {
      top: '1cm',
      right: '1cm',
      bottom: '1cm',
      left: '1cm',
    },
  });

  await browser.close();
}

适用场景:生成报告、归档发票、保存内容

模式 6:带验证的表单自动化
async function fillFormWithValidation(page) {
  // Fill fields
  await page.fill('input[name="firstName"]', 'John');
  await page.fill('input[name="lastName"]', 'Doe');
  await page.fill('input[name="email"]', '[email protected]');

  // Handle dropdowns
  await page.selectOption('select[name="country"]', 'US');

  // Handle checkboxes
  await page.check('input[name="terms"]');

  // Wait for validation
  await page.waitForSelector('input[name="email"]:valid');

  // Submit
  await page.click('button[type="submit"]');

  // Wait for success message
  await page.waitForSelector('.success-message', { timeout: 10000 });
}

适用场景:创建账户、测试表单、自动录入数据

模式 7:使用指数退避重试
async function retryWithBackoff(fn, maxRetries = 3) {
  for (let i = 0; i < maxRetries; i++) {
    try {
      return await fn();
    } catch (error) {
      if (i === maxRetries - 1) throw error;

      const delay = Math.pow(2, i) * 1000; // 1s, 2s, 4s
      console.log(`Retry ${i + 1}/${maxRetries} after ${delay}ms`);
      await new Promise(resolve => setTimeout(resolve, delay));
    }
  }
}

// Usage
await retryWithBackoff(async () => {
  await page.goto('https://unreliable-site.com');
});

适用场景:不稳定的网络、受速率限制的 API、不可靠的网站


使用内置资源

模板(templates/)

所有模板均为可直接使用的 TypeScript 文件。请从 ~/.claude/skills/playwright-local/templates/ 复制:

  • basic-scrape.ts - 简单的页面抓取
  • stealth-mode.ts - 完整的隐身配置
  • authenticated-session.ts - 登录 + 抓取模式
  • infinite-scroll.ts - 持续滚动,直到没有新内容
  • screenshot-capture.ts - 整页屏幕截图
  • pdf-generation.ts - 导出 PDF

用法示例:

# Copy template
cp ~/.claude/skills/playwright-local/templates/stealth-mode.ts ./scrape.ts

# Edit for your use case
# Run with tsx
npx tsx scrape.ts
参考资料(references/)

Claude 可按需加载的文档:

  • references/stealth-techniques.md - 完整的反检测指南
  • references/selector-strategies.md - CSS、XPath 和文本选择器
  • references/common-blocks.md - 已知的拦截模式及绕过方法

Claude 应在以下情况下加载这些资料:排查机器人检测、选择器问题或特定网站的拦截时

脚本(scripts/)
  • scripts/install-browsers.sh - 安装所有 Playwright 浏览器

用法:

chmod +x ~/.claude/skills/playwright-local/scripts/install-browsers.sh
~/.claude/skills/playwright-local/scripts/install-browsers.sh

高级主题

Playwright MCP 服务(v1.56+)

Microsoft 提供了官方的 Playwright MCP 服务,用于 AI Agent 集成:

# Initialize AI agent configurations
npx playwright init-agents

这会为以下工具生成配置文件:

  • VS Code - Copilot 集成
  • Claude Desktop - Claude MCP 客户端
  • opencode - 开源 AI 编码工具

主要功能:

  • 使用无障碍树代替屏幕截图(速度更快,也更可靠)
  • 适合大模型的结构化数据格式
  • 与 GitHub Copilot 的 Coding Agent 集成
  • 符合 Model Context Protocol(MCP)规范

MCP 服务设置:

# Install globally
npm install -g @anthropic/mcp-playwright

# Or add to Claude Desktop config
{
  "mcpServers": {
    "playwright": {
      "command": "npx",
      "args": ["@anthropic/mcp-playwright"]
    }
  }
}

适用场景:构建需要浏览器自动化的 AI Agent,将 Playwright 与 Claude 或其他大模型集成。


使用 Speedboard 进行性能分析(v1.57+)

Playwright v1.57 在 HTML 报告器中引入了 Speedboard,这是一个专门用于识别运行缓慢的测试和性能瓶颈的选项卡。

在配置中启用:

export default defineConfig({
  reporter: 'html',
});

查看 Speedboard:

npx playwright test --reporter=html
npx playwright show-report

Speedboard 显示内容:

  • 所有测试按执行时间排序(最慢的排在最前)
  • 等待时间明细
  • 网络请求耗时
  • 帮助识别低效的选择器和不必要的等待

适用场景:

  • 优化测试套件的运行时间
  • 查找包含过多 waitForTimeout() 调用的测试
  • 识别影响测试的慢速 API 响应
  • 优先重构运行最慢的测试

Docker 部署

官方 Docker 镜像提供一致且可复现的环境:

当前镜像(v1.57.0):

FROM mcr.microsoft.com/playwright:v1.57.0-noble

# Create non-root user for security
RUN groupadd -r pwuser && useradd -r -g pwuser pwuser
USER pwuser

WORKDIR /app
COPY --chown=pwuser:pwuser . .

RUN npm ci

CMD ["npx", "playwright", "test"]

可用标签:

  • :v1.57.0-noble - Ubuntu 24.04 LTS(推荐)
  • :v1.57.0-jammy - Ubuntu 22.04 LTS

使用推荐标志运行:

docker run -it --init --ipc=host my-playwright-tests
标志用途
--init防止僵尸进程(处理 PID=1)
--ipc=host防止 Chromium 内存耗尽
--cap-add=SYS_ADMIN仅用于本地开发(启用沙盒)

Python 镜像:

FROM mcr.microsoft.com/playwright/python:v1.57.0-noble

安全注意事项:

  • 始终在容器内创建非 root 用户
  • root 用户会禁用 Chromium 沙盒(存在安全风险)
  • 对不可信网站使用 seccomp 配置文件
  • 固定到特定版本标签(避免使用 :latest)

通过 Bash 工具在 Claude Code 中运行 Playwright

Claude Code 可以编排浏览器自动化:

// scrape.ts
import { chromium } from 'playwright';

async function scrape(url: string) {
  const browser = await chromium.launch({ headless: true });
  const page = await browser.newPage();

  await page.goto(url);
  const data = await page.evaluate(() => {
    return {
      title: document.title,
      headings: Array.from(document.querySelectorAll('h1, h2'))
        .map(el => el.textContent),
    };
  });

  await browser.close();
  console.log(JSON.stringify(data, null, 2));
}

scrape(process.argv[2]);

Claude Code 工作流:

  1. 使用 Bash 工具编写脚本:npx tsx scrape.ts https://example.com
  2. 捕获 JSON 输出
  3. 解析并分析结果
  4. 生成摘要或后续操作
屏幕截图审查工作流
// screenshot-review.ts
import { chromium } from 'playwright';

async function captureForReview(url: string) {
  const browser = await chromium.launch();
  const page = await browser.newPage();

  await page.goto(url);
  await page.screenshot({ path: '/tmp/review.png', fullPage: true });
  await browser.close();

  console.log('Screenshot saved to /tmp/review.png');
}

captureForReview(process.argv[2]);

Claude Code 随后可以:

  1. 通过 Bash 工具运行脚本
  2. 使用 Read 工具读取屏幕截图
  3. 分析视觉布局
  4. 提出改进建议
使用并行浏览器上下文提升速度
import { chromium } from 'playwright';

async function scrapeConcurrently(urls: string[]) {
  const browser = await chromium.launch();

  // Use separate contexts for isolation
  const results = await Promise.all(
    urls.map(async (url) => {
      const context = await browser.newContext();
      const page = await context.newPage();

      await page.goto(url);
      const title = await page.title();

      await context.close();
      return { url, title };
    })
  );

  await browser.close();
  return results;
}

性能提升:并行处理 10 个 URL 所需时间约等于处理 1 个 URL

浏览器指纹识别防护
async function setupStealthContext(browser) {
  const context = await browser.newContext({
    userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
    viewport: { width: 1920, height: 1080 },
    locale: 'en-US',
    timezoneId: 'America/New_York',

    // WebGL fingerprinting defense
    screen: {
      width: 1920,
      height: 1080,
    },

    // Geolocation (if needed)
    geolocation: { longitude: -74.006, latitude: 40.7128 },
    permissions: ['geolocation'],
  });

  return context;
}
处理动态内容加载
async function waitForDynamicContent(page, selector) {
  // Wait for initial element
  await page.waitForSelector(selector);

  // Wait for content to stabilize (no changes for 2s)
  let previousContent = '';
  let stableCount = 0;

  while (stableCount < 4) {
    await page.waitForTimeout(500);
    const currentContent = await page.locator(selector).textContent();

    if (currentContent === previousContent) {
      stableCount++;
    } else {
      stableCount = 0;
    }

    previousContent = currentContent;
  }

  return previousContent;
}

快速参考

选择器策略
策略示例适用场景
CSSpage.click('button.submit')标准 HTML 元素
XPathpage.click('xpath=//button[text()="Submit"]')复杂 DOM 查询
文本page.click('text=Submit')文本唯一时
数据属性page.click('[data-testid="submit"]')测试自动化
第 N 个子元素page.click('ul > li:nth-child(2)')基于位置选择
等待策略
方法使用场景
waitUntil: 'load'所有资源均已加载(默认)
waitUntil: 'domcontentloaded'DOM 已就绪,适合访问速度较慢的网站
waitUntil: 'networkidle'500ms 内没有网络活动(适用于 SPA)
page.waitForSelector(selector)等待元素出现在 DOM 中
page.waitForLoadState('networkidle')导航后等待
page.waitForTimeout(ms)固定延迟(尽可能避免)
浏览器启动选项
选项值用途
headlesstrue/false显示浏览器界面
slowMo100 (ms)放慢速度以便调试
args['--no-sandbox']禁用沙盒(Docker)
executablePath/path/to/chrome使用自定义浏览器
downloadsPath./downloads下载位置
用于隐身的常见启动参数
args: [
  '--disable-blink-features=AutomationControlled',
  '--no-sandbox',
  '--disable-setuid-sandbox',
  '--disable-dev-shm-usage',
  '--disable-accelerated-2d-canvas',
  '--no-first-run',
  '--no-zygote',
  '--single-process',
  '--disable-gpu',
]

依赖项

必需:

  • [email protected] - 核心浏览器自动化库
  • Node.js 20+(不再支持 Node.js 18)或 Python 3.9+ - 运行时

可选(Node.js 隐身):

可选(Python 隐身):

Docker 镜像:

  • mcr.microsoft.com/playwright:v1.57.0-noble - Ubuntu 24.04、Node.js 22 LTS
  • mcr.microsoft.com/playwright/python:v1.57.0-noble - Python 版本

官方文档


软件包版本(2026-01-10 验证)

{
  "devDependencies": {
    "playwright": "^1.57.0",
    "@playwright/test": "^1.57.0",
    "playwright-extra": "^4.3.6",
    "puppeteer-extra-plugin-stealth": "^2.11.2"
  }
}

Python:

playwright==1.57.0
playwright-stealth==0.0.1

生产环境示例

此技能基于生产环境中的网页抓取系统:

  • 每日抓取量:10,000+ 个页面/天
  • 成功率:98%+(含重试)
  • 绕过机器人检测:在受保护网站上的成功率达 95%+
  • 错误:通过上述模式预防了所有 8 个已知问题
  • 验证:✅ 已测试住宅 IP、隐身模式和会话持久化

故障排除

问题:“Executable doesn't exist”错误

解决方案:

npx playwright install chromium
# Or for all browsers:
npx playwright install
问题:Docker 中性能缓慢

解决方案:增加共享内存大小

# In Dockerfile
RUN playwright install --with-deps chromium

# Run with:
docker run --shm-size=2gb your-image
问题:截图为空白

解决方案:等待内容加载

await page.goto(url, { waitUntil: 'networkidle' });
await page.waitForTimeout(1000); // Extra buffer
await page.screenshot({ path: 'output.png' });
问题:“Page crashed”错误

解决方案:降低并发量或增加内存

const browser = await chromium.launch({
  args: ['--disable-dev-shm-usage'], // Use /tmp instead of /dev/shm
});
问题:总是出现验证码

解决方案:

  1. 确认隐身模式已启用(检查 bot.sannysoft.com)
  2. 轮换用户代理
  3. 在操作之间加入随机延迟
  4. 如有需要,使用住宅代理
问题:Ubuntu 25.10 安装失败

错误:Unable to locate package libicu74、Package 'libxml2' has no installation candidate 来源:GitHub Issue #38874 解决方案:

# Use Ubuntu 24.04 Docker image (officially supported)
docker pull mcr.microsoft.com/playwright:v1.57.0-noble

# Or wait for Ubuntu 25.10 support in future releases
# Track issue: https://github.com/microsoft/playwright/issues/38874

临时解决方法(如果无法使用 Docker):

# Manually install compatible libraries
sudo apt-get update
sudo apt-get install libicu72 libxml2

完整设置检查清单

使用此清单验证设置:

  • 已安装 Playwright(npm list playwright 或 pip show playwright)
  • 已下载浏览器(npx playwright install chromium)
  • 基础脚本运行成功
  • 已配置隐身模式(如有需要)
  • 会话持久化正常
  • 截图已正确保存
  • 错误处理包含重试
  • 浏览器已正确关闭(没有僵尸进程)
  • 首先使用 headless: false 进行了测试
  • 生产脚本使用 headless: true

有疑问?遇到问题?

  1. 查看 references/common-blocks.md,了解特定网站的拦截
  2. 在 https://bot.sannysoft.com/ 验证隐身设置
  3. 查看官方文档:https://playwright.dev/docs/intro
  4. 确保已安装浏览器二进制文件:npx playwright install chromium

最后验证时间:2026-01-21 | 技能版本:3.1.0 | 变更:新增 2 个关键 CI 问题(page.pause() 超时、扩展程序权限提示),v1.57 功能(Speedboard、webServer 等待配置),Ubuntu 25.10 兼容性指南

评论

支持 Markdown