ストックドッグ

KatoTakahiro。金融系の会社で働くSEが株やPython、その他諸々について書いています。サービスも運営してます→http://fmbrain.work

seleniumの2つの待機方法

seleniumの待機方法でsleep.time()を使っている人は、この記事を読んで、心を清めてほしい。


seleniumには、2つの待機方法がある。

  • Explicit Waits(明示的な待機)
  • Implicit Waits(暗黙的な待機)

この2つの待機方法の使い分けは、こんな感じ。

  • Explicit Waits = 複雑な条件で待機させたい
  • Implicit Waits = 単純な待機

Implicit Waitsは、要素が出現するまで待機する、という設定になる。

そのため、ごくごく単純なスクレイピング、例えば静的サイトなどであればImplicit Waitsで十分である。


しかし、非同期のWebサイトであればImplicit Waitsでは対応できないことが多い。

例えば、スクロールしてくことでページが読み込まれていくサイトのスクレイピングなど。


非同期のWebサイトからスクレイピングするときは、「このログインボタンがクリックできるようになるまで待つ」などの条件を指定したほうが、エラーは出にくいコードが書ける。


ま、使い分けと言いつつ、実際に使うときはImplicit Waitsに10秒くらい指定して、各ポイントでExplicit Waitsに30秒とかを指定することが多い。

implicitly_waitの使い方

driver = webdriver.Chrome()
driver.implicitly_wait(20)

これだけ。

とても簡単。書くのは1回だけ。

デフォルトは、0が設定されている。

上の例では、要素が読み込まれるまで20秒待機している。

例えば、Webページの読み込むスピードが単純に遅いときは、コードが実行された時点で要素が現れておらず、エラーとなることがある。

selenium.common.exceptions.ElementNotVisibleException: Message: element not visible: Element is not currently visible and may not be manipulated

seleniumを使っていれば、このエラーに100%エンカウントしているはず。

その時は、implicitly_wait(20)の待機時間を充分な時間を指定して、ページが読み込まれるのを待てばいい。


ただ、implicitly_waitは基本的に一律の設定である。

この処理は何秒待つ、とかの指定はできない。

できないことはないけど、絶対に人に見せたくないコードになる。

↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓

driver.implicitly_wait(5)
driver.find_element_by_id("mul_t").send_keys("ログインしたい")

driver.implicitly_wait(20)
driver.find_element_by_xpath('//*[@id="control_object_class1"]/div/div[3]/div[1]/div').click()

driver.implicitly_wait(10)
driver.find_element_by_name("forward_sch").click()

Explicit Waitsの使い方

Explicit Waitsは複雑な条件を指定して、条件が満たされるまで待機するという設定ができる。

この例は、"sch"というIDを持つ要素がクリックできるようになるまで最大30待機する、という指示。

↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓

from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By

WebDriverWait(driver, 30).until(EC.element_to_be_clickable((By.ID, "sch")))

# 探している要素↓ よくある検索ボタンのhtmlだと思う。
# <input type="button" value="検索" id="sch">


seleniumの公式ページにものっているし、定番はこれ。

IDが"myDynamicElement"の要素が読み込まれるまで待機。使いやすい。

WebDriverWait(driver, 30).until(EC.presence_of_element_located((By.ID, "myDynamicElement"))


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


また、By.はID以外も指定できる。

ID
XPATH
LINK_TEXT
PARTIAL_LINK_TEXT
NAME
TAG_NAME
CLASS_NAME
CSS_SELECTOR

まとめ

大抵のサイトは、非同期技術が使ってあるため、requestsでリクエストをWebサイトに送って、返ってきたレスポンスをbeautifulsoup4で読み込む、という定番のスクレイピング方法では対応できないことが多い。

ただ、seleniumでも非同期技術を考慮なしに、スクレイピングをしても、とても不安定なコードになってしまう。

面倒ではあるが、ある程度は考えて待機時間は設定したほうが、後々のメンテも楽になるので、がんばりましょう。


最後にtime.sleep()で待機させてから、スクレイピングをしている方へ。

time.sleep()は、要素が読み込まれていようといまいと待機することになるので、時間がもったいないからやめよう。

以上、終わり!!