Webブラウザの自動操作において、要素が表示されるまで待機する処理は安定したスクレイピングやテストを実現するために不可欠です。
SeleniumではWebDriverWaitとexpected_conditionsを組み合わせるのが一般的ですが、標準の条件だけでは対応できない複雑なケースが存在します。
例えば、特定の要素のテキストが動的に変化した瞬間を捉えたい場合や、特定の属性値が書き換わるまで待ちたい場合などです。
このような場面で非常に役立つのが、lambda(ラムダ)式を用いたカスタム待機条件の実装です。
本記事では、PythonのSeleniumを使用して、lambda式で独自の待機ロジックを構築する方法について、実用的なコード例を交えて詳しく解説します。
Seleniumにおける待機処理の重要性
モダンなWebアプリケーションはJavaScriptを多用しており、DOMの更新が非同期に行われることが当たり前となっています。
プログラムがページの読み込み完了を待たずに操作を続行しようとすると、要素が見つからないことによるエラーが発生します。
この問題を解決するために、指定した条件が満たされるまで処理を一時停止する「明示的待機」が必要になります。
明示的待機を適切に設定することで、実行速度を損なうことなく、スクリプトの堅牢性を大幅に向上させることが可能です。
標準的なexpected_conditionsの限界
Seleniumの標準ライブラリには、expected_conditions(EC)という便利なクラスが用意されています。
要素の存在確認やクリック可能状態の判定など、基本的な操作にはこれで十分対応できます。
しかし、「リスト内の要素が5個以上になったら続行する」といった、数値の比較や複雑なロジックを伴う条件は用意されていません。
独自の条件を記述したい場合、従来のクラス定義を行う方法もありますが、コードが冗長になりがちです。
lambda式(無名関数)を用いた待機の基本構文
Pythonのlambda式を使用すると、関数を定義することなく、その場で簡潔にロジックを記述できます。
WebDriverWaitのuntilメソッドは、引数として受け取った関数にdriverを渡し、その戻り値が真(Trueまたは要素オブジェクトなど)になるまで待機します。
基本的な構文は以下の通りです。
from selenium.webdriver.support.ui import WebDriverWait
# タイムアウトを10秒に設定
wait = WebDriverWait(driver, 10)
# lambda式で条件を指定
wait.until(lambda d: d.find_element(By.ID, "target_id").is_displayed())
このコードでは、dという変数にdriverが渡され、指定した要素が表示された瞬間に待機が解除されます。
untilメソッドは、内部で渡された関数がFalseやNoneを返す限り、ループを繰り返して条件判定を続けます。
実践的なカスタム待機条件の実装例
ここからは、実務でよく遭遇する具体的なシナリオをもとに、lambda式を活用したカスタム待機の実装方法を見ていきましょう。
要素のテキストが特定の文字列を含むまで待機
通信状況によって内容が書き換わるステータス表示などを待機する場合に有効です。
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
driver = webdriver.Chrome()
driver.get("https://example.com/status")
# ステータスが「完了」になるまで待機する
wait = WebDriverWait(driver, 15)
wait.until(lambda d: "完了" in d.find_element(By.ID, "status_label").text)
print("処理が完了しました。")
このコードは、status_labelというIDを持つ要素のテキストに「完了」という文字が含まれるまで、最大15秒間ループを回します。
特定の属性値が変化するまで待機
ボタンのクラス名がdisabledからactiveに変わるまで待ちたい場合などに非常に便利です。
# ボタンが活性化(class属性から 'disabled' が消える)するのを待つ
target_button = driver.find_element(By.ID, "submit_btn")
wait.until(lambda d: "disabled" not in target_button.get_attribute("class"))
このように、すでに取得済みの要素オブジェクトを利用して条件を判定することも可能です。
子要素の数が指定した数に達するまで待機
動的なリスト読み込み(無限スクロールや追加読み込み)の完了を判定する際に役立ちます。
# アイテムリストの数が10個以上になるまで待機
wait.until(lambda d: len(d.find_elements(By.CSS_SELECTOR, ".item-list li")) >= 10)
# 実行結果の確認
current_count = len(driver.find_elements(By.CSS_SELECTOR, ".item-list li"))
print(f"現在のアイテム数: {current_count}")
現在のアイテム数: 10
find_elementsの結果であるリストの長さを評価基準にすることで、標準のECでは不可能な「数の判定」が行えます。
JavaScriptの実行結果を待機条件にする
Seleniumの機能だけでは判定が難しい場合、ブラウザ側でJavaScriptを実行し、その戻り値を待機条件にすることもできます。
例えば、ページ全体の読み込み状態(readyState)や、特定のグローバル変数の値をチェックする場合です。
# ページのレンダリングが完了(complete)するまで待機
wait.until(lambda d: d.execute_script("return document.readyState") == "complete")
execute_scriptの結果を直接lambda式の戻り値にすることで、複雑なブラウザ内部の状態をトリガーにできます。
標準のexpected_conditionsとカスタム条件の使い分け
すべてをlambda式で書く必要はありません。
状況に応じて使い分けるのがベストです。
| 待機方法 | メリット | 適したケース |
|---|---|---|
| expected_conditions (EC) | 可読性が高く、標準的 | 要素の表示、クリック、存在確認 |
| lambda式 (カスタム) | 柔軟性が極めて高い | 数値比較、属性値の判定、複数条件の組み合わせ |
| JavaScript連携 | ブラウザの内部状態を考慮できる | readyStateの確認、JS変数の監視 |
基本的な操作は標準のECを使い、ECでは表現できない特殊な条件が必要になった際にlambda式を導入するのがプロフェッショナルなアプローチです。
実装時の注意点とエラーハンドリング
lambda式を使ったカスタム待機を実装する際には、いくつか注意すべきポイントがあります。
NoSuchElementExceptionへの対策
lambda式の中で要素を探す際、まだ要素がDOM上に存在しないとNoSuchElementExceptionが発生してスクリプトが停止してしまいます。
これを防ぐには、要素の有無をリストで判定するか、try-exceptを検討する必要があります。
# 要素が存在しない場合にエラーにならない書き方
wait.until(lambda d: len(d.find_elements(By.ID, "slow_element")) > 0)
find_elements(複数形)を使えば、要素が見つからない場合に空のリストを返すだけでエラーにはならないため、安全に待機できます。
タイムアウトの設定
カスタム待機は非常に便利ですが、条件が永久に満たされない場合に備えて、必ず適切なタイムアウト時間を設定してください。
WebDriverWaitの第2引数で秒数を指定し、タイムアウト時にはTimeoutExceptionが発生するように管理します。
from selenium.common.exceptions import TimeoutException
try:
wait.until(lambda d: d.find_element(By.ID, "non_existent").is_displayed())
except TimeoutException:
print("指定した条件が時間内に満たされませんでした。")
まとめ
SeleniumのWebDriverWaitとlambda式を組み合わせることで、自動化スクリプトの柔軟性は飛躍的に高まります。
標準のexpected_conditionsだけでは対応できない「テキストの変化」「属性値の書き換え」「要素数の増減」といった複雑な条件も、わずか1行のコードで実装可能です。
ただし、要素が見つからない場合のエラー回避策としてfind_elementsを活用するなど、エラー耐性を考慮した記述を心がけることが重要です。
今回紹介したカスタム待機のテクニックを駆使して、より安定し、メンテナンス性の高いブラウザ自動操作プログラムを構築してください。
