Playwright: ネットスクレイピングとテスト

テストおよびスクレイピング用のブラウザーオートメーションをマスターしましょう

目次

Playwright は、ウェブスクレイピングやエンドツーエンドテストを革命的に変える、強力で現代的なブラウザ自動化フレームワークです。

Microsoft が開発したこのフレームワークは、Chromium(Chrome および Edge も含む)、Firefox、WebKit(Safari)のブラウザを統一された API で自動化でき、かつてない信頼性と速度を実現しています。

playwright ui

Playwright とは?

Playwright は、開発者が信頼性の高いエンドツーエンドテストを書くことや、高度なウェブスクレイピングソリューションを構築することを可能にするオープンソースのブラウザ自動化フレームワークです。従来の自動化ツールとは異なり、Playwright は動的なコンテンツ、シングルページアプリケーション(SPAs)、複雑な JavaScript フレームワークを扱う現代のウェブアプリケーションのために、根本から構築されています。

このフレームワークは、以前の自動化ツールが直面した主要な問題である「不安定性」に取り組んでいます。Playwright は、操作を行う前に要素がアクション可能になるまで自動的に待つメカニズムを備えており、テストの信頼性を著しく高め、任意のタイムアウトや sleep 文を必要としません。

主な特徴

クロスブラウザサポート: Playwright は、Chromium(Chrome および Edge も含む)、Firefox、WebKit(Safari)のすべての主要なブラウザエンジンをサポートしています。これにより、一度の自動化スクリプトを書くだけで、ブラウザの変更なしにさまざまなブラウザで実行でき、ウェブアプリケーションがどこでも一貫して動作することを保証します。

自動待機: Playwright の最も強力な機能の一つは、組み込みの自動待機メカニズムです。任意の操作を行う前に、Playwright は要素が表示され、有効で、安定し、隠れていないことを自動的に待機します。これにより、Selenium のようなツールで明示的な待機が必要な場合に比べて、テストがはるかに信頼性が高まります。

ネットワークの挿入: Playwright は、ネットワークリクエストとレスポンスを挿入、変更、モック化することができます。これは、エッジケースのテスト、遅いネットワークのシミュレーション、スクレイピング中の不要なリソースのブロック、バックエンドを必要としない API レスポンスのモック化に非常に役立ちます。

モバイルエミュレーション: 特定のビューポートサイズ、ユーザーエージェント、タッチイベントを持つさまざまなモバイルデバイスでモバイルウェブアプリケーションをテストできます。Playwright には、人気のあるスマートフォンやタブレットのデバイス記述子が含まれています。

強力なセレクター: CSS および XPath セレクターに加えて、Playwright はテキストセレクター、アクセシビリティテスト用の役割ベースセレクター、さらには React および Vue のコンポーネントベースフレームワーク用の実験的なセレクターもサポートしています。

インストールとセットアップ

Playwright のセットアップは、さまざまなプログラミング言語で簡単にできます。

Python インストール

Python プロジェクトでは、pip を使用して Playwright をインストールし、同期および非同期 API の両方を含みます。より高速で現代的な Python パッケージマネージャーを探している場合は、uv - Python パッケージ、プロジェクト、環境マネージャー に関するガイドをご覧ください:

# Playwright パッケージのインストール
pip install playwright

# ブラウザのインストール(Chromium、Firefox、WebKit)
playwright install

# 特定のブラウザのみ
playwright install chromium

Playwright と連携して作業する際の Python 構文とよく使われるコマンドの包括的なリファレンスについては、Python クイックリファレンス をご参照ください。

JavaScript/TypeScript インストール

Node.js プロジェクトでは、npm または yarn を使用して Playwright をインストールします:

# npm を使用
npm init playwright@latest

# yarn を使用
yarn create playwright

# 手動インストール
npm install -D @playwright/test
npx playwright install

npm init playwright コマンドは、プロジェクトに例のテスト、設定ファイル、GitHub Actions ワークフローを設定するインタラクティブなセットアップを提供します。

基本的な設定

playwright.config.ts(TypeScript)または playwright.config.js(JavaScript)ファイルを作成します:

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

export default defineConfig({
  testDir: './tests',
  timeout: 30000,
  retries: 2,
  workers: 4,
  use: {
    headless: true,
    viewport: { width: 1280, height: 720 },
    screenshot: 'only-on-failure',
    video: 'retain-on-failure',
  },
  projects: [
    {
      name: 'chromium',
      use: { ...devices['Desktop Chrome'] },
    },
    {
      name: 'firefox',
      use: { ...devices['Desktop Firefox'] },
    },
    {
      name: 'webkit',
      use: { ...devices['Desktop Safari'] },
    },
  ],
});

Playwright を使用したウェブスクレイピング

Playwright は、伝統的なスクレイピングライブラリが苦手とする動的なコンテンツを持つ現代のウェブサイトで特に優れています。

基本的なスクレイピング例

以下は、コアなスクレイピングコンセプトを示す Python の包括的な例です:

from playwright.sync_api import sync_playwright
import json

def scrape_website():
    with sync_playwright() as p:
        # ブラウザの起動
        browser = p.chromium.launch(headless=True)
        
        # 隔離のためのコンテキストの作成
        context = browser.new_context(
            viewport={'width': 1920, 'height': 1080},
            user_agent='Mozilla/5.0 (Windows NT 10.0; Win64; x64)'
        )
        
        # 新しいページの作成
        page = context.new_page()
        
        # URL への移動
        page.goto('https://example.com/products')
        
        # コンテンツの読み込みを待機
        page.wait_for_selector('.product-item')
        
        # データの抽出
        products = page.query_selector_all('.product-item')
        
        data = []
        for product in products:
            title = product.query_selector('h2').inner_text()
            price = product.query_selector('.price').inner_text()
            url = product.query_selector('a').get_attribute('href')
            
            data.append({
                'title': title,
                'price': price,
                'url': url
            })
        
        # クリーンアップ
        browser.close()
        
        return data

# スクレイパーの実行
results = scrape_website()
print(json.dumps(results, indent=2))

動的コンテンツの処理

現代のウェブサイトでは、JavaScript を介してコンテンツが動的に読み込まれることがよくあります。Playwright はこの処理をスムーズに行います:

async def scrape_dynamic_content():
    async with async_playwright() as p:
        browser = await p.chromium.launch()
        page = await browser.new_page()
        
        await page.goto('https://example.com/infinite-scroll')
        
        # より多くのコンテンツを読み込むためにスクロール
        for _ in range(5):
            await page.evaluate('window.scrollTo(0, document.body.scrollHeight)')
            await page.wait_for_timeout(2000)
        
        # ネットワークがアイドル状態になるのを待つ
        await page.wait_for_load_state('networkidle')
        
        # すべての読み込まれたアイテムの抽出
        items = await page.query_selector_all('.item')
        
        await browser.close()

抽出されたコンテンツを Markdown に変換

Playwright を使って HTML コンテンツを抽出した後、より使いやすい形式に変換する必要があります。HTML を Markdown に変換する方法については、Python で HTML を Markdown に変換する:包括的なガイド で6つの異なる Python ライブラリを比較し、LLM と Ollama を使用して HTML コンテンツを Markdown に変換 で AI による変換方法を紹介しています。Word ドキュメントを処理している場合は、Word ドキュメントを Markdown に変換 に関するガイドをご覧ください。

認証とセッション管理

認証が必要なスクレイピングでは、Playwright はブラウザの状態を簡単に保存して再利用できます:

def login_and_save_session():
    with sync_playwright() as p:
        browser = p.chromium.launch(headless=False)
        context = browser.new_context()
        page = context.new_page()
        
        # ログイン
        page.goto('https://example.com/login')
        page.fill('input[name="username"]', 'your_username')
        page.fill('input[name="password"]', 'your_password')
        page.click('button[type="submit"]')
        
        # ログイン後のナビゲーションの待機
        page.wait_for_url('**/dashboard')
        
        # 認証された状態を保存
        context.storage_state(path='auth_state.json')
        
        browser.close()

def scrape_with_saved_session():
    with sync_playwright() as p:
        browser = p.chromium.launch()
        # 保存された認証状態を再利用
        context = browser.new_context(storage_state='auth_state.json')
        page = context.new_page()
        
        # すでに認証済み!
        page.goto('https://example.com/protected-data')
        # ... 保護されたコンテンツのスクレイピング
        
        browser.close()

このアプローチは、API を使用するか、AI の統合のために MCP サーバーを構築する際に特に役立ちます。AI ツールの統合でウェブスクレイピングを実装するための完全なガイドについては、Python で MCP サーバーを構築:WebSearch & Scrape に関する記事をご覧ください。

エンドツーエンドテスト

Playwright の主な用途は、ウェブアプリケーションに信頼性の高いエンドツーエンドテストを書くことです。

最初のテストの作成

以下は、TypeScript での完全なテスト例です:

import { test, expect } from '@playwright/test';

test('user can add item to cart', async ({ page }) => {
  // ホームページへの移動
  await page.goto('https://example-shop.com');
  
  // 商品の検索
  await page.fill('[data-testid="search-input"]', 'laptop');
  await page.press('[data-testid="search-input"]', 'Enter');
  
  // 検索結果の待機
  await expect(page.locator('.product-card')).toBeVisible();
  
  // 最初の商品のクリック
  await page.locator('.product-card').first().click();
  
  // 商品ページの読み込みを確認
  await expect(page).toHaveURL(/\/product\/.+/);
  
  // カートへの追加
  await page.click('[data-testid="add-to-cart"]');
  
  // カートの更新を確認
  const cartCount = page.locator('[data-testid="cart-count"]');
  await expect(cartCount).toHaveText('1');
});

ページオブジェクトモデル

より大きなテストスイートでは、メンテナンス性を向上させるためにページオブジェクトモデルパターンを使用します:

// pages/LoginPage.ts
export class LoginPage {
  constructor(private page: Page) {}
  
  async navigate() {
    await this.page.goto('/login');
  }
  
  async login(username: string, password: string) {
    await this.page.fill('[name="username"]', username);
    await this.page.fill('[name="password"]', password);
    await this.page.click('button[type="submit"]');
  }
  
  async getErrorMessage() {
    return await this.page.locator('.error-message').textContent();
  }
}

// tests/login.spec.ts
import { test, expect } from '@playwright/test';
import { LoginPage } from '../pages/LoginPage';

test('login with invalid credentials shows error', async ({ page }) => {
  const loginPage = new LoginPage(page);
  await loginPage.navigate();
  await loginPage.login('invalid@email.com', 'wrongpass');
  
  const error = await loginPage.getErrorMessage();
  expect(error).toContain('Invalid credentials');
});

高度な機能

Codegen - 自動テスト生成

Playwright の Codegen ツールは、ウェブページとのあなたのインタラクションを記録してテストを生成します:

# Codegen の起動
playwright codegen example.com

# 特定のブラウザで
playwright codegen --browser firefox example.com

# 保存された認証状態で
playwright codegen --load-storage=auth.json example.com

ページとインタラクションするたびに、Codegen はリアルタイムでコードを生成します。これは、テストのプロトタイピングや Playwright のセレクタ構文の学習に非常に役立ちます。

デバッグ用の Trace Viewer

テストが失敗した場合、なぜ失敗したかを理解するのは困難な場合があります。Playwright の Trace Viewer は、テスト実行のタイムラインビューを提供します:

// 設定でトレースを有効にする
use: {
  trace: 'on-first-retry',
}

テストが失敗し再試行した後、トレースを表示します:

playwright show-trace trace.zip

Trace Viewer は、各アクションごとのスクリーンショット、ネットワークアクティビティ、コンソールログ、DOM スナップショットを表示し、デバッグを簡単にします。

ネットワークの挿入とモック

エッジケースのテストのためにネットワークトラフィックを挿入および変更します:

def test_with_mocked_api():
    with sync_playwright() as p:
        browser = p.chromium.launch()
        page = browser.new_page()
        
        # API レスポンスのモック
        def handle_route(route):
            if 'api/products' in route.request.url:
                route.fulfill(
                    status=200,
                    body=json.dumps({
                        'products': [
                            {'id': 1, 'name': 'Test Product', 'price': 99.99}
                        ]
                    })
                )
            else:
                route.continue_()
        
        page.route('**/*', handle_route)
        
        page.goto('https://example.com')
        # ページはモックデータを使用
        browser.close()

モバイルテスト

さまざまなデバイスでリスポンシブデザインをテストします:

from playwright.sync_api import sync_playwright

def test_mobile():
    with sync_playwright() as p:
        # デバイス記述子の使用
        iphone_13 = p.devices['iPhone 13']
        
        browser = p.webkit.launch()
        context = browser.new_context(**iphone_13)
        page = context.new_page()
        
        page.goto('https://example.com')
        
        # モバイルユーザーとしてインタラクション
        page.locator('#mobile-menu-button').click()
        
        browser.close()

ベストプラクティス

ウェブスクレイピング向け

  1. 本番環境ではヘッドレスモードを使用: ヘッドレスブラウジングはより高速でリソース使用量が少ない
  2. レート制限の実装: リクエスト間の遅延でターゲットウェブサイトを尊重
  3. エラーの柔軟な処理: ネットワーク問題、タイムアウト、セレクタの変更は避けられない
  4. ユーザーエージェントのローテーション: ブラウザの指紋を変更して検出を避ける
  5. robots.txt の遵守: ウェブサイトのスクレイピングポリシーを確認し、遵守
  6. コンテキストの分離: 並列スクレイピングのために別々のブラウザコンテキストを作成

スクレイピングされたコンテンツをマークダウン形式に変換する際には、LLMベースの変換ツールやHTMLからマークダウンへの変換に特化したPythonライブラリを使用して、よりクリーンな出力を得ることを検討してください。

テスト向け

  1. data-testid 属性の使用: よく変更されるCSSクラスよりも安定している
  2. ハードウェイトの回避: sleep() の代わりに Playwright の組み込みの待機メカニズムを使用
  3. テストの独立性の保持: 各テストは孤立して実行できるようにする
  4. フィクスチャの使用: テスト間でセットアップコードを効率的に共有
  5. 並列実行: Playwright のワーカースレッドを活用して速度を向上
  6. 失敗時のトレースの記録: デバッグが容易になるようにトレースの記録を有効にする

パフォーマンス最適化

# 不要なリソースの無効化
def fast_scraping():
    with sync_playwright() as p:
        browser = p.chromium.launch()
        context = browser.new_context()
        page = context.new_page()
        
        # 画像やスタイルシートをブロックしてスクレイピングを高速化
        async def block_resources(route):
            if route.request.resource_type in ['image', 'stylesheet', 'font']:
                await route.abort()
            else:
                await route.continue_()
        
        page.route('**/*', block_resources)
        page.goto('https://example.com')
        
        browser.close()

Playwright と代替ソフトウェアとの比較

Playwright vs Selenium

Playwright の利点:

  • 組み込みの自動待機により、不安定なテストがなくなる
  • 現代的なアーキテクチャにより実行が速い
  • ネットワークの挿入とモックがより優れている
  • デバッグツール(Trace Viewer)がより強力
  • よりシンプルなAPIで余計なコードが少ない
  • 単一のインストールで複数のブラウザをサポート

Selenium の利点:

  • より成熟したエコシステムと広範なコミュニティ
  • より多くのプログラミング言語をサポート
  • より古いバージョンのブラウザとの互換性が良い

Playwright vs Puppeteer

Playwright の利点:

  • Firefox、WebKit、Chromiumの真のクロスブラウザサポート
  • Puppeteerの教訓に基づいたより優れたAPI設計
  • より強力なデバッグツール
  • Microsoftの支援と活発な開発

Puppeteer の利点:

  • 稍微小さい足跡
  • Chrome DevTools Protocolの専門知識

新しいプロジェクトでは、現代的なアーキテクチャと包括的な機能セットにより、Playwright が推奨されます。Go ではなく Python や JavaScript を使用してウェブスクレイピング機能が必要な場合は、Beautiful Soup 代替の Go ツール に関するガイドをご覧ください。

一般的なユースケース

AI/LLMアプリケーション用のデータ抽出

Playwright は、AIモデルのトレーニングデータの収集や、ウェブ検索機能の作成に非常に適しています。MCP(Model Context Protocol)サーバーを構築する際、Playwright はウェブスクレイピングコンポーネントを処理し、LLM が抽出されたコンテンツを処理します。

CI/CD での自動テスト

Playwright テストを継続的インテグレーションパイプラインに統合します:

# .github/workflows/playwright.yml
name: Playwright Tests
on:
  push:
    branches: [ main, master ]
  pull_request:
    branches: [ main, master ]
jobs:
  test:
    timeout-minutes: 60
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v3
    - uses: actions/setup-node@v3
      with:
        node-version: 18
    - name: Install dependencies
      run: npm ci
    - name: Install Playwright Browsers
      run: npx playwright install --with-deps
    - name: Run Playwright tests
      run: npx playwright test
    - uses: actions/upload-artifact@v3
      if: always()
      with:
        name: playwright-report
        path: playwright-report/
        retention-days: 30

ウェブサイトの監視

本番環境のウェブサイトの稼働状態と機能を監視します:

import schedule
import time

def monitor_website():
    with sync_playwright() as p:
        try:
            browser = p.chromium.launch()
            page = browser.new_page()
            page.goto('https://your-site.com', timeout=30000)
            
            # 重要な要素の確認
            assert page.is_visible('.header')
            assert page.is_visible('#main-content')
            
            print("✓ ウェブサイトは健全です")
        except Exception as e:
            print(f"✗ ウェブサイトの問題が検出されました: {e}")
            # アラートの送信
        finally:
            browser.close()

# 5分ごとに実行
schedule.every(5).minutes.do(monitor_website)

while True:
    schedule.run_pending()
    time.sleep(1)

一般的な問題のトラブルシューティング

ブラウザのインストール問題

ブラウザがダウンロードできない場合:

# カスタムダウンロード場所の設定
PLAYWRIGHT_BROWSERS_PATH=/custom/path playwright install

# キャッシュをクリアして再インストール
playwright uninstall
playwright install

タイムアウトエラー

遅いネットワークや複雑なページに対してタイムアウトを増やします:

page.goto('https://slow-site.com', timeout=60000)  # 60秒
page.wait_for_selector('.element', timeout=30000)  # 30秒

セレクターが見つからない

Playwright インスペクターを使用して正しいセレクターを特定します:

PWDEBUG=1 pytest test_file.py

これにより、要素をホバーしてセレクターを確認できるインスペクターが開きます。

結論

Playwright は、強力な機能と優れた開発者体験を組み合わせたブラウザ自動化技術の最前線を表しています。ウェブスクレイピングパイプラインの構築、包括的なテストカバレッジの実装、または自動化されたワークフローの作成に関係なく、Playwright は必要なツールと信頼性を提供します。

その自動待機メカニズムは不安定なテストを排除し、クロスブラウザサポートによりアプリケーションがどこでも動作することを保証し、強力なデバッグツールによりトラブルシューティングが簡単になります。ウェブアプリケーションが複雑さを増すにつれて、Playwright の現代的なアーキテクチャと活発な開発により、ブラウザ自動化のニーズに最適な選択肢です。

Python 開発者でデータパイプラインやウェブスクレイピングプロジェクトに従事している場合は、Playwright は現代的なパッケージマネージャーとシームレスに統合し、pandas、requests、その他のデータサイエンスツールと非常にうまく連携します。複雑な現代のウェブサイトから構造化されたデータを抽出する能力により、AI アプリケーション、研究プロジェクト、ビジネスインテリジェンスに非常に価値があります。HTMLからマークダウンへの変換ツールと適切なコンテンツ処理と組み合わせることで、Playwright はウェブデータを大規模に抽出、変換、利用するための完全なソリューションになります。

有用なリンク

その他の参考資料