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 は Chromium の代わりに Chrome for Testing ビルドを使用するようになりました。これにより、より本物に近いブラウザーの動作が得られますが、ブラウザーのアイコンとタイトルバーが変わります。


クイックスタート(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)データセンター(簡単に検出可能)
ステルスプラグイン完全にサポート利用不可
レート制限なし無料枠は 1 日あたり 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 として検出される
  • プラグインが設定されている
  • ページに危険を示す項目がない

重要なルール

必ず行うこと

✅ SPA(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 発生する理由:SPA のナビゲーションによって要素が再レンダリングされました 防止策:

// Re-query element after navigation
async function safeClick(page, selector) {
  await page.waitForSelector(selector);
  await page.click(selector);
  await page.waitForLoadState('networkidle');
}
問題 #5:ボット検出(403/Captcha)

エラー:ページが 403 を返す、または CAPTCHA を表示する 出典: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;
}
Issue #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'],
});
Issue #9:ヘッドレスモードでは page.pause() によってタイムアウトが無効になる

エラー:page.pause() が存在すると、CI でテストが無期限に停止します ソース:GitHub Issue #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 パイプラインが無期限に停止する可能性があります

Issue #10:権限の確認ダイアログが CI での拡張機能テストを妨げる

エラー:ブラウザー拡張機能のテスト中、権限の確認ダイアログでテストが停止します ソース:GitHub Issue #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 は、AI エージェントとの連携に使用できる公式の Playwright MCP サーバーを提供しています。

# Initialize AI agent configurations
npx playwright init-agents

次の設定ファイルが生成されます。

  • VS Code - Copilot との連携
  • Claude Desktop - Claude MCP クライアント
  • opencode - オープンソースの AI コーディングツール

主な機能:

  • スクリーンショットの代わりにアクセシビリティツリーを使用(より高速で、信頼性が向上)
  • LLM に適した構造化データ形式
  • 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 エージェントを構築するときや、Playwright を Claude などの LLM と連携するときに使用します。


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=hostChromium のメモリ不足を防ぎます
--cap-add=SYS_ADMINローカル開発専用(サンドボックスを有効化)

Python イメージ:

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

セキュリティに関する注意事項:

  • コンテナ内には必ず root 以外のユーザーを作成してください
  • root ユーザーでは Chromium のサンドボックスが無効になり、セキュリティリスクが生じます
  • 信頼できない Web サイトには 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;
}

パフォーマンスの向上:URL 10 件を並列処理した場合、1 件の場合とほぼ同じ時間で完了します

ブラウザーフィンガープリント対策
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"]')テストの自動化
Nth childpage.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ブラウザー UI を表示します
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

本番環境での例

このスキルは、本番環境で稼働するウェブスクレイピングシステムをベースにしています:

  • 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 | 変更点:重要な CI の問題を 2 件追加(page.pause() のタイムアウト、拡張機能の権限プロンプト)、v1.57 の機能(Speedboard、webServer の wait 設定)、Ubuntu 25.10 の互換性に関するガイダンス

コメント