Pythonで大規模なスクレイピング基盤を運用していると、最終的には「ブラウザ自動化をどこまで安定して回せるか」という問題に突き当たります。
特に近年のWebサイトはJavaScript前提で構築されていることが多く、単純なHTTPリクエストだけではデータ取得が成立しないケースも珍しくありません。
その結果、多くの開発現場でSeleniumが採用されてきました。
実際、私も長い間Seleniumを使って数百万ページ規模のデータ収集を行っていました。
ところが、クロール対象の増加に伴って、徐々に無視できない問題が積み上がっていったのです。
たとえば、ブラウザ起動の遅延、要素取得の不安定さ、並列実行時のリソース消費、そして原因特定に時間がかかるタイムアウトエラーなどです。
特に厄介だったのは、「たまに失敗する」というタイプの障害でした。
再実行すると通るため根本原因を追いにくく、結果としてリトライ処理が肥大化し、コードベース全体の保守性まで悪化していきました。
そこで検証を始めたのがPlaywrightです。
単なる「新しい自動化ツール」程度の認識で試したのですが、実運用へ投入してみると、想像以上に設計思想が異なっていました。
- 待機処理の考え方。
- 非同期実行との相性。
- ブラウザコンテキストの扱い。
- 並列スクレイピング時の安定性。
- CI環境での再現性。
これらの違いは、単なる書き味の問題ではなく、大規模運用時の障害率や運用コストに直結します。
この記事では、実際にSeleniumベースの既存基盤をPlaywrightへ移行した際に、何が改善され、逆にどこで苦労したのかを、実案件レベルの視点で整理します。
単なるライブラリ比較ではなく、「大規模スクレイピングを長期間運用する」という前提で、技術的・運用的な変化を具体的に解説していきます。
Seleniumによる大規模スクレイピング運用で限界を感じ始めた理由

Seleniumは長年にわたり、Pythonによるブラウザ自動化の事実上の標準として使われてきました。
実際、Webアプリケーションのテスト用途だけでなく、JavaScriptレンダリングを伴うスクレイピング案件でも非常に多く採用されています。
私自身も、初期段階では「多少重いが安定している」という印象を持っていました。
しかし、スクレイピング対象が数十サイト規模から数百サイト規模へ増加し、さらにクロール対象ページ数が数百万単位に到達すると、Selenium特有の設計上の課題が徐々に表面化してきました。
特に問題だったのは、「単体では問題なく動作するが、大規模並列運用になると急激に不安定化する」という点です。
これは小規模検証だけでは見えにくく、実運用フェーズに入って初めて顕在化します。
数百万ページ規模のPythonスクレイピングで発生した典型的な障害
最初に直面したのは、ブラウザ自動化に伴う不安定な失敗です。
たとえば、以下のような症状が頻発するようになりました。
TimeoutExceptionが断続的に発生する- 要素取得前にDOMが再構築され
StaleElementReferenceExceptionが発生する - ヘッドレスChromeが突然クラッシュする
- メモリ使用量増加によってノード全体が不安定化する
- 同一コードでも成功率が環境によって変化する
特に厄介だったのは、「リトライすると成功する」というタイプの障害です。
この種のエラーは、根本原因の切り分けが非常に難しくなります。
ネットワーク遅延なのか、JavaScript描画タイミングなのか、Selenium Serverの内部状態なのか、あるいはChromeDriverとの通信問題なのかを即座に特定できません。
結果として、コードベースには防御的な待機処理が大量に追加されることになります。
例えば、初期段階では次のようなシンプルなコードで済んでいました。
driver.get(url)
title = driver.find_element(By.TAG_NAME, "h1").text
ところが運用が大規模化すると、実際には次のような実装へ肥大化していきます。
WebDriverWait(driver, 20).until(
EC.presence_of_element_located((By.TAG_NAME, "h1"))
)
time.sleep(random.uniform(1.0, 3.0))
for _ in range(3):
try:
title = driver.find_element(By.TAG_NAME, "h1").text
break
except StaleElementReferenceException:
time.sleep(1)
この問題は単なるコード量増加ではありません。
保守性が低下し、障害発生時の調査コストが指数関数的に増加していく点が本質的な問題でした。
また、大規模スクレイピングでは「例外発生率1%」ですら深刻です。
仮に100万ページを処理する場合、1%失敗するだけで1万件の異常系対応が必要になります。
さらに実運用では、失敗ページの再投入、重複防止、監視、通知などの仕組みも必要になるため、システム全体が複雑化していきます。
特にSeleniumは、内部的にWebDriverプロトコルを介してブラウザと通信しているため、通信レイヤー由来の不安定性も無視できませんでした。
ブラウザ自動化そのものより、「ブラウザ制御のための外部通信」に起因する問題が多かった印象があります。
Selenium Grid運用で顕在化したリソース問題
並列実行数が増えるにつれ、次に問題化したのがSelenium Gridのリソース消費でした。
小規模用途では便利なSelenium Gridですが、大規模スクレイピングでは想像以上に重くなります。
特にChromeを大量起動する構成では、CPU・メモリ・I/Oの消費量が急増します。
実際の運用では、以下のような構成を採用していました。
| 構成要素 | 用途 | 主な問題 |
|---|---|---|
| Selenium Hub | セッション管理 | セッション増加で遅延 |
| Chrome Node | ブラウザ実行 | メモリ消費が大きい |
| Python Worker | クロール制御 | 待機時間が長い |
| Redis Queue | タスク管理 | 再試行制御が複雑 |
特にChrome Node側の負荷は深刻でした。
ブラウザ1プロセスあたり数百MB単位でメモリを消費するため、並列数を増やすほどノード単位で不安定化します。
しかもJavaScriptが重いサイトではCPU使用率も急上昇します。
結果として、以下のような悪循環に入りやすくなります。
- 並列数を増やす
- Chromeプロセスが増える
- ノード負荷が上昇する
- タイムアウト率が増える
- リトライが増える
- さらに負荷が増大する
つまり、スケールアウトによって処理能力が比例して増えないのです。
さらに問題だったのは、Selenium Grid自体が「スクレイピング専用設計」ではない点です。
もともとはE2Eテスト用途を中心に発展してきた経緯があるため、数千〜数万セッションを長時間維持する用途とは設計思想がやや異なります。
その結果、以下のような運用ノウハウが大量に必要になります。
- 定期的なChromeプロセス掃除
- Node再起動スケジューラ
- Zombieセッション監視
- Driverバージョン固定
- Hub側メモリ監視
この時点で、私たちのチームでは「スクレイピング本体」より「Selenium基盤維持」に工数を使う割合が増え始めていました。
そして最終的に、「これはブラウザ制御ライブラリの問題というより、アーキテクチャそのものを見直す必要があるのではないか」という結論に至ったのです。
Playwrightを検証対象に選んだ理由と技術的な期待

Selenium運用が限界に近づくにつれて、私たちのチームでは「待機処理を工夫する」「ノード数を増やす」といった対症療法ではなく、ブラウザ自動化基盤そのものを見直す必要性が高まっていました。
そこで候補に挙がったのがPlaywrightです。
当初は「比較的新しいブラウザ自動化ツール」という程度の認識でした。
しかし内部実装や設計思想を調査していくと、Seleniumとはかなり異なるアプローチを採用していることが分かりました。
特に興味深かったのは、「ブラウザをどう制御するか」ではなく、「ブラウザ自動化における不安定要因をどのように排除するか」に重点が置かれていた点です。
これは大規模スクレイピングにおいて非常に重要です。
小規模用途では多少不安定でもリトライで吸収できますが、数百万ページ単位になると、1%未満の失敗率ですら運用コストに直結するからです。
Microsoft製Playwrightのアーキテクチャが優秀だった
Playwrightを本格的に評価し始めて最初に驚いたのは、内部アーキテクチャの合理性でした。
Seleniumは基本的に以下の構造で動作します。
| 構成 | 役割 | 問題になりやすい点 |
|---|---|---|
| Selenium Client | Pythonコード実行 | Driver依存が強い |
| WebDriver Protocol | 通信制御 | 通信遅延が発生する |
| ChromeDriver | ブラウザ仲介 | バージョン差異問題 |
| Chrome | 実ブラウザ | リソース消費が大きい |
一方、PlaywrightはDriver依存を極力減らし、ブラウザとの統合度を高めています。
この設計によって、Selenium時代に頻発していた「Driverとブラウザのバージョン不整合」がかなり減少しました。
実運用では、Chrome更新による突然の障害は意外なほど多いので、これは非常に大きなメリットです。
さらに重要だったのは、自動待機機構です。
Seleniumでは、多くの場合開発者が待機タイミングを明示的に制御する必要があります。
しかしPlaywrightは、クリック可能状態やDOM描画完了を内部的に待機してくれます。
例えば、Playwrightでは以下のようなコードが自然に動作します。
await page.locator("button.submit").click()
一見すると単純ですが、内部では要素の可視性・操作可能性・描画状態などを考慮しています。
つまり、開発者側が「いつDOMが安定するか」を毎回推測しなくてよいのです。
この思想は、大規模スクレイピングにおいて極めて合理的でした。
なぜなら、スクレイピング障害の多くは「要素取得そのもの」ではなく、「取得タイミングのズレ」だからです。
また、ブラウザコンテキスト機能も優秀でした。
Seleniumでは並列処理時にブラウザインスタンスを大量生成しがちですが、Playwrightでは単一ブラウザ内に軽量コンテキストを複数持てます。
その結果、以下の改善が期待できました。
- メモリ消費削減
- ブラウザ起動時間短縮
- Cookie分離管理
- 並列セッション効率化
- コンテナ密度向上
特にKubernetes運用を考慮すると、この軽量性は非常に魅力的でした。
PythonからPlaywrightを扱ったときの学習コスト
PlaywrightはNode.js界隈で先行普及したライブラリという印象が強かったため、Python環境との相性については当初少し懸念していました。
しかし実際に触ってみると、Python APIの完成度はかなり高く、学習コストは想像より低かったです。
特に良かったのは、API設計に一貫性がある点でした。
例えば、SeleniumではDriver操作・要素取得・待機処理がやや分散的ですが、PlaywrightはLocator中心設計になっています。
title = await page.locator("h1").text_content()
この書き方は直感的で、コードの意図が読み取りやすいです。
また、Locatorが遅延評価されるため、DOM変化にも比較的強くなっています。
これはSPA系サイトを扱う際に特に効果を感じました。
一方で、最初に戸惑いやすいのは非同期処理です。
PlaywrightはPythonでも asyncio ベース利用が推奨されており、同期コード中心だった既存資産とは設計思想が異なります。
例えば、初学者は以下のような問題に直面しやすいです。
awaitの付け忘れ- イベントループ管理
- 非同期例外処理
- 並列タスク制御
- セッションライフサイクル管理
ただし、この非同期設計こそがPlaywright最大の強みでもあります。
スクレイピングではI/O待機時間が支配的なので、CPUより「待機効率」が重要になるケースが多いからです。
実際、Selenium時代はThreadPoolExecutorベースで無理やり並列化していましたが、Playwright移行後は非同期タスク管理がかなり自然になりました。
また、公式ドキュメント品質も非常に高く、Python版サンプルも充実しています。
これは実務上かなり助かります。
特にMicrosoft製プロダクトらしく、「実運用で困るポイント」をかなり意識して設計されている印象がありました。
結果として、私たちのチームでは「Seleniumを頑張って延命する」より、「Playwright前提で再設計した方が長期的コストが低い」という判断に変わっていったのです。
SeleniumからPlaywrightへ移行したPythonコードの違い

Playwrightへの移行で最も大きな変化を感じたのは、「ブラウザ自動化コードそのものがシンプルになった」という点です。
これは単なる記述量削減ではありません。
大規模スクレイピングでは、コード量の増加はそのまま障害率上昇につながります。
特に待機処理、リトライ処理、状態監視などの防御コードは、規模が大きくなるほど保守負荷を急激に押し上げます。
Selenium運用時代は、「動かすコード」より「壊れないように守るコード」の方が長くなるケースすらありました。
しかしPlaywrightへ移行すると、ブラウザ自動化の前提そのものが変わります。
これは単にAPIが新しいからではなく、「非同期前提」「自動待機前提」で設計されていることが大きいです。
結果として、コード構造がかなり整理されました。
待機処理の自動化でコード量が大幅に減少した
Seleniumで最も神経を使う部分の一つが待機制御です。
Webサイトは常に同期的に描画されるわけではありません。
特にSPA構成のサイトでは、DOM生成・API通信・レンダリングが段階的に行われるため、「要素が存在する瞬間」と「操作可能になる瞬間」が一致しません。
そのためSeleniumでは、以下のような実装が増えがちです。
WebDriverWaitExpectedConditionstime.sleep- リトライループ
- JavaScript描画待機
問題は、これらが積み重なるとコードの可読性が著しく低下することです。
さらに厄介なのは、「適切な待機時間」がサイトごとに異なる点です。
高速回線では動作しても、負荷が高い時間帯には失敗するケースもあります。
一方、Playwrightでは自動待機がかなり強力です。
例えば、Seleniumではクリック前に明示待機を書くことが多いですが、Playwrightでは操作時点で必要な待機が内部実行されます。
await page.locator(".login-button").click()
このコードだけで、要素の表示状態・クリック可能状態・DOM安定性などを考慮してくれます。
実際に移行して感じたのは、「待機コードを書かなくてよい」ことの影響が想像以上に大きい点でした。
特に以下の改善が顕著でした。
| 項目 | Selenium時代 | Playwright移行後 |
|---|---|---|
| 明示待機 | 多い | 激減 |
| sleep使用 | 頻繁 | ほぼ不要 |
| リトライ処理 | 必須 | 大幅削減 |
| DOM不整合 | 発生しやすい | 発生率低下 |
また、Locator中心設計も非常に合理的でした。
Playwrightでは、要素を即時取得するのではなく、「操作時点で評価する」設計になっています。
つまり、DOM変化にある程度追従できるのです。
Selenium時代は、レンダリング途中で取得した要素参照が無効化されるケースが頻発していました。
しかしPlaywrightでは、Locator自体が抽象化レイヤーとして機能するため、DOM変化に強くなっています。
さらに、コードレビュー効率も改善しました。
Seleniumコードでは「このsleepは何のためか」を毎回確認する必要がありましたが、Playwright移行後はコード意図がかなり明確になります。
つまり、「人間がタイミング制御を頑張るコード」から、「ライブラリ側に待機責務を委譲するコード」へ変化したわけです。
これは長期保守において非常に重要でした。
非同期処理asyncioとの相性が圧倒的に良かった
Playwright移行で最もインパクトが大きかったのは、実は非同期処理との親和性でした。
大規模スクレイピングでは、CPU処理よりI/O待機の割合が圧倒的に高くなります。
例えば、以下の時間が支配的です。
- DNS解決
- TLS接続
- JavaScript実行
- APIレスポンス待機
- DOM描画待機
つまり、CPUは意外と暇です。
そのため、「大量待機をどう効率的に扱うか」がスループットを左右します。
Selenium時代は、ThreadPoolExecutorを使ったマルチスレッド構成を採用していました。
しかしこれはメモリ消費が大きく、ブラウザ数増加に比例して不安定化しやすい問題がありました。
一方、Playwrightでは asyncio ベース設計との相性が非常に良いです。
例えば、複数URL処理も比較的自然に記述できます。
async def fetch(page, url):
await page.goto(url)
return await page.title()
tasks = [fetch(page, url) for url in urls]
results = await asyncio.gather(*tasks)
この設計の利点は、「待機中に他タスクへ即座に切り替えられる」点です。
つまり、ブラウザ描画待機時間をCPUアイドルにしません。
結果として、以下の改善が見られました。
- 同一ノードで処理できるURL数増加
- メモリ使用量削減
- CPU利用率最適化
- スループット向上
- ノード密度改善
特にKubernetes環境では恩恵が大きかったです。
Selenium時代は「ブラウザ数 = Pod増加」に近い構造でしたが、Playwright移行後は単一Pod内で多数タスクを効率処理できるようになりました。
さらに、非同期設計は監視との相性も良好です。
例えば、タイムアウト管理・キャンセル処理・リソース回収などを一元制御しやすくなります。
これは大規模運用ではかなり重要です。
スクレイピング基盤は長時間稼働するため、「異常終了しないこと」より「異常時に綺麗に回復できること」の方が重要になる場面が多いからです。
結果として、Playwright移行後は単なるブラウザ自動化改善ではなく、「スクレイピングシステム全体の設計思想」が変化しました。
以前はブラウザ操作そのものに振り回されていましたが、移行後はようやく「データ取得ロジック」に集中できる状態へ近づいたのです。
Playwright導入後に改善したスクレイピング性能と安定性

Playwrightへの移行で最も大きかった成果は、「ブラウザ自動化コードが書きやすくなったこと」ではありません。
本当に重要だったのは、大規模スクレイピング基盤全体の安定性が向上したことです。
これは実運用において非常に大きな意味を持ちます。
小規模な検証環境では、多少不安定でも人間が再実行すれば済みます。
しかし、数百万ページ規模のクロールでは、「人間が介入しなくても継続運用できるか」が重要になります。
つまり、必要なのは単なる高速化ではなく、以下を満たすことです。
- 障害率が低い
- 長時間稼働できる
- ノード再現性が高い
- 環境差異に強い
- リソース効率が良い
Playwrightは、この「運用レイヤー」に強い影響を与えました。
特に大きかったのは、ブラウザ起動効率とCI環境での安定性です。
Selenium時代は、ブラウザ自動化そのものより、「ブラウザを正常状態で維持すること」に工数を取られていました。
しかしPlaywright移行後は、その運用コストがかなり減少しました。
ブラウザ起動時間とメモリ使用量の変化
大規模スクレイピングでは、ブラウザ起動コストは無視できません。
特にSelenium構成では、以下のレイヤーが必要になります。
- Selenium Client
- WebDriver
- ChromeDriver
- Chrome本体
つまり、ブラウザを操作するために複数プロセス間通信が発生します。
一方、Playwrightはブラウザ統合度が高く、構造が比較的シンプルです。
そのため、ブラウザ起動やセッション生成がかなり軽量でした。
実際に移行後、以下の変化が確認できました。
| 項目 | Selenium運用時 | Playwright移行後 |
|---|---|---|
| ブラウザ起動時間 | 長い | 短縮 |
| メモリ消費 | 高い | 減少 |
| 並列セッション効率 | 低い | 向上 |
| Node不安定化 | 発生しやすい | 大幅減少 |
特に効果が大きかったのは、Browser Context機能です。
Seleniumでは、セッション分離のためにブラウザインスタンス自体を増やす構成になりがちでした。
しかしPlaywrightでは、単一ブラウザ内に軽量コンテキストを多数生成できます。
これはOSレベルで見ると非常に重要です。
Chromeプロセス自体はかなり重いため、「プロセス数削減」はそのままメモリ効率改善につながります。
実際、Selenium時代は並列数を増やすと以下の症状が頻発していました。
- OOM Killer発動
- Chromeクラッシュ
- CPUスパイク
- swap増加
- Zombie process残留
特にLinuxサーバーでは、Chromeが一定時間後に不安定化するケースが多く、定期再起動ジョブを組み込んでいました。
しかしPlaywright移行後は、ノード寿命がかなり伸びました。
これは単にメモリ削減だけでなく、「ブラウザ制御構造そのものが安定している」影響が大きい印象です。
また、ブラウザ起動高速化によってオートスケール効率も改善しました。
Kubernetes環境では、Pod起動直後にブラウザ準備が完了するまで待機時間が発生します。
この初期化コストが大きいと、短命ジョブとの相性が悪くなります。
しかしPlaywrightは初期化が比較的軽いため、短時間Podとの親和性が高かったです。
その結果、以下のような運用改善につながりました。
- Pod密度向上
- オートスケール高速化
- ノード集約率改善
- コンテナコスト削減
- ジョブ回転率向上
つまり、Playwright導入は単なるライブラリ変更ではなく、「クラスタ全体のリソース効率改善」に近いインパクトがありました。
CI環境でのスクレイピング再現性が向上した理由
Playwright移行後に地味ながら非常に助かったのが、CI環境での再現性向上です。
Selenium運用時代は、「ローカルでは成功するがCIでは失敗する」という問題に何度も悩まされました。
原因は複数あります。
- ChromeDriverバージョン差異
- Linux依存ライブラリ不足
- Headless動作差異
- 描画タイミング違い
- GPU設定差異
特にChromeDriver周辺はかなり不安定要素になりやすく、ブラウザ更新だけでCIが突然壊れるケースもありました。
一方、Playwrightはブラウザ管理がかなり統合されています。
例えば、以下のコマンドだけで必要ブラウザをセットアップできます。
playwright install
さらに、Playwright公式Dockerイメージの完成度が高いです。
つまり、「Playwright前提で整備済みの環境」をそのまま利用できます。
これは実務上かなり大きいです。
スクレイピング基盤では、「ブラウザを正しく動かす環境構築」が意外と大きな負担になるからです。
Playwright移行後は、CI設定がかなり単純化しました。
例えばGitHub Actionsでも、以前ほど複雑なChromeセットアップを記述する必要がありません。
結果として、以下の改善が見られました。
| 問題 | Selenium時代 | Playwright移行後 |
|---|---|---|
| CI再現性 | 不安定 | 安定 |
| Driver管理 | 必須 | ほぼ不要 |
| ブラウザ更新影響 | 大きい | 小さい |
| Linux依存調整 | 多い | 少ない |
また、Trace Viewer機能も非常に優秀でした。
Playwrightでは、ブラウザ操作履歴を詳細に記録できます。
つまり、失敗時に以下を後追い確認できます。
- DOM状態
- クリック位置
- スクリーンショット
- ネットワーク通信
- Consoleログ
これは大規模運用で非常に助かります。
Selenium時代は、「なぜ失敗したのか分からない」ケースが本当に多かったからです。
スクレイピング運用では、障害率そのものより「障害解析コスト」の方が長期的には重要になります。
その意味でPlaywrightは、「デバッグ可能性」をかなり意識して設計されている印象がありました。
結果として、移行後は障害発生件数だけでなく、障害調査時間そのものも大幅に短縮されたのです。
Docker環境でPlaywrightを運用して感じたメリット

Playwright移行によって改善したのは、ブラウザ自動化コードだけではありません。
実際には、インフラ運用レイヤーへの影響の方が大きかった印象があります。
特に顕著だったのがDocker環境との相性です。
大規模スクレイピングでは、開発環境と本番環境の差異をどれだけ減らせるかが非常に重要になります。
なぜなら、ブラウザ自動化はOS依存性が強く、ライブラリ不足やブラウザ差異によって簡単に不安定化するからです。
Selenium運用時代もDocker化はしていました。
しかし実際には、以下のような問題に頻繁に直面していました。
- ChromeDriverのバージョン不整合
- Linuxパッケージ不足
- Headless Chrome依存関係
- GPU関連エラー
/dev/shmサイズ不足
特に厄介だったのは、「ローカルでは動くがコンテナでは壊れる」という問題です。
ブラウザ自動化は通常のバックエンドアプリケーションより環境依存性が高く、コンテナ化しても意外と再現性が安定しません。
しかしPlaywrightは、この問題へのアプローチがかなり優秀でした。
公式Dockerイメージによる環境構築の容易さ
Playwright導入で最初に助かったのが、公式Dockerイメージの完成度です。
Playwright公式イメージには、ブラウザ実行に必要なライブラリや依存関係があらかじめ整理されています。
つまり、「ブラウザをHeadless Linux上で安定実行する」という面倒な部分をかなり吸収してくれます。
例えば、Playwrightでは以下のようなDockerfileから比較的簡単に構築できます。
FROM mcr.microsoft.com/playwright/python:v1.52.0
WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY . .
CMD ["python", "main.py"]
このシンプルさは、Selenium運用経験があるほど価値を感じると思います。
Selenium時代は、以下のような追加調整が必要になるケースが多かったからです。
- Chromeインストール
- ChromeDriver配置
- apt依存パッケージ追加
- フォント導入
- sandbox回避設定
つまり、「スクレイピングコードを書く前の準備」が非常に長いのです。
一方、Playwrightではブラウザ実行環境込みでかなり整備されています。
さらに良かったのは、ブラウザ管理がPlaywright側へ統合されている点です。
Selenium時代は、Chrome本体とChromeDriverのバージョン対応を常に意識する必要がありました。
しかしPlaywrightでは、基本的に以下で完結します。
playwright install chromium
この設計によって、「Driver更新忘れで本番障害」という事故がほぼ消えました。
実務ではこれがかなり大きいです。
また、Dockerイメージサイズ効率も比較的良好でした。
もちろんブラウザを含む以上、完全に軽量ではありません。
しかしSelenium Grid構成と比較すると、管理対象コンポーネントが減るため、全体構成はかなり整理されます。
実際、Playwright移行後は以下の改善がありました。
| 項目 | Selenium運用 | Playwright運用 |
|---|---|---|
| Driver管理 | 必須 | 不要に近い |
| Docker調整量 | 多い | 少ない |
| Headless依存問題 | 頻発 | 減少 |
| 環境再現性 | 不安定 | 安定 |
特にCI/CDとの統合がかなり楽になりました。
スクレイピング案件では、「環境構築の複雑さ」がそのまま開発速度低下につながるため、この差は想像以上に大きかったです。
Kubernetes上での大規模スクレイピング運用にも適していた
Playwrightの真価を感じたのは、Kubernetes環境へ投入してからでした。
大規模スクレイピングでは、単一サーバー運用には限界があります。
特にクロール対象が増えると、以下を考慮する必要が出てきます。
- 並列分散
- 障害復旧
- ジョブ再投入
- オートスケール
- ノード効率
そのため、最終的にはKubernetesベース構成へ移行するケースが多くなります。
しかしSelenium Gridは、Kubernetesと必ずしも相性が良いとは言えませんでした。
理由は単純で、「ブラウザを大量常駐させる構成」が重すぎるからです。
特に以下の問題がありました。
- Podメモリ消費増大
- Chrome暴走時のノード不安定化
- セッション管理複雑化
- Hub単一障害点化
- スケールアウト効率低下
さらに、Selenium Gridは中央集権的構造になりやすく、クラスタ全体設計が複雑化しやすいです。
一方、Playwrightは比較的シンプルなワーカー型構成へ寄せやすいです。
つまり、「1Pod = 自律スクレイピングWorker」という設計が自然に作れます。
これはKubernetesとの親和性が非常に高いです。
特にBrowser Contextを活用すると、単一ブラウザ内で多数セッションを効率処理できるため、Podあたり処理密度を高めやすくなります。
実際、Playwright移行後は以下の改善がありました。
- Pod数削減
- ノード集約率向上
- メモリ効率改善
- ジョブ再起動高速化
- スケール挙動安定化
また、短命Pod運用とも相性が良かったです。
スクレイピング基盤では、ブラウザ長時間稼働によるリークや不安定化を避けるため、一定時間ごとにWorkerを廃棄する戦略を取ることがあります。
Playwrightは起動コストが比較的低いため、この「使い捨てWorker設計」が成立しやすいです。
これはクラウドネイティブ設計と非常に相性が良いです。
さらに、Playwrightはデバッグ性も高いため、障害Pod解析もかなり楽でした。
例えば以下を取得しやすくなります。
- スクリーンショット
- Traceログ
- Consoleログ
- ネットワーク履歴
- DOM状態
大規模運用では、「失敗しない」ことより「失敗時に解析できる」ことの方が重要になる場面があります。
その意味でPlaywrightは、単なるブラウザ自動化ライブラリというより、「クラウド環境前提で再設計されたブラウザ実行基盤」に近い印象でした。
結果として、Docker・Kubernetes環境へPlaywrightを投入したことで、スクレイピング基盤全体の運用負荷がかなり減少したのです。
Playwright移行で逆に苦労したポイント

ここまでPlaywright移行による改善点を中心に書いてきましたが、当然ながら良いことばかりではありません。
特に注意すべきなのは、「Playwrightへ変えれば全て解決する」という単純な話ではない点です。
実際の大規模スクレイピング案件では、既存システムとの整合性、運用資産、監視設計、対bot戦略など、多数の要素が絡みます。
そのため、Playwright移行には一定以上の再設計コストが発生します。
特に苦労したのは以下の2点でした。
- Selenium前提で構築された既存資産の移植
- ブラウザ検知対策の再設計
この2つは、単なるコード書き換え以上に影響が大きかったです。
既存Selenium資産の移植コストは軽くなかった
Playwrightは非常に優秀なライブラリですが、既存Seleniumコードをそのまま置換できるわけではありません。
むしろ実際には、「ブラウザ自動化の考え方そのもの」がかなり違います。
特に大規模案件では、単純なスクレイピングコード以外にも大量の周辺資産があります。
例えば、私たちの環境では以下がSelenium前提で作られていました。
- 共通Driverラッパー
- 待機ユーティリティ
- リトライ制御
- セッション管理
- エラーハンドリング
- Grid接続管理
- スクリーンショット取得基盤
問題は、Playwrightではこれらの前提がかなり変わることです。
例えばSelenium時代は、「Driverオブジェクト中心設計」になりやすいです。
しかしPlaywrightでは、Browser・Context・Pageという概念分離があります。
つまり、アーキテクチャ自体を組み直す必要がありました。
さらに厄介だったのは、既存コードが「Selenium特有の不安定性」を前提に最適化されていた点です。
例えば以下のようなコードは大量に存在していました。
for _ in range(5):
try:
element.click()
break
except Exception:
time.sleep(2)
Playwrightでは自動待機があるため、この種のコードは逆に悪影響になります。
つまり、単純移植ではなく、「不要になった防御コードを削除する作業」が必要でした。
これは意外と難しいです。
長期間運用されたコードには、「なぜ存在するか分からないsleep」が大量に残っているからです。
しかも、その待機が本当に不要かは実運用でしか分からないケースもあります。
結果として、以下のような段階的移行戦略を取りました。
| フェーズ | 内容 | 主な目的 |
|---|---|---|
| Phase 1 | 小規模機能移植 | API理解 |
| Phase 2 | 並列処理検証 | 性能確認 |
| Phase 3 | Worker再設計 | 非同期化 |
| Phase 4 | 本番段階移行 | 安定性検証 |
特に非同期化は想像以上に影響範囲が広かったです。
Playwright自体は同期APIも提供しています。
しかし、大規模スクレイピングでは結局 asyncio ベースへ寄せた方が効率が良いため、最終的には非同期化が必要になります。
すると、既存システム全体へ波及します。
例えば以下も見直し対象になりました。
- Queue設計
- DB接続管理
- ログ収集
- 監視処理
- タイムアウト制御
つまり、Playwright移行は「ライブラリ交換」というより、「ブラウザ自動化基盤の再設計」に近かったです。
また、開発チーム教育コストも無視できませんでした。
Selenium経験者ほど、「従来の癖」が残るためです。
例えば、Playwrightでは不要な明示待機を書いてしまったり、同期的な制御フローを維持しようとしたりします。
そのため、単なるAPI説明ではなく、「設計思想の違い」を共有する必要がありました。
ブラウザ検知対策は別途設計が必要だった
Playwright移行で誤解されやすいのが、「Playwrightならbot検知も回避できる」という認識です。
これは半分正しく、半分間違っています。
確かにPlaywrightはモダンブラウザとの統合度が高く、Selenium特有の検知ポイントは減ります。
しかし、近年のbot検知はそれほど単純ではありません。
現在の検知システムは、以下を複合的に見ています。
- ブラウザFingerprint
- TLS特性
- Canvas挙動
- WebGL情報
- 入力イベント
- 操作速度
- ネットワーク特性
- Cookie履歴
つまり、「ライブラリを変えれば終わり」という問題ではないのです。
実際、Playwrightへ移行した直後は、一部サイトで逆に検知率が上がりました。
理由は単純で、「Playwrightらしい挙動」が存在するからです。
特にHeadlessモードでは、サイト側が自動化を判定しやすいケースがあります。
そのため、追加対策が必要でした。
例えば、以下の調整を行いました。
- User-Agent最適化
- Headful実行切替
- Proxy分散
- Viewportランダム化
- Browser Context分離
- 入力速度調整
特に重要だったのは、「人間らしい操作」を演出することです。
例えばPlaywrightでは、マウス移動も制御できます。
await page.mouse.move(100, 200)
こうした細かい挙動が、bot検知回避では意外と効きます。
また、Playwrightは強力すぎるがゆえに、「高速すぎる操作」が逆に検知要因になるケースもあります。
つまり、性能を最大化すれば良いわけではありません。
ここは大規模スクレイピング特有の難しさです。
さらに厄介なのは、bot検知が継続的に進化する点です。
つまり、一度突破しても永続的には通用しません。
そのため、最終的には以下の考え方が重要になります。
- 検知回避を前提化しない
- 再試行戦略を持つ
- サイト別対策を分離する
- ブラウザFingerprintを監視する
- 検知率を可視化する
結果として分かったのは、Playwrightは非常に優秀なブラウザ自動化基盤ではあるものの、「スクレイピング運用の全問題を解決する魔法のライブラリ」ではないという点でした。
ただし少なくとも、Selenium時代に苦しめられていた「ブラウザ制御自体の不安定性」はかなり減少しました。
そのおかげで、ようやく本来向き合うべき「データ取得戦略」へ集中できるようになったのです。
スクレイピング基盤と相性が良かった周辺サービス構成

Playwright導入によってブラウザ自動化そのものはかなり安定しました。
しかし、大規模スクレイピング案件では、ブラウザライブラリ単体だけでシステム品質が決まるわけではありません。
実際には、以下のような周辺構成との組み合わせが非常に重要です。
- コンテナ実行基盤
- ジョブスケジューラ
- キュー管理
- ログ収集
- ストレージ
- CI/CD
- 監視システム
特にスクレイピング基盤では、「大量ジョブを継続的に回し続ける」必要があります。
そのため、一時的に高速でも、長期運用で不安定化する構成は現実的ではありません。
Playwright移行後、最終的にかなり相性が良かったのが「AWS + Docker + GitHub Actions」の組み合わせでした。
これは決して唯一の正解ではありませんが、少なくともPythonベースの大規模スクレイピングでは、かなり運用しやすい構成だったと感じています。
AWSとDockerを組み合わせた運用が安定していた
スクレイピング基盤では、「どこでブラウザを動かすか」が意外と重要です。
ローカルサーバー運用も可能ですが、大規模化すると以下の問題が出てきます。
- ノード追加負荷
- 障害時復旧
- リソース偏り
- IP分散
- メンテナンス工数
そのため、最終的にはクラウド運用へ寄せるケースが多くなります。
私たちの環境では、最終的にAWS上へ集約しました。
理由はいくつかありますが、特に大きかったのはDocker運用との相性です。
PlaywrightはDocker環境との親和性が高いため、ECSやEKSへ比較的自然に載せられます。
実際の構成イメージは、概ね以下のようなものでした。
| コンポーネント | 用途 | 採用理由 |
|---|---|---|
| ECS/EKS | Worker実行 | オートスケール |
| SQS | URLキュー | 耐障害性 |
| S3 | HTML保存 | 安価で大容量 |
| CloudWatch | ログ監視 | AWS統合性 |
| RDS | 結果管理 | SQL集計 |
特にSQSとの組み合わせは非常に安定しました。
スクレイピングでは、どうしても一部ジョブが失敗します。
しかしキュー駆動構成にしておくと、リトライ制御や再投入管理がかなり整理されます。
また、Playwright Workerを「短命コンテナ」として扱える点も良かったです。
従来のSelenium Gridでは、長時間稼働Nodeの管理がかなり大変でした。
例えば、以下のような問題が発生しやすいです。
- メモリリーク
- Chrome暴走
- Zombie process蓄積
- Driver不整合
- セッション詰まり
しかしPlaywright移行後は、「一定件数処理したらPod廃棄」という設計がかなり成立しやすくなりました。
これはDockerベース運用と非常に相性が良いです。
つまり、コンテナを「使い捨て前提」にできます。
この設計にすると、障害解析がかなり単純化します。
- 問題Workerを即廃棄
- 新Workerを自動起動
- ステートレス構成維持
- ノード再現性向上
特に大規模案件では、「復旧可能性」の方が重要です。
完全無停止を目指すより、「壊れても即復帰できる設計」の方が現実的だからです。
さらにAWS環境では、スポットインスタンス活用も効果的でした。
スクレイピングWorkerは比較的短命なので、中断耐性を持たせればコストをかなり削減できます。
結果として、以下の改善が見られました。
- インフラコスト削減
- ノード運用負荷減少
- オートスケール安定化
- Worker再現性向上
- 障害復旧高速化
つまり、Playwright移行は単なるライブラリ変更ではなく、「クラウドネイティブ運用へ寄せやすくなった」という側面が大きかったです。
GitHub Actionsによる定期スクレイピング自動化
もう一つ相性が良かったのがGitHub Actionsです。
以前はcronベースでスクレイピングを実行していました。
しかし大規模案件になると、cronだけでは徐々に管理が苦しくなります。
特に以下の問題がありました。
- 実行履歴が見えにくい
- 障害通知が弱い
- 並列実行制御が難しい
- デプロイ連携が煩雑
- 環境差異が出やすい
その点、GitHub Actionsはかなり実用的でした。
特にPlaywrightはCI環境との親和性が高く、Headlessブラウザ実行が比較的安定しています。
例えば、定期スクレイピングは以下のような構成へ整理できました。
on:
schedule:
- cron: "0 * * * *"
jobs:
scrape:
runs-on: ubuntu-latest
このシンプルさは非常に大きいです。
従来はサーバー側cron管理やSSH経由デプロイが必要でしたが、GitHub Actionsではコード管理と実行管理を一体化できます。
さらに便利だったのは、Actionsログの可視性です。
スクレイピング案件では、「なぜ失敗したか」を後から追跡できることが重要になります。
GitHub Actionsでは以下が比較的簡単に確認できます。
- 実行履歴
- 標準出力
- エラーログ
- Artifact
- 実行時間
特にPlaywrightのスクリーンショットやTraceをArtifact保存できるのは便利でした。
例えば、失敗時だけ以下をアップロードする運用を行っていました。
- HTML
- PNG
- Trace
- Consoleログ
- Networkログ
これによって、CI上で発生した問題をかなり再現しやすくなりました。
また、GitHub Actionsは小規模定期ジョブとも相性が良いです。
例えば以下の用途では非常に便利でした。
- 差分監視
- 価格追跡
- RSS補完
- APIキャッシュ更新
- 定期データ収集
一方で、超大規模クロールには向きません。
Actionsには実行時間制限や並列制約があるため、最終的にはEKS側ジョブへ逃がす必要があります。
ただし、「軽量な定期スクレイピング」用途では、かなり実務的な選択肢でした。
結果として、Playwright導入後は「ブラウザ自動化基盤」「Docker実行環境」「CI/CD自動化」がかなり自然に統合されました。
これはSelenium時代には意外と難しかった部分です。
特にクラウドネイティブ環境との親和性は、Playwrightの大きな強みの一つだと感じています。
大規模Pythonスクレイピング案件ではPlaywright移行が有力な選択肢になる

ここまで、実際にSeleniumからPlaywrightへ移行した際に発生した変化を、運用・性能・設計・インフラの観点から整理してきました。
結論から言えば、大規模Pythonスクレイピング案件において、Playwright移行はかなり有力な選択肢になり得ます。
もちろん、全ての案件で即座に移行すべきという話ではありません。
例えば、以下のようなケースでは、Seleniumでも十分実用的です。
- 小規模クロール
- 単発データ取得
- 社内向け簡易自動化
- テスト用途中心
- 並列数が少ない環境
特に既存Selenium資産が安定稼働している場合、無理に全面移行する必要はありません。
問題は、「運用規模が一定ラインを超えたとき」です。
具体的には、以下のような状況になると、Selenium特有の運用負荷が徐々に支配的になります。
- 数百万ページ規模
- 高並列クロール
- Kubernetes運用
- 長時間常時稼働
- Headlessブラウザ大量実行
- bot検知対応
- CI/CD統合
この領域に入ると、単なるコード改善だけでは吸収できなくなります。
実際、私たちのチームでも、最初は待機処理改善やNode追加で対応していました。
しかし最終的には、「ブラウザ自動化アーキテクチャそのもの」の限界が見え始めました。
特に大きかったのは、以下の問題です。
| 問題 | Selenium時代の課題 |
|---|---|
| 待機処理 | 防御コード肥大化 |
| 並列処理 | メモリ効率悪化 |
| Driver管理 | バージョン不整合 |
| Grid運用 | 管理負荷増大 |
| CI再現性 | 環境差異が多い |
| デバッグ | 障害解析が難しい |
一方、Playwright移行後は、この「ブラウザ制御周辺の不安定性」がかなり減少しました。
特に印象的だったのは、「運用時に人間が気を使うポイント」が減ったことです。
例えばSelenium時代は、以下を常に意識する必要がありました。
- ChromeDriver互換性
- sleepタイミング
- Nodeメモリ枯渇
- Zombieセッション
- Headless差異
- DOM不安定性
しかしPlaywrightでは、自動待機やBrowser Context設計によって、多くがライブラリ側へ吸収されています。
つまり、「人間がブラウザ挙動を毎回制御する必要」が減るのです。
これは長期運用において非常に大きいです。
大規模スクレイピングでは、コードを書く時間より、「障害対応」「再現調査」「Node復旧」に使う時間の方が長くなりやすいからです。
その意味でPlaywrightは、単なる高機能ライブラリではなく、「運用負荷を減らすための設計」がかなり意識されている印象でした。
特に以下の点は、大規模案件で非常に強力でした。
- Browser Contextによる軽量並列化
- 非同期処理との高い親和性
- Docker環境との統合容易性
- CI再現性の高さ
- Traceベースの障害解析
- Headless実行安定性
また、クラウドネイティブ環境との相性も非常に良いです。
Selenium時代は、Kubernetes上でGridを安定運用するだけでもかなり工数が必要でした。
しかしPlaywrightでは、比較的シンプルなWorker構成へ寄せられます。
これは実務ではかなり重要です。
システムは、複雑になるほど障害率が上がるからです。
特にスクレイピング基盤では、「高性能」より「継続運用できること」の方が重要になります。
また、Playwrightは今後さらに主流化する可能性が高いと感じています。
理由は単純で、現代Webとの相性が非常に良いからです。
現在のWebサイトは、以下の特徴を持つケースが増えています。
- SPA化
- 動的DOM生成
- API駆動描画
- 遅延レンダリング
- JavaScript依存
つまり、「単純HTML取得」が成立しにくくなっています。
その結果、ブラウザ自動化そのものが必要になる場面が増えています。
Playwrightは、この「モダンWeb前提」の設計がかなり強いです。
特にLocator中心設計や自動待機は、React/Vue系サイトで非常に効果を感じました。
一方で、Playwrightにも限界はあります。
例えば、以下は依然として難しい領域です。
- 高度bot検知回避
- CAPTCHA突破
- 超大規模IP分散
- Fingerprint完全偽装
- 人間行動シミュレーション
つまり、Playwrightは「ブラウザ制御問題」をかなり改善してくれますが、「スクレイピング全問題」を解決するわけではありません。
ただし少なくとも、Selenium時代のように「ブラウザ操作自体が不安定」という状態からはかなり脱却できます。
これは本当に大きかったです。
実際、Playwright移行後は、開発チームの意識も変わりました。
以前は、「Chromeが落ちる」「待機が不安定」「Gridが詰まる」といった問題に時間を使っていました。
しかし移行後は、ようやく以下へ集中できるようになったのです。
- データ品質
- クロール戦略
- 差分検知
- 分散制御
- コスト最適化
つまり、本来価値を出すべき領域へリソースを戻せました。
これは技術選定として非常に重要です。
ライブラリ選定の本質は、「何ができるか」ではなく、「何に集中できるようになるか」だからです。
その意味で、Playwrightは大規模Pythonスクレイピング案件において、単なる代替ライブラリではなく、「運用設計を変える選択肢」になり得ると感じています。


コメント