TL;DR
- Custom Wait Conditionsとdriver.execute_scriptを組み合わせる
Seleniumで「ある特定の動作を待つ」
最近はSPA的なサイトが増えてきたので「求めているDOMがブラウザにレンダリングされた」後に「ある特定の動作をさせたい」という気持ちになるときは多いのではないでしょうか。
そういう場合のために、Seleniumには、「待機する」用のClassが用意されています。例えば WebDriverWait
や expected_conditions
です。
公式ドキュメントの例ですが、下記のように書くことができます。
5. Waits — Selenium Python Bindings 2 documentation
下記の例の場合、「idがmyDynamicElement
の要素が出現するまで10秒ごと待機する」コードになります。
from selenium import webdriver from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC driver = webdriver.Firefox() driver.get("http://somedomain/url_that_delays_loading") try: element = WebDriverWait(driver, 10).until( EC.presence_of_element_located((By.ID, "myDynamicElement")) ) finally: driver.quit()
様々な条件で待つ
presence_of_element_located
以外にも下記のようにメソッドが用意されており、様々な条件で待機することができます。
* 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
JS Objectをチェックしたい
ただ問題としては全てHTMLの条件をチェックするということです。 待機する要素が多数に渡る場合は全ての要素をチェックするのはかなりしんどいので「一連の動作が終わったあとに、あるJS Objectの要素が書き換わる」ことをチェックしたいケースは少なからずあるのではないでしょうか。そんな場合は上記のメソッドは使えません。
Custom Wait Conditions
そんなときに使えるのがCustom Wait Conditionsで自分で特定の条件を書くことができます。
公式のも例がありますが、最もシンプルな例ではないので、もっとシンプルに書くと第二引数に driver
を受け取る __call__
さえあればOKです。
__call__
は「ある特定の条件を満たしていなかったら False
をそれ以外は受け取りたい要素を返す」ように実装します。
class CustomCondition: def __call__(self, driver): some_value = driver.some_method() if some_value == “some_condition": return some_value else: return False wait = WebDriverWait(driver, 1) some_condition = wait.until(CustomCondition())
この例だと「1秒ごとに状況をチェックしにいって、Falseでなかったら終了(値が帰ってくる)」コードになります。
driver.execute_script
でJS Objectの状況取得
あとはこの条件にJS Objectの状況を入れられれば任務完了になります。SeleniumからJS Objectを取得するには driver.execute_script
メソッドを使えば良いです。
7. WebDriver API — Selenium Python Bindings 2 documentation
例えばグローバルに以下のようなオブジェクトがあった時に
foo = {bar: ‘hoge'}
下記のようにして値を取得することが出来ます。
value = driver.driver.execute_script(‘return window.foo.bar’) print(value) # => hoge
組み合わせる
これをCustom Wait Conditionsと組み合わせるとこのようになります。
class CustomCondition: def __call__(self, driver): some_value = driver.driver.execute_script(‘return window.foo.bar’) if some_value == “hoge": return some_value else: return False wait = WebDriverWait(driver, 1) some_value = wait.until(CustomCondition())
これで「 foo.bar == “hoge”
になるまで動作を待機する」コードが書けました。めでたしめでた;。