了解 selenium move_to_element 行为 + StaleElementReference 异常

Understanding selenium move_to_element behaviour + StaleElementReference exception

我正在使用 Python3.9+Selenium 编写一个小脚本来为我填写在线表格。

一点上下文:该网页包含一个字段 (locationField),需要将街道地址作为输入,并且位于某种“google 地图包装器”之上。 When typing in the field, it loads a drop-down list of one element (locationField_sugg) with the compatible complete address, and when this option is selected the map zooms-in on the chosen part of the city. 除此之外,还有一个 descriptionField 用于填充一些随机文本,然后单击 submitButton 以发送表单。

我注意到如果我使用 actionChains.move_to_element(locationField_sugg).click().perform() 点击下拉列表中的地址,然后 submitButton 抛出一个 StaleElementReference 异常,而如果我只使用 locationField_sugg.click() 这个情况并非如此,代码将按应有的方式进行。

我通读了许多 Q/A 关于这个臭名昭著的异常处理的文章,但其中 none 似乎解释了我的代码中发生这种情况的原因。 对我来说,它似乎与 move_to_element() 结合“地图包装器”(?)的行为有关,但我不明白为什么,因为这个函数只是应该在给定的中间移动鼠标元素.

网页中似乎没有重新加载或其他更改(我验证了如果我在开头查询按钮并在脚本结尾重新查询,我会得到完全相同的实例表示字符串)。 此外,我在对其执行操作之前查询了 ​​submitButton,并且我认为它已被正确找到,因为它可以被打印出来。

下面是我的代码片段和我得到的输出(注意:如果我使用替代的注释选项,代码可以正常工作,但我很想知道我遗漏了什么)

代码片段

from selenium import webdriver
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By

websiteUrl = "https://mywebsite"
option = webdriver.ChromeOptions()
option.add_argument("-incognito")
browser = webdriver.Chrome(executable_path="/Applications/chromedriver", options=option)
browser.get(websiteUrl)
actionChains = ActionChains(browser)

# Write and select complete address
locationField = browser.find_element_by_id("location")
print("locationField = ", locationField)
locationField.send_keys("my location")
locationField_sugg = WebDriverWait(browser, 10).until(EC.visibility_of_element_located((By.ID, "as-listbox")))
print("locationField_sugg = ", locationField_sugg)
#
# this throws stale element reference exception:
actionChains.move_to_element(locationField_sugg).click().perform()
#
# this does not:
# locationField_sugg.click()

# Write description
descriptionField = browser.find_element_by_id("description")
print("descriptionField = ", descriptionField)
descriptionField.send_keys("my description")

# Submit form
submitButton = WebDriverWait(browser, 10).until(EC.visibility_of_element_located((By.CSS_SELECTOR, "span.value")))
print("submitButton = ", submitButton)
actionChains.move_to_element(submitButton).click().perform()

输出

locationField =  <selenium.webdriver.remote.webelement.WebElement (session="e10dc716790c61a0c160599624dd30c6", element="e75539a7-ebd0-4090-9c04-0c4994afe03f")>
locationField_sugg =  <selenium.webdriver.remote.webelement.WebElement (session="e10dc716790c61a0c160599624dd30c6", element="53ef0269-aceb-451a-b559-d9e5e7aa7851")>
descriptionField =  <selenium.webdriver.remote.webelement.WebElement (session="e10dc716790c61a0c160599624dd30c6", element="dce478de-a23d-4ca2-817c-81ee5ce0c232")>
nowButton =  <selenium.webdriver.remote.webelement.WebElement (session="e10dc716790c61a0c160599624dd30c6", element="f2036f1c-d164-4211-a37c-2ee50e5c55c1")>
Traceback (most recent call last):
  File "/Users/alice/Desktop/wasteComplaints_selenium.py", line 44, in <module>
    actionChains.move_to_element(nowButton).click().perform()
  File "/Users/alice/miniconda3/lib/python3.9/site-packages/selenium/webdriver/common/action_chains.py", line 80, in perform
    self.w3c_actions.perform()
  File "/Users/alice/miniconda3/lib/python3.9/site-packages/selenium/webdriver/common/actions/action_builder.py", line 76, in perform
    self.driver.execute(Command.W3C_ACTIONS, enc)
  File "/Users/alice/miniconda3/lib/python3.9/site-packages/selenium/webdriver/remote/webdriver.py", line 321, in execute
    self.error_handler.check_response(response)
  File "/Users/alice/miniconda3/lib/python3.9/site-packages/selenium/webdriver/remote/errorhandler.py", line 242, in check_response
    raise exception_class(message, screen, stacktrace)
selenium.common.exceptions.StaleElementReferenceException: Message: stale element reference: element is not attached to the page document
  (Session info: chrome=92.0.4515.131)

我不确定我是否知道为什么在不调试的情况下会发生这种情况。当焦点从输入字段中移开时,建议的地址可能会消失吗?如果是这样,当您将地址字符串插入输入字段然后立即单击建议的地址时,它会正常工作,但如果在插入输入地址字符串后出现建议的地址,您正在应用 actionChains.move_to_element 这会移动鼠标从它的初始位置到建议的地址元素。因此焦点从输入字段移动到鼠标光标。这会导致建议的地址消失,这就是抛出 StaleElementReference exception 的原因。