Playwright Pythonで「厄介な」サイトをスクレイピング:JavaScript多用サイトへの最適解

Python tutorial - IT technology blog
Python tutorial - IT technology blog

背景とPlaywrightが必要な理由

BeautifulSoupやRequestsを使って意気揚々とデータを取得しようとしたものの、返ってきたのは中身が空のHTMLや、<script>タグばかりだった経験はありませんか? これは、ShopeeやFacebook、あるいは動的なチャートを扱うモダンなWebサイトでよく直面する問題です。これらのサイトはSingle Page Application (SPA)を採用しており、JavaScriptが実行された後に初めてデータがレンダリングされるためです。

こうなると、静的なHTMLパースライブラリでは太刀打ちできません。以前はSeleniumをよく使っていましたが、動作が重く、特にLinux上でのブラウザドライバーのバージョン管理には苦労しました。ある自動化プロジェクトでは、当初200行だったSeleniumのコードが2000行まで膨れ上がり、ドライバー管理が迷宮入りしたこともあります。Playwrightに移行したことで、実行速度は劇的に向上し、特に非同期処理(async)のハンドリングが非常にスムーズになりました。

Playwright is 速いだけでなく、「Auto-wait(自動待機)」という強力な武器を備えています。要素が出現するのを自動的に待ってから操作を実行するため、time.sleep()を無闇に多用するよりもスクリプトが格段に安定します。

一瞬で終わるインストール

ブラウザのバージョンに適合するChromedriverを探してダウンロードする手間はもう不要です。Playwrightがすべて代行してくれます。まずは、ライブラリをインストールしましょう:

pip install playwright

次に、ブラウザエンジン(Chromium, Firefox, WebKit)をダウンロードするコマンドを実行します:

playwright install chromium

実務上のヒント:一般的なWebサイトをスクレイピングするだけなら、install chromiumだけで十分です。これでディスク容量を約300MB節約できます。

実戦的な構成

単にgotoしてすぐにデータを取得しようとするだけでは、空のリストが返ってくるかもしれません。JavaScriptを多用するサイトでは、適切な構成が成功の鍵を握ります。

1. Async APIを活用した高速化

同時に100ページをスクレイピングする必要がある場合は、同期処理ではなくasyncioを使いましょう。実際のプロジェクトでは、Asyncに切り替えただけで、500商品の取得時間を15分から3分未満に短縮することができました。

import asyncio
from playwright.async_api import async_playwright

async def run():
    async with async_playwright() as p:
        # サーバーで実行する際はRAMを節約するためにheadless=Trueを使用
        browser = await p.chromium.launch(headless=True) 
        context = await browser.new_context(
            user_agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36..."
        )
        page = await context.new_page()
        
        await page.goto("https://tiki.vn/search?q=laptop")
        
        # 商品リストが表示されるまで自動待機
        await page.wait_for_selector(".product-item")
        
        titles = await page.locator(".product-name").all_text_contents()
        print(f"{len(titles)}個の商品が見つかりました!")

        await browser.close()

asyncio.run(run())

2. 無限スクロール(Infinite Scroll)の処理

FacebookやTikTokなどのサイトは、スクロールダウンした時にのみ追加データが読み込まれます。5秒間待機するといった固定の待ち時間ではなく、実際のユーザー行動をシミュレートするスクリプトを使いましょう。これにより、自然な形でデータの読み込みをトリガーできます。

async def scroll_page(page):
    await page.evaluate("""
        async () => {
            let totalHeight = 0;
            let distance = 200;
            while (totalHeight < document.body.scrollHeight) {
                window.scrollBy(0, distance);
                totalHeight += distance;
                await new Promise(r => setTimeout(r, 100));
            }
        }
    """)

3. Page Object Model (POM) の適用

コードが1000行を超えたら、すべてを一つの関数に詰め込むのはやめましょう。各ページを代表するクラスに分割してください。これにより、バグ修正が迅速になり、3〜6ヶ月後にコードを再確認した際のメンテナンスも容易になります。

監視とデバッグ:最強ツール Trace Viewer

サーバー上(ヘッドレス)で実行中のスクリプトがエラーになった時、どうやって原因を特定すればいいでしょうか? 無機質なログテキストを眺めるだけでは限界があります。

1. 「ブラックボックス」Trace Viewer

これはPlaywrightの「無敵」な機能です。飛行機のブラックボックスのように、動画、クリック、ネットワークリクエスト、コンソールエラーなどをミリ秒単位で記録します。

await context.tracing.start(screenshots=True, snapshots=True)
# スクレイピングのロジック...
await context.tracing.stop(path="debug_trace.zip")

作成されたdebug_trace.zipファイルをtrace.playwright.devにドラッグ&ドロップするだけで、すべての実行過程を確認できます。なぜスクリプトが失敗したのかを推測する時間を何時間も節約できました。

2. 「罠」にかかった時のスクリーンショット

常にコードをtry...exceptでラップしましょう。エラーが発生した場合は、すぐにスクリーンショットを撮ります。これが、Webサイトのレイアウト変更や、Captcha、ボット検知に引っかかっていないかを確認する最短の方法です。

JavaScriptを多用するサイトのスクレイピングは、適切なツールを知っていれば難しくありません。Playwrightを使えば、ブラウザ管理の悩みから解放され、データ抽出のロジックに集中できます。まずは小さなスクリプトから始め、最初からTrace Viewerを設定しておきましょう。そうすれば、自動化作業が格段にプロフェッショナルなものになるはずです。

Share: