QR Send

スマホとPCのChrome間で、URLやテキストをQRコード1回のペアリングで送り合える自作Chrome拡張。アカウント不要・無料。

使ってみる →

「一度動いても壊れる」Seleniumスクレイパーを安定運用するための設計チェックリスト

Selenium・自動化

「一度動いた」と「安定して動き続ける」の間には大きな溝がある

Seleniumで書いたスクレイパーは、手元で1回叩いて画面が想定通りに動いた時点では、まだ半分もできていません。本番環境で日次・時間単位で回し続けると、待機のタイミングのずれ、クリックの失敗、要素参照の破棄、ドライバプロセスの異常終了、長時間稼働によるメモリ肥大、ログの破損、対象サイトの追加、実行環境の維持といった問題が入れ替わり立ち替わり顔を出します。

それぞれの対処法はこのブログで個別に書いてきましたが、実際に新しいスクレイパーを作るときは「今回はどの観点を確認したか」を横断的にチェックできる一覧があるほうが実務的です。ここでは、運用に乗せる前に潰しておきたい8つの設計観点をまとめました。各項目の詳細な実装は個別記事にリンクしているので、該当する箇所だけ読みに行ってください。

1. 待機は sleep ではなく明示的待機にする

固定のsleep(3)は、軽いページでは無駄に待ち、重いページでは待ち時間が足りずにNoSuchElementErrorで落ちます。同じコードなのに実行するタイミングで成否が変わる「フレーキーテスト」の典型的な原因の一つです。WebDriverWaituntilによる明示的待機に置き換え、標準条件で足りない場合は自作の待機条件を用意し、タイムアウト時には何を待っていたか分かるメッセージを残す設計にしておくと、失敗時の調査コストが大きく下がります。詳しくは「Seleniumの明示的待機設計」を参照してください。

2. クリックは多段フォールバックにする

element.clickは、他の要素に覆われている・アニメーション中・画面外にあるなどの理由でelement click interceptedを返すことがあります。原因は毎回同じではないので、通常クリック→スクロールしてから再試行→JavaScriptでのclick()実行、のように複数の手段を順番に試すフォールバック構成にしておくと、個別の原因を都度追いかけずに済みます。詳しくは「element click interceptedの4段フォールバック」を参照してください。

3. 要素参照は使い回さず、使う直前に掴み直す

一覧画面やフォームを操作していると、find_elementで取得した要素を変数に保持したまま使い回した結果、DOMが更新された途端にStaleElementReferenceErrorが飛ぶことがあります。要素はキャッシュせず「使う直前に毎回findし直す」パターンに統一し、リトライヘルパーを共通化しておくと、この例外そのものを設計上根絶できます。詳しくは「StaleElementReferenceErrorの再取得パターン」を参照してください。

4. driverが壊れたら、直そうとせず作り直す

driver.getが突然失敗するようになったとき、原因をその場で特定しようとするより、driver.quitでドライバを一度手放し、丸ごと再生成してからリトライするほうが復旧が早く再現性も高いです。この設計は業種の異なる複数の自動化案件で、それぞれ独立に同じ形へ収束していました。詳しくは「driver再生成によるリトライ設計」を参照してください。

5. 長時間実行には自己回復の仕組みを組み込む

何十分・何時間も同じブラウザセッションを使い回すバッチは、途中で必ず何かが起きます。稼働時間に上限を決めて自分から終了しスケジューラに再起動を任せる、ドライバプロセスは確実にkillしてから作り直す、JS実行エラーの連続をメモリ肥大のサインとして扱いDOMを間引く、といった「壊れたら回復する」前提の設計が有効です。詳しくは「長時間実行バッチの自己回復設計」を参照してください。

6. ログの書き込みは壊れないやり方にする

実行結果をCSVに集計・記録する処理を「既存ファイルを読み込んで上書き保存」で実装していると、書き込みの途中でプロセスが強制終了した瞬間にファイルが壊れます。長時間バッチはネットワーク断やスケジューラによる強制終了で不意に止まる場面が少なくないため、tmpファイルに書いてからrenameする、壊れないアトミックな書き込みに統一しておくべきです。詳しくは「tmp→renameによるCSVアトミック書き込み」を参照してください。

7. 対象サイトが増えるなら、共通設計をあらかじめ用意する

一覧ページを巡回するスクレイパーは、最初の1サイトならすぐ書けます。しかし対象が増えるたびに「動いているコードをコピーしてセレクタだけ書き換える」対応を続けると、待機やリトライ、ログの形式を直したいときに全コピー先を回って修正する羽目になります。基底クラス・サイトごとのアダプタ・設定の3層に分けて共通処理とサイト差分を最初から切り分けておくと、この増殖を防げます。詳しくは「複数サイト対応スクレイパーのアダプタ設計」を参照してください。

8. 定期実行はローカルPCに頼らない

cronやタスクスケジューラでローカルPCを常時起動してSeleniumバッチを回す運用は、PCの再起動やスリープで簡単に止まります。GitHub Actionsのschedule(cron構文)とヘッドレスChromeを組み合わせれば、無料枠内に収まる形で定期実行環境を外に出せます。詳しくは「GitHub Actionsによる定期実行」を参照してください。

まとめ

新しいSeleniumスクレイパーを運用に乗せる前に、以下をチェックリストとして確認してください。

  • 待機を固定sleepではなく明示的待機にしているか
  • クリック処理に複数手段のフォールバックを用意しているか
  • 要素参照をキャッシュせず、使う直前に毎回取得し直しているか
  • driverの異常時に、直そうとせず作り直すリトライ設計にしているか
  • 長時間実行を前提に、稼働時間の上限とドライバ再作成の仕組みを入れているか
  • ログやCSVの書き込みを、実行中に落ちても壊れないアトミックな方式にしているか
  • 対象サイトが増える前提で、共通処理とサイト差分を切り分けた設計にしているか
  • 定期実行をローカルPCに依存しない形で組んでいるか

一つでも未対応の項目があれば、それが次に本番で踏む障害の候補です。各項目の実装は個別記事にまとめてあるので、必要な箇所から手を入れてください。

コメント

タイトルとURLをコピーしました