没有这样的元素:无法在生产环境中使用 chromedriver 和 Selenium 定位元素

Posted

技术标签:

【中文标题】没有这样的元素:无法在生产环境中使用 chromedriver 和 Selenium 定位元素【英文标题】:no such element: Unable to locate element using chromedriver and Selenium in production environment 【发布时间】:2018-11-29 23:24:22 【问题描述】:

我对 selenium chromedriver 有疑问,我无法弄清楚是什么原因造成的。几周前一切正常,突然这个错误开始出现。 问题来自以下功能。

 def login_(browser):
    try:
        browser.get("some_url")
        # user credentials
        user = browser.find_element_by_xpath('//*[@id="username"]')
        user.send_keys(config('user'))
        password = browser.find_element_by_xpath('//*[@id="password"]')
        password.send_keys(config('pass'))
        login = browser.find_element_by_xpath('/html/body/div[1]/div/button')
        login.send_keys("\n")
        time.sleep(1)
        sidebar = browser.find_element_by_xpath('//*[@id="sidebar"]/ul/li[1]/a')
        sidebar.send_keys("\n")
        app_submit = browser.find_element_by_xpath('//*[@id="sidebar"]/ul/li[1]/ul/li[1]/a')
        app_submit.send_keys("\n")
    except TimeoutException or NoSuchElementException:
        raise LoginException

此函数在开发环境(macOS 10.11)下运行没​​有问题,但在生产环境中抛出如下错误:

Message: no such element: Unable to locate element: "method":"xpath","selector":"//*[@id="sidebar"]/ul/li[1]/a"
(Session info: headless chrome=67.0.3396.79)
(Driver info: chromedriver=2.40.565383 (76257d1ab79276b2d53ee97XXX),platform=Linux 4.4.0-116-generic x86_64)

我已经在每个环境中更新了 Chrome 和 chromedriver(分别为 v67 和 2.40)。我还给了它更多time.sleep(15)。但问题仍然存在。我最近的猜测是可能是webdriver的初始化没有正常工作:

def initiate_webdriver():
   option = webdriver.ChromeOptions()
   option.binary_location = config('GOOGLE_CHROME_BIN')
   option.add_argument('--disable-gpu')
   option.add_argument('window-size=1600,900')
   option.add_argument('--no-sandbox')
   if not config('DEBUG', cast=bool):
       display = Display(visible=0, size=(1600, 900))
       display.start()
       option.add_argument("--headless")
   else:
       option.add_argument("--incognito")
   return webdriver.Chrome(executable_path=config('CHROMEDRIVER_PATH'), chrome_options=option)

因为,如果Display 不起作用,那么可能没有提到的sidebar 而是其他一些按钮。

所以我的问题是:有人遇到过类似的问题吗?有没有办法知道驱动程序在寻找这样的元素时显示的页面是什么?

【问题讨论】:

您可以添加显式或隐式等待加载元素 两种环境之间的 HTML 是否可能不同?您是否验证过相同的定位器在两者中都可以手动工作? @JeffC 这个定位器几周前工作正常,html 代码没有改变。 @Prany,我不再认为这只是时间问题。增加一些额外的时间并不能解决问题。 孩子们,这就是为什么你永远不应该像这样使用 xpath //*[@id="sidebar"]/ul/li[1]/a。它不会告诉您单击了什么或为什么会失败...没用!同时你有一个a 元素,它总是有一个唯一的属性或值来引用。如果没有找到,至少你会知道这不是因为“html结构” 【参考方案1】:

根据login_(browser) 方法的几件事:

正如您通过以下方式识别出登录按钮:

login = browser.find_element_by_xpath('/html/body/div[1]/div/button')

我建议通过login.click() 调用send_keys("\n") 借助onclick() 事件来模拟单击登录按钮,如下所示:

login = browser.find_element_by_xpath('/html/body/div[1]/div/button')
login.click()

接下来,当您识别 sidebar 时,如下所示诱导 WebDriverWait 以使 元素可点击

WebDriverWait(driver, 20).until(EC.element_to_be_clickable((By.XPATH, '//*[@id="sidebar"]/ul/li[1]/a'))).click()

正如您所提到的,您的代码块在 ma​​cOS 10.11 环境中运行良好,但在生产环境 (Linux) 中引发以下错误很可能是不同的浏览器呈现HTML DOM 在不同的操作系统架构中有所不同。因此,您必须使用 relative xpath 而不是 absolute xpath,如下所示:

WebDriverWait(driver, 20).until(EC.element_to_be_clickable((By.XPATH, "//a[@attribute='value']"))).click()

根据initiate_webdriver() 方法的几件事:

根据Getting Started with Headless Chrome,参数--disable-gpu 仅适用于Windows,但不适用于Linux 操作系统。所以需要删除:

option.add_argument('--disable-gpu')

注意:您必须添加以下导入:

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

【讨论】:

这个例子需要以下导入才能工作:from selenium.webdriver.support.ui import Select, WebDriverWaitfrom selenium.webdriver.support import expected_conditions as ECfrom selenium.webdriver.common.by import By 谢谢@MelquíadesOchoa,你是对的,我在答案中添加了出口。【参考方案2】:

您需要等到元素可见,否则您将收到此错误。试试这样的:

from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support.expected_conditions import visibility_of_element_located
from selenium.webdriver.common.by import By
TIMEOUT = 5

...
xpath = '//*[@id="sidebar"]/ul/li[1]/a'
WebDriverWait(self.selenium, TIMEOUT).until(visibility_of_element_located((By.XPATH, xpath)))
browser.find_element_by_xpath(xpath)
...

【讨论】:

【参考方案3】:

每当我在 Selenium 中遇到这样的奇怪问题时,我宁愿重试找到导致间歇性问题的特定元素。一种方法是将它包裹在一个 try-except 块周围:

try:
   sidebar = browser.find_element_by_xpath('//*[@id="sidebar"]/ul/li[1]/a')
except NoSuchElementException:
   time.sleep(10)
   print("Unable to find element in first time, trying it again")
   sidebar = browser.find_element_by_xpath('//*[@id="sidebar"]/ul/li[1]/a')

您还可以将try 代码放入带有合适计数变量的循环中,以使自动化代码工作。 (检查this)。根据我使用 JAVA 的经验,这个想法已经解决了多个问题。

【讨论】:

【参考方案4】:

在您提供登录信息后报告元素未找到错误,所以我认为登录失败并且页面重定向到某个地方。您可以使用屏幕截图选项对页面进行屏幕截图,然后查看驱动程序加载的页面。

driver.save_screenshot("path to save screen.jpeg")

您还可以保存原始 html 代码并检查同一页面。

Webdriver Screenshot

Using Selenium in Python to save a webpage on Firefox

【讨论】:

以上是关于没有这样的元素:无法在生产环境中使用 chromedriver 和 Selenium 定位元素的主要内容,如果未能解决你的问题,请参考以下文章

无法使用 Webpack 在生产环境中使用 Bootstrap CSS

在 Chrome 中查看元素大小

@media 打印在 Chrome 中无法正常工作

chrome账号登录后无法同步书签等

代码混淆,不再让生产环境代码裸奔

使用 jquery 的 Chrome 上的 Flex 无法正确呈现