タイムアウトの設定って、使い方によってはスクレピングの無駄を省いてくれるのですが、情報が少なくピンと来ませんでした。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サイトに迷惑が掛からないようにするためです。相手あっての事ですので。
コメント