はじめに
ブラウザポチポチ作業をselenium * pythonで自動化したいときのtips集。
seleniumは4系を利用。
PS > pip list | Select-String selenium
selenium 4.27.1
導入
・pythonにseleniumのライブラリをインストール
pip install selenium
・利用するブラウザに応じたWebDriverをDLして配置
手元では、使っているchrome のバージョンに合わせて128.0.6613.138を利用。
https://developer.chrome.com/docs/chromedriver/downloads/version-selection?hl=ja にも
https://googlechromelabs.github.io/chrome-for-testing/ にも見つからないので
https://storage.googleapis.com/chrome-for-testing-public/128.0.6613.138/win64/chromedriver-win64.zip
を直接ブラウザに打ってDL。
seleniumからchromeを起動
DLしたchromeのドライバを指定して、seleniumからchromeを起動する。
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.chrome.service import Service as ChromeService
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
options = webdriver.ChromeOptions()
options.add_experimental_option("detach", True)
service = ChromeService(executable_path="./chromedriver-win64/chromedriver.exe")
driver = webdriver.Chrome(service=service, options=options)
driver.get('https://www.google.co.jp/')
要素の取得・操作
chrome起動後は、
・要素を取得
→要素の操作(クリック・値の入力・ボタン押下など)
を何度も繰り返していくことになる。
特に前者は、「欲しい要素が持つid, name, class, text() 等から、その要素を一意に特定できるものを選んで指定し、要素を取得する」という動きを繰り返しすることになる。
どうやって一意に指定するか、という部分が肝。
明らかに一意なid(本来一意のはず), classが振られていれば、それを指定すれば済むが、
それが難しい場合、XPATHでフルパスを指定するか、and条件を使うなど工夫することになる。
要素の取得方法は以下などを参照。
sample_idというid属性を持つ要素をクリックする
sample_id= driver.find_element(By.ID, "sample_id")
sample_id.click()
例えば以下が引っかかる。
<button id="sample_id" class="btn-class">ボタン</button>
※find_elementだと、仮に複数見つかっても最初の一つしか返さない。
find_elementsにすると、リストで返してくれる。
※単一のつもりが誤ってfind_elementsを指定してしまっていて、思っていた返り値と違う・・・というミスが発生しがち。
LoginIdというname属性を持つinputフィールドに値を入れる
input_login_id = driver.find_element(By.NAME, "LoginId")
input_login_id.send_keys("alphajinsei")
例えば以下が引っかかる(値入力後)
<input name="LoginId" type="text" value="alphajinsei" class="input-class">
XPATHをフルパスで指定して要素を指定・クリックする
wariai = driver.find_element(By.XPATH, "/html/body/form[1]/div/div[11]/table[1]/tbody/tr/td[3]/a")
wariai.click()
XPATHのフルパスの指定は開発者ツールから。
「サンプルテキスト」という文字列を含む要素をクリックする
driver.find_element(By.XPATH, '//*[contains(text(),"サンプルテキスト")]').click()
たとえば以下のような要素が引っかかる
<span>サンプルテキスト</span>
<button class="btn-class">サンプルテキスト</button>
ボタンのタグで、「選択」というテキストを含んでいて、かつname属性がselectButtonである要素を取得する
driver.find_element(By.XPATH, '//button[contains(text(),"選択") and @name="selectButton"]').click()
例えば以下が引っかかる
<button type="button" name="selectButton">選択</button>
XPATHの表記について
上記のXPATHの表記'//button[contains(text(),"選択") and @name="selectButton"]
は、
-
//
: 本来XPATHは/html/body/form[1]
のようにフルパス表記するが、ワイルドカード的に省略して//
とできる。 -
button[]
: buttonタグである要素。ここも省略して//*[XXX]
のようにしてもよい。 -
contains(text(),"選択")
: 持っているテキストが「選択」を含んでいること。決め打ちでよければ//button[text()='選択']
と書いても良い -
@name="selectButton"
: name属性にselectButtonと表記されていること を指定。属性(<button name="XX">
のようにタグ内に書かれているやつ)は@で指定する
以下に詳しい。
複数のclassが指定されている要素を全て指定して選択する
以下のように、classが複数指定されていて、指定されているclassの全量("aaa bbb ccc")でひっかけたい場合、
<div class="aaa bbb ccc"> ★これを取得したい
<div class="aaa bbb ddd">
以下のように(By.CLASS_NAME
ではなく)XPATHで複数クラスを全量指定することで取得できる。
aaa_bbb_ccc = driver.find_element(By.XPATH, '//div[@class="aaa bbb ccc"]')
※driver.find_element(By.CLASS_NAME, 'aaa bbb ccc')
とは書けない。
find_element
で取得したWebElementに対して、さらに子要素をXPATHで指定したい
より、XPATHの指定の先頭に、.
を付ける。
##get elements from parent element using XPATH
##NOTE: in order to utilize XPATH from current element, you must add "." to beginning of path
# Get first element of tag 'ul'
element = driver.find_element(By.XPATH, '//ul')
# get children of tag 'ul' with tag 'li'
elements = driver.find_elements(By.XPATH, './/li')
selenium開発TIPS集
途中まで動かしたあと、対話モードで自由に確認したい
python -i selenium_test.py
とすることで、実行後にそのまま対話モードに移行する。
また、単にpython
で対話モードを起動したのち、
exec(open("./selenium_test.py", encoding="utf-8").read())
とすることで、対話モード内で書いたスクリプトを実行させることができる。
この後も対話モードのままなので、スクリプトで利用した変数、WebElementの内容を確認することができる。
今driverが認識しているURL,htmlの内容を確認したい
driver.current_url
driver.page_source
選択した要素のhtml表記を確認したい
.get_attribute('outerHTML')
でhtml要素を確認できる
date_item = driver.find_element(By.XPATH, "/html/body/div[4]/table/tbody")
date_item.get_attribute('outerHTML')
選択した要素が持つ特定の属性の値を確認したい
例えば以下のようなhtmlがあった際に、
<input type="hidden" name="input_value" value="99" disabled="">
以下のようにget_property()
メソッドを利用することで、所望の属性の値(value=99
)を取得することができる
value = driver.find_element(By.NAME, "input_value").get_property("value")
# valueに99が入る
その他、WebElement(find_elementで取得できるクラス)に備わったプロパティ・メソッド集は下記。
その他
selenium3 → 4に変わって、要素の選択の記述方法が微妙に変わっている点に注意。
jsk_send_btn = driver.find_element(By.ID, "send_btn") # 新 (直近はこちらを利用)
jsk_send_btn = driver.find_element_by_id("send_btn") # 旧