SeleniumをPythonで使う
Seleniumとは
Web ScrapingやWeb UIのテストで利用するブラウザ操作自動化ツールです。
Java、Python、C#、Ruby、JavaScript、Kotlinなどの言語で利用できます。
今回はPythonで利用します。
基本的にはテスト自動化ツール
基本的にはテスト自動化ツールと考えた方がよいです。
勘違いしてはいけないのは既存手動業務(Webサーバへの入力作業)を全自動化するためのツールとしては必ずしも最適ではないということです。
そのような複雑な自動化はWeb APIを使った方が確実ですし、安定性や性能面でも優れたものになるはずです。
Seleniumはきれいに動くこともあればきれいに動かないこともあります。
原因はWebページのDOM構造だったり、Selenium自体の仕様や問題だったりします。
単純なWeb Scrapingなら問題ないことが多いですが、はまるときは本当にはまるので注意してください。
準備
必要なものはchromedriverとseleniumです。
chromedriverはMacならhomebrewでインストールできます。
brew install chromedriver
次にseleniumをpip/pipenvでインストールしましょう。
pip install selenium
Hello World
とりあえず動かしてみましょう。
Selenium公式サイトにあるサンプルです。
import time from selenium import webdriver driver = webdriver.Chrome() driver.get("http://selenium.dev") time.sleep(5) driver.quit()
環境のセットアップが完了していればコピペで動きます。
実行するとChromeブラウザが起動し、公式サイトにアクセスし、5秒後にブラウザが終了します。
基本的な操作
以下に基本的な操作の方法を紹介します。
文字を取得
message_element = driver.find_element(by=By.ID, value='message') print(message_element.text)
- 前提条件として
By
をimportする必要がある
from selenium.webdriver.common.by import By
文字を入力
- 先程と同じ方法で文字列入力フィールドのエレメントを取得
send_keys
メソッドでテキストを送る
text_field_element = driver.find_element(by=By.ID, value='text') text_field_element.send_keys('some text')
ボタンをクリック
- 先程と同じ方法でボタンのエレメントを取得
click
メソッドで押す
button = self.browser.find_element(by=By.ID, value='button') button.click()
エレメントが表示されるまで待つ
- 特定のエレメントが表示されるまで待つことができる
- ページの読み込み・表示が遅いときに有効
- 下の例ではid=messageのエレメントが見つかるまで最大60秒間待つ
portal_name_node = WebDriverWait(driver, 60).until( EC.visibility_of_element_located((By.ID, 'message')))
- 前提条件として
WebDriverWait
をimportする必要がある
from selenium.webdriver.support.ui import WebDriverWait
iframeへアクセス
- iframeの中へアクセスする場合はiframeのエレメントを取得してswitch_toする
iframe = driver.find_element(by=By.TAG_NAME, value='iframe') driver.switch_to.frame(iframe)
Shadow DOMへアクセス
- Shadow DOMは他のDOMから独立したTreeを作成する技術
- Shadow DOMにアクセスする場合はShadow DOMのエレメントを取得してshadow_rootを取得する
shadow_dom = driver.find_element(by=By.ID, value='foo') root = shadow_dom.shadow_root
Python終了時にChromeを閉じない
options = Options() options.add_experimental_option('detach', True) driver = webdriver.Chrome(options=options)
- 前提条件として
Options
のimportが必要
from selenium.webdriver.chrome.options import Options
試験作成時の注意点
公式に色々と有益な情報があります。
https://www.selenium.dev/documentation/test_practices/
ここでは一部のみご紹介します。
Page Objectパターンを使う
- DOMアクセス処理をテストコードから排除しPage Objectへ集中させる
- DOM構造が変化した場合でもテストコードは影響を受けない
Fluent APIを使う
- Page Objectのメソッドは次のPage Objectを返す
- メソッドチェーンするときれいに見える
Fluent APIを使わない場合
signin_page.login(username, password) home_page = HomePage() home_page.go_to_profile_page() profile = Profile() profile.change_name('new name')
Fluent APIを使った場合
signin_page.login(username, password).go_to_profile_page().change_name('new name')
Seleniumでログインしない
force-aloha-page
の苦労話
SalesforceのあるアプリがSeleniumで読めたり、読めなかったりと挙動が不安定でした。
色々と調査した結果、以下のことがわかりました。
- force-aloha-pageタグがShadow DOM
- Shadow DOMのすぐ下にiframeがある
iframeにはすぐ気づきましたがforce-aloha-pageがShadow DOMであることに気づけず時間を無駄にしました。
そこに気づけば対処はシンプルです。
- force-aloha-pageタグのshadow_rootを取得
- shadow_rootを利用してiframeのエレメントを取得
- iframeのエレメントへswitch_to
ここまで進んだところでiframeの中が表示されるのにかなり時間がかかることがわかりました。
iframeにswtich_toした後にiframe内のエレメントにアクセスしようとすると例外がガンガン発生します。
苦肉の策としてiframe内のエレメントに最初にアクセスするところで数回Retryさせることにしました。
また、IDがLocatorとして使えないケースが多く、XPATHを多用することになりました。