実践!PythonとSeleniumでWebの自動操作を行うコツ(前編)

Python Python入門

スクレイピングやWebサイト自動操作に使われるSelenium。
便利な関数がいろいろ揃っています。

しかし、Seleniumの操作には数多くの「コツ」が数存在します。
「コツ」を知らないと、「正しい」コードを書いているのに動きません。うまくタイミングが合わないとか、なぜここでWebDriverExceptionが発生するのか分からないとかいうことになり、デバッグに大変時間を費やします。なにせ、コードは「正しい」のですから。通常のプログラムと違い、正攻法ではデバッグできません。

そこで、前編と後編に分けて、Seleniumの「コツ」を詳しく解説していきます。
あわせてスクレイピングの法的規制についても触れます。

スポンサーリンク

要素の特定の仕方

要素を操作するには、まず要素を特定しなければなりません。
JavaScriptのDOM操作を知っている方であれば基本は同じなので関数の働きはすぐ分かるでしょうが、でも、タイミングを待つ方法、サイトのDOM構造が変わっても通用するコードの書き方など、Selenium独自の作法があります。
それらから3つピックアップして解説します。

wait.untilを使うとき

find_element(s)_by_XXXを使った方がいいのか、wait.untilを使った方がいいのか迷うときがあります。

ここでの選択のポイントは、「次に特定しようとしている要素がDOMに表れる/表示される/クリック可能になるのが、未来か否か」です。

例えば、親をチェックしたらVisibleになるような要素を特定する場合、親をチェックしてからサイトの処理が動き要素がVisibleになるまでは時間がかかります。人間の目には同時でも、Seleniumはプログラムです。サイトの処理よりSeleniumの処理が速かった場合、要素がVisibleにならずにその要素への操作は失敗します。このような場合には、必ずwait.untilを使います。

要素が既に現れていることが分かっている場合には、find_element(s)_by_XXXを使います。こちらの方が処理が幾分高速なようです。

ID、クラス、XPATH

IDで特定したらいいのか、クラスで特定したらいいのか、XPATHを使うべきか、迷うことがあります。

一般的には、IDが割り振られていたら迷うことなくIDを使います。IDはHTML上でユニークで、かつ、変わることは(一般的には)ありません。

クラスで特定するのも、そのクラスがHTML上でユニークそうである場合は有効です。

XPATHは最後の手段と心得てください。理由は、XPATHは一般的にDOMの構造に依存するため、サイトのDOMが変更されたら(それはしばしば起こることです)、XPATHが変わり、無効になるからです。サイトのDOM構造が変わっても通用する、堅牢なシステムを開発するべきです。

このようにID、クラス、XPATHを使い分けます。

相対位置で要素を特定

find_element_by_XXXなどで特定した要素に、更にfind_element_by_XXXを続けると、最初の要素の配下の要素を特定します。DOM全体に対してではありません。

これを使い、どんどん範囲を狭め、要素を特定できることがあります。

例えば、

color = driver.find_element_by_class_name("product-sku").find_element_by_tag_name("div").find_element_by_tag_name("div").find_element_by_tag_name("div").find_element_by_tag_name("span")

ページによってDOMの構造が変わる複数のページをスクレイピングするために書いたコードです。
クラス
product-sku
はユニークだったので、そこから追いかけました。
このようなコードを書かなければならないこともあります。

要素を操作

「正しい」コードを書いているのに、要素がうまく操作できないことがあります。
ここでも、「コツ」があります。例えばタイミングが少しでもずれると要素への操作は失敗します。Seleniumはプログラムなので、タイミングはぴったりと合わさなければなりません。
要素の操作の仕方から、4つピックアップして解説します。

visibility_of_element_locatedとelement_to_be_clickable

wait.untilで要素を待つとき、visibility_of_element_locatedとelement_to_be_clickableのどちらを使うかは、きちんとした決まりがあります。

その要素から属性値やinnerHTMLを取得する場合にはvisibility_of_element_locatedを使います。

ところが、visibility_of_element_locatedで取得出来ても、その瞬間にはクリックは出来ません。
クリックしたい場合はelement_to_be_clickableを使います。
element_to_be_clickableを使わないと、クリックは失敗します。

リンクをクリック

クリックできるのは、インスペクターでeventが設定されていることが分かっている要素のみです。
aタグでのリンクにはeventは設定されていません。

だから、aタグをそのままクリックしても、遷移しません。

aタグをクリックするには、find_element_by_link_textでリンクのテキスト文を指定し、取得出来た要素をクリックします。

send_keysとperform

テキストフィールドに文字を書き込むとき、send_keysを使います。

ところが、うまく送れないときや、またクリックしたいけどその要素の特定が難しいときがあります。

例えばモーダルダイアログを閉じたいとき、performを使ってEnterキーを画面にそのまま送ると処理が簡潔になる場合があります。

例えば、

#ポップアップを閉じる
wait.until(expected_conditions.presence_of_element_located((By.ID, "popup-notice-area")))
webdriver.ActionChains(driver).send_keys(Keys.ENTER).perform()

このようにperformを使います。
このコードは、ポップアップが出現するのを待ってはいますが、何か特定の要素にEnterを送っている訳ではありません。
画面全体にEnterを送ることにより、ポップアップを閉じています。

Select

コンボボックスの中から特定の要素を選択するときはSelectを使います。

ただ、これはSelectオブジェクトを作らなければならないので注意してください。

サンプルコードは以下です。

element = driver.find_element_by_xpath("/html/body/div[1]/div/section[2]/form/div[1]/section[2]/div[7]/div[2]/select")
select = Select(element)
select.select_by_index(5)

このコードでは、6番目のインデックスを選択しています。インデックスは0から始まりますので、ここも注意してください。

まずは要素の特定の仕方と操作の仕方について解説しました。後編に続きます。

実践!PythonとSeleniumでWebの自動操作を行うコツ(後編)
Selenium実践編の後編です。前編に続き技術的な解説をした後、スクレイピングの法的規制について解説します。 ウィン...