Seleniumのコードを安定して動かすためにWaitを使って要素を待つ
以前の記事 SeleniumでFirefoxの複数タブを同時に開く時にポップアップブロックされる問題に対処する では、ちょっとニッチなFirefoxでSeleniumを動かす時のTipsを紹介しました。
今回はSeleniumのコードを書く時のTipsを紹介します。
Seleniumは動作が早すぎる
本来人間が行う操作をSeleniumが肩代わりをしてコードを実行することで高速に操作するわけですが、そこでうっかり見落としがちなのは 「待ち」 です。
例えば、ボタンをクリックなどの何らかのイベントをトリガーとして、ブラウザは通信や画面の描画をします。 ブラウザが処理している間、人間はブラウザ動作の完了を待っています。一通り画面がさわれそうになったら 「あ、触れそうかな」 と判断して、次の動作をします。 これと同じことをSeleniumでも実現する必要があるのです。
Seleniumは人間の操作と比べて動作が高速であるため、正しく「待ち」を設定しましょう。
WebDriverWait を使って表示を待つ
WebDriverWait
を使って、 任意のHTMLの要素が特定の状態になるまで待つ 方法を紹介します。
sleep
関数で指定秒数を待つ方法よりも、実行環境への依存が減らせますし、実行時間の削減にも繋がります。
サンプルコードは以下のようになります。
1from selenium import webdriver
2from selenium.webdriver.common.by import By
3from selenium.webdriver.support.ui import WebDriverWait
4from selenium.webdriver.support import expected_conditions as EC
5
6driver = webdriver.Firefox()
7driver.get("http://somedomain/url_that_delays_loading")
8
9element = WebDriverWait(driver, 10).until(
10 EC.presence_of_element_located((By.ID, "myDynamicElement"))
11)
WebDriverWait
への第一引数はWaitさせたいWebDriver、第二引数はタイムアウト値を指定します。
until
の中では 「充足されるまでWebDriverをWaitさせ続ける条件」 を記載します。
上記の例では、myDynamicElement
というid属性を持ったHTMLの要素が存在するまで待っています。
presence_of_element_located
がHTML要素の存在をチェックするための関数ですが、
他にも様々な条件が用意されています。
- title_is
- title_contains
- presence_of_element_located
- visibility_of_element_located
- visibility_of
- presence_of_all_elements_located
- text_to_be_present_in_element
- text_to_be_present_in_element_value
- frame_to_be_available_and_switch_to_it
- invisibility_of_element_located
- element_to_be_clickable
- staleness_of
- element_to_be_selected
- element_located_to_be_selected
- element_selection_state_to_be
- element_located_selection_state_to_be
- alert_is_present
URLは変えずにJavaScriptによってHTML要素の表示/非表示を切り替えて画面遷移を表現するWebアプリケーションの場合には、
要素が見えているかで判断したほうが良いため、 visibility_of_element_located
を使います。
暗黙的な待ち時間を設定する
先程は WebDriverWait
にタイムアウト値を設定しましたが、
共通的に暗黙的な待ち時間を設定することもできます。それが implicitly_wait
を使う方法です。
1from selenium import webdriver
2
3driver = webdriver.Firefox()
4driver.implicitly_wait(10) # seconds