【Excel VBA×Selenium】「Element is not clickable」エラーの原因と対処法|ScrollIntoView・待機・JavaScript
みなさんこんにちは。
Excel VBAとSeleniumを使用してWebサイトを自動操作していると、要素自体は取得できているにもかかわらず、Clickを実行したところでエラーになってしまうことがあります。
その中でもよく見かけるのが、「Element is not clickable」や、環境によって表示される「element click intercepted」といったクリックに関するエラーです。
このエラーは、対象のボタンやリンクが取得できていないわけではなく、Seleniumから見たときにその要素を正常にクリックできる状態になっていない場合に発生します。
今回は、Excel VBA×Seleniumで「Element is not clickable」エラーが発生する主な原因と、ScrollIntoView・待機処理・JavaScriptを使用した対処法についてご紹介したいと思います。
この記事はこんな人におすすめ
この記事はこんな人におすすめです!
- VBAプログラミング:初学者~中級者
- Excel VBA×SeleniumでWebサイトを自動操作している人
- 要素は取得できているのにClickでエラーになる人
- 画面外にあるボタンやリンクを確実にクリックしたい人
- Webページの読み込みや表示タイミングによるクリックエラーを減らしたい人
といったところでしょうか。
VBA×Seleniumに関する過去の記事
VBA×Seleniumで要素を取得する方法や、指定した要素まで画面をスクロールする方法については、過去の記事でもご紹介しておりますのでそちらもどうぞ。
「Element is not clickable」エラーとは
Seleniumで要素をクリックする場合、単純にHTML内へ対象要素が存在していればよいというわけではありません。
対象要素が画面上で操作できる状態になっていることや、別の要素に覆われていないことなども重要になります。
例えば、以下のようなコードで要素の取得自体は成功していても、クリック時にエラーになる場合があります。
' id="targetButton"の要素を取得してクリック
' 要素が画面外にある場合や、別の要素に覆われている場合はクリックエラーになることがある
driver.FindElementById("targetButton").Click

エラーが発生する主な原因
クリックエラーが発生する原因はいくつかありますが、私がVBA×Seleniumで確認する場合は、まず以下の内容を確認します。
- 対象要素がブラウザの表示範囲外にある
- 固定ヘッダーやメニューなど、別の要素がクリック対象に重なっている
- ページの読み込みやJavaScriptによる描画が完了していない
- ボタンが一時的に無効化されている
- ローディング画面やモーダルなどが対象要素を覆っている
特に最近のWebサイトでは、ページを開いた後にJavaScriptで画面が描画されるものも多いため、HTML上で要素を取得できたタイミングと、実際にクリックできるタイミングが一致しない場合があります。
対処法① ScrollIntoViewで要素までスクロールする
まず試したいのが、ScrollIntoViewを使用してクリック対象の要素をブラウザの表示範囲内へ移動させる方法です。
' クリック対象の要素を格納する変数を宣言
Dim targetElement As Selenium.WebElement
' id="targetButton"の要素を取得
Set targetElement = driver.FindElementById("targetButton")
' 対象要素が見える位置まで画面をスクロール
' Trueを指定すると、可能な範囲で要素を画面上側へ配置する
targetElement.ScrollIntoView True
' スクロール後に対象要素をクリック
targetElement.Click

固定ヘッダーなどがあるWebサイトでは、通常のクリック時に行われるスクロールによって、対象要素がヘッダーの裏側へ移動してしまう場合があります。
そのような場合にも、ScrollIntoView Trueを明示的に実行してからクリックすることで改善する場合があります。
対処法② 要素が操作できる状態になるまで待機する
次に確認したいのが、クリックするタイミングです。
ページを開いた直後や、画面遷移後すぐにクリック処理を実行すると、対象要素の表示や有効化が完了する前にVBA側の処理が進んでしまうことがあります。
SeleniumBasicのWebElementには、要素が表示されるまで待機するWaitDisplayedと、要素が有効になるまで待機するWaitEnabledがあります。
' クリック対象の要素を格納する変数を宣言
Dim targetElement As Selenium.WebElement
' クリック対象となる要素を取得
Set targetElement = driver.FindElementById("targetButton")
' 要素が画面に表示されるまで最大5秒待機
' SeleniumBasicのtimeoutはミリ秒で指定する
targetElement.WaitDisplayed True, 5000
' 要素が有効な状態になるまで最大5秒待機
targetElement.WaitEnabled True, 5000
' 表示・有効化を確認した後にクリック
targetElement.Click

driver.Waitで固定時間待つ方法
原因を切り分けるだけであれば、以下のように一定時間処理を止めてからクリックする方法もあります。
' 1000ミリ秒(1秒)待機
driver.Wait 1000
' 待機後にクリック
driver.FindElementById("targetButton").Click
ただし、Webサイトの応答速度は毎回同じとは限りません。
本番の自動化処理では、単純に長い固定待機時間を設定するよりも、WaitDisplayedやWaitEnabledなどを使用して必要な状態になるまで待つほうが扱いやすいかと思います。
対処法③ JavaScriptでクリックする
ScrollIntoViewや待機処理を行ってもクリックできない場合は、JavaScriptから対象要素のclick()を実行する方法もあります。
' JavaScriptでクリックする要素を格納する変数を宣言
Dim targetElement As Selenium.WebElement
' クリック対象の要素を取得
Set targetElement = driver.FindElementById("targetButton")
' Seleniumの通常のClickではなく、JavaScriptからclick()を実行
targetElement.ExecuteScript "this.click();"

JavaScriptクリックは、Seleniumの通常のClickで行われる「ユーザーが実際にクリックできる位置かどうか」といった判定を経由せずにDOM側のクリック処理を実行できます。
そのため便利ではありますが、最初からJavaScriptクリックだけを使用するのではなく、ScrollIntoView → 待機 → 通常のClickを確認したうえで、最後の手段として使用するのがおすすめです。
サンプルコード
それでは、ここまでの内容をまとめたサンプルコードをご紹介します。
以下のコードでは、対象要素を取得した後に表示・有効化を待機し、画面内へスクロールしてからクリックします。
URLや要素のIDについては、実際に操作するWebサイトに合わせて変更してください。
Sub ElementClickableSample()
' SeleniumでChromeを操作するためのWebDriverを宣言
Dim driver As New Selenium.WebDriver
' クリック対象の要素を格納する変数を宣言
Dim targetElement As Selenium.WebElement
With driver
' Google Chromeを起動
.Start "Chrome"
' 操作対象となるWebページを開く
' 実際に使用する場合は対象サイトのURLへ変更する
.Get "https://example.com"
' id="targetButton"の要素を取得
' 実際のWebサイトに合わせてIDやXPathなどを変更する
Set targetElement = .FindElementById("targetButton")
' 対象要素が画面に表示されるまで最大5秒待機
targetElement.WaitDisplayed True, 5000
' 対象要素が有効になるまで最大5秒待機
targetElement.WaitEnabled True, 5000
' 対象要素が見える位置までスクロール
' 固定ヘッダーなどとの重なりを避けるためTrueを指定
targetElement.ScrollIntoView True
' 表示・有効化・スクロールを確認した後に通常のClickを実行
targetElement.Click
' 動作確認のため、ここでVBAの処理を一時停止
Stop
' Chromeを終了
.Quit
End With
End Sub
このサンプルでもクリックできない場合は、最後のtargetElement.Clickを以下のJavaScriptクリックへ変更して確認してみてください。
' 通常のClickで操作できない場合にJavaScriptクリックを試す
targetElement.ExecuteScript "this.click();"
それでもクリックできない場合に確認すること
別の要素が重なっていないか確認する
Cookie同意画面、ローディング表示、モーダル、固定ヘッダーなどが対象要素の上に重なっている場合があります。
Chromeのデベロッパーツールで対象要素周辺のHTMLを確認し、クリックを妨げている要素がないか確認してください。
取得している要素が正しいか確認する
同じclass名を持つ要素が複数ある場合などは、意図していない非表示要素を取得している可能性もあります。
ID、XPath、CSS Selectorなどを見直して、本当に画面上でクリックしたい要素を取得できているか確認してみてください。
iframe内の要素ではないか確認する
クリックしたい要素がiframe内にある場合は、クリック処理の前にSwitchToFrameでiframe内へ操作対象を切り替える必要があります。
iframe内の操作については、以下の記事でも詳しくご紹介しています。
対処方法の使い分け
「Element is not clickable」が発生した場合は、私は以下の順番で確認するのがおすすめです。
- 対象要素を正しく取得できているか確認する
- ScrollIntoViewで対象要素を画面内へ移動する
- WaitDisplayed・WaitEnabledでクリック可能な状態になるまで待つ
- 固定ヘッダーやローディング画面などが重なっていないか確認する
- どうしても通常のClickで操作できない場合にJavaScriptクリックを試す
いきなりJavaScriptで強制的にクリックするよりも、原因を順番に確認したほうが、Webサイト側の表示が変わった場合にも修正しやすいコードになるかと思います。
最後に
いかがでしたでしょうか。
今回は、Excel VBA×Seleniumで「Element is not clickable」エラーが発生する原因と、ScrollIntoView・待機・JavaScriptを使用した対処法についてご紹介しました。
要素を取得できているのにクリックできない場合は、要素の位置だけではなく、表示タイミングや他の要素との重なりなども確認することが大切です。
まずはScrollIntoViewと待機処理を試し、それでも解決しない場合にJavaScriptクリックを使用する流れにしておくと、原因を切り分けやすくなるかと思います。
少しでもご参考になれば幸いです。
開発依頼について
ココナラでWebスクレイピング開発サービスを出品しております。
自分で開発をしようと思ったけど、VBAでのネット記事が少なく困っている方も多いかと思います。
そんな方はいつでもお気軽にご相談ください。
また、本ブログからご依頼いただいた方については割引特典がございますので、
ご不明点と合わせてメッセージをいただけると幸いです。
Excelにてブラウザ操作自動化ツールを作成します その作業、webスクレイピングを使って自動化しましょう!Webスクレイピング以外にもWebアプリの開発サービスも出品しております。
こちらについても、本ブログからご依頼いただいた方については割引特典がございますので、
お気軽にご相談ください。
スモールスケールのWebアプリ開発します 先ずはスモールスケールのWebアプリから始めませんか?
