Seleniumでスクレイピングをする際にページの読み込みが完了するまで待機することがよくあります。このような処理を待機処理といいます。
取得する要素が静的に決まっていて、HTMLをパースする時点で要素の値が存在している場合は既存のメソッドで待機処理を実現することが可能ですが、非同期通信(Ajax)によって要素の値が動的に決定される場合、すなわち描画が終わった後に動的な変更によって要素の値が決まる場合は、既存のメソッドで待機処理を実現することができません。
今回の記事ではこうした課題を解決するための手法を紹介します。
背景
使い捨てEメールサービス「10 Minutes Mail」は、ユーザーに割り当てられるEメールは非同期処理によって取得しています。

このEメールをSeleniumで取得することが目標です。
しかしながら、通常の待機処理では非同期的かつ動的なデータを取得するメソッドが用意されていないため、自分で作る必要があります。
通常の待機処理
通常の待機処理は、例として以下が挙げられます。
メソッド | 条件 |
---|---|
visibility_of_element_located | 指定した要素の表示される |
text_to_be_present_in_element | 指定したテキストが表示される |
presence_of_all_elements_located | ページ内のすべての要素が読み込まれる |
presence_of_element_located | DOM要素内に指定した要素が現れる |
alert_is_present | Alertが表示される |
element_to_be_clickable | 要素がクリック出来る状態になる |
試したこと
まず初めに試したのは以下です。
# text_to_be_present_in_elementを使う
# Eメールアドレスは待機した後にわかるので待機処理の段階で記述できない。
element = driver_wait.until(EC.text_to_be_present_in_element((By.XPATH, xpath), "{{Eメールアドレス}}"))
しかしながら、このメソッドでは、動的にテキストを指定することができません。当然ですが、どんなEメールアドレスが与えられるかがわからないので、このメソッドにテキストを指定することができないのです。
やりたいこと
Eメールアドレスを取得したいが、どんなEメールアドレスかはわからない。
→正規表現で取ればいいのでは?この時、監視対象のHTML要素に入るのはメールアドレスであることは自明なので、任意の文字列にマッチする正規表現を用いればよい。
結果、書いたコードは以下です。
import re
def email_has_arrived(x):
element = x.find_element_by_xpath(xpath)
if bool(re.match("[^ ]", element.get_attribute("value"))):
return element
else:
return False
# メールアドレスを取得
xpath = '//*[@id="mail"]'
element = driver_wait.until(lambda x: email_has_arrived(x))
今回は、「Python Seleniumで汎用waitしたくない?」を参考にしました。詳細はそちらでご確認ください。
また、今回のやり方は正式なものではないので、ちゃんとやりたい方は「Selenium & Python で待機条件をカスタマイズするやり方」を参照されるといいと思います。
関連ブログ
https://kentarojay.com/archives/247