【Python×Playwright】SPA・動的サイトに対応するモダンWebスクレイピング完全ガイド
2026/08/11
「スクレイピングで取得したHTMLデータが、JavaScriptの描画前で空っぽだった……」
Webエンジニアとして自動データ収集を行っていると、近年のWebサイト(ReactやVueなどのSPA)で従来の requests + BeautifulSoup の組み合わせが通用しないケースが激増しています。
Seleniumを使う選択肢もありますが、動作が重く環境構築も複雑になりがちです。そこで現在もっとも推奨されるのが、Microsoftが開発するヘッドレスブラウザ自動化ライブラリ Playwright for Python です。今回は、頑丈でエラーに強いスクレイピング基盤の構築手順を徹底解説します。
なぜ Selenium ではなく Playwright なのか?
かつてブラウザ自動化の主流は Selenium でしたが、ドライバのバージョン管理や要素の出現待機(Wait処理)の記述が煩雑で、頻繁にタイムアウトエラーが発生するのが悩みでした。
Playwrightを導入して感動したポイントは以下の3点です。
- Auto-waiting(自動待機):要素がクリック可能・表示状態になるまでライブラリ側が自動で待機してくれる。
- 高速な実行速度:Chromium, Firefox, WebKit を同一コードで極めて軽量かつ並列に動かせる。
- 直感的なデバッグ:コード実行時のスクリーンショットやビデオトレースの採取が標準でサポートされている。
データ取得フローの設計
動的コンテンツの読み込み完了を確実に待ち受け、指定した要素データを抽出して構造化データ(JSON / CSV)として保存する堅牢なワークフローを構築します。
graph TD
A["スクレイパー起動 (Playwright)"] --> B["ヘッドレスブラウザ起動"]
B --> C["ターゲットページへナビゲーション"]
C --> D["特定要素の自動読み込み待機 (Auto-wait)"]
D --> E{"ページの読み込み成功?"}
E -- "成功" --> F["DOM要素からテキスト・属性データを抽出"]
E -- "タイムアウト/異常" --> G["スクリーンショット撮影 & ログ保存"]
F --> H["JSONファイルへの出力・保存"]
G --> I["管理者へアラート"]
実践Pythonコード:安全かつ確実なスクレイピング実装
以下は、非同期処理(asyncio)を活用し、複数ページの情報を安定して取得するプロダクションレベルのコード例です。
import asyncio
import json
import logging
from playwright.async_api import async_playwright, TimeoutError as PlaywrightTimeoutError
# ログの設定
logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(levelname)s] %(message)s")
TARGET_URL = "https://example.com/products"
async def scrape_product_data():
async with async_playwright() as p:
# ヘッドレスブラウザを立ち上げ (Chromium)
browser = await p.chromium.launch(headless=True)
# アンチスクレイピング対策としてUser-Agentを設定
context = await browser.new_context(
user_agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"
)
page = await context.new_page()
try:
logging.info(f"ページへアクセス中: {TARGET_URL}")
# domcontentloaded を待機
await page.goto(TARGET_URL, wait_until="domcontentloaded", timeout=15000)
# 動的要素が表示されるまで明示的に待機
await page.wait_for_selector(".product-card", state="visible", timeout=10000)
products = []
cards = await page.query_selector_all(".product-card")
for card in cards:
title_elem = await card.query_selector(".product-title")
price_elem = await card.query_selector(".product-price")
if title_elem and price_elem:
title = await title_elem.inner_text()
price = await price_elem.inner_text()
products.append({"title": title.strip(), "price": price.strip()})
logging.info(f"正常取得完了: {len(products)} 件のデータを抽出しました")
# 結果をJSON保存
with open("output.json", "w", encoding="utf-8") as f:
json.dump(products, f, ensure_ascii=False, indent=2)
except PlaywrightTimeoutError:
logging.error("要素の待機中にタイムアウトが発生しました。スクリーンショットを撮影します。")
await page.screenshot(path="error_timeout.png")
except Exception as e:
logging.error(f"予期せぬエラーが発生しました: {e}")
finally:
await browser.close()
if __name__ == "__main__":
asyncio.run(scrape_product_data())
スクリプト構築時の必須テクニック
- アクセス相手への配慮(礼儀と間隔)
ループ処理で複数ページを巡回する場合は、
await asyncio.sleep(2)などで必ず適切なウェイト時間を差し込んでください。アクセス過多によるサーバー過負荷は絶対に避けなければなりません。 - タイムアウト時の証拠保存
await page.screenshot(path="error.png")を例外処理に埋め込んでおくと、「デザイン変更でセレクタが変わったのか」「ネットワークエラーか」が即座に判別できます。 - robots.txt と利用規約の遵守
スクレイピングを開始する前に、対象ドメインの
/robots.txtおよび利用規約を確認し、収集が許可されている範囲を必ず確認してください。
実際に自動運用して得られた効果
- スクレイピング失敗率が90%減少 Selenium時代に頻発していた「原因不明の要素未検出エラー」が、Playwrightの柔軟なAuto-waitによってほぼ絶滅しました。
- 取得スピードの高速化 ブラウザコンテキストの軽量化により、1回の収集ジョブにかかる時間が半分以下に削減されました。
- 保守工数の大幅削減 トラブル時のスクショ自動生成により、エラー発生から原因特定までの調査時間が10分程度に短縮されました。
まとめ:モダンなツールで堅牢なデータ収集を
JavaScriptが当たり前のように使われる現代のWebにおいて、Playwrightはエンジニアにとって最強の武器になります。
「定期的な価格リサーチ」や「特定サイトの更新チェック」など、日常の手作業を自動化したいと考えている方は、ぜひPlaywrightを試してみてください。驚くほど滑らかで安定した自動化の世界が広がっています!