はじめに
Seleniumを使っていると、StaleElementReferenceExceptionが発生しました。
だたし、操作対象のelementは直前に取得しており、なおかつ表示されているので存在していないとは考えられませんでした。
そこで、自分が変更した個所と動作を検証した結果、原因らしきものが分かりました。
もっとも、原因に関しては個人の予想の範疇なので間違っているかもしれません。
例外が発生する記述
単純化すると以下のような記述の場合に発生します。
actions = ActionChains(driver)
element = driver.find_element(By.ID, "1")
actions.move_to_element(element).perform()
# 何かの処理
# その結果、IDが"1"のelementの参照が切れる
element = driver.find_element(By.ID, "2")
actions.move_to_element(element).perform()
この記述だと、2度目のactions.move_to_element(element).perform()でStaleElementReferenceExceptionが発生するはずです。
なぜならば、move_to_elementの出発点がidが"1"のelementのためにその参照が必要なためと思われます。
もしくは、以下のようにしても例外が発生すると思います。
actions.click(element)は、内部でactions.move_to_element(element)を呼んでいるからです。
actions = ActionChains(driver)
element = driver.find_element(By.ID, "1")
actions.click(element).perform()
# 何かの処理
# その結果、IDが"1"のelementの参照が切れる
element = driver.find_element(By.ID, "2")
actions.click(element).perform()
修正方法
上記の記述で問題だったのは、ActionChainsのインスタンスを使いまわしてしまったことです。
これを避けるためには、インスタンスを作り直せば良いだけです。
actions = ActionChains(driver)
element = driver.find_element(By.ID, "1")
actions.move_to_element(element).perform()
# 何かの処理
# その結果、IDが"1"のelementの参照が切れる
actions = ActionChains(driver)
element = driver.find_element(By.ID, "2")
actions.move_to_element(element).perform()
最後に
わざわざインスタンス作り直す必要ないだろうと横着しようとしたら思わぬエラーにぶつかりました。
適当な事をしては駄目という事でしょうか。
とりあえず対策できて一安心です。