如何告诉ChromeDriver在放弃之前等待更长时间才能启动Chrome?

时间:2018-03-28 22:24:37

标签: python selenium raspberry-pi selenium-chromedriver

背景

我使用Selenium和Python在Raspberry Pi 3上的Ubuntu MATE 16.04上自动显示和导航Chromium中的网站。(想想无人值守的数字标牌。)这个组合在今天的最新版本中运行良好通过自动更新安装Chromium(带有匹配的ChromeDriver)。

由于Chromium需要在下次启动时执行一些升级内务处理任务,因此需要比平常更长的时间。请记住,这是在Raspberry Pi上,因此I / O严重受到SD卡的瓶颈。不幸的是,我的Python脚本花了很长时间才失败,因为ChromeDriver开始放弃Chromium:

Traceback (most recent call last):
  File "call-tracker-start", line 15, in <module>
    browser = webdriver.Chrome(executable_path=chromedriver_path, options=chrome_options)
  File "/home/pi/.local/lib/python3.5/site-packages/selenium/webdriver/chrome/webdriver.py", line 75, in __init__
    desired_capabilities=desired_capabilities)
  File "/home/pi/.local/lib/python3.5/site-packages/selenium/webdriver/remote/webdriver.py", line 154, in __init__
    self.start_session(desired_capabilities, browser_profile)
  File "/home/pi/.local/lib/python3.5/site-packages/selenium/webdriver/remote/webdriver.py", line 243, in start_session
    response = self.execute(Command.NEW_SESSION, parameters)
  File "/home/pi/.local/lib/python3.5/site-packages/selenium/webdriver/remote/webdriver.py", line 312, in execute
    self.error_handler.check_response(response)
  File "/home/pi/.local/lib/python3.5/site-packages/selenium/webdriver/remote/errorhandler.py", line 242, in check_response
    raise exception_class(message, screen, stacktrace)
selenium.common.exceptions.WebDriverException: Message: chrome not reachable
  (Driver info: chromedriver=2.35 (0),platform=Linux 4.4.38-v7+ armv7l)

当然,当脚本在抛出此异常后死亡时,Chromium实例会在它完成其内务处理之前被终止,这意味着下次它必须重新开始时,所以它需要与上次一样长并且失败同样努力。

如果我然后手动干预并以普通用户身份运行Chromium,我只是......等待......一两分钟,让Chromium完成升级内务管理,然后打开浏览器窗口,然后我干净地退出了应用程序。现在管家已经完成,Chromium下次以更正常的速度启动,所以我的Python脚本突然运行没有任何错误,因为ChromeDriver在其接受的超时窗口内看到Chromium完成启动。

在下一次自动更新失败之前,一切都会好起来,然后这个问题会再次发生。我不想在每次更新后手动干预,也不想禁用自动更新。

问题的根源

如何告诉ChromeDriver在启动Chromium时不要放弃这么快?

我找了一些我可以设置的超时值,但我在ChromeDriver或Selenium for Python文档中找不到任何值。

有趣的是,一个timeout参数,可以传递给Firefox WebDriver,as shown in the Selenium for Python API documentation

  

超时 - 使用扩展程序连接时等待Firefox启动的时间。

此参数也列在Internet Explorer WebDriver中,但在Chrome WebDriver API文档中显然不存在。

我也不介意通过service_args将某些内容直接传递给ChromeDriver,但我无法在ChromeDriver文档中找到任何相关选项。

更新:找到升级后缓慢的根本原因

在努力寻找重现此问题的方法以便测试解决方案之后,我能够确定Chromium在升级后永远启动的原因。

似乎作为升级后管家的一部分,Chromium重建了用户的字体缓存。这是一个CPU&amp;对于Raspberry Pi及其SD卡来说,I / O密集型进程尤其困难,因此每当必须重建字体缓存时,启动时间最长为2.5分钟。

可以通过故意删除强制重建的字体缓存来重现该问题:

pi@rpi-dev1:~$ killall chromium-browser
pi@rpi-dev1:~$ time chromium-browser --headless --disable-gpu --dump-dom 'about:blank'
[0405/132706.970822:ERROR:gpu_process_transport_factory.cc(1019)] Lost UI shared context.
<html><head></head><body></body></html>

real    0m0.708s
user    0m0.340s
sys     0m0.200s

pi@rpi-dev1:~$ rm -Rf ~/.cache/fontconfig
pi@rpi-dev1:~$ time chromium-browser --headless --disable-gpu --dump-dom 'about:blank'
[0405/132720.917590:ERROR:gpu_process_transport_factory.cc(1019)] Lost UI shared context.
<html><head></head><body></body></html>

real    2m9.449s
user    2m8.670s
sys     0m0.590s

3 个答案:

答案 0 :(得分:0)

您是对的,没有选项可以显式设置初始驱动程序创建的timeout。我建议访问他们的git页面HERE并创建一个新问题。它还具有直接ChromeDriver网站的链接,以防您想在那里创建错误。目前,没有选项可以设置我能找到的超时

你可以在此期间尝试这样的事情:

import webbrowser
from selenium import webdriver
from selenium.common.exceptions import WebDriverException

try:
    driver = webdriver.Chrome()
except WebDriverException:
    webbrowser.open_new('http://www.Google.com')
# Let this try and get Chrome open, then go back and use webdriver

以下是关于webbrowser的文档: https://docs.python.org/3/library/webbrowser.html

答案 1 :(得分:0)

根据您的问题,如果没有您的代码试用,很难分析您所看到的错误背后的原因:

selenium.common.exceptions.WebDriverException: Message: chrome not reachable

或许有关您正在使用的二进制文件的版本信息的详细信息可能会对我们有所帮助。

事实上,要求 ChromeDriver 等待更长时间才能启动 Chrome ,然后放弃它将无法帮助我们,因为 ChromeDriver 的默认配置需要关心最佳需求。

然而 WebDriverException:消息:chrome无法访问是二进制版本不兼容时非常常见的问题。您可以在org.openqa.selenium.WebDriverException: chrome not reachable - when attempting to start a new session

找到有关此问题的详细讨论

答案 2 :(得分:0)

坏消息

事实证明,Selenium不仅没有传递给ChromeDriver的timeout选项,而且还没有重新编译自己的自定义ChromeDriver,目前无法以编程方式更改此值。遗憾的是,looking at the source code显示Google已硬编码超时值为60秒!

来自铬/src/chrome/test/chromedriver/chrome_launcher.cc@208:
std::unique_ptr<DevToolsHttpClient> client(new DevToolsHttpClient(
    address, context_getter, socket_factory, std::move(device_metrics),
    std::move(window_types), capabilities->page_load_strategy));
base::TimeTicks deadline =
    base::TimeTicks::Now() + base::TimeDelta::FromSeconds(60);
Status status = client->Init(deadline - base::TimeTicks::Now());

在更改此代码以允许自定义截止日期之前,唯一的选择是解决方法。

解决方法

在Selenium召唤ChromeDriver之前,我最终选择了一种“引导”Chromium的方法。这使得ChromeDriver开始倒计时之前的一次性升级后缓慢启动。 @PixelEinstein给出的答案帮助我走上了正确的道路,但这个解决方案在两个方面有所不同:

  1. 此处打开独立Chromium的调用是阻止,而webbrowser.open_new()则不是。
  2. 独立Chromium始终在ChromeDriver之前启动,无论是否需要。我这样做是因为等待ChromeDriver暂停一分钟,然后等待另外2.5分钟让Chromium启动,然后再次尝试ChromeDriver创建了超过3.5分钟的总延迟。当您跳过最初的ChromeDriver超时时,启动Chromium作为第一个操作会将总等待时间缩短到大约2.5分钟。如果长时间启动时间不发生,那么Chromium的“双重加载”可以忽略不计,因为整个过程在几秒钟内完成。
  3. 以下是代码段:

    #!/usr/bin/env python3
    import subprocess
    from selenium import webdriver
    
    some_site = 'http://www.google.com'
    chromedriver_path = '/usr/lib/chromium-browser/chromedriver'
    
    # Block until Chromium finishes launching and self-terminates
    subprocess.run(['chromium-browser', '--headless', '--disable-gpu', '--dump-dom', 'about:blank'])
    
    browser = webdriver.Chrome(executable_path=chromedriver_path)
    browser.get(some_site)
    # Continue on with your Selenium business...
    

    在实例化webdriver.Chrome()对象之前,无论需要多长时间,都会等待Chromium完成升级后的内务处理。 Chromium以无头模式启动,其中--dump-dom是一次性操作,将请求的网页(在本例中为about:blank)写入stdout,这将被忽略。 Chromium在完成操作后自行终止,然后从subprocess.run()调用,解锁程序流返回。之后,让ChromeDriver开始倒计时是安全的,因为Chromium将在几秒钟内启动。