Selenium」タグアーカイブ

Pythonのseleniumを使ってWeb画面操作を自動化する~サンプルを使ってWebスクライピンングのチャレンジ~

概要~Pythonのseleniumを使って値取得/値入力/クリック/スクリーンショット等、画面操作を自動化する~

少しずつ毎日のWeb生活を快適にするために最近は Python selenium 使ったWeb画面自動化処理を行っています。
今回の投稿は、その自動化処理をする上で繰り返し使える基本コードのまとめから、ちょっとした便利ノウハウをまとめた内容になっています。
これを実行すれば誰でも簡単にWeb画面の自動化処理ができ、日々単調なルーチンワークから脱却できるので、ぜひ試してみてください。

対象読者

  • Pythonを触ったことがある人
  • ブラウザの自動化で楽したい人

出来るようになること

  • Web画面を自動操作する
    • 画面を開く
    • 特定のHTML要素から値を取得する
    • 特定のHTML要素に値を入力する
    • ボタンをクリックする
  • Web画面のスクリーンショットを取得する

事前準備

今回は selenium を使って自動化します。その為に2つの事前準備が必要なのでその準備をします。

事前準備1~ブラウザのドライバをダウンロード~

Web画面の自動化はブラウザの専用ドライバーを使って画面操作を行います。そのためにドライバーをダウンロードします。ドライバーはブラウザ別に用意されているのでお好みのブラウザのドライバーをダウンロードしましょう。(どのドライバーでも自動化のソースコードは同じです)

chromeドライバー

https://chromedriver.chromium.org/downloads

Edge ドライバー

https://developer.microsoft.com/ja-jp/microsoft-edge/tools/webdriver/

事前準備2~seleniumのインストール~

これはpipを使うだけで簡単です。以下のコマンド実行します。

pip install selenium

事前準備は以上です!では、さっそくWeb画面の自動化にチャレンジしていきましょう!!

補足~Google Colaboratoryでのselenium実行~

seleniumu は Google Colaboratory でも実行できるので、サクッと試してみたい時はこちらがおすすめです。
その場合、ドライバー、selenium のインストールは以下のコマンドを実行しておくことになります。

!apt-get update
!apt install chromium-chromedriver
!cp /usr/lib/chromium-browser/chromedriver /usr/bin
!pip install selenium
!pip freeze

Google Colaboratory では、日本語フォントがインストールされていないので文字化けが発生する場合は、以下のコマンドで日本語フォントをダウンロードします。

# 日本語フォントをダウンロードする。
!apt-get -y install fonts-ipafont-gothic

それでは、本編に戻ります!

ページを開く

最初は最も基本的な特定のページを開く操作です。
getメソッドに開きたいページのURLを指定します。

from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.common.by import By

# ドライバーの設定
CHROME_DRIVER = "{ドライバーの格納フォルダ}\\chromedriver"

driver = webdriver.Chrome(CHROME_DRIVER,options=Options())

# 指定したURLの画面を開く
driver.get("http://www.google.com")

# ブラウザを閉じる
driver.quit()

【参考ドキュメント】
https://www.selenium.dev/ja/documentation/webdriver/drivers/options/#normal-default

サンプルソースの{ドライバー格納フォルダ}を自身でドライバーを保存したフォルダに変更して実行してみてください。(サンプルはChromeドライバーを使った例になります)

簡単にブラウザの特定のページが開けたと思います。すぐに閉じてしまっているので一瞬だったかもしれませんが、ここから自動化処理のスタートです!

要素を探す

seleniumで実現したいことはWeb画面の自動操作だと思います。
その中で最も基本的なことは操作したい要素を探す(指定する)ことであり、慣れるまでここが最も大変と思うのでこの部分を重点的に説明します。

要素の選択(指定)

要素の選択(指定)方法には以下の8種類があります。
(ロケータと呼びます)

ロケータ詳細コード例
idid属性が一致する要素を探すdriver.find_element(By.ID,"abc")
class nameclass名に値を含む要素を探す(複合クラス名は使えない)driver.find_elements(By.CLASS_NAME,"abc")
css selectorCSSセレクタが一致する要素を探すdriver.find_elements(By.CSS_SELECTOR,"abc")
xpathXPathと一致する要素を探すdriver.find_elements(By.XPATH,"abc")
link texta要素のテキストが一致する要素を探すdriver.find_elements(By.LINK_TEXT,"abc")
partial link texta要素のテキストが部分一致する要素を探すdriver.find_elements(By.PARTIAL_LINK_TEXT,"abc")
namename属性が一致する要素を探すdriver.find_elements(By.NAME,"abc")
tag nameタグ名が一致する要素を探すdriver.find_elements(By.TAG_NAME,"abc")

【参考ドキュメント】
https://www.selenium.dev/ja/documentation/webdriver/elements/locators/#%E8%A6%81%E7%B4%A0%E9%81%B8%E6%8A%9E%E3%81%AE%E6%96%B9%E6%B3%95

この表は私が良く使う順に記載しています。
次から具体的に説明していきますが、コード例のidだけはfind_element最後の(s)がないことに注意してください。
これはidだけはページ内で1つというHTMLのルールがあるため、1つの要素が返却されることを意味しています。
ちなみに他のロケータにもfind_elementがありますが、先頭の1つの要素が取れるという仕様になっており、限られたケースでしか使用しないため、私は基本的にはid以外はfind_elements最後の(s)ありのメソッドを使用しています。

使用優先度1:ID指定による要素選択

ここからは具体例とともにそれぞれの要素選択方法のサンプルを記載していきます。
まずはID指定です。取得したい要素にid指定の属性があった場合、迷わずこれを使えば良いと思います。

サンプルコード

login_form = driver.find_element(By.ID, 'loginForm')

サンプルHTML

<html>
 <body>
  <form id="loginForm">
   <input name="username" type="text" />
   <input name="password" type="password" />
   <input name="continue" type="submit" value="Login" />
  </form>
 </body>
</html>

実行結果サンプル

login_form = driver.find_element(By.ID, 'loginForm')
print(login_form.tag_name)
# form が出力される

【参考ドキュメント】
https://selenium-python.readthedocs.io/locating-elements.html#locating-by-id

これで formタグの要素(element)が取得できます。id属性があれば最も簡単です。

使用優先度2:クラス名指定による要素選択

次はクラス名指定に要素選択方法です。
idがなくてもクラスが指定されているケースはよくあるので良く使用しています。
(普段使うのは find_elements メソッドですが、サンプルをわかりやすくするため find_element (sなし)メソッドを使っています。)

コードサンプル

content = driver.find_element(By.CLASS_NAME, 'content')

HTMLサンプル

<html>
 <body>
  <p class="content">Site content goes here.</p>
</body>
</html>

実行結果サンプル

content = driver.find_element(By.CLASS_NAME, 'content')
print(content.text)
# Site content goes here.

【参考ドキュメント】
https://selenium-python.readthedocs.io/locating-elements.html#locating-elements-by-class-name

クラス名に複数の指定がある場合

クラス名指定は結構便利ですが、1つ注意点があります。
クラス名はよく複数指定されるケースがあります。↓こんな場合、

<div class="content test">TEST</div>

こんな場合は、.(ドット)区切りで指定します。

content = driver.find_element(By.CLASS_NAME, 'content.test')

これで取得できるのですが、たまに取得出来ないケースがあるようです。
私の場合は、AWS Lambdaがこのケースで取得できず、結局次のCSSセレクタを使って取得しました。(ちなみにLambdaは、Python 3.7 Selenium 3.141でした)

使用優先度3:CSSセレクタ指定による要素選択

次にCSSセレクタです。
CSSに詳しい人なら迷わず使えますね。私もクラス名指定の所で書いたクラス名指定ができないケースがあるので、最近はこれを一番多用しています。

コードサンプル

content = driver.find_element(By.CSS_SELECTOR, 'p.content')

HTMLサンプル

<html>
 <body>
  <p class="content">Site content goes here.</p>
</body>
</html>

実行結果サンプル

content = driver.find_element(By.CSS_SELECTOR, 'p.content')
print(content.text)
# Site content goes here.

【参考ドキュメント】
https://selenium-python.readthedocs.io/locating-elements.html#locating-elements-by-css-selectors

クラス名にタブを.(ドット)区切りで指定というクラス名指定と同じようなシンプルな指定で使えるので簡単ですね。

クラス名の所で記載した複数のクラスが指定されていた場合の指定方法は以下になります。

# <div class="a b">text</div> の場合
find_element(By.CLASS_NAME, "a.b")# ←Lambda NG
find_element(By.CSS_SELECTOR, "div.a.b")# ←OK
find_element(By.XPATH, "//div[@class='a b']")# ←XPathのこれもOK

使用優先度4:XPath指定による要素選択

次はXPathによる指定です。
XPathは柔軟な指定ができるので、私はクラス名やCSSセレクタ指定で複数の要素が取得できてしまうケースで、敢えて1つのみの要素を指定したい場合とかに使用しています。

コードサンプル

login_form = driver.find_element(By.XPATH, "/html/body/form[1]")
login_form = driver.find_element(By.XPATH, "//form[1]")
login_form = driver.find_element(By.XPATH, "//form[@id='loginForm']")

HTMLサンプル

<html>
 <body>
  <form id="loginForm">
   <input name="username" type="text" />
   <input name="password" type="password" />
   <input name="continue" type="submit" value="Login" />
   <input name="continue" type="button" value="Clear" />
  </form>
</body>
</html>

実行結果サンプル

login_form = driver.find_element(By.XPATH, "/html/body/form[1]")
login_form = driver.find_element(By.XPATH, "//form[1]")
login_form = driver.find_element(By.XPATH, "//form[@id='loginForm']")
print(login_form.tag_name)
# form が出力される(どの指定方法でも同じ結果です)

【参考ドキュメント】

https://selenium-python.readthedocs.io/locating-elements.html#locating-by-xpath

3種類の指定方法をサンプルにしていますが、取得結果はどれも同じです。

  1. htmlタグからの絶対パスで指定しています。直感的な指定が可能ですが、HTML構造が変わったら取得できなくなるので使用ケースは注意が必要です
  2. formタグの1つ目という指定です。// を使用すること絶対パス指定が不要になります。
  3. formタグ、かつ、id指定という指定の仕方です。2.のケースよりわかりやすく指定できますね。

使用優先度5:a要素テキスト指定による要素選択

次はa(アンカー)タグ内のテキスト指定する方法になります。
完全一致で指定する LINK_TEXT と 部分一致で指定する PARTIAL_LINK_TEXT があります。

コードサンプル

continue_link = driver.find_element(By.LINK_TEXT, 'Continue')
continue_link = driver.find_element(By.PARTIAL_LINK_TEXT, 'Can')

HTMLサンプル

<html>
 <body>
  <p>Are you sure you want to do this?</p>
  <a href="continue.html">Continue</a>
  <a href="cancel.html">Cancel</a>
</body>
</html>

実行結果サンプル

continue_link = driver.find_element(By.LINK_TEXT, 'Continue')
print(continue_link.text)
# Continue
continue_link = driver.find_element(By.PARTIAL_LINK_TEXT, 'Can')
print(continue_link.text)
# Cancel

【参考ドキュメント】
https://selenium-python.readthedocs.io/locating-elements.html#locating-hyperlinks-by-link-text

この指定方法は、Web画面に表示されている文字をそのまま使えるので結構便利だったりします。ただ、最近のWebでアンカータグがどれだけあるのか、画面表示の文字は頻繁に変わらないか、と考えると使用ケースは限られるため、個人的に使用ケースがあまりないかなと思っています。

使用優先度6:name属性指定による要素選択

次はname属性による指定です。
先に紹介したクラス名指定とよく勘違いするのですが、こちらはname属性になります。

コードサンプル

username = driver.find_element(By.NAME, 'username')
password = driver.find_element(By.NAME, 'password')

HTMLサンプル

<html>
 <body>
  <form id="loginForm">
   <input name="username" type="text" />
   <input name="password" type="password" />
   <input name="continue" type="submit" value="Login" />
   <input name="continue" type="button" value="Clear" />
  </form>
</body>
</html>

実行結果サンプル

username = driver.find_element(By.NAME, 'username')
print(username.tag_name)
# input

【参考ドキュメント】
https://selenium-python.readthedocs.io/locating-elements.html#locating-by-name

この指定ケースも直感的でわかりやすいのですが、name属性が指定された要素がそんなにあるかな。。。という印象なので、どちらかというとクラス名指定を良く使っています。

使用優先度7:タグ名指定による要素選択

最後は、タグ名による指定です。
これはシンプルですね。タグ名をそのまま指定します。

コードサンプル

heading1 = driver.find_element(By.TAG_NAME, 'h1')

HTMLサンプル

<html>
 <body>
  <h1>Welcome</h1>
  <p>Site content goes here.</p>
</body>
</html>

実行結果サンプル

heading1 = driver.find_element(By.TAG_NAME, 'h1')
print(heading1.text)
# Welcome

【参考ドキュメント】
https://selenium-python.readthedocs.io/locating-elements.html#locating-by-name

使用方法はシンプルなのですが、タグの直接指定がそれほど使用ケースがない気がしているので最後の紹介になります。

補足~便利?コードサンプル~

ここからは私が個人的に良く使っているコードサンプルの紹介です。

要素取得判定

冒頭で記載した通り、私は find_elements(find_elementではなく)を多用しています。
そのため、要素が意図通り取得できたかを以下のように確認するメソッドを使って判定しています。

コードサンプル

# 要素取得判定関数
def element_check(element, target):
    if len(element) > 0:
        print(target + " 個数:" + str(len(element)))
        return True
    else:
        print(target + ":要素が見つかりません")
        return False

# 要素取得判定関数呼び出し例
elements = driver.find_elements(By.TAG_NAME, 'a')
if element_check(elements, 'a') :
    for element in elements:
        print(element.text)

要素に対する操作コードサンプル

要素を取得した後の操作は基本的には以下の操作と思っています。
これだけは覚えておけば基本的な操作はできるかなというものを記載しておきます。

  • テキスト取得
  • テキスト入力
  • テキストクリア
  • クリック

コードサンプル

element.text
element.send_keys("abc")
element.clear()
element.click()

ページのタイトルを取得する

各処理を始める前にまずタイトルを取得する場合、以下のコードで簡単に取得できます。

driver = webdriver.Chrome(CHROME_DRIVER,options=Options())
print(driver.title)

HTMLを表示する

意図した動作にならない場合など、実際のHTMLを確認したいケースがあると思います。その時は、以下のコードでHTMLを取得します。

driver = webdriver.Chrome(CHROME_DRIVER,options=Options())
print(driver.page_source.encode('UTF-8'))

スクリーンショットを保存する

Web画面の操作結果を保存する、意図した動作になっているか途中の画面遷移を保存する際に利用するのがスクリーンショットです。
seleniumu では簡単にスクリーンショットを取ることができます。

コードサンプル

# スクロールを含めて全画面のスクリーンショットを取得するためヘッドレスモードにする
options = Options()
options.add_argument('--headless')
driver = webdriver.Chrome(CHROME_DRIVER,options=options)

# 全画面のスクリーンショット取得
w = driver.execute_script('return document.body.scrollWidth')
h = driver.execute_script('return document.body.scrollHeight')
driver.set_window_size(w, h)
driver.save_screenshot("screenshot.png")

指定した要素部分のみの一部のスクリーンショットを保存する

上の例だと全画面表示になってしまうため、必要な要素部分だけ取得したい場合(例えば特定のframe内だけ取得したい場合)は事前に要素取得してから、スクリーンショットを保存します。

# スクロールを含めて全画面のスクリーンショットを取得するためヘッドレスモードにする
options = Options()
options.add_argument('--headless')
driver = webdriver.Chrome(CHROME_DRIVER,options=options)

# 一部要素の全画面スクリーンショット取得
w = driver.execute_script('return document.body.scrollWidth')
h = driver.execute_script('return document.body.scrollHeight')
driver.set_window_size(w, h)
# 取得したい要素を指定する(ID指定でなくても良い)
element = driver.find_element(By.ID, 'main-element')
png = element.screenshot_as_png
# 任意のファイル名を指定してpngファイルに保存
with open('ElementScreenshot.png', 'wb') as f:
    f.write(png)

ドライバーの自動更新

ドライバーのバージョンは自分が普段使用するブラウザのバージョンと一致しておく必要があるため、ブラウザのバージョンアップが定期的にあり、その度にドライバーのダウンロードが面倒という方は以下でドライバーのインストール含めて自動化できます。

# Webdriver ManagerでChromeDriverを取得
from webdriver_manager.chrome import ChromeDriverManager
# ChromeDriverManager().install()を指定して自動インストール
driver = webdriver.Chrome(ChromeDriverManager().install(),options=options)

ちなみにブラウザとドライバーのバージョンは不一致の場合、以下のエラーメッセージが出力され、処理が異常終了します。

selenium.common.exceptions.SessionNotCreatedException: Message: session not created: This version of ChromeDriver only supports Chrome version 104
Current browser version is 106.0.5249.119 with binary path C:\Program Files (x86)\Google\Chrome\Application\chrome.exe

逆引き参考ページ

以下はseleniumでやりたいことの目的別に逆引きができるサイトになります。

https://www.seleniumqref.com/api/webdriver_gyaku.html

属性ごとの取得方法まとめ

以下は属性ごとに指定方法がまとめられて非常に参考になるサイトになります。
XPathの使い方がうまい。

https://qiita.com/VA_nakatsu/items/0095755dc48ad7e86e2f

「Chrome の自動操作を一通り」

タイトルの通り、「Chrome の自動操作を一通り」まとめてくれているサイトになります。網羅的に書かれていて非常に参考になります。

https://qiita.com/memakura/items/20a02161fa7e18d8a693

まとめ

今回はPythonのSeleniumでWeb画面操作を自動化する基礎をまとめてみました。
自分なりによく使う辞書的な位置づけでまとめてみたので、同じような人の役に立てれば嬉しいです(^^)