没有这样的元素:无法在生产环境中使用 chromedriver 和 Selenium 定位元素
no such element: Unable to locate element using chromedriver and Selenium in production environment
我对 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
,而是其他一些按钮。
所以我的问题是:有人遇到过类似的问题吗?有没有办法知道驱动程序正在寻找这样的元素时显示的页面是什么?
报告在您提供登录名后找不到元素错误,所以我认为登录失败并且页面被重定向到某个地方。您可以使用屏幕截图选项截取页面的屏幕截图,然后查看驱动程序加载了哪个页面。
driver.save_screenshot("path to save screen.jpeg")
您还可以保存原始 html 代码并检查同一页面。
Webdriver Screenshot
Using Selenium in Python to save a webpage on Firefox
每当我在 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 的经验,这个想法解决了多个问题。
根据 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()
正如您提到的 您的代码代码块在 macOS 10.11 环境中运行完美,但在生产环境 (Linux) 很可能不同的浏览器在不同的 OS 架构中以不同方式呈现 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 但不适用于 LinuxOS。所以需要删除:
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.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)
...
我对 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
,而是其他一些按钮。
所以我的问题是:有人遇到过类似的问题吗?有没有办法知道驱动程序正在寻找这样的元素时显示的页面是什么?
报告在您提供登录名后找不到元素错误,所以我认为登录失败并且页面被重定向到某个地方。您可以使用屏幕截图选项截取页面的屏幕截图,然后查看驱动程序加载了哪个页面。
driver.save_screenshot("path to save screen.jpeg")
您还可以保存原始 html 代码并检查同一页面。
Webdriver Screenshot
Using Selenium in Python to save a webpage on Firefox
每当我在 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 的经验,这个想法解决了多个问题。
根据 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()
正如您提到的 您的代码代码块在 macOS 10.11 环境中运行完美,但在生产环境 (Linux) 很可能不同的浏览器在不同的 OS 架构中以不同方式呈现 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 但不适用于 LinuxOS。所以需要删除: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.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)
...