mcpField
P
puppeteer-automation

puppeteer-automation

github.com/mindrally開発2871.8k 回インストール3 週間前に更新
リポジトリ

puppeteer-automation について

Puppeteer によるブラウザー自動化、Web スクレイピング、Chrome または Chromium のスクリプト作成を支援するスキルです。ページ操作やキャプチャ、ネットワーク制御、ブラウザー管理を扱います。

主な機能

  1. 01ページ遷移と要素操作
  2. 02スクリーンショットと PDF の生成
  3. 03ネットワークリクエストのインターセプト
  4. 04認証と Cookie の処理
  5. 05待機・リトライ・エラー処理
  6. 06ブラウザーコンテキストと複数ページ

活用シーン

  1. 01フォーム入力やページ操作の自動化
  2. 02ページのテキストや属性の抽出
  3. 03ページを画像や PDF として保存
  4. 04スクレイピング中のリクエスト制御

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/mindrally/skills --skill puppeteer-automation

インストール後、Claude Code で/puppeteer-automationを入力して読み込まれたか確認します。

詳細

Puppeteer ブラウザー自動化

Puppeteer、Node.js によるブラウザー自動化、ウェブスクレイピング、Chrome および Chromium ブラウザー向けの信頼性の高い自動化スクリプトの構築に精通しています。

主な専門知識

  • Puppeteer API とブラウザー自動化のパターン
  • ページのナビゲーションと操作
  • 要素の選択と操作
  • スクリーンショットと PDF の生成
  • ネットワークリクエストのインターセプト
  • ヘッドレスおよびヘッドフルのブラウザーモード
  • パフォーマンスの最適化とメモリ管理
  • テストフレームワーク(Jest、Mocha)との統合

主な原則

  • 読みやすさを重視し、async/await ベースのクリーンなコードを書く
  • try/catch ブロックで適切にエラーを処理する
  • 動的コンテンツに対応した堅牢な待機方法を実装する
  • メモリーリークを防ぐため、ブラウザーインスタンスを適切に閉じる
  • 再利用可能な自動化コードにはモジュール設計パターンを採用する
  • ブラウザーコンテキストとページのライフサイクルを適切に扱う

プロジェクトのセットアップ

npm init -y
npm install puppeteer
基本構成
const puppeteer = require('puppeteer');

async function main() {
  const browser = await puppeteer.launch({
    headless: 'new',
    args: ['--no-sandbox', '--disable-setuid-sandbox']
  });

  try {
    const page = await browser.newPage();
    await page.goto('https://example.com');
    // Your automation code here
  } finally {
    await browser.close();
  }
}

main().catch(console.error);

ブラウザーの起動オプション

const browser = await puppeteer.launch({
  headless: 'new',  // 'new' for new headless mode, false for visible browser
  slowMo: 50,       // Slow down operations for debugging
  devtools: true,   // Open DevTools automatically
  args: [
    '--no-sandbox',
    '--disable-setuid-sandbox',
    '--disable-dev-shm-usage',
    '--disable-accelerated-2d-canvas',
    '--disable-gpu',
    '--window-size=1920,1080'
  ],
  defaultViewport: {
    width: 1920,
    height: 1080
  }
});

ページのナビゲーション

// Navigate to URL
await page.goto('https://example.com', {
  waitUntil: 'networkidle2',  // Wait until network is idle
  timeout: 30000
});

// Wait options:
// - 'load': Wait for load event
// - 'domcontentloaded': Wait for DOMContentLoaded event
// - 'networkidle0': No network connections for 500ms
// - 'networkidle2': No more than 2 network connections for 500ms

// Navigate back/forward
await page.goBack();
await page.goForward();

// Reload page
await page.reload({ waitUntil: 'networkidle2' });

要素の選択

クエリーセレクター
// Single element
const element = await page.$('selector');

// Multiple elements
const elements = await page.$$('selector');

// Wait for element
const element = await page.waitForSelector('selector', {
  visible: true,
  timeout: 5000
});

// XPath selection
const elements = await page.$x('//xpath/expression');
ページコンテキストでの評価
// Get text content
const text = await page.$eval('selector', el => el.textContent);

// Get attribute
const href = await page.$eval('a', el => el.getAttribute('href'));

// Multiple elements
const texts = await page.$$eval('.items', elements =>
  elements.map(el => el.textContent)
);

// Execute arbitrary JavaScript
const result = await page.evaluate(() => {
  return document.title;
});

ページの操作

クリック
await page.click('button#submit');

// Click with options
await page.click('button', {
  button: 'left',  // 'left', 'right', 'middle'
  clickCount: 1,
  delay: 100       // Time between mousedown and mouseup
});

// Click and wait for navigation
await Promise.all([
  page.waitForNavigation(),
  page.click('a.nav-link')
]);
入力
// Type text
await page.type('input#username', 'myuser', { delay: 50 });

// Clear and type
await page.click('input#username', { clickCount: 3 });
await page.type('input#username', 'newvalue');

// Press keys
await page.keyboard.press('Enter');
await page.keyboard.down('Shift');
await page.keyboard.press('Tab');
await page.keyboard.up('Shift');
フォームの操作
// Select dropdown
await page.select('select#country', 'us');

// Check checkbox
await page.click('input[type="checkbox"]');

// File upload
const inputFile = await page.$('input[type="file"]');
await inputFile.uploadFile('/path/to/file.pdf');

待機方法

// Wait for selector
await page.waitForSelector('.loaded');

// Wait for selector to disappear
await page.waitForSelector('.loading', { hidden: true });

// Wait for function
await page.waitForFunction(
  () => document.querySelector('.count').textContent === '10'
);

// Wait for navigation
await page.waitForNavigation({ waitUntil: 'networkidle2' });

// Wait for network request
await page.waitForRequest(request =>
  request.url().includes('/api/data')
);

// Wait for network response
await page.waitForResponse(response =>
  response.url().includes('/api/data') && response.status() === 200
);

// Fixed timeout (use sparingly)
await page.waitForTimeout(1000);

スクリーンショットと PDF

スクリーンショット
// Full page screenshot
await page.screenshot({
  path: 'screenshot.png',
  fullPage: true
});

// Element screenshot
const element = await page.$('.chart');
await element.screenshot({ path: 'chart.png' });

// Screenshot options
await page.screenshot({
  path: 'screenshot.png',
  type: 'png',  // 'png' or 'jpeg'
  quality: 80,   // jpeg only, 0-100
  clip: {
    x: 0,
    y: 0,
    width: 800,
    height: 600
  }
});
PDF の生成
await page.pdf({
  path: 'document.pdf',
  format: 'A4',
  printBackground: true,
  margin: {
    top: '20px',
    right: '20px',
    bottom: '20px',
    left: '20px'
  }
});

ネットワークのインターセプト

// Enable request interception
await page.setRequestInterception(true);

page.on('request', request => {
  // Block images and stylesheets
  if (['image', 'stylesheet'].includes(request.resourceType())) {
    request.abort();
  } else {
    request.continue();
  }
});

// Modify requests
page.on('request', request => {
  request.continue({
    headers: {
      ...request.headers(),
      'X-Custom-Header': 'value'
    }
  });
});

// Monitor responses
page.on('response', async response => {
  if (response.url().includes('/api/')) {
    const data = await response.json();
    console.log('API Response:', data);
  }
});

認証と Cookie

// Basic HTTP authentication
await page.authenticate({
  username: 'user',
  password: 'pass'
});

// Set cookies
await page.setCookie({
  name: 'session',
  value: 'abc123',
  domain: 'example.com'
});

// Get cookies
const cookies = await page.cookies();

// Clear cookies
await page.deleteCookie({ name: 'session' });

ブラウザーコンテキストと複数ページ

// Create incognito context
const context = await browser.createIncognitoBrowserContext();
const page = await context.newPage();

// Multiple pages
const page1 = await browser.newPage();
const page2 = await browser.newPage();

// Get all pages
const pages = await browser.pages();

// Handle popups
page.on('popup', async popup => {
  await popup.waitForLoadState();
  console.log('Popup URL:', popup.url());
});

エラー処理

async function scrapeWithRetry(url, maxRetries = 3) {
  for (let i = 0; i < maxRetries; i++) {
    try {
      const browser = await puppeteer.launch();
      const page = await browser.newPage();

      // Set timeout
      page.setDefaultTimeout(30000);

      await page.goto(url, { waitUntil: 'networkidle2' });
      const data = await page.$eval('.content', el => el.textContent);

      await browser.close();
      return data;
    } catch (error) {
      console.error(`Attempt ${i + 1} failed:`, error.message);
      if (i === maxRetries - 1) throw error;
      await new Promise(r => setTimeout(r, 2000 * (i + 1)));
    }
  }
}

パフォーマンスの最適化

// Disable unnecessary features
await page.setRequestInterception(true);
page.on('request', request => {
  const blockedTypes = ['image', 'stylesheet', 'font'];
  if (blockedTypes.includes(request.resourceType())) {
    request.abort();
  } else {
    request.continue();
  }
});

// Reuse browser instance
const browser = await puppeteer.launch();

async function scrape(url) {
  const page = await browser.newPage();
  try {
    await page.goto(url);
    // ... scraping logic
  } finally {
    await page.close();  // Close page, not browser
  }
}

// Use connection pool for parallel scraping
const cluster = require('puppeteer-cluster');

主な依存関係

  • puppeteer
  • puppeteer-core(カスタムの Chrome インストール用)
  • puppeteer-cluster(並列スクレイピング用)
  • puppeteer-extra(プラグイン用)
  • puppeteer-extra-plugin-stealth(検出回避用)

ベストプラクティス

  1. ブラウザーインスタンスは必ず finally ブロックで閉じる
  2. 要素を操作する前に waitForSelector を使用する
  3. 読み込みを高速化するには、networkidle0 より networkidle2 を優先する
  4. ボット対策の回避には stealth プラグインを使用する
  5. 適切なエラー処理と再試行を実装する
  6. 長時間実行するスクリプトではメモリ使用量を監視する
  7. 分離されたセッションにはブラウザーコンテキストを使用する
  8. すべての操作に適切なタイムアウトを設定する

コメント