Selenium

Seleniumは各ブラウザに対応しますが、ここではGoogle Chromeを操作することを前提に解説します。

導入

C:\python -m pip install selenium
Install a Selenium library | Selenium
from selenium import webdriver

driver = webdriver.Chrome()      # Chromeが起動する
driver.get("http://example.com") # example.comが開く
driver.quit()                    # Chromeが閉じられる
The Selenium Browser Automation Project | Selenium

WebDriver

Selenium 4.6.0以降はSelenium Managerにより、必要なWebDriverが自動でインストーされます。Selenium 4.6.0 Released! | Selenium

その場所は%USERPROFILE%\.cache\seleniumになります。

ドライバのパスの指定を誤ると、「Unable to locate or obtain driver for chrome; For documentation on this error, please visit: https://www.selenium.dev/documentation/webdriver/troubleshooting/errors/driver_location」としてNoSuchDriverExceptionで失敗します。

ChromeDriver

Chrome for Testing availability

ChromeDriver とは何ですか?  |  Chrome for Developers

セッションの開始

セッションを開始や停止することは、ブラウザを開いたり閉じたりすることです。Driver Sessions | Selenium

セッションの作成

driver = webdriver.Chrome(options=options)

optionsで、ブラウザの動作を指定します。Browser Options | Selenium

ブラウザ固有のオプションはSupported Browsersのリンク先にあり、ChromeはChrome specific functionalityにあります。

User-Agent

options.add_argument("--user-agent=test")

とすることでUser-Agentを指定できますが、これは起動オプションとして設定するため、"debuggerAddress"を指定して既存のブラウザに結びつけたときは作用しません。

セッションの放棄

driver.quit()

既存のブラウザに結びつける方法

セッションを開始するたびに新しいブラウザが開かずに、既に開かれているブラウザに結びつける方法を考えます。

そのためにはChromeを、ポート番号を指定して起動します。ポート転送でローカル サーバーと Chrome インスタンスにアクセスする  |  Chrome DevTools  |  Chrome for Developers

ポート番号には、9222などの使用可能な番号を指定します。 Step 2: Launch browser with options - How to connect Selenium to an existing browser that was opened manually? – CosmoCode (Formerly TeachMeSelenium) Chromium Blog: Remote debugging with Chrome Developer Tools

chrome.exe --remote-debugging-port=9222

プロファイルを分けたいならば、それも指定します。

chrome.exe --remote-debugging-port=9222 --user-data-dir="C:\ChromeProfile"

セッションを作成するときにそのポート番号をオプションで、chromedriverのパスをサービスで指定します。

options = webdriver.ChromeOptions()
options.add_experimental_option("debuggerAddress", "127.0.0.1:9222")

service = Service(executable_path="path/chromedriver.exe")

driver = webdriver.Chrome(options=options, service=service)
Can Selenium interact with an existing browser session? - Stack Overflow How to connect Selenium to an existing browser that was opened manually? – CosmoCode (Formerly TeachMeSelenium)

ブラウザごとの機能

Supported Browsers | Selenium

Google Chrome

Chrome specific functionality | Selenium

バイナリの指定

Chromiumを基にしたブラウザが複数インストールされているならば、それらを指定できます。

options.binary_location = chrome_bin
Start browser in a specified location - Chrome specific functionality | Selenium

ブラウザのナビゲーション

ページを開いたり、移動させたりできます。Browser navigation | Selenium

指定のページを開く

driver.get("http://example.com/index.html")

リンク先を開く

リンク先を開くには、その要素に対してclick()を呼びます。

anchor = driver.find_element(By.LINK_TEXT, "TEST")
anchor.click()

そのとき確実に新しいタブで開くには、Ctrlキーの押下を模擬してクリックするか、新しいタブを開いた上でそこで新しいページを開きます。 selenium - How to open a new window while clicking on a link using java - Stack Overflow How to open a URL in new window using Selenium Webdriver? - Codekru

anchor = driver.find_element(By.TAG_NAME, "a")
url = anchor.get_attribute("href")

driver.switch_to.new_window('tab') # 新しいタブを開く
driver.get(url)                    # 指定のURLを開く

戻る

driver.back()

進む

driver.forward()

更新

driver.refresh()

ブラウザの情報

ウィンドウ ハンドルやブラウザのサイズや位置、それにCookieなどの情報を要求できます。Browser interactions | Selenium

WebDriverはウィンドウとタブを区別せず、ウィンドウ ハンドルによってそれらを識別します。Windows and tabs - Working with windows and tabs | Selenium

driver.current_window_handle

ウィンドウを閉じる

close()を呼ぶことでウィンドウを閉じられます。Closing a window or tab - Working with windows and tabs | Selenium

driver.close()

ただし閉じたウィンドウを後から操作しようとすると「no such window: target window already closed」としてNoSuchWindowExceptionが投げられるため、そのような状況では閉じるウィンドウに切り替える前に

original_window = driver.current_window_handle

としてハンドルを保持しておき、閉じた後に

driver.switch_to.window(original_window)

として元のウィンドウに切り替えます。

指定のウィンドウにフォーカスを合わせる

現在のセッションのウィンドウ ハンドルを順に取得し、そのウィンドウにフォーカスを合わせます。そしてそのウィンドウが条件に一致していたら処理を打ち切ります。

for window_handle in driver.window_handles:
    driver.switch_to.window(window_handle)
    if driver.current_url.startswith("http://example.cm/"):
        break

待機

ページの要素を取得や操作するには、それが可能になるまで待機する必要がある場合があります。Waiting Strategies | Selenium

要素が見つかるまで、またはコマンドが完了するまでの待ち時間を指定できます。これはセッション毎に1回の呼び出しが必要です。ブラウザのオプションでも指定できます。単位は秒です。既定値は0です。

driver.implicitly_wait(0.5)

指定条件が満たされるまで待機

要素がユーザーに可視となるまで待たせるには、次のようにします。

from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions
element1 = driver.find_element(By.ID, "test1")
element1.click()

wait = WebDriverWait(driver, timeout=2)

element2 = driver.find_element(By.ID, "test2")
wait.until(lambda _: element2.is_displayed())

expected_conditionsを用いれば、これは次のようにも記述できます。Waiting with Expected Conditions | Selenium

wait.until(expected_conditions.visibility_of_element_located((By.ID, "test2")))

ウィンドウが開かれるまで待機

ウィンドウ ハンドルの数が増加するまで待機することで、ウィンドウが開かれるまで待機することになります。Python. Selenium. How to wait for new window opens? - Stack Overflow

要素の検索

要素に関連する操作をするためには、まずその要素を見つける必要があります。Finding web elements | Selenium

from selenium import webdriver
from selenium.webdriver.common.by import By

driver = webdriver.Chrome()
driver.get("http://example.com")

sample = driver.find_element(By.CLASS_NAME, "sample")

driver.quit()

Byには、下表の種類を指定できます。

定数 対象
ID id属性
XPATH XPath
LINK_TEXT リンクとして表示されているテキスト
PARTIAL_LINK_TEXT リンクとして表示されているテキストの一部
NAME name属性
TAG_NAME HTMLタグ名
CLASS_NAME クラス名
CSS_SELECTOR CSSセレクタ
Locator strategies | Selenium

XPathやCSSセレクタを用いれば、複雑な条件で一致させられます。

elements = driver.find_elements(By.XPATH, "//a[starts-with(@href, 'http://example.com/')]")

一致する要素がなかったときには空のリストが返されるか、「no such element: Unable to locate element」としてNoSuchElementExceptionが投げられます。NoSuchElementException - Understanding Common Errors | Selenium

これを処理するにはNoSuchElementExceptionをインポートした上で、exception - python selenium webscraping "NoSuchElementException" not recognized - Stack Overflow

from selenium.common.exceptions import NoSuchElementException

それをexceptで処理します。

element = []
try:
    element = driver.find_element(By.ID, "test")
except NoSuchElementException:
    pass

if len(element) == 0:
    print("no such element")

すべての要素の検索

find_element()では最初に一致した要素だけが返されるため、これをすべての要素とするには複数形のfind_elements()を用います。All matching elements - Finding web elements | Selenium

items = driver.find_elements(By.TAG_NAME, "li")

要素の操作

要素をクリックしたり、キーを送信したりできます。Interacting with web elements | Selenium

click()

要素をクリックできます。

check_input = driver.find_element(By.NAME, "checkbox_input")
check_input.click()

要素の情報の要求

要素の可視、有効、選択状態やタグ名などを取得できます。Information about web elements | Selenium

メソッド 機能
is_displayed() 要素が表示されているならば、Trueを返す
is_enabled() 要素が有効ならば、Trueを返す
is_selected() 要素が選択されているならば、Trueを返す

text

描画されているテキストを取得できます。Text Content - Information about web elements | Selenium

element.text

get_attribute()

要素の属性を取得できます。Fetching Attributes or Properties - Information about web elements | Selenium

element.get_attribute("href")

ファイル

Upload

アップロード ダイアログを操作することはできないため、ダイアログを開かずにアップロードする必要があります。たとえばtypeがfileであるinput要素にファイルのパスを入力し、送信をクリックします。File Upload | Selenium

Download

リンクをクリックすることでダウンロードを開始することはできますが、その進捗や、ダウンロードされたファイルを検証することはできません。よってその必要があるならば、リンクの情報を他のライブラリに渡して処理します。File downloads | Selenium

driver.find_element(By.TAG_NAME , "a").click()

ファイル名を指定してダウンロードすることはできないため、ダウンロードの完了を待ってからファイル名を変更します。python - Selenium give file name when downloading - Stack Overflow

ダウンロードの完了を待たずにブラウザを閉じるとファイルが不完全な状態となるため、それを待つか、ブラウザを閉じないようにします。

保存先

保存先は、ブラウザのオプションを指定することで変更できます。

prefs = {"download.default_directory" : r"C:\Downloads"}

options = webdriver.ChromeOptions()
options.add_experimental_option("prefs",prefs)

driver = webdriver.Chrome(options)
changing default download location in chrome using Python selenium - Stack Overflow

この方法が使用できない、または実行時に動的に変更するにはCDP Commandで指定します。[py] add execute_cdp_cmd to Remote by Delta456 · Pull Request #14809 · SeleniumHQ/selenium · GitHub

params = {"behavior": "allow", "downloadPath": r"C:\Downloads"}
driver.execute_cdp_cmd("Page.setDownloadBehavior", params)
Custom Download Path Not Working with undetected_chrome · Issue #1935 · ultrafunkamsterdam/undetected-chromedriver Source: chromium.js

ただしこのCDP (Chrome DevTools Protocol) は、新しいWebDriver BiDiが提供されると使用できなくなる恐れがあります。Chrome DevTools Protocol | Selenium

動作

キーボード

Keyboard actions | Selenium

マウス

Mouse actions | Selenium
複数の技術系サイトから、まとめて検索