【问题标题】:Selenium not waiting for element to be visible causing error: invalid element stateSelenium 不等待元素可见导致错误:无效元素状态
【发布时间】:2021-11-04 13:52:55
【问题描述】:

错误:元素当前不可交互且可能无法操作。

所以我有 selenium 请求一个页面,然后填写几个输入框,然后单击一个按钮提交表单。

我遇到的问题是 selenium 没有等待元素可见,所以我必须手动放置 time.sleep(2) 来解决这个问题,但我更愿意等待它们出现以防加载时间更长或更短,或者根本不加载。

我目前有这样的设置来请求页面,找到输入字段,检查元素是否存在并在其中输入文本,我在 element.clear() 部分收到此错误,当我在请求 url 后包含了 time.sleep(2):

错误

Traceback (most recent call last):
  File "C:\Users\example\AppData\Roaming\JetBrains\PyCharmCE2021.2\scratches\example.py", line 125, in <module>
    completeForm(
  File "C:\Users\example\AppData\Roaming\JetBrains\PyCharmCE2021.2\scratches\example.py", line 89, in completeForm
    typeText(Field, text)
  File "C:\Users\example\AppData\Roaming\JetBrains\PyCharmCE2021.2\scratches\example.py", line 50, in typeText
    element.clear()
  File "C:\Users\example\Desktop\selenium-program\venv\lib\site-packages\selenium\webdriver\remote\webelement.py", line 92, in clear
    self._execute(Command.CLEAR_ELEMENT)
  File "C:\Users\example\Desktop\selenium-program\venv\lib\site-packages\selenium\webdriver\remote\webelement.py", line 693, in _execute
    return self._parent.execute(command, params)
  File "C:\Users\example\Desktop\selenium-program\venv\lib\site-packages\selenium\webdriver\remote\webdriver.py", line 400, in execute
    self.error_handler.check_response(response)
  File "C:\Users\example\Desktop\selenium-program\venv\lib\site-packages\selenium\webdriver\remote\errorhandler.py", line 236, in check_response
    raise exception_class(message, screen, stacktrace)
selenium.common.exceptions.InvalidElementStateException: Message: invalid element state: Element is not currently interactable and may not be manipulated
  (Session info: chrome=93.0.4577.63)

代码

from seleniumwire.undetected_chromedriver.v2 import Chrome, ChromeOptions
from selenium.common.exceptions import TimeoutException
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
from selenium.webdriver import ActionChains
import random
import time

#Setup selenium chrome browser with options

def setupBrowser():
    global driver

    options = {}

    chrome_options = ChromeOptions()
    driver = Chrome(seleniumwire_options=options, options=chrome_options)


#Type like a human
def typeText(element, text):
    success = False

    if doesElementExist(element):
        while success == False:
            actions = ActionChains(driver)
            actions.move_to_element(element)
            actions.click()

            element.clear()

            for character in text:
                actions.send_keys(character)
                actions.perform()
                time.sleep(random.uniform(0.13, 0.4))

            if element.get_attribute("data-initial-value") == text:
                success = True


#check if element exists, times out after some time and returns error
def doesElementExist(element):

    timeout = 10
    try:
        element_present = EC.visibility_of_element_located(element)
        WebDriverWait(driver, timeout).until(element_present)
    except TimeoutException:
        print("Error: Timed out after {} seconds waiting for element to load!".format(timeout))
        return False
    finally:
        return True


driver.get(url)
driver.implicitly_wait(60)

Field = driver.find_element_by_xpath('//*[@id="mG61Hd"]/div[2]/div/div[2]/div[1]/div/div/div[2]/div/div[1]/div/div[1]/input')
text = "exampletexttowrite123"
typeText(Field, text)

【问题讨论】:

  • 网址是公开的吗?
  • 是的,当我将 time.sleep(1) 放在 driver.get(url) 之后,整个程序都可以工作,只有当我删除睡眠时,它才不会等待元素加载。网址是谷歌表单
  • 见下文,可能会有所帮助

标签: python selenium selenium-webdriver selenium-chromedriver


【解决方案1】:

您的typeText 需要更正。它没有.perform(),所以发生的情况是所有项目都将存储在 Actioncahins 队列中,而当您写入 .perform() 时,它们将被一一释放。

代码:

def typeText(element, text):
    success = False

    if doesElementExist(element):
        while success == False:
            actions = ActionChains(driver)
            actions.move_to_element(element).perform()
            element.click()
            element.clear()
            

            for character in text:
                actions.send_keys(character).perform()
                time.sleep(random.uniform(0.13, 0.4))

            if element.get_attribute("data-initial-value") == text:
                success = True

PS:

此方法直接接受web element,请确保网页元素应正确呈现。

更新:

driver = webdriver.Chrome(driver_path)
driver.maximize_window()
#driver.implicitly_wait(50)
driver.get("https://docs.google.com/forms/d/e/1FAIpQLSfMJRFOPF0qeJG3DHrbzbDR7nETPF0qE2D-r_F3kjjqdP9B1w/viewform")
wait = WebDriverWait(driver, 20)
ActionChains(driver).move_to_element(wait.until(EC.visibility_of_element_located((By.XPATH, "//div[text()='Your answer']/preceding-sibling::input")))).perform()
wait.until(EC.visibility_of_element_located((By.XPATH, "//div[text()='Your answer']/preceding-sibling::input"))).send_keys('cruise')

【讨论】:

  • 看到你在这里传递元素,doesElementExist(element): 你是怎么得到元素的?渲染是否正确?
  • Xpath 看起来很脆弱,你能给我看一些 html,也许我们可以构造一个相对的 xpath 而不是绝对的 xpath。
  • 用这个//div[text()='Your answer']/preceding-sibling::input替换这个//*[@id="mG61Hd"]/div[2]/div/div[2]/div[1]/div/div/div[2]/div/div[1]/div/div[1]/input
  • 也可以代替driver.find_element使用WebDriverWait
  • 见上面我已经更新没有time.sleep(1)
猜你喜欢
  • 2017-08-14
  • 2019-10-21
  • 2019-11-02
  • 2014-05-03
  • 1970-01-01
  • 2017-08-07
  • 2020-02-23
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多