動的に要素が出入りするJavaScript対応サイトでも、Seleniumを使えば人間の代わりにリンクをクリックしたり、フォームに入力したり、スクリーンショットやダウンロードを自動で行えます。
本記事ではPython初心者の方に向けて、環境構築から基本レシピ、よくあるエラー対応までを段階的に丁寧に解説します。
Selenium×Python入門
Seleniumとは何か
SeleniumはWebブラウザをプログラムで操作するための自動化フレームワークです。
PythonやJavaなど複数言語に対応しており、実際のブラウザ(Chrome、Edge、Firefoxなど)を立ち上げて、人間が行う操作を忠実に再現します。
JavaScriptで生成される動的な要素に対しても操作や情報取得ができるため、静的HTML向けのライブラリでは難しいケースでも確実に処理できます。
Seleniumの中核はWebDriverという仕組みです。
WebDriverは各ブラウザ専用のドライバ(ChromeならChromeDriver)を通して、クリックや入力、スクロールといった命令をブラウザへ送ります。
動的サイトの自動操作でできること
動的サイト(SPAやAjaxを多用するサイト)では、ページ読み込み後にJavaScriptがDOMを更新し続けます。
Seleniumを使うと次のようなことが可能です。
- 非同期に出現するボタンやリンクを待ってからクリックすることができます。
- 無限スクロールやモーダルダイアログに対応したデータ取得ができます。
- ログインが必要なページ遷移や、ファイルダウンロードなど人間のブラウザ操作に近いシナリオを自動化できます。
静的HTMLを対象にするrequests
やBeautiful Soup
と比べ、Seleniumは「見えている通りに操作できる」ことが最大の強みです。
以下は簡単な比較です。
アプローチ | 得意分野 | 不向きな場面 |
---|---|---|
requests/Beautiful Soup | 静的HTMLの高速取得・解析 | JSで生成される要素の操作 |
Selenium | JSで動くUIの操作、ログイン、ファイルDL | ヘッドレス負荷の高い大量クローリング |
初心者が押さえる用語
Seleniumを使ううえで最低限知っておくと理解がスムーズです。
用語 | 概要 |
---|---|
WebDriver | ブラウザを操作するドライバ。Seleniumから命令を受け取りブラウザを動かします。 |
DOM | ブラウザが解釈したHTMLの内部構造。要素取得やクリック対象はこのDOM上にあります。 |
CSSセレクタ | #id や.class など、要素を指定する表現。Seleniumでは最優先で使うと安定しやすいです。 |
XPath | XML/HTML用のパス表現。複雑な構造も指定できますが、変更に弱い場合があります。 |
明示的待機(Explicit Wait) | 条件を満たすまで待つ仕組み。安定化の要です。 |
ヘッドレス | 画面を表示せずにブラウザを動かすモード。サーバー上の自動実行に便利です。 |
iframe | ページ内に別ページを埋め込む仕組み。操作にはswitch_to.frame が必要です。 |
環境構築と準備
Pythonとpipの確認
Pythonとpipのバージョンを確認します。
3.8以上を推奨します。
# Pythonのバージョン確認
python --version
# pipのバージョン確認
pip --version
Python 3.11.6
pip 24.0 from .../site-packages/pip (python 3.11)
seleniumのインストール
Selenium本体をインストールします。
# Selenium 4系をインストールまたはアップグレード
pip install -U selenium
バージョン確認の一例です。
python -c "import selenium; print(selenium.__version__)"
4.20.0
SeleniumはGoogle ChromeやMicrosoft Edgeなど、ブラウザ本体がインストールされていないと使用できません。
ChromeDriverの自動管理
Selenium 4.6以降はSelenium Managerが標準搭載され、ドライバの自動ダウンロードとバージョン整合を自動化します。
基本的にはwebdriver.Chrome()
と書くだけでドライバの準備が行われます。
# 最小コードでの起動例(Chrome)。Selenium Managerが自動でChromeDriverを取得/管理します。
from selenium import webdriver
driver = webdriver.Chrome() # 追加設定なしで起動
driver.get("https://example.com")
print("Title:", driver.title)
driver.quit()
DevTools listening on ws://127.0.0.1:57979/devtools/browser/8608f173-68ee-4abd-b485-c9e2d4a2fbbe
Title: Example Domain
企業プロキシ環境などでは自動取得に失敗することがあります。
その際は、事前にドライバを取得してPATHを通すか、webdriver-manager
を使う方法もありますが、まずは標準のSelenium Managerを試すのがおすすめです。
Chrome/Edge/Firefoxの起動設定
各ブラウザをオプション付きで起動する例です。
# Chromeの起動オプション例
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
options = Options()
options.add_argument("--incognito") # シークレットモード
options.add_argument("--lang=ja-JP") # UI言語
options.add_argument("--disable-notifications")# 通知ブロック
# options.add_argument("--headless=new") # ヘッドレス(必要に応じて)
driver = webdriver.Chrome(options=options)
driver.get("https://www.google.com")
print(driver.capabilities.get("browserName"), driver.capabilities.get("browserVersion"))
driver.quit() # 上記の処理が完了即終了します
# Microsoft Edgeの起動オプション例
from selenium import webdriver
from selenium.webdriver.edge.options import Options as EdgeOptions
options = EdgeOptions()
options.add_argument("--inprivate")
options.add_argument("--lang=ja-JP")
# options.add_argument("--headless=new")
driver = webdriver.Edge(options=options)
driver.get("https://www.bing.com")
print(driver.capabilities.get("browserName"), driver.capabilities.get("browserVersion"))
driver.quit() # 上記の処理が完了即終了します
# Firefoxの起動オプション例
from selenium import webdriver
from selenium.webdriver.firefox.options import Options as FirefoxOptions
options = FirefoxOptions()
options.set_preference("intl.accept_languages", "ja-JP,ja")
# options.add_argument("-headless")
driver = webdriver.Firefox(options=options)
driver.get("https://example.com")
print(driver.capabilities.get("browserName"), driver.capabilities.get("browserVersion"))
driver.quit() # 上記の処理が完了即終了します
最初のスクリプト
最初はページにアクセスしてタイトルと現在のURLを表示するだけのシンプルな例から始めましょう。
# 最初のSeleniumスクリプト: ページを開いてタイトルとURLを出力します
from selenium import webdriver
driver = webdriver.Chrome() # Chrome起動
driver.get("https://example.com") # アクセス
print("Page Title:", driver.title) # タイトルを出力
print("Current URL:", driver.current_url)
driver.quit() # ブラウザを終了
Page Title: Example Domain
Current URL: https://example.com/
ヘッドレスモードの設定
サーバーやCIでGUIがない環境でも動かせるヘッドレスは重要です。
Chrome/Edgeは--headless=new
、Firefoxは-headless
を指定します。
# ヘッドレス実行の例(Chrome/Edgeは --headless=new が安定)
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
options = Options()
options.add_argument("--headless=new") # 画面なしで起動
options.add_argument("--window-size=1280,800") # 画面サイズは明示すると要素レイアウトが安定
driver = webdriver.Chrome(options=options)
driver.get("https://example.com")
print("Headless Title:", driver.title)
driver.quit() # 上記の処理が完了即終了します
Headless Title: Example Domain
一部サイトはヘッドレスを検知して挙動を変える場合があります。
必要に応じてUser-Agent
を変更するなど調整してください。
基本操作レシピ
要素の探し方
基本のBy戦略
要素はfind_element
またはfind_elements
で取得します。
Selenium 4ではBy
を使うのが標準です。
# 代表的な要素取得パターン
from selenium import webdriver
from selenium.webdriver.common.by import By
driver = webdriver.Chrome()
driver.get("https://example.com")
# IDで取得: 最も安定しやすい
elem_by_id = driver.find_element(By.ID, "some-id")
# CSSセレクタで取得: 汎用性が高く推奨
elem_by_css = driver.find_element(By.CSS_SELECTOR, "div.card > a.button.primary")
# XPathで取得: 複雑な構造指定が可能
elem_by_xpath = driver.find_element(By.XPATH, "//div[@class='card']//a[contains(@class, 'button')]")
# 複数取得
links = driver.find_elements(By.CSS_SELECTOR, "a[href]")
print("リンク数:", len(links))
driver.quit() # 上記の処理が完了即終了します
優先順位はID → CSS → XPathが基本です。
CSSセレクタは可読性と変更耐性のバランスが良いので、IDがなければCSSを検討します。
安定した属性(data-testidやdata-qa)がある場合は積極的に利用すると壊れにくくなります。
明示的待機
非同期に表示される要素には明示的待機(WebDriverWait)を使います。
time.sleepは基本的に避けるのがコツです。
# 明示的待機の基本パターン
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
driver = webdriver.Chrome()
driver.get("https://example.com")
wait = WebDriverWait(driver, 10) # 最大10秒待機
# DOMに現れるまで待つ(表示されていなくてもOK)
presence = wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, "h1")))
print("見つかった要素のテキスト:", presence.text)
# 表示されるまで待つ(visibility)
visible = wait.until(EC.visibility_of_element_located((By.CSS_SELECTOR, "p")))
print("表示中の要素:", visible.tag_name)
# クリック可能になるまで待つ
button = wait.until(EC.element_to_be_clickable((By.CSS_SELECTOR, "a")))
button.click()
driver.quit() # 上記の処理が完了即終了します
目的に応じてpresence
、visibility
、element_to_be_clickable
を使い分けるのがポイントです。
クリック/テキスト入力/送信
フォーム操作の基本です。
# ログインフォームを例にしたクリックと入力
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
driver = webdriver.Chrome()
driver.get("https://example.com/login")
wait = WebDriverWait(driver, 10)
# 入力欄を待ってから入力
user = wait.until(EC.visibility_of_element_located((By.CSS_SELECTOR, "input[name='username']")))
pwd = wait.until(EC.visibility_of_element_located((By.CSS_SELECTOR, "input[type='password']")))
user.clear(); user.send_keys("my_user")
pwd.clear(); pwd.send_keys("my_password")
# ログインボタンをクリック
login_btn = wait.until(EC.element_to_be_clickable((By.CSS_SELECTOR, "button[type='submit']")))
login_btn.click()
# Enterキーで送信する方法
# pwd.send_keys(Keys.ENTER)
# 成功後の要素を待つ
wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, "nav .avatar")))
print("ログイン完了")
driver.quit() # 上記の処理が完了即終了します
スクロールと要素まで移動
動的読み込みや画面外の要素にはスクロールが必要です。
# 要素までスクロールしてクリック、ページ末尾まで連続スクロール
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
driver = webdriver.Chrome()
driver.get("https://example.com/long-page")
wait = WebDriverWait(driver, 10)
# 要素までスクロール
target = wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, "#footer-link")))
driver.execute_script("arguments[0].scrollIntoView({behavior:'smooth', block:'center'});", target)
target.click()
# 無限スクロール(コンテンツが増えなくなるまで)
last_height = driver.execute_script("return document.body.scrollHeight")
while True:
driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")
# サーバ負荷を避けるため短い自然な待機
driver.implicitly_wait(0) # 明示的待機を推奨、ここでは簡略化
new_height = driver.execute_script("return document.body.scrollHeight")
if new_height == last_height:
break
last_height = new_height
print("スクロール完了")
driver.quit() # 上記の処理が完了即終了します
ページ遷移と履歴操作
ページ間の移動や戻る/進むを扱います。
# ページ遷移と戻る/進む/更新
from selenium import webdriver
from selenium.webdriver.common.by import By
driver = webdriver.Chrome()
driver.get("https://example.com")
print("1:", driver.title)
driver.get("https://www.python.org")
print("2:", driver.title)
driver.back()
print("Back:", driver.title)
driver.forward()
print("Forward:", driver.title)
driver.refresh()
print("Refreshed:", driver.title)
driver.quit() # 上記の処理が完了即終了します
1: Example Domain
2: Welcome to Python.org
Back: Example Domain
Forward: Welcome to Python.org
Refreshed: Welcome to Python.org
タブ/ウィンドウの切り替え
新しいタブを開いて切り替える例です。
# 新しいタブを開いて切り替え
from selenium import webdriver
driver = webdriver.Chrome()
driver.get("https://example.com")
# 新規タブを開く
driver.switch_to.new_window("tab")
driver.get("https://www.python.org")
# ウィンドウハンドルの一覧と切り替え
handles = driver.window_handles
print("タブ数:", len(handles))
driver.switch_to.window(handles[0]) # 最初のタブへ戻る
print("現在のURL:", driver.current_url)
driver.quit() # 上記の処理が完了即終了します
アラートの処理
JavaScriptのアラート/確認ダイアログを扱います。
# アラートを受け取って操作する
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
driver = webdriver.Chrome()
driver.get("https://the-internet.herokuapp.com/javascript_alerts")
wait = WebDriverWait(driver, 10)
# アラートを出すボタンをクリック
driver.find_element(By.XPATH, "//button[text()='Click for JS Alert']").click()
# アラートへ切り替えてOK
alert = wait.until(EC.alert_is_present())
print("Alert text:", alert.text)
alert.accept()
# 確認ダイアログのキャンセル例
driver.find_element(By.XPATH, "//button[text()='Click for JS Confirm']").click()
alert = wait.until(EC.alert_is_present())
alert.dismiss()
driver.quit() # 上記の処理が完了即終了します
iframe内の操作
iframe内の要素はフレームを切り替えないと取得できません。
# iframeへ切り替えてから操作する
from selenium import webdriver
from selenium.webdriver.common.by import By
driver = webdriver.Chrome()
driver.get("https://www.w3schools.com/tags/tryit.asp?filename=tryhtml_input_test")
# iframe要素を取得して切り替え
iframe = driver.find_element(By.ID, "iframeResult")
driver.switch_to.frame(iframe)
# ここから先はiframe内のDOM
input_box = driver.find_element(By.NAME, "fname")
input_box.clear(); input_box.send_keys("Taro")
# メインコンテンツに戻る
driver.switch_to.default_content()
driver.quit() # 上記の処理が完了即終了します
スクリーンショット保存
ページ全体や要素単位でPNG保存できます。
# ページ全体と要素のスクリーンショット
from selenium import webdriver
from selenium.webdriver.common.by import By
from pathlib import Path
driver = webdriver.Chrome()
driver.get("https://example.com")
out_dir = Path("screens")
out_dir.mkdir(exist_ok=True)
# ページ全体
page_path = out_dir / "page.png"
driver.save_screenshot(str(page_path))
# 要素単体
h1 = driver.find_element(By.TAG_NAME, "h1")
elem_path = out_dir / "h1.png"
h1.screenshot(str(elem_path))
print("Saved:", page_path, elem_path)
driver.quit() # 上記の処理が完了即終了します
Saved: screens/page.png screens/h1.png
ファイルダウンロードの基本
Chromeではダウンロードディレクトリを設定できます。
ヘッドレスでも近年はダウンロードが安定しています。
# Chromeでのダウンロード設定(標準モード)
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from pathlib import Path
download_dir = Path.cwd() / "downloads"
download_dir.mkdir(exist_ok=True)
options = Options()
prefs = {
"download.default_directory": str(download_dir),
"download.prompt_for_download": False,
"download.directory_upgrade": True,
"safebrowsing.enabled": True
}
options.add_experimental_option("prefs", prefs)
driver = webdriver.Chrome(options=options)
driver.get("https://file-examples.com/index.php/sample-documents-download/sample-pdf-download/")
# 実際のサイトでは適切なセレクタでダウンロードリンクをクリックします
# 例: driver.find_element(By.CSS_SELECTOR, "a[href*='sample.pdf']").click()
print("ダウンロード先:", download_dir)
driver.quit() # 上記の処理が完了即終了します
ヘッドレスの場合でも--headless=new
では多くのケースでそのまま動作します。
もし特殊なサイトでブロックされる場合、ChromeのDevTools Protocol経由で許可する方法もあります。
# 参考: CDPでダウンロード許可を明示(必要な場合のみ)
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from pathlib import Path
download_dir = Path.cwd() / "downloads"
download_dir.mkdir(exist_ok=True)
options = Options()
options.add_argument("--headless=new")
driver = webdriver.Chrome(options=options)
# Page.setDownloadBehavior を呼ぶ
driver.execute_cdp_cmd(
"Page.setDownloadBehavior",
{"behavior": "allow", "downloadPath": str(download_dir)}
)
driver.get("https://example.com/download")
# driver.find_element(...).click()
print("ヘッドレスでのDL許可:", download_dir)
driver.quit() # 上記の処理が完了即終了します
サイトの利用規約(TOS)に反する自動ダウンロードは行わないよう細心の注意を払ってください。
ログイン自動化の流れ
ログインは次の流れが基本です。
セキュリティ上、資格情報は環境変数や安全な秘密管理に置くことを強く推奨します。
# ログイン自動化の典型例(疑似セレクタで解説)
import os
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
USER = os.environ.get("SITE_USER")
PASS = os.environ.get("SITE_PASS")
driver = webdriver.Chrome()
wait = WebDriverWait(driver, 15)
driver.get("https://example.com/login")
# 入力
wait.until(EC.visibility_of_element_located((By.CSS_SELECTOR, "input#username"))).send_keys(USER)
wait.until(EC.visibility_of_element_located((By.CSS_SELECTOR, "input#password"))).send_keys(PASS)
# クリック
wait.until(EC.element_to_be_clickable((By.CSS_SELECTOR, "button[type='submit']"))).click()
# ログイン成功の証拠(ユーザ名表示など)
wait.until(EC.visibility_of_element_located((By.CSS_SELECTOR, "header .user-name")))
print("ログインに成功しました。")
driver.quit() # 上記の処理が完了即終了します
2段階認証(2FA)があるサイトでは、ワンタイムコードの入力や認証アプリ連携など追加対応が必要です。
可能ならAPIや専用のテスト用環境を使う方が安全です。
よくあるエラーと対処法
NoSuchElementExceptionの原因と対策
要素が見つからないときに発生します。
主な原因は表示前に探している、セレクタが誤っている、iframe内を切り替えていないなどです。
まずは明示的待機とセレクタ見直しが効果的です。
# 正しい: 表示を待ってから取得
wait.until(EC.visibility_of_element_located((By.CSS_SELECTOR, "button.buy")))
# iframe内なら切り替えが必要
iframe = driver.find_element(By.CSS_SELECTOR, "iframe.payment")
driver.switch_to.frame(iframe)
# ... iframe内の操作
driver.switch_to.default_content()
StaleElementReferenceExceptionの回避
取得した要素がDOM更新で無効になったときに起きます。
再取得するのが基本です。
# 再取得でStaleを回避する例
from selenium.common.exceptions import StaleElementReferenceException
element_locator = (By.CSS_SELECTOR, "ul.items li:first-child")
item = wait.until(EC.presence_of_element_located(element_locator))
# DOM更新が起きる操作...
driver.refresh()
# 古い参照を使わず、改めて取り直す
item = wait.until(EC.presence_of_element_located(element_locator))
print("再取得に成功")
TimeoutExceptionの対処
待機条件を満たせないと発生します。
URL遷移先が違う、要素が別フレーム、ローディングが長いなどが原因です。
待機時間を延ばす、正しい条件に変える、ネットワーク状況を確認します。
# 例: URLが特定のパターンになるまで待つ
wait.until(EC.url_contains("/dashboard"))
要素がクリックできない時のコツ
「他の要素が覆っている」「座標的に押せない」などのケースでは以下を試します。
scrollIntoView
で中央へスクロールelement_to_be_clickable
で状態を待つ- どうしても無理ならJSによるクリックを最後の手段として使う
target = wait.until(EC.visibility_of_element_located((By.CSS_SELECTOR, ".submit")))
driver.execute_script("arguments[0].scrollIntoView({block:'center'});", target)
wait.until(EC.element_to_be_clickable((By.CSS_SELECTOR, ".submit")))
try:
target.click()
except Exception:
# 最終手段: JSクリック(イベント未発火の場合もある点に注意)
driver.execute_script("arguments[0].click();", target)
動作が遅い時の改善ポイント
動作が重い場合は、以下を検討します。
むやみにtime.sleep
を増やすのは逆効果です。
- 明示的待機に統一し、不要な固定待機をなくす。
- ヘッドレス
--headless=new
や--window-size
の明示で描画コストを安定化。 - ページ読み込み戦略
pageLoadStrategy='eager'
で待機短縮(必要に応じて)。 - 画像やアニメーションの抑制(サイトによっては
prefers-reduced-motion
対応)。 - 不要なナビゲーションを減らし、1回で必要な情報を取得。
# pageLoadStrategy=eager の例
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
opts = Options()
opts.page_load_strategy = "eager" # DOMContentLoadedで制御を返す
driver = webdriver.Chrome(options=opts)
セレクタ設計のベストプラクティス
壊れにくいセレクタが自動化の寿命を延ばします。
悪い例 | 理由 | 良い例 |
---|---|---|
div:nth-child(3) > span > a | 並び順に依存しやすい | a[data-testid='buy-button'] |
//div[2]/div[4]/a | 構造変更に弱い | //a[@aria-label='購入'] |
.btn.blue.large | 見た目のクラスは変わりやすい | #checkout や[data-qa='checkout'] |
- 安定属性(ID、data-属性、ARIAラベル)を優先すること。
- テキスト一致は便利ですが多言語化で崩れやすいため、サイトの仕様に応じて使い分けます。
例外処理とリトライの基本
Selenium操作は外的要因で失敗しやすいので、例外処理と限定的なリトライを入れると堅牢になります。
# シンプルなリトライラッパー
import time
from selenium.common.exceptions import WebDriverException
def retry(times=3, delay=1.0):
def deco(fn):
def wrapper(*args, **kwargs):
last = None
for i in range(times):
try:
return fn(*args, **kwargs)
except WebDriverException as e:
last = e
print(f"リトライ {i+1}/{times} 失敗: {e}")
time.sleep(delay)
raise last
return wrapper
return deco
@retry(times=3, delay=2)
def click_with_wait(driver, locator, wait):
from selenium.webdriver.support import expected_conditions as EC
element = wait.until(EC.element_to_be_clickable(locator))
element.click()
# 使い方
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
driver = webdriver.Chrome()
wait = WebDriverWait(driver, 10)
driver.get("https://example.com")
click_with_wait(driver, (By.CSS_SELECTOR, "button.start"), wait)
print("クリック成功")
driver.quit()
無制限リトライは禁物です。
回数やタイムアウトを明確にし、失敗時はログを残して原因を特定できるようにしましょう。
まとめ
Selenium×Pythonは、JavaScriptで動く動的サイトを人間の操作に近い形で自動化できる強力な道具です。
Selenium Managerによりドライバ管理は簡単になり、明示的待機の活用で安定性が大きく向上します。
本記事で紹介した「要素の探し方」「待機」「スクロール」「タブ切替」「アラート/iframe対応」「スクリーンショット/ダウンロード」「ログインの流れ」といった基本レシピを組み合わせれば、多くの現場要件に対応できます。
仕上げとしては壊れにくいセレクタ設計と丁寧なエラーハンドリングを心がけてください。
さらに、サイトの利用規約やマナーを尊重し、過剰なアクセスや禁止行為を避けることが重要です。
小さく試しながら改善を重ね、安定した自動化フローを構築していきましょう。