[Selenium Ruby] Element is not currently interactable and may not be manipulatedのエラー

Seleniumでウェブブラウザの操作を自動化しています。社内システムや航空会社の予約システム等のウェブサービスの煩雑な操作のオートメーションに絶大な力を発揮するので感動しているのですが、webページの読み込みのタイミングによっては、たまに以下のような「Element is not currently interactable and may not be manipulated」エラーが出ることがあります。

gems/
selenium-webdriver-2.43.0/lib/selenium/webdriver/safari/bridge.rb:73:
in `raw_execute': Element is not currently interactable and may not be manipulated
(Selenium::WebDriver::Error::InvalidElementStateError)
	from gems/selenium-webdriver-2.43.0/lib/selenium/webdriver/remote/bridge.rb:616:in `execute'
	from gems/selenium-webdriver-2.43.0/lib/selenium/webdriver/remote/bridge.rb:415:in `sendKeysToElement'
	from gems/selenium-webdriver-2.43.0/lib/selenium/webdriver/common/element.rb:126:in `send_keys'
	from show_kaikei_rs.rb:39:in `sendKey'
	from show_kaikei_rs.rb:81:in `'

もちろん、エレメントの操作(clickやsend_keys)の前には、以下のように要素が存在することを確認するwait.untilを入れています。

1
2
3
4
5
6
	def click(xpath)
		@wait.until {
			@driver.find_element(:xpath, xpath) != nil
		}
		@driver.find_element(:xpath, xpath).click()
	end

原因を調べてみると、社内システム等では「画面の全ての要素がダウンロードされ表示可能になるまで、要素のコントロールを禁止する」ためのJavascriptが動いていることがあるようです。

全要素がダウンロードされ表示されていることの確認は結構ややこしいので、安直には、操作対象のエレメントに対するwait.untilの後、十分長い(5秒とか)sleepを入れてからclick()等を実行するようにしています。

つまり、以下のようなfind()を用意しておいて、

1
2
3
4
5
6
7
8
9
10
	def find(xpath)
		begin
			@wait.until {
				@driver.find_element(:xpath, xpath) != nil
			}
			return @driver.find_element(:xpath, xpath)
		rescue
			return nil
		end
	end

find()で操作対象のエレメントが存在することが確認できたあと、sleep 5とかしてclickしています。

1
2
3
4
5
6
7
xpath="//input[@value='登 録']"
if(find(xpath)!=nil)then #操作対象のinputが登場するまで待って、
	sleep 5              #さらに他の全要素が読み込まれるまで十分(この場合5秒)待ってから
	click(xpath)         #操作対象をclick()
else
	#エラー処理
end

関連情報:

Qiita - RubyでSeleniumを使ってスクレイピング

酒と泪とRubyとRailsと - Seleniumでスクレイピング

おおたの物置 - selenium-webdriverでRuby からブラウザを操作する

hello-world.jp.net - [Ruby]seleniumで遊んでみる