【问题标题】:StaleElementReferenceException on Python SeleniumPython Selenium 上的 StaleElementReferenceException
【发布时间】:2015-01-16 04:17:45
【问题描述】:

在 python 中使用 Selenium 时出现以下错误:

selenium.common.exceptions.StaleElementReferenceException: Message: u'stale element reference: element is not attached to the page document\n

有趣的是,错误在 for 循环中的不同时间弹出。有时它会通过例如。 4次迭代和其他时间,例如。 7.

一些正在运行的相关代码是:

for i in range(0, 22):
    u = driver.find_elements_by_id("data")
    text = u[0].get_attribute("innerHTML")
    driver.find_elements_by_class_name("aclassname")[0].click()

这个错误是什么意思,我可以尝试解决什么问题?

【问题讨论】:

  • 这个错误或多或少意味着“我找不到元素”。解决这个问题的方法是以某种方式减慢它的速度。可能通过隐式或显式等待。
  • 好的。什么是隐式等待?
  • 或者,更好的是,我将如何显式等待直到 aclassname 元素被加载?
  • 这里已经回答了:stackoverflow.com/q/24775988/3124333
  • @SiKing no answers 解决了这个问题。因为我们在 2-3 次迭代后得到 StaleElementReferenceException。你能给我确切的解决方案吗

标签: python html selenium-webdriver dom


【解决方案1】:

在我的例子中,我试图找到元素,然后将它传递给另一个类构造函数以供重用。当我在它上面调用“点击”时 - 我得到了 StaleException

所以我只是将 css 类传递给构造函数而不是对象并在新类中查找元素

【讨论】:

    【解决方案2】:

    我在尝试单击具有相同名称的多个类时遇到异常。

        for i in range(0, 5):
        actions = ActionChains(driver)
        button = driver.find_element_by_class_name('dlJyA')
        time.sleep(2)
        if button.is_displayed():
            actions.click(on_element=button).perform()
    

    我通过使用 time.sleep 并检查按钮是否显示来修复它。

    【讨论】:

      【解决方案3】:

      在我的情况下的一个场景: 我查询了一次元素,并将其分配给一个变量以供重用,

      captchaInput = driver.find_element_by_css_selector('input[name="CaptchaCode"]')
      

      那么它就冒着StaleElementReferenceException的风险,用上面的代码再次查询元素有助于消除异常。

      【讨论】:

        【解决方案4】:

        这取决于您使用的网站。

        如果您有一个不会自行改变的网站,请使用 Emilio 的答案。 WebDriverWait(...).until(...) 是网站的正确/最佳解决方案,不会自行更改(仅通过用户交互):

        WebDriverWait(driver, delay, ignored_exceptions=(NoSuchElementException,StaleElementReferenceException)).until(expected_conditions.presence_of_element_located((By.ID, "my_id"))).click()
        

        (解释见 Emilio 的回答)

        如果您使用一个网站,该网站会因服务器推送或基于时间或类似的东西而改变,唯一可行的解​​决方案是像 Rashids 回答中那样重试。

        def retry_func(func, max_tries=MAX_TRIES):
            for i in range(max_tries):
                try:
                    return func()
                except Exception as e:
                    if i >= max_tries - 1:
                        raise e
        
        
        def click_button_helper(driver, selector, selector_type=By.XPATH, delay=DELAY, ignored_exceptions=IGNORED_EXCEPTIONS):
            WebDriverWait(driver, delay, ignored_exceptions=ignored_exceptions).until(
                EC.presence_of_element_located((selector_type, selector))).click()
        
        
        def click_button(driver, selector, selector_type=By.XPATH, delay=DELAY, ignored_exceptions=IGNORED_EXCEPTIONS,
                         max_tries=MAX_TRIES):
            retry_func(lambda: click_button_helper(driver, selector, selector_type, delay, ignored_exceptions), max_tries)
        

        因为,抱歉@Emilio,您的回答没有解决像 Sylvan LE DEUNFF 提到的特定情况。

        以下竞争条件仍然是可能的(WebDriverWait(...).until(...) 无法解决,但通常通过重试解决):

        1. 页面正在发生变化(例如,在问题中点击类似后)
        2. 我们正在尝试查找元素。
        3. WebDriverWait(...).until(...) 等待我们,直到我们的 expected_conditions 被满足(所以基本上直到它完成更改)
        4. 我们找到了元素
        5. 它再次开始变化(通过推送,通过达到时间步长,...)
        6. 我们尝试使用它
        7. 砰!元素又旧了。

        【讨论】:

          【解决方案5】:
          from selenium.webdriver.support.expected_conditions import staleness_of
          

          上面的导入为我节省了一天。

          【讨论】:

            【解决方案6】:
            >>>Stale Exceptions can be handled using **StaleElementReferenceException** to continue the for loop execution.  
            
            from selenium.common import exceptions  
            
            # and customize your code of for loop as:  
            
            for i in range(0, 22):  
               try:  
                    u = driver.find_elements_by_id("data")  
                    text = u[0].get_attribute("innerHTML")  
                    driver.find_elements_by_class_name("aclassname")[0].click()  
               except exceptions.StaleElementReferenceException,e:
                    print(e)
                    pass  
            

            注意:Python 3+:替换 exceptions.StaleElementReferenceException,e -> exceptions.StaleElementReferenceException 为 e

            【讨论】:

              【解决方案7】:

              对于我的 python 脚本,在非常简单的页面上,上述所有解决方案都不起作用。我在这里找到了 -> https://www.softwaretestingmaterial.com/stale-element-reference-exception-selenium-webdriver/ 解决我的问题的最简单方法。只是在点击之前刷新。

              driver.refresh()
              driver.find_element_by_id('normal').click()
              

              注意:我使用的是driver.implicitly_wait(5),而不是显式等待,但它适用于两个选项。

              【讨论】:

                【解决方案8】:

                就我而言,问题

                过时的元素引用:元素未附加到页面文档

                是因为我尝试使用 actions.move_to_element(element).perform() 关于从 Selenium 选项卡创建的操作

                所以解决方案是在打开新标签后创建新的操作实例:

                actions = ActionChains(browser)
                actions.move_to_element(element).perform()
                

                【讨论】:

                  【解决方案9】:

                  这意味着元素不再在 DOM 中,或者它发生了变化。

                  以下代码将通过控制和忽略 StaleElementExceptions 并像处理任何其他 NoSuchElementException 一样帮助您找到元素。它等待元素不会过时,就像它等待元素存在一样。它也是如何在 Selenium 中正确等待条件的一个很好的例子。

                  from selenium.common.exceptions import NoSuchElementException
                  from selenium.common.exceptions import StaleElementReferenceException
                  from selenium.webdriver.common.by import By
                  from selenium.webdriver.support.ui import WebDriverWait
                  from selenium.webdriver.support import expected_conditions
                  
                  my_element_id = 'something123'
                  ignored_exceptions=(NoSuchElementException,StaleElementReferenceException,)
                  your_element = WebDriverWait(your_driver, some_timeout,ignored_exceptions=ignored_exceptions)\
                                          .until(expected_conditions.presence_of_element_located((By.ID, my_element_id)))
                  

                  为了更好地理解问题,假设你在一个 for 循环中,并思考在迭代过程中会发生什么:

                  1. 单击元素时会发生一些变化(最后一行)
                  2. 所以页面正在改变
                  3. 您进入下一个迭代。现在正在尝试找到一个新元素(循环中的第一行)。
                  4. 你找到了元素
                  5. 完成更改
                  6. 你尝试通过获取属性来使用它
                  7. 砰!元素是旧的。您在第 4 步得到了它,但它在第 5 步完成了更改

                  【讨论】:

                  • 一般来说讨论得很好。步骤 4-6 也可能非常微妙……我正在抓取的页面上有一个下拉列表,在加载的某个时刻,它被其自身的精确副本替换,因此在选择<option>,单击它并提交导致几个可能错误之一的表单。
                  • 我已经为过时的元素引用异常挣扎了好几天,直到我偶然发现了你的答案。实现ignore_exceptions 后,我的代码每次都能正常工作并获取所有请求的数据。谢谢!
                  • @Emilio M. 这个答案对我不起作用。它给出了超时异常。如果有其他解决方案请帮忙
                  • @VishavGupta,如果它给您该错误,则表示您的 ID 未正确设置或该元素不存在。尝试通过其他选择器而不是 By.ID 进行过滤...尝试 css 或 xpath...
                  • @EmilioM。我也尝试过其他选择器,但问题仍然存在。
                  【解决方案10】:

                  除了这里的答案之外,如果您正在使用 ActionChain,并且页面已更改,请务必重新实例化您的 ActionChains 对象(不要重复使用旧对象),否则您的 ActionChain 将使用陈旧的 DOM。 IE。这样做;

                  action_chain = ActionChains(driver)     
                  action_chain.double_click(driver.find_element_by_xpath("//tr[2]/p")).perform()
                  

                  或者最好不要使用实例化;

                  ActionChains(driver).double_click(driver.find_element_by_xpath("//tr[2]/p")).perform()
                  

                  【讨论】:

                  • 嗯,我不使用实例化,所以按照你的“蓝色”代码示例,但仍然得到这个错误。
                  【解决方案11】:

                  我想在这里再添加一个对我有用的解决方案。

                  刷新同一页面上的内容区域后,我试图访问网页顶部菜单面板中的按钮,这给了我以下错误,

                      raise exception_class(message, screen, stacktrace)
                  selenium.common.exceptions.StaleElementReferenceException: Message: The element reference of <span id="dk1-combobox" class="dk-selected combinationText visibleElements "> is stale; either the element is no longer attached to the DOM, it is not in the current frame context, or the document has been refreshed
                  

                  然后我开始寻找解决方案来点击网页上的陈旧元素。经过两天的思考和谷歌搜索,我得到了解决方案。

                  要访问页面上的陈旧元素,首先,我们需要将鼠标放在特定部分并执行点击选项

                  EditReport1 = driver.find_element_by_id('internalTab1')
                  ActionChains(driver).move_to_element(EditReport1).click(EditReport1).perform()
                  

                  move_to_element 将鼠标移到我们需要访问的陈旧元素上,一旦我们控制了元素,点击操作就成功执行了。

                  这对我有用。如果有人发现它有效,请评论您的,这将有助于将来的其他人。

                  谢谢

                  【讨论】:

                  • 我试过了,但这项工作最多只能用于三个迭代。请提供一些其他解决方案,因为 StaleElementReferenceException 非常烦人
                  【解决方案12】:

                  这是解决这个问题的python解决方案:

                  def clickAndCatchStaleRefException(locator):
                  
                  
                  
                      driver = sel2._current_browser()
                      result = False
                      attempts = 0
                  
                      locator = locator[6:]
                      # This line is optional because sometimes you pass a xpath from a varibles file
                      # that starts with 'xpath='. This should be omitted otherwise the find_element_by_xpath 
                      # function will throw an error.
                      # But if you pass an xpath directly you don't need this
                  
                  
                      while attempts < 2:
                          try:
                              driver.find_element_by_xpath(locator).click()
                              result = True
                              break
                          except EC as e:
                              raise e
                          finally:
                              attempts += 1
                      return result
                  

                  【讨论】:

                  • 这不是一个好的解决方案。 WebDriver 具有 selenium.webdriver.support.wait.WebDriverWait 和 selenium.webdriver.support.expected_conditions 以正确解决此问题,如其他答案中所述。重新尝试获取元素可能会起作用,但这不是正确的方法。
                  • 我同意,但它并不总是有效。例如,如果您使用 jquery 处理应用程序,则某些元素可能会被意外替换,并且即使在 WebDriverWait 完成后也会引发 StaleElementReferenceException。在这些特殊情况下,在获得预期结果之前,从未听说过比尝试更好的解决方案。 (注意:我不是使用 selenium 来执行自动化测试,而是废弃公司的应用程序)
                  • Sylvan,如果您检查我上面的答案,它会处理您提到的特定情况。这种方式是最好的方式。您仍在等待,但使用 Selenium 提供的标准工具而不是自定义代码。它不仅更清洁,而且更安全,因为它是一种适用于所有人的经过验证的解决方案。
                  【解决方案13】:

                  当网页被刷新或从不同的窗口或表单切换回来并尝试访问元素时,用户将得到 staleelementexception。

                  在带有 for 循环的 try --except 块中使用 webdriverwait: 前:

                  我得到 staleelementexception 的代码:


                  driver.find_element_by_id(tc.value).click()
                  

                  driver.find_element_by_xpath("xpath").click()


                  修复:

                  from selenium.webdriver.common.by import By
                  from selenium.webdriver.support.ui import WebDriverWait
                  from selenium.webdriver.support import expected_conditions as EC
                  
                  driver.find_element_by_id(tc.value).click()
                  for i in range(4):
                     try:
                          run_test = WebDriverWait(driver, 120).until( \
                          EC.presence_of_element_located((By.XPATH, "xpath")))
                          run_test.click()
                          break
                     except StaleElementReferenceException as e:
                          raise e
                  

                  【讨论】:

                    【解决方案14】:

                    Selenium 支持 ExplicitImplicit 等待。如果您认为等待一定时间足以加载您的页面,请使用:

                    driver.implicitly_wait(secs)
                    

                    但如果您想等待特殊事件(例如等待加载特定元素),您可以执行以下操作:

                    from selenium.webdriver.support.ui import WebDriverWait
                    ...
                    ...
                    def find(driver):
                        element = driver.find_elements_by_id("data")
                        if element:
                            return element
                        else:
                            return False
                    element = WebDriverWait(driver, secs).until(find)
                    

                    【讨论】:

                      猜你喜欢
                      • 2018-09-14
                      • 1970-01-01
                      • 2014-11-15
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 2021-10-26
                      • 1970-01-01
                      • 2011-06-18
                      相关资源
                      最近更新 更多