【Selenium】ヘッドレスモード時だけ"element not interactable"になる

- Python -
2024.01.04
Python[パイソン]

Python × Selenium × ChromeDriverでブラウザ操作をしようとして、

  • 通常のブラウザモードで起動すると、処理成功
  • headlessオプションで起動すると、決まってエラー(例外)になる

という事象が起きました。

具体的に発生した例外は以下「ElementNotInteractableException: Message: element not interactable」というものです。

Traceback (most recent call last):
...省略...
File "/opt/anaconda3/lib/python3.7/site-packages/selenium/webdriver/remote/webelement.py", line 479, in send_keys
'value': keys_to_typing(value)})
File "/opt/anaconda3/lib/python3.7/site-packages/selenium/webdriver/remote/webelement.py", line 633, in _execute
return self._parent.execute(command, params)
File "/opt/anaconda3/lib/python3.7/site-packages/selenium/webdriver/remote/webdriver.py", line 321, in execute
self.error_handler.check_response(response)
File "/opt/anaconda3/lib/python3.7/site-packages/selenium/webdriver/remote/errorhandler.py", line 242, in check_response
raise exception_class(message, screen, stacktrace)
selenium.common.exceptions.ElementNotInteractableException: Message: element not interactable
(Session info: headless chrome=84.0.4147.135)

NoSuchElementException(要素が見つからない)ではなくNotInteractable(要素が操作できない)という例外なので、「要素は見つかったが操作ができない」という状態である点がポイントのようです。

結論から言うと、この例外は以下8行目のようにChromeDriverのset_windows_size('幅', '高さ')でウィンドウサイズを拡大することで解決できました。

Pythonimport chromedriver_binary
from selenium import webdriver
 
option = webdriver.ChromeOptions()
option.add_argument('--headless')
driver = webdriver.Chrome(options=option)
driver.implicitly_wait(10)
driver.set_window_size('1200', '1000')
driver.get('https://xxxxxx.com')

以下、僕が直面した事象が解決するまでの経緯を書いていきます。

Selenium ヘッドレスモードで"element not interactable"になる問題を解決するまで

headlessオプションを指定しない通常モードでは成功するので、両モードで実行時の差分を知るためにdriver.save_screenshot('xxx.png')でスクリーンショットをそれぞれキャプチャしてみました。すると...

通常モードとヘッドレスモードでウィンドウサイズが異なる

通常のブラウザモードのデフォルトウィンドウサイズは 1200 * 710

selenium element not interactable

ヘッドレスモードのデフォルトウィンドウサイズは 800 * 600 になっていることが判明。

selenium element not interactable

つまり、ヘッドレスモードのときはウィンドウサイズが小さいことが分かりました。

.is_displayed()で表示領域内に要素があるかチェック

通常モードとヘッドレスモードでディスプレイサイズが異なるという差分から、

「ヘッドレス実行時のみ操作対象の要素がウィンドウ表示領域内にないのでは?」

と思い、.is_displayed()で操作対象の要素がウィンドウ内にあるかを確認してみたところ...(以下11行目)

Pythonimport chromedriver_binary
from selenium import webdriver
 
option = webdriver.ChromeOptions()
option.add_argument('--headless')
driver = webdriver.Chrome(options=option)
driver.implicitly_wait(10)
driver.set_window_size('1200', '1000')
driver.get('https://xxxxxx.com')
element = driver.find_element_by_class_name('xxx')
print(element.is_displayed())
  • 通常モード → True (表示領域内にある)
  • ヘッドレスモード → False (表示領域内にない)

となりました。

つまり、ヘッドレスモードのときだけ要素が表示領域にないということが分かったので、ヘッドレスモード時のウィンドウサイズを変更したら上手くいったという流れです。

▼体型的なスクレイピング学習におすすめ

技術評論社Pythonクローリング&スクレイピング[増補改訂版] -データ収集・解析のための実践開発ガイド
Amazonで見る ≫
↑TOP