playwright-web-scraper について
Playwright Web Scraperは、Playwright MCPのブラウザツールで複数ページから構造化データを抽出するスキルです。URLとrobots.txtの確認、速度を抑えたクロール、エラー処理を扱い、JSON、CSV、Markdownに出力します。
主な機能
- 01URLとrobots.txtの検証
- 02複数ページの速度制限付きクロール
- 03JavaScriptによるデータ抽出
- 04ネットワークエラーと制限への対応
- 05JSON、CSV、Markdown出力
活用シーン
- 01複数ページの商品一覧の収集
- 02表やページ内の構造化項目の抽出
- 03分析用データへの変換
MCP 対応
Playwright MCPのブラウザツールを使い、ページの移動、内容の確認、データ抽出を行います。MCPサーバーを提供するとの記載はありません。
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ dawiddutoit/ custom-claude --skill playwright-web-scraperインストール後、Claude Code で/playwright-web-scraperを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
references/ethical-scraping.mdextraction-patterns.mdscripts/process_results.pyvalidate_urls.py詳細
Playwright Web Scraper
礼儀正しく倫理的なクローリングを行い、複数の Web ページから構造化データを抽出します。
このスキルを使用する場面
「scrape data from」「extract information from pages」「collect data from site」「crawl multiple pages」といった表現で、Web サイトから構造化データを抽出する場合に使用します。
ワークフローのテストには使用しないでください(playwright-e2e-testingを使用してください)。エラーの監視には使用しないでください(playwright-console-monitorを使用してください)。ネットワークの分析には使用しないでください(playwright-network-analyzerを使用してください)。必ず robots.txt とレート制限を尊重してください。
クイックスタート
eコマースサイトの商品一覧をスクレイピングします。
// 1. Validate URLs
python scripts/validate_urls.py urls.txt
// 2. Scrape pages with rate limiting
const results = [];
for (const url of urls) {
await browser_navigate({ url });
await browser_wait_for({ time: Math.random() * 2 + 1 }); // 1-3s delay
const data = await browser_evaluate({
function: `
Array.from(document.querySelectorAll('.product')).map(el => ({
title: el.querySelector('.title')?.textContent?.trim(),
price: el.querySelector('.price')?.textContent?.trim(),
url: el.querySelector('a')?.getAttribute('href')
}))
`
});
results.push(...data);
}
// 3. Process results
python scripts/process_results.py scraped.json -o products.csv
目次
- 基本的なワークフロー
- レート制限の戦略
- URL の検証
- データの抽出
- エラー処理
- 結果の処理
- 関連ファイル
- 想定される結果
基本的なワークフロー
ステップ 1:URL リストを準備する
スクレイピング対象の URL を記載したテキストファイルを作成します(1 行に 1 件)。
https://example.com/products?page=1
https://example.com/products?page=2
https://example.com/products?page=3
URL を検証し、robots.txt に準拠しているか確認します。
python scripts/validate_urls.py urls.txt --user-agent "MyBot/1.0"
ステップ 2:スクレイピングセッションを初期化する
サイトに移動し、スナップショットを取得して構造を把握します。
await browser_navigate({ url: firstUrl });
await browser_snapshot();
スナップショットを使って、データ抽出用の CSS セレクターを特定します。
ステップ 3:レート制限付きのクローリングを実装する
リクエストの間にランダムな遅延を設けます(最低 1~3 秒)。
const results = [];
for (const url of urlList) {
// Navigate to page
await browser_navigate({ url });
// Wait for content to load
await browser_wait_for({ text: 'Expected content marker' });
// Add respectful delay (1-3 seconds)
const delay = Math.random() * 2 + 1;
await browser_wait_for({ time: delay });
// Extract data
const pageData = await browser_evaluate({
function: `/* extraction code */`
});
results.push(...pageData);
// Check console for errors/warnings
const console = await browser_console_messages();
// Monitor for rate limit warnings
}
ステップ 4:構造化データを抽出する
browser_evaluateを使い、JavaScript でデータを抽出します。
const data = await browser_evaluate({
function: `
try {
return Array.from(document.querySelectorAll('.item')).map(el => ({
title: el.querySelector('.title')?.textContent?.trim(),
price: el.querySelector('.price')?.textContent?.trim(),
rating: el.querySelector('.rating')?.textContent?.trim(),
url: el.querySelector('a')?.getAttribute('href')
})).filter(item => item.title && item.price); // Filter incomplete records
} catch (e) {
console.error('Extraction failed:', e);
return [];
}
`
});
包括的な抽出パターンについては、references/extraction-patterns.mdを参照してください。
ステップ 5:エラーとレート制限に対処する
レート制限を示す兆候を監視します。
// Check HTTP responses via browser_network_requests
const requests = await browser_network_requests();
const rateLimited = requests.some(r => r.status === 429 || r.status === 503);
if (rateLimited) {
// Back off exponentially
await browser_wait_for({ time: 10 }); // Wait 10 seconds
// Retry or skip
}
// Check console for blocking messages
const console = await browser_console_messages({ pattern: 'rate limit|blocked|captcha' });
if (console.length > 0) {
// Handle blocking
}
ステップ 6:結果を集約して保存する
結果を JSON ファイルに保存します。
// In your scraping script
fs.writeFileSync('scraped.json', JSON.stringify({ results }, null, 2));
処理して、希望する形式に変換します。
# View statistics
python scripts/process_results.py scraped.json --stats
# Convert to CSV
python scripts/process_results.py scraped.json -o output.csv
# Convert to Markdown table
python scripts/process_results.py scraped.json -o output.md
レート制限の戦略
最小遅延時間
リクエストの間には、必ず遅延を設けてください。
- 一般的なサイト:1~3 秒(ランダム)
- アクセス数の多いサイト:3~5 秒
- 小規模なサイト:5~10 秒
- エラー発生後:指数バックオフ(5 秒、10 秒、20 秒、40 秒)
実装
// Random delay between 1-3 seconds
const randomDelay = () => Math.random() * 2 + 1;
await browser_wait_for({ time: randomDelay() });
// Exponential backoff after rate limit
let backoffSeconds = 5;
for (let retry = 0; retry < 3; retry++) {
try {
await browser_navigate({ url });
break; // Success
} catch (e) {
await browser_wait_for({ time: backoffSeconds });
backoffSeconds *= 2; // Double delay each retry
}
}
適応型レート制限
応答に応じて遅延時間を調整します。
| 応答コード | 対応 |
|---|---|
| 200 OK | 通常の遅延(1~3 秒)で続行 |
| 429 Too Many Requests | 遅延を 10 秒に延長して再試行 |
| 503 Service Unavailable | 60 秒待ってから再試行 |
| 403 Forbidden | このドメインのスクレイピングを中止 |
レート制限の戦略について詳しくは、references/ethical-scraping.mdを参照してください。
URL の検証
スクレイピングを開始する前にvalidate_urls.pyを使用して、準拠状況を確認します。
# Basic validation
python scripts/validate_urls.py urls.txt
# Check robots.txt with specific user agent
python scripts/validate_urls.py urls.txt --user-agent "MyBot/1.0"
# Strict mode (exit on any invalid/disallowed URL)
python scripts/validate_urls.py urls.txt --strict
出力に含まれる内容:
- URL 形式の検証
- ドメイン別のグループ化
- robots.txt への準拠状況の確認
- 統計の概要
データの抽出
基本パターン
// Single page extraction
const data = await browser_evaluate({
function: `
Array.from(document.querySelectorAll('.item')).map(el => ({
field1: el.querySelector('.selector1')?.textContent?.trim(),
field2: el.querySelector('.selector2')?.getAttribute('href')
}))
`
});
ページネーションパターン
let hasMore = true;
let page = 1;
while (hasMore) {
await browser_navigate({ url: `${baseUrl}?page=${page}` });
await browser_wait_for({ time: randomDelay() });
const pageData = await browser_evaluate({ function: extractionCode });
results.push(...pageData);
// Check for next page
hasMore = await browser_evaluate({
function: `document.querySelector('.next:not(.disabled)') !== null`
});
page++;
}
以下については、references/extraction-patterns.mdを参照してください。
- 高度なセレクター
- データのクリーニングパターン
- テーブルの抽出
- JSON-LD の抽出
- Shadow DOM へのアクセス
エラー処理
ネットワークエラー
try {
await browser_navigate({ url });
} catch (e) {
console.error(`Failed to load ${url}:`, e);
failedUrls.push(url);
continue; // Skip to next URL
}
コンテンツの検証
const data = await browser_evaluate({ function: extractionCode });
if (!data || data.length === 0) {
console.warn(`No data extracted from ${url}`);
// Log for manual review
}
// Validate data structure
const validData = data.filter(item =>
item.title && item.price // Ensure required fields exist
);
監視する兆候
ブロックやエラーがないか確認します。
// Monitor console
const console = await browser_console_messages({
pattern: 'error|rate|limit|captcha',
onlyErrors: true
});
if (console.length > 0) {
console.log('Warnings detected:', console);
}
// Monitor network
const requests = await browser_network_requests();
const errors = requests.filter(r => r.status >= 400);
結果の処理
統計を表示する
python scripts/process_results.py scraped.json --stats
出力:
📊 Statistics:
Total records: 150
Fields (5): title, price, rating, url, image
Sample record: {...}
形式を変換する
# To CSV
python scripts/process_results.py scraped.json -o products.csv
# To JSON (compact)
python scripts/process_results.py scraped.json -o products.json --compact
# To Markdown table
python scripts/process_results.py scraped.json -o products.md
統計の表示と変換を組み合わせる
python scripts/process_results.py scraped.json -o products.csv --stats
関連ファイル
スクリプト
scripts/validate_urls.py- URL リストを検証し、robots.txt への準拠状況を確認して、ドメイン別にグループ化しますscripts/process_results.py- スクレイピングした JSON を CSV/JSON/Markdown に変換し、統計を表示します
リファレンス
references/ethical-scraping.md- レート制限、robots.txt、エラー処理、監視に関する包括的なガイドreferences/extraction-patterns.md- データ抽出、セレクター、ページネーション、テーブルに関する JavaScript パターン
想定される結果
スクレイピング成功時
✅ Validated 50 URLs
✅ Scraped 50 pages in 5 minutes (6 req/min)
✅ Extracted 1,250 products
✅ Zero rate limit errors
✅ Exported to products.csv (1,250 rows)
エラー処理を含む場合
⚠️ Validated 50 URLs (2 disallowed by robots.txt)
✅ Scraped 48 pages
⚠️ 3 pages returned no data (logged for review)
✅ Extracted 1,100 products
⚠️ 1 rate limit warning (backed off successfully)
✅ Exported to products.csv (1,100 rows)
レート制限の検出
❌ Rate limited after 20 pages (429 responses)
✅ Backed off exponentially (5s → 10s → 20s)
✅ Resumed scraping successfully
✅ Extracted 450 products from 25 pages
期待される効果
| 指標 | 導入前 | 導入後 |
|---|---|---|
| セットアップ時間 | 30-45 分 | 5-10 分 |
| レート制限エラー | 頻繁 | まれ |
| robots.txt 違反 | 発生する可能性あり | 防止 |
| データ形式の変換 | 手動 | 自動 |
| エラー検出 | 手動レビュー | 自動監視 |
成功指標
- 成功率 > 95%(スクレイピングに成功したページ)
- レート制限エラー リクエストの < 5%
- 有効なデータの割合 > 90%(完全なレコード)
- スクレイピング速度 6-12 リクエスト/分(配慮したクローリング)
要件
ツール
- Playwright MCP ブラウザツール
- Python 3.8+(スクリプト用)
- 標準ライブラリのみ(スクリプトに外部依存関係なし)
知識
- CSS セレクターの基礎
- データ抽出のための JavaScript
- HTTP ステータスコードの理解
- ウェブスクレイピングの倫理に関する認識
避けるべき危険信号
- ❌ robots.txt を確認せずにスクレイピングする
- ❌ リクエスト間に待機時間を設けない(サーバーに過剰な負荷をかける)
- ❌ 429/503 レスポンスコードを無視する
- ❌ 個人情報やプライベート情報をスクレイピングする
- ❌ ブロックを示すメッセージがないかコンソールを監視しない
- ❌ 明示的に禁止しているサイトをスクレイピングする(利用規約を確認する)
- ❌ 著作権に違反する形でスクレイピングしたデータを使用する
- ❌ ページネーションを正しく処理せず、データを取りこぼす
- ❌ フォールバックなしでセレクターをハードコードする
- ❌ 抽出したデータ構造を検証しない
注意事項
- 配慮したクローリングを基本とします:最低 1-3 秒の待機時間を設け、サイトの応答に応じて調整します
- 必ず最初に robots.txt を確認します:スクレイピング前に
validate_urls.pyを使用します - コンソールとネットワークを監視します:レート制限の警告に注意し、待機時間を調整します
- 小規模から始めます:数百件に拡大する前に、5-10 件の URL でテストします
- 進行状況を保存します:中断に備え、結果を逐次書き込みます
- 利用規約を尊重します:利用規約でスクレイピングを禁止しているサイトもあります
- 説明的なユーザーエージェントを使用します:ボットであることが明確にわかるようにします
- エラーを適切に処理します:失敗を記録して手動レビューに回し、クラッシュさせないようにします