【Python】Seleniumのタイムアウト設定の使い方

seleniumによるHTML解析を10倍速くした方法 Pythonの使い方

タイムアウトの設定って、使い方によってはスクレピングの無駄を省いてくれるのですが、情報が少なくピンと来ませんでした。seleniumの公式サイトを見ても、少しわかりづらいです。

恐らく他の人もそうなんじゃないかと思って、参考になるように、日頃から使って分かっている事をまとめました。

対象としているメソッドは、下の4つです。

  • webdriver.set_page_load_timeout()
  • webdriver.implicitly_wait()
  • webdriver.support.ui.WebDriverWait()
  • time.sleep()

ページが完全に読み終わる待つ時間を設定するset_page_load_timeout()

Seleniumでスクレイピングする場合、下のようにページを読み込んでHTML解析します。driver.get()は、ページの読み込みが完了するまで、そこでブロックします。

from selenium import webdriver

driver = webdriver.Chrome()
driver.get('https://asdfgh.jp')
# スクレピング

稀に、というか特定のサイトで、get()で20秒以上もブロックされるページがあります。せっかちさんだと、このページを待たずに他のサイトを拾いに行くかもしれません。この我慢できる秒数をset_page_load_timeout()で設定できるのです。

from selenium import webdriver

driver = webdriver.Chrome()     

# 10秒まで待つ
driver.set_page_load_timeout(10)

driver.get('https://asdfgh.jp')
# スクレピング

set_page_load_timeout()のタイムアウトで例外発生

get()でページの読み込みが完了しなかったら例外が発生します。

selenium.common.exceptions.TimeoutException: Message: timeout: Timed out receiving message from renderer: 0.768

ちなみに、独自ドメインがないページをget()した場合、別の例外が発生します。これはdriver.set_page_load_timeout()の設定秒数によりません。

selenium.common.exceptions.WebDriverException: Message: unknown error: net::ERR_NAME_NOT_RESOLVED

私は、実はタイムアウトまでの時間を短くしていません。スクレピングなので、ページが完全に読み終わるまで待ちます。

しかし、余りにも長すぎるようだったら、警告ログを残しています。サイトに迷惑が及んでいる可能性があるため、後でサイトを見て確認するためです。

crt_counter = time.perf_counter()
driver.get('http://localhost/index.html')
ts = time.perf_counter() - crt_counter
if ts > 30:
	#	掛かった時間とdriver.current_urlをログに残す

set_page_load_timeout()の適用効果が及ぶメソッド

適用される関数は、下記のものと分かっています。

  • webdriver.get()
  • Element.click()

implicitly_wait()の使い方

find_element_by~()で要素を取得できるまで、ブロックされます。時間切れタイムアウトで取得できないことがありますが、implicitly_wait()はその秒数を設定するものです。

webdriver.get()でページが完全に読み込まれたのだから必要ないように思えます。しかし、今のウェブページはクラスを追加して、動きのあるデザインを表現しているものもあります。そのため、条件によっては要素の特定方法が変わってくるのです。

例えば、下のような動きのあるウェブページでは、ページから5秒後にaタグにクラスgaouが追加されます。

<html>
<head>
<style>
.gaou
{
	font-size:10em;
	color:green;
}
</style>
</head>

<body> 
<p class="bm"><a id=href="https://www.google.com/?hl=ja" title="">Google</a></p>
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script>
   $(function(){
        setTimeout( function(){
            $("a").addClass("gaou");
        }, 5000);
    });
</script>
</body>
</html>

もし、このウェブページをページ表示後、要素取得に3秒しか待たなかったら、driver.find_element_by_css_selector()で例外が発生します。get()で読み込みが完了していても、必ずしも要素が取得できるとは限りません。

driver.implicitly_wait(3)
driver.get('http://localhost/index.html')
elem = driver.find_element_by_css_selector("a.gaou")

support.ui.WebDriverWait()の使い方

implicitly_wait()と同様に要素を取得できるまで待つ秒数を設定できますが、同時に要素の状態も設定可能です。要素の状態とは、クリック可能になるまで、要素が表示されるまでといったものです。

ID”foo”の要素が見つかるまで、16秒間待つサンプルは下のようになります。

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.Chrome()
driver.get("http://localhost/index.html")
elem = WebDriverWait(driver, 16).until(EC.presence_of_element_located((By.ID, "foo"))

WebDriverWait()は、少ないコード量で要素を特定する方法を提供しています。ページ表示後に表示物が変化するウェブページを見る機会が多くなってますので、今後はこちらの利用したいですね。

要素を特定する識別子の一覧

要素特定はIDの他、クラスやXPathやCSS セレクター等による指定も可能です。

<

  • By.ID
  • By.XPATH
  • By.LINK_TEXT
  • By.PARTIAL_LINK_TEXT
  • By.NAME
  • By.TAG_NAME
  • By.CLASS_NAME
  • By.CSS_SELECTOR

要素の状態の条件

selenium\webdriver\support\expected_conditions.pyを見ると、条件の一覧が分かります。ここでは省きましたが、ソースにコメントがあるため、凡その判定条件が分かります。

  • EC.alert_is_present
  • EC.all_of
  • EC.any_of
  • EC.element_attribute_to_include
  • EC.element_located_selection_state_to_be
  • EC.element_located_to_be_selected
  • EC.element_selection_state_to_be
  • EC.element_to_be_clickable
  • EC.element_to_be_selected
  • EC.frame_to_be_available_and_switch_to_it
  • EC.invisibility_of_element
  • EC.invisibility_of_element_located
  • EC.new_window_is_opened
  • EC.none_of
  • EC.number_of_windows_to_be
  • EC.presence_of_all_elements_located
  • EC.presence_of_element_located
  • EC.staleness_of
  • EC.text_to_be_present_in_element
  • EC.text_to_be_present_in_element_value
  • EC.title_contains
  • EC.title_is
  • EC.url_changes
  • EC.url_contains
  • EC.url_matches
  • EC.url_to_be
  • EC.visibility_of
  • EC.visibility_of_all_elements_located
  • EC.visibility_of_any_elements_located
  • EC.visibility_of_element_located

time.sleep()の役割

ウェブページの読み取り状況に関わらず、指定した秒数の間、問答無用でその場で待ちます。非推奨と言われる所以です。

そもそもまともに開発に関わっている人なら、待つためだけのsleep()を見た途端、侮蔑の眼差しを向けるでしょう。

ですが、私は使ってます。WEBサイトに迷惑が掛からないようにするためです。相手あっての事ですので。

コメント

タイトルとURLをコピーしました