推測しますが、Selenium を使用してサイトをスクレイピングし始めたところ、突然、恐ろしいタイムアウト エラー、古い要素の例外、不安定なロケーターに直面することになります。おなじみですね?
私たちの多くはそこに行ったことがあるでしょう!今日の動的な Web では、信頼性の高い自動化を実現するには、ページが完全に読み込まれるまで適切に待ってから対話することが重要です。
この 3200 語以上の包括的なガイドでは、プロの Web スクレイピング専門家としての 5 年以上の経験を活かして、Selenium で正常に待機するためのさまざまな方法とベスト プラクティスを探ります。
初心者でも、熟練したプロでも、安定性を確保するには堅牢な待機ロジックが必須のツールです。飛び込んでみましょう!
急いで入ってはいけない理由
Web の初期の頃、ページのほとんどは順次レンダリングされる単純な HTML でした。スクレイパーはページの読み込み時にすぐに抽出を開始する可能性があります。
しかし、今日のウェブは非常にダイナミックです。によると グーグルリサーチ最初のペイントにかかる時間の中央値は 1.7 秒ですが、完全にインタラクティブになるまでの時間の中央値は驚くべきものです。 15 seconds。コンテンツの読み込みにはかなりの時間がかかります。
スクレーパーとして、急いで作業を開始すると、次のような一般的な問題に直面することになります。
- 要素がまだレンダリングされていないため、ボタンのクリック エラーが発生する
- サーバーのコンテンツをロードしていないテーブルからデータを読み取ろうとしています
- 画面に表示されていない入力にテキストを送信する
- ページの読み込み後に設定される空の要素をスクレイピングする
これらのタイプの例外は、操作する前にページの準備ができるまで長時間待つ必要があるという症状です。
数字で見る: ページの読み込み時間
どれくらいの時間を待つ必要があるかを理解するために、ページ読み込みパフォーマンスに関する実際のメトリクスをいくつか見てみましょう。 2020 年のウェブの現状レポート アカマイによる:
- インタラクティブになるまでの時間の中央値: 15s
- 平均ページ重量: 2744KB
- 平均リクエスト数: 105
- ページあたりの平均画像: 53
- ページあたりの JavaScript バイト数: 453KB
現在、ページはより大きく、より複雑になっており、最初の対応の後にさらに多くの作業が発生しています。スクレイパーにとっては、最初のペイントだけでなく、インタラクティブ性を待つことが重要です。
待機なしによって発生する一般的な例外
要素の準備がまだ整っていない場合に発生する可能性のある特定の例外をいくつか示します。
- StaleElementReferenceException – フェッチ後に要素が DOM から削除されました
- ElementNotInteractableException – 見えない要素をクリックしようとしています
- NoSuchElementException – 要素がまだ存在しないため、検索がタイムアウトしました
これらはそれぞれ、スクレーパーがさらに待機する必要があることを示しています。
明示的な待機はあなたの味方です
これらのエラーを回避するには、ページが完全にレンダリングされるまで待ってから操作する必要があります。 Selenium には主に 2 つのアプローチがあります。
暗黙的な待機 – ドライバーにグローバル待機時間を設定する
明示的な待機 – 特定の条件が発生するのを待ちます
ほとんどの場合、暗黙的な待機よりも明示的な待機の方が優先されます。その理由を理解しましょう。
暗黙的な待機: スレッジハンマーアプローチ
暗黙的な待機では、要素を見つけるときに DOM をポーリングするためにドライバーにタイムアウトを設定します。これは、電話をかけるたびに次のことを意味します。
driver.find_element_by_id(‘some-id‘)
ドライバーは、NoSuchElementException をスローする前に、その要素を見つけるために暗黙的な待機期間まで再試行します。
次のように使用できます。
driver = webdriver.Chrome()
driver.implicitly_wait(10)
すぐに存在しない場合、すべての検索で要素の検索が最大 10 秒間再試行されるようになりました。
欠点は、ページの準備が整っているかどうかを判断するために必要でないロケーターも含めて、すべてのロケーターを待機することです。これにより、スクレーパーの速度が大幅に低下する可能性があります。
暗黙的な待機は、すべての要素のフェッチに 5 秒間のスリープを追加するようなものだと考えてください。それは合計されます!
明示的な待機の精度
明示的な待機により、続行する前に準備が整っていることを示す特定の条件を正確に待つことができます。
重要なアイデアは次のとおりです。
- 必要なときだけ待つ – ページの準備に関係のない不必要な待機を回避します
- 正確な条件 – 単なるブランケットタイムではなく、正確な要素または状態を待ちます
- 柔軟性 – さまざまな条件でページごとの待機ロジックをカスタマイズ
- 可読 – 古いコードを再検討するときに意図を理解しやすい
要素が表示されるのを待機する典型的な例を次に示します。
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
WebDriverWait(driver, 10).until(
EC.presence_of_element_located((By.ID, "myDynamicElement"))
)
これにより、ID「myDynamicElement」の要素がロードされるか、10 秒が経過するまで実行が一時停止されます。
その他の有用な期待条件 セレンによって提供される 次のとおりです。
title_contains()
– ページタイトルが更新されるまで待ちますstaleness_of()
– 要素が DOM にアタッチされなくなるまで待ちますelement_to_be_clickable()
– 要素が表示されて有効になるまで待ちます
明示的なビートと暗黙的なビート: 実際の例
2 つの待機を実際の例で比較してみましょう。
ナビゲーション バー、左側のパネル、メイン コンテンツを備えたサイトをスクレイピングしているとします。
待つ必要がある重要な要素は、データがレンダリングされる ID「#main-content」です。
暗黙的な待機の場合:
- 必要でない場合でも、要素の検索ごとに 10 秒追加される
- 速すぎると依然として古い要素エラーが発生する傾向があります
明示的に待機する場合:
- #main-content セレクターは必要な場合にのみ待機します
- ナビやサイドパネルの不必要な待ち時間を避ける
- データがロードされるまで待ってから続行してください
要素などの 1 つの準備完了状態を選択的に待機することで、不必要な遅延を回避します。
効果的な明示的待機のパターン
明示的待機が最適な方法であると確信できたので、明示的待機を効果的に使用するためのベスト プラクティスをいくつか検討してみましょう。
ページの読み込み待機
ドキュメントの準備完了状態を待つことは、読み込みがいつ完了したかを判断するための一般的な手法です。
WebDriverWait(driver, 10).until(
lambda d: d.execute_script(‘return document.readyState‘) == ‘complete‘
)
これにより、準備完了状態が「完了」になるまでブラウザがポーリングされ、すべてのアセットがロードされたことが示されます。
より軽量なパターンは、特定の高レベル要素を監視します。
WebDriverWait(driver, 10).until(
EC.presence_of_element_located((By.ID, "main-content"))
)
これは、メイン コンテンツ セクションが読み込まれるときに成功し、他のすべてを待つ必要はありません。
アクションごとの待機
要素をクリックするなどのアクションを実行する直前に待機することもできます。
menu = WebDriverWait(driver, 10).until(
EC.presence_of_element_located((By.ID, "top-menu"))
)
submenu = WebDriverWait(driver, 10).until(
EC.element_to_be_clickable((By.ID, "submenu"))
)
submenu.click()
これにより、クリックする前にトップ メニューとサブメニューの両方が準備完了になります。
並列待機
複数の条件を待機することで、ページの準備ができていることを確認できます。
WebDriverWait(driver, 10).until(
EC.presence_of_element_located((By.ID, "header")),
EC.presence_of_element_located((By.ID, "footer")),
EC.presence_of_element_located((By.ID, "main"))
)
ヘッダー、フッター、メイン コンテンツの読み込みを必須にすることで、誤検知が減少します。
チェーン化された待機とネストされた待機
高度なシナリオでは、待機をネストすることもできます。
element = WebDriverWait(driver, 10).until(
EC.presence_of_element_located((By.ID, "dropdown"))
)
menu = WebDriverWait(element, 10).until(
EC.presence_of_element_located((By.ID, "menu"))
)
これは、最初に親要素を待機し、次にその中の子要素を待機します。
AJAX ポーリング待機
一部のサイトは、継続的な AJAX リクエストを介してロードされます。変更を待機してループすることができます。
while True:
current_count = driver.find_element_by_id(‘result-count‘).text
# If count changed since last check, page is still loading
if current_count != previous_count:
previous_count = current_count
continue
break # Page loaded!
これは、要素をポーリングして変更を探し、読み込みを検出します。
非同期待機
asyncio のような非同期フレームワークでは、Promise を待機できます。
await page.waitForSelector(‘#content‘)
構文は少し異なりますが、非同期待機を提供します。
暗黙的 + 明示的組み合わせ
暗黙的待機と明示的待機の両方を組み合わせることもできます。
driver.implicitly_wait(10)
my_element = WebDriverWait(driver, 10).until(
EC.presence_of_element_located((By.ID, "my-element"))
)
こうすることで、特定の待機だけでなく全体的な待機も行うことができます。適切な期間を使用するようにしてください。
準備ができていることを示すロケーターの選択
待機するロケーターを選択するときは、次の基準に一致する要素が必要です。
- 読み込みプロセスの後半に表示されます
- 変更されない固有の ID またはクラスを持つ
- スクロールしないと見えない部分の上に配置されているため、迅速にチェックできます
- サイト変更による移転の可能性は低い
- DOM から削除されて古くならないようにする
一般的な例は次のとおりです。
- アセットの後にロードされるメインヘッダーまたはナビゲーション
- 主要なコンテンツコンテナまたはウィジェット
- ページフッター
- ボタンなどの小さな動的 UI 要素
スピナーなどの負荷インジケーターも、消えたときに待機トリガーとして最適です。
最適な待機のためのタイムアウトの調整
タイムアウトの設定が長すぎると、スクレイパーの速度が大幅に低下する可能性がありますが、短すぎると不安定な障害が発生する可能性があります。
チューニング期間に関するベスト プラクティスをいくつか示します。
- ページ読み込みタイムアウトを 10 ~ 20 秒ほど長く設定します。
- 個々の要素には 3 ~ 5 秒などの短いタイムアウトを使用します。
- モバイルとデスクトップのブラウザのパフォーマンスを考慮してください。
- ブロードバンドと 3G のネットワーク遅延を考慮します。
- タイムアウト エラーを監視し、必要に応じて値を大きく調整します。
- 一般的な読み込み時間についてページ読み込みウォーターフォールを分析します。
- バッファーとして 1 ~ 2 秒追加します。
- コードベース全体で同様の待機を標準化します。
より多くのページをスクレイピングするほど、信頼性を高めるための最適な待機についてより良い直観が得られるようになります。
待機障害とタイムアウト障害の処理
待機が堅牢であっても、タイムアウトが発生する場合があります。それらに対処するいくつかの方法を次に示します。
- デバッグの詳細をログに記録する – プリントを追加すると、待機が失敗した場所を診断するのに役立ちます。
- タイムアウト時に再試行 – 失敗した場合は、短い明示的な待機を最大 3 回再試行します。
- タイムアウトを増やす – タイムアウトが多数発生する場合は、待機時間を段階的に増やします。
- try/excel を使用する – StaleElementReference などの特定の例外をキャッチします。
- 失敗時に無効にする – 失敗が繰り返された場合は待機をスキップして、テストを続行できます。
ある程度の回復力が組み込まれているため、このような散発的な問題によってスクレイパーが壊れることはありません。
他言語でお待ちしています
これまでの例は Python で説明しましたが、明示的な待機は言語を問わず利用できます。
- Java –
WebDriverWait
&ExpectedConditions
- C# –
WebDriverWait
&ExpectedConditions
- ルビー –
WebDriver::Wait
&ExpectedConditions
- JavaScriptを –
browser.wait()
およびユーティリティメソッド
概念は非常に似ていますが、構文がわずかに異なるだけです。
Selenium を超えて: さらなる待機ツール
Selenium 以外にも役立つ待機ライブラリがいくつかあります。
- Time –
time.sleep()
単純ですが、すべての実行を一時停止します。 - リトライ - パッケージの再試行 再試行と待機が簡単になります。
- あいおhttp –
await response.text()
ネットワーク呼び出しが完了するのを待ちます。 - 美しいスープ –
BeautifulSoup(page.content, features="lxml")
完全な解析を待ちます。 - スクラップ –
yield scrapy.Request(url, callback=self.parse)
非同期です。
これらを Selenium と組み合わせると、コード全体で堅牢な待機が実現します。
要約: よく待って確実に廃棄する
最後に、重要なポイントを 5 つ挙げます。
明示的な待機を使用する – 不必要なタイムアウトを回避し、特定の条件をターゲットにします。
複数の信号を待つ – ヘッダー、本文、フッターなどの待機を組み合わせて、ページの準備ができているかを確認します。
タイムアウトを賢く調整する – 実際のページ読み込みデータに基づいて値を設定し、遅延を最適化します。
待機を標準化する – コードベース全体で一貫したパターンを再利用します。
回復力を追加する – 動的ページを考慮して再試行と失敗処理を実装します。
待つことは最初は退屈に思えるかもしれません。しかし、堅牢な待機ロジックに投資すると、最新の Web 向けに準備された信頼性の高い復元力のあるスクレーパーが得られます。
プロのウェブスクレイピングスペシャリストとしての私の長年の経験から抽出したこれらのパターンとヒントが、あなたがうまく待つのに役立つことを願っています。スクラップオン!