Seleniumは、Webブラウザの自動操作を可能にする強力なツールですが、要素の取得に失敗することがあります。この記事では、PythonのSeleniumで要素を取得できない主な原因と、その解決策を詳しく解説します。
Seleniumで要素を取得できない主な原因
Seleniumで特定の要素を取得できない原因はさまざまです。
主なものとして、以下のような理由が挙げられます。
- セレクタが間違っている: XPathやCSSセレクタの指定ミス。
- 要素の読み込みが完了していない: ページのレンダリングが遅く、要素がまだDOMに追加されていない。
- 要素が非表示または不可視状態: CSSで
display: none;
やvisibility: hidden;
になっている。 - iframe内の要素を取得しようとしている: 直接アクセスできないため、iframeを切り替える必要がある。
- 要素が他の要素と重なっている: 別の要素が被さっていてクリックや操作ができない。
これらの問題に適切に対処することで、要素を取得できるようになります。
以下、対処法をお伝えします。
要素の検索方法を見直す
Seleniumでは、要素を取得するために find_element
メソッドを使用します。まず、基本的な検索方法を確認しましょう。
from selenium import webdriver
from selenium.webdriver.common.by import By
driver = webdriver.Chrome()
driver.get("https://example.com")
# CSSセレクタで検索
element = driver.find_element(By.CSS_SELECTOR, ".example-class")
# XPathで検索
element = driver.find_element(By.XPATH, "//div[@id='example-id']")
ポイント:
By.ID
やBy.CLASS_NAME
など、適切な検索方法を選ぶ。XPath
を使用する場合、//
を用いることで相対パスで検索できる。find_elements
を使用すれば、要素がリストとして取得されるため、複数の要素に対応可能。
要素の読み込みタイミングを考慮する
Webページは非同期でロードされるため、要素が表示される前に取得しようとすると失敗します。この問題を回避するには、待機処理(Wait) を使いましょう。
暗黙的待機(Implicit Wait)
driver.implicitly_wait(10) # 最大10秒間待機
この方法は全ての find_element
に適用されますが、明示的待機(Explicit Wait)の方が柔軟です。
明示的待機(Explicit Wait)
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
# 最大10秒間、要素が表示されるのを待機
element = WebDriverWait(driver, 10).until(
EC.presence_of_element_located((By.ID, "example-id"))
)
presence_of_element_located
のほかに、visibility_of_element_located
などもあります。
非表示または不可視の要素を取得する方法
JavaScriptのボタンを押す方法
通常の .click()
メソッドではボタンが押せない場合、JavaScriptやマウス操作を利用してクリックする方法があります。
JavaScriptを利用したクリック
driver.execute_script("document.querySelector('button').click()")
この方法では、Seleniumの .click()
メソッドでは操作できない要素にもアプローチできます。
座標を使ったクリック
次のコードは、要素の座標 (location
) とサイズ (rect
) を取得し、その中心座標にマウスを移動してクリックする方法です。
from selenium import webdriver
from selenium.webdriver.common.action_chains import ActionChains
dive = webdriver.Chrome()
dive.get('*****') # 適切なURLを設定
# 要素を取得
elem = dive.find_element_by_xpath('****')
# 要素の座標を取得
loc = elem.location
print(loc)
# 要素のサイズを取得
rec = elem.rect
print(rec)
# 中心座標を計算
x, y = loc['x'] + rec['width'] / 2, loc['y'] + rec['height'] / 2
# マウスを移動してクリック
actions = ActionChains(dive)
actions.move_by_offset(x, y)
actions.click()
actions.perform()
actions.reset_actions()
なぜこの方法を使うのか?
.click()
では反応しない要素もクリックできる。- JavaScriptの
display: none;
になっている要素でも座標を計算すればクリック可能。 - マウスカーソルを実際に動かすため、UIの動作を再現しやすい。
webdriver.common.action_chains.ActionChains(driver) を使った対処法
Seleniumでは ActionChains
を利用すると、要素のホバーやドラッグ&ドロップ、クリック動作を再現できます。
要素の上にマウスを移動してクリック
from selenium.webdriver.common.action_chains import ActionChains
element = dive.find_element_by_xpath("****")
action = ActionChains(dive)
# 要素の上にマウスを移動してクリック
action.move_to_element(element).click().perform()
ドラッグ&ドロップ
source = dive.find_element_by_id("source-element")
target = dive.find_element_by_id("target-element")
action.drag_and_drop(source, target).perform()
move_to_element_with_offset を使う方法
btn = dive.find_element_by_id("button-id")
action = ActionChains(dive)
# ボタンの左上から (5,5) ピクセル右下の位置に移動してクリック
action.move_to_element_with_offset(btn, 5, 5).click().perform()
Seleniumでiframe内の要素を取得する方法と対処法
Seleniumを使用して要素を取得する際、iframe の中にある要素を取得しようとして失敗することがあります。これは、Seleniumがデフォルトではページのメインコンテンツ(親フレーム)しか認識せず、iframe内の要素にアクセスできないためです。
PythonのSeleniumでiframe内の要素を正しく取得する方法と対処法について詳しく解説します。
iframeを適切に操作することで、Seleniumを使ったWeb自動化スクリプトをより柔軟に作成できます!
iframeとは?
iframe(インラインフレーム) は、Webページの中に別のWebページを埋め込むためのHTMLタグです。たとえば、以下のようにWebサイト内に別のページが表示される場合、iframeが使われています。
<iframe src="https://example.com" width="500" height="300"></iframe>
この場合、Seleniumはメインのページしか認識しないため、iframe内の要素に直接アクセスすることはできません。そのため、まずiframeに切り替えてから要素を取得する必要があります。
iframe内の要素を取得する基本的な方法
ステップ①:iframeを特定して切り替える
iframe内の要素を取得するためには、switch_to.frame()
を使用してiframeに切り替える必要があります。
from selenium import webdriver
from selenium.webdriver.common.by import By
# WebDriverを起動
driver = webdriver.Chrome()
driver.get('https://example.com')
# iframeを取得し、切り替える
iframe = driver.find_element(By.XPATH, "//iframe[@id='iframe_id']")
driver.switch_to.frame(iframe)
# iframe内の要素を取得
element_inside_iframe = driver.find_element(By.ID, "element-id")
print(element_inside_iframe.text)
# iframeから親ページに戻る
driver.switch_to.default_content()
ステップ②:iframeの取得方法
iframeを特定する方法はいくつかあります。
方法 | コード例 |
---|---|
iframeのIDで取得 | driver.find_element(By.ID, "iframe_id") |
iframeのXPathで取得 | driver.find_element(By.XPATH, "//iframe[@name='frame_name']") |
iframeのCSSセレクタで取得 | driver.find_element(By.CSS_SELECTOR, "iframe[src='https://example.com']") |
インデックスで取得(ページにiframeが複数ある場合) | driver.switch_to.frame(0) |
ウィンドウハンドルを使用してiframeにアクセスする
iframeが別のウィンドウに関連付けられている場合、ウィンドウを切り替えてからiframeを操作する必要があります。
handle_array = driver.window_handles
driver.switch_to.window(handle_array[2]) # 3番目のウィンドウに切り替え
iframe = driver.find_element(By.XPATH, '/html/body/form/div[4]/iframe')
driver.switch_to.frame(iframe)
element_inside_iframe = driver.find_element(By.ID, "element-id")
この方法を使うと、iframeがポップアップウィンドウや新しいタブ内にある場合でも適切にアクセスできます。
Seleniumでiframeのネスト(入れ子)に対応する
iframeの中にさらにiframeがある場合(ネストされたiframe)、複数回switch_to.frame()
を使用する必要があります。
# 最初のiframeに切り替え
driver.switch_to.frame(driver.find_element(By.XPATH, "//iframe[@id='outer-iframe']"))
# さらに内部のiframeに切り替え
driver.switch_to.frame(driver.find_element(By.XPATH, "//iframe[@id='inner-iframe']"))
# iframe内の要素を取得
element = driver.find_element(By.ID, "element-id")
print(element.text)
# iframeから元のページに戻る
driver.switch_to.default_content()
ポイント
switch_to.frame()
は階層ごとに1回ずつ行う必要があるswitch_to.default_content()
で最上位のフレームに戻る- 途中のフレームに戻りたい場合は
switch_to.parent_frame()
を使う
iframe内の要素が見つからない場合の対処法
iframe内の要素を取得できない場合、次の原因が考えられます。
原因①:iframeに切り替えていない
エラーメッセージ:
selenium.common.exceptions.NoSuchElementException: Message: no such element
解決策: iframeに切り替えてから要素を取得しているか確認する。
driver.switch_to.frame("iframe_id") # まずiframeに切り替える
element = driver.find_element(By.ID, "element-id")
原因②:iframeの読み込みが完了していない
エラーメッセージ:
selenium.common.exceptions.TimeoutException
解決策: iframeが完全にロードされるまで待機する。
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
# iframeがロードされるまで待機
iframe = WebDriverWait(driver, 10).until(
EC.presence_of_element_located((By.XPATH, "//iframe[@id='iframe_id']"))
)
driver.switch_to.frame(iframe)
原因③:iframeの識別方法が間違っている
解決策: iframeが複数ある場合、インデックスを指定して切り替える。
driver.switch_to.frame(0) # 最初のiframe
driver.switch_to.frame(1) # 2番目のiframe
対処法を盛り込んだ実際のコード例
実務において、Seleniumのコードを書いて動かしていくと、要素がうまく取得できないことが多いです。その場合、上記にご紹介した方法で対処していきます。
以下に実際のコード例をご紹介しますので、是非、参考にしてください。
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import Select # Selectをインポート
from selenium.webdriver.common.action_chains import ActionChains
from time import sleep
import os
import shutil
import pyautogui
from PIL import Image
# 月指定
month = 2025.1
month_path = '/html/body/×××'
# 保存フォルダを指定
movebase_dir = "C:/Users/Username//Documents/〇〇ファイル"
#スクリーンショットの範囲を指定
prtsc_range = (500,200,1000,800) #必要に応じて変更
#ダウンロードフォルダパス指定
downloadsdir_path = '.\Downloads" # ダウンロードフォルダを指定
# Chromeオプションを設定
options = webdriver.ChromeOptions()
options.add_experimental_option('detach', True) # ブラウザが閉じないように設定
# ChromeDriverのサービスを設定
cService = Service(executable_path='.\chromedriver.exe') # クロームドライバーが保存されているフォルダを指定
actionChains = ActionChains(driver)
# 暗黙的な待機時間を設定
driver.implicitly_wait(10)
# ログインページを開く
url_login = 'https://www.×××' # URLを指定
driver.get(url_login)
try:
# ID入力
text_box = driver.find_element(By.XPATH, '/html/body/×××/input')
text_box.send_keys('×××')
sleep(5)
# パスワード入力
text_box = driver.find_element(By.XPATH, '/html/body/×××/input')
text_box.send_keys('×××')
# ログインボタンをクリック
btn = driver.find_element(By.XPATH,'/html/body/×××/input')
btn.click()
sleep(10)
# 施設選択
btn = driver.find_element(By.XPATH,'/html/body/×××/input')
btn.click()
sleep(10)
# 遷移先ページに移動
handle_array = driver.window_handles
driver.switch_to.window(handle_array[1])
iframe = driver.find_element(By.XPATH,'/html/body/×××/iframe')
driver.switch_to.frame(iframe)
# 精算をクリック
btn = driver.find_element(By.NAME,"×××")
btn.click()
sleep(5)
# 月選択
driver.switch_to.default_content()
btn = driver.find_element(By.XPATH,'/html/body/×××/select')
btn.click()
btn = driver.find_element(By.XPATH,month_path)
btn.click()
#表示する
elem = driver.find_element(By.XPATH,'/html/body/×××/input')
loc = elem.location
print(loc)
rec = elem.rect
print(rec)
x,y = loc['x']+rec['width']/2,loc['y']+rec['height']/2
actions = ActionChains(driver)
actions.move_by_offset(x,y)
actions.click()
actions.perform()
actions.reset_actions()
sleep(5)
#スクショ
data = '〇〇〇screenshot.pdf'
img1 = pyautogui.screenshot(region=prtsc_range)
file_path = os.path.join(movebase_dir,data)
img1.save(file_path)
#実績一覧ダウンロード
btn = driver.find_element(By.XPATH,'/html/body/×××/input')
btn.click()
sleep(5)
#フォルダ移動
data = "〇〇〇_実績一覧.csv"
files = os.listdir(downloadsdir_path)
path_dir = os.path.join(downloadsdir_path,*files)
print(path_dir)
move_dir = os.path.join(movebase_dir,data)
shutil.move(path_dir,move_dir)
#ブラウザを閉じる
driver.quit()
except Exception as e:
print(f"エラーが発生しました: {e}")
# スクリプト終了を防ぐ
input("終了するには Enter キーを押してください...")
ダウンロードしたファイルをリネームし、格納フォルダに移動させる方法はこちらの記事に詳しく紹介しています。
Python Selemiumで経理業務を効率化:ダウンロードファイルのリネームと所定フォルダへの移動を自動化
また、スクリーンショットの方法についてはこちらの記事で紹介しています。
Pythonで経理業務を効率化 Webページへログインし、スクリーンショット・保存を自動化する方法と実例
まとめ
Seleniumで要素を取得できない場合、以下の点を確認し、適切な対策を講じることが重要です。
実務で実際に起きたケースおよびその対処法をまとめてみましたので、是非、参考にしてください。
- 要素の読み込みタイミング:明示的待機を使用して、要素がロードされるまで待機する。
- iframe内の要素:iframeに切り替えてから、内部の要素にアクセスする。
- 要素の表示状態:非表示の要素をJavaScriptで表示状態に変更する。
- セレクタの指定:適切なセレクタを使用して、正確に要素を特定する。
- 要素の重なり:
ActionChains
を使用して、特定の位置にマウスを移動し、クリックする。
コメント