首页 > 解决方案 > Python selenium 如何等到元素消失/变得陈旧?

问题描述

我正在尝试以编程方式解决此地址出现的问题:https ://www.arealme.com/brain-memory-game/en/ ,简而言之,它将连续显示几个闪烁的数字,并要求您输入序号倒序。

现在这里是详细信息,当你进入网站时,会有一个开始按钮,它有一个从左侧填充它的蓝条,我发现如果你让你的机器人在蓝条填满之前点击那个按钮按钮,则按钮的功能不会被触发,脚本的其余部分也不会运行。

单击开始按钮后,页面将刷新,您将看到以下闪烁的文本:

这些数字很快就会到来。请注意...

之后,你会看到几个闪烁的数字,它们通过改变alpha来闪烁,它们从完全可见变为完全不可见,当一个数字消失时会出现另一个数字,即使它们具有相同的值,它们也是不同的数字。

然后会有这样的信息:

请单击您刚刚在反向顺序中看到的数字。

还有一个带有十个按钮的数字键盘。

按照它说的做,然后会出现一个按钮,告诉你是否正确,你需要点击它继续下一个问题。

一共有十道题。

现在介绍技术细节。

我设法找到了所涉及元素的类名、xpath 和 id。

开始按钮的 id 是'start',我发现time.sleep(3)它足以填满。

类的层次结构如下:

<div class="questionWrapper">
    <div class="question">
        <div class="gnumber_title">
            <div class="gend-tip">Please click on the number you just saw IN REVERSE ORDER.</div>
        </div>
        <div class="gnumber_btns" id="gbtn0" style=""><button data-n="9">9</button><button data-n="8">8</button><button data-n="7">7</button><button data-n="6">6</button><button data-n="5">5</button><button data-n="4">4</button><button data-n="3">3</button><button data-n="2">2</button><button data-n="1">1</button><button data-n="0">0</button></div>
    </div>
    <div class="answer" value="10">1</div>
</div>

问题包装在"questionWrapper"类中,每个类中只有一个实例"gnumber_title",对象包含消息和数字。

类在任何时候都只包含一个元素,类所包含的内容会随着时间而变化,但它们都相对于所述类共享相同的 xpath:'./div'

在问题开始时,当提示是第一条消息时,所述位置内的对象是 class "gstart-tip blink_me"

然后它会消失,取而代之的是一个 class 的对象,'gflash-num'可以使用.text属性访问其内容。

它通过改变样式闪烁,样式会从"opacity: 1.000000;"变为"opacity: 0.000000;"(我不知道确切的值,但它是一个从 1 到 0 的六位小数的浮点数),然后它会变成"display: none;",然后它将被删除并另一个实例的"glash-num"会出现。

在 的几个实例之后"gflash-num",出现第二条消息,它的类是"gend-tip",位于与数字和第一条消息相同的 xpath 中。

并且数字键盘将可见,其类为"gnumber_btns"

显示相同数量的按钮被点击后,"answer"出现所属类的按钮,点击进入下一题。

这是我解决问题的尝试:

import time
from selenium import webdriver

Firefox = webdriver.Firefox()
Firefox.get('https://www.arealme.com/brain-memory-game/en/')

time.sleep(3)
Firefox.find_element_by_id('start').click()

questions = Firefox.find_elements_by_class_name("questionWrapper")
for q in questions:
    numbers = []
    title = q.find_element_by_class_name('gnumber_title')
    while True:
        if title.find_element_by_xpath('./div').get_attribute('class') != 'gstart-tip blink_me':
            break
    while True:
        if title.find_element_by_xpath('./div').get_attribute('class') == "gend-tip":
            break
        numbers.append(title.find_element_by_class_name('gflash-num').text)
        while True:
            if not title.find_element_by_class_name('gflash-num').get_attribute('style').startswith('opacity'):
                break
        while True:
            if not title.find_element_by_class_name('gflash-num').get_attribute('style').startswith('display'):
                break
    buttons = q.find_element_by_class_name("gnumber_btns")
    for n in reversed(numbers):
        buttons.find_element_by_xpath(f'.//*[text() = "{n}"]').click()
    time.sleep(0.5)
    q.find_element_by_class_name("answer").click()

和错误:

---------------------------------------------------------------------------
StaleElementReferenceException            Traceback (most recent call last)
<ipython-input-1-9d34e88c4046> in <module>
     20         numbers.append(title.find_element_by_class_name('gflash-num').text)
     21         while True:
---> 22             if not title.find_element_by_class_name('gflash-num').get_attribute('style').startswith('opacity'):
     23                 break
     24         while True:

c:\program files\python39\lib\site-packages\selenium\webdriver\remote\webelement.py in get_attribute(self, name)
    137         attributeValue = ''
    138         if self._w3c:
--> 139             attributeValue = self.parent.execute_script(
    140                 "return (%s).apply(null, arguments);" % getAttribute_js,
    141                 self, name)

c:\program files\python39\lib\site-packages\selenium\webdriver\remote\webdriver.py in execute_script(self, script, *args)
    632             command = Command.EXECUTE_SCRIPT
    633
--> 634         return self.execute(command, {
    635             'script': script,
    636             'args': converted_args})['value']

c:\program files\python39\lib\site-packages\selenium\webdriver\remote\webdriver.py in execute(self, driver_command, params)
    319         response = self.command_executor.execute(driver_command, params)
    320         if response:
--> 321             self.error_handler.check_response(response)
    322             response['value'] = self._unwrap_value(
    323                 response.get('value', None))

c:\program files\python39\lib\site-packages\selenium\webdriver\remote\errorhandler.py in check_response(self, response)
    240                 alert_text = value['alert'].get('text')
    241             raise exception_class(message, screen, stacktrace, alert_text)
--> 242         raise exception_class(message, screen, stacktrace)
    243
    244     def _value_or_default(self, obj, key, default):

StaleElementReferenceException: Message: The element reference of <div class="gflash-num"> 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

动态元素的任何迭代都可能引发异常,我必须等到位于该 xpath 处的元素不再"gstart-tip blink_me"开始执行代码的下一个阶段,然后添加 的值"gflash-num"并等到元素消失再添加下一个值,然后最后当它变为"gend-tip"按相反顺序单击按钮时。

我试图通过不分配变量并即时获取属性来避免异常,但异常仍然被引发。

但是它引发异常的时间正是它应该从睡眠中醒来并添加数字的时间。

那么我怎么能等到一个元素变得陈旧/不可见/删除/不存在/无论如何,所有的谷歌搜索都告诉了如何做相反的事情:等到元素不再陈旧,但我想等到元素已经消失了,那么该怎么做呢?


我想我发现了一些对解决问题非常重要的东西,使用 ffmpeg 从过程的屏幕录制中提取帧,我能够确定闪烁的确切时间。

第一个提示正好显示 3 秒,每个数字正好闪烁 1 秒。

标签: pythonpython-3.xselenium

解决方案


等到任何 elelemt 变得不可见,我们有这个预期的条件:

invisibility_of_element

在我可以看到的代码中:

class invisibility_of_element(invisibility_of_element_located):
    """ An Expectation for checking that an element is either invisible or not
    present on the DOM.

    element is either a locator (text) or an WebElement
    """
    def __init(self, element):
        self.target = element

如果你有一个正在运行的 webdriverwait 对象,那么你可以试试这个:

WebDriverWait(driver, 10).until(EC.invisibility_of_element((By.XPATH, "xpath here")))

这将等到 xpath 定义的元素不可见。


推荐阅读