简体   繁体   English

初始化Selenium WebDriver时如何解决python-Selenium错误“连接被拒绝”?

[英]How to fix python-selenium error “connection refused” when initializing a selenium webdriver?

I am running very complex python-selenium tests on non-public webpages. 我在非公开网页上运行非常复杂的python-selenium测试。 In most cases these tests run fine, but sometimes one of these tests fail during the initialization of the webdriver itself. 在大多数情况下,这些测试运行良好,但有时在webdriver自身初始化期间这些测试之一会失败。

Hint: This error happens when trying to initialize a webdriver, ie when doing something like this: 提示:尝试初始化网络驱动程序时,即执行以下操作时,会发生此错误:

# Start of the tests
mydriver =  webdriver.Firefox(firefox_profile=profile, log_path=logfile)
# ERROR HAPPENS HERE

# Doing other stuff here
....
# Doing tests here
....
# Doing shutdown here
mydriver.quit()

Here is a full example of such an error: 这是此类错误的完整示例:

___________ ERROR at setup of TestSuite.test_synaptic_events_fitting ___________

>   lambda: ihook(item=item, **kwds),
    when=when,
            )

/usr/local/lib/python2.7/dist-packages/flaky/flaky_pytest_plugin.py:273: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
conftest.py:157: in basedriver
    mydriver = firefox.get_driver(*args)
bsp_usecase_tests/tools/firefox.py:44: in get_driver
    driver = webdriver.Firefox(firefox_profile=profile, log_path=logfile)  #### INITIALIZING OF WEBDRIVER HERE
/usr/local/lib/python2.7/dist-packages/selenium/webdriver/firefox/webdriver.py:158: in __init__
    keep_alive=True)
/usr/local/lib/python2.7/dist-packages/selenium/webdriver/remote/webdriver.py:154: in __init__
    self.start_session(desired_capabilities, browser_profile)
/usr/local/lib/python2.7/dist-packages/selenium/webdriver/remote/webdriver.py:243: in start_session
    response = self.execute(Command.NEW_SESSION, parameters)
/usr/local/lib/python2.7/dist-packages/selenium/webdriver/remote/webdriver.py:311: in execute
    self.error_handler.check_response(response)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <selenium.webdriver.remote.errorhandler.ErrorHandler object at 0x7efd3b702f90>
response = {'status': 500, 'value': '{"value":{"error":"unknown error","message":"connection refused","stacktrace":"stack backtra...s::imp::thread::{{impl}}::new::thread_start\n                        at /checkout/src/libstd/sys/unix/thread.rs:84"}}'}

    def check_response(self, response):
        """
            Checks that a JSON response from the WebDriver does not have an error.

            :Args:
             - response - The JSON response from the WebDriver server as a dictionary
               object.

            :Raises: If the response contains an error message.
            """
        status = response.get('status', None)
        if status is None or status == ErrorCode.SUCCESS:
            return
        value = None
        message = response.get("message", "")
        screen = response.get("screen", "")
        stacktrace = None
        if isinstance(status, int):
            value_json = response.get('value', None)
            if value_json and isinstance(value_json, basestring):
                import json
                try:
                    value = json.loads(value_json)
                    if len(value.keys()) == 1:
                        value = value['value']
                    status = value.get('error', None)
                    if status is None:
                        status = value["status"]
                        message = value["value"]
                        if not isinstance(message, basestring):
                            value = message
                            message = message.get('message')
                    else:
                        message = value.get('message', None)
                except ValueError:
                    pass

        exception_class = ErrorInResponseException
        if status in ErrorCode.NO_SUCH_ELEMENT:
            exception_class = NoSuchElementException
        elif status in ErrorCode.NO_SUCH_FRAME:
            exception_class = NoSuchFrameException
        elif status in ErrorCode.NO_SUCH_WINDOW:
            exception_class = NoSuchWindowException
        elif status in ErrorCode.STALE_ELEMENT_REFERENCE:
            exception_class = StaleElementReferenceException
        elif status in ErrorCode.ELEMENT_NOT_VISIBLE:
            exception_class = ElementNotVisibleException
        elif status in ErrorCode.INVALID_ELEMENT_STATE:
            exception_class = InvalidElementStateException
        elif status in ErrorCode.INVALID_SELECTOR \
                or status in ErrorCode.INVALID_XPATH_SELECTOR \
                or status in ErrorCode.INVALID_XPATH_SELECTOR_RETURN_TYPER:
            exception_class = InvalidSelectorException
        elif status in ErrorCode.ELEMENT_IS_NOT_SELECTABLE:
            exception_class = ElementNotSelectableException
        elif status in ErrorCode.ELEMENT_NOT_INTERACTABLE:
            exception_class = ElementNotInteractableException
        elif status in ErrorCode.INVALID_COOKIE_DOMAIN:
            exception_class = InvalidCookieDomainException
        elif status in ErrorCode.UNABLE_TO_SET_COOKIE:
            exception_class = UnableToSetCookieException
        elif status in ErrorCode.TIMEOUT:
            exception_class = TimeoutException
        elif status in ErrorCode.SCRIPT_TIMEOUT:
            exception_class = TimeoutException
        elif status in ErrorCode.UNKNOWN_ERROR:
            exception_class = WebDriverException
        elif status in ErrorCode.UNEXPECTED_ALERT_OPEN:
            exception_class = UnexpectedAlertPresentException
        elif status in ErrorCode.NO_ALERT_OPEN:
            exception_class = NoAlertPresentException
        elif status in ErrorCode.IME_NOT_AVAILABLE:
            exception_class = ImeNotAvailableException
        elif status in ErrorCode.IME_ENGINE_ACTIVATION_FAILED:
            exception_class = ImeActivationFailedException
        elif status in ErrorCode.MOVE_TARGET_OUT_OF_BOUNDS:
            exception_class = MoveTargetOutOfBoundsException
        elif status in ErrorCode.JAVASCRIPT_ERROR:
            exception_class = JavascriptException
        elif status in ErrorCode.SESSION_NOT_CREATED:
            exception_class = SessionNotCreatedException
        elif status in ErrorCode.INVALID_ARGUMENT:
            exception_class = InvalidArgumentException
        elif status in ErrorCode.NO_SUCH_COOKIE:
            exception_class = NoSuchCookieException
        elif status in ErrorCode.UNABLE_TO_CAPTURE_SCREEN:
            exception_class = ScreenshotException
        elif status in ErrorCode.ELEMENT_CLICK_INTERCEPTED:
            exception_class = ElementClickInterceptedException
        elif status in ErrorCode.INSECURE_CERTIFICATE:
            exception_class = InsecureCertificateException
        elif status in ErrorCode.INVALID_COORDINATES:
            exception_class = InvalidCoordinatesException
        elif status in ErrorCode.INVALID_SESSION_ID:
            exception_class = InvalidSessionIdException
        elif status in ErrorCode.UNKNOWN_METHOD:
            exception_class = UnknownMethodException
        else:
            exception_class = WebDriverException
        if value == '' or value is None:
            value = response['value']
        if isinstance(value, basestring):
            if exception_class == ErrorInResponseException:
                raise exception_class(response, value)
            raise exception_class(value)
        if message == "" and 'message' in value:
            message = value['message']

        screen = None
        if 'screen' in value:
            screen = value['screen']

        stacktrace = None
        if 'stackTrace' in value and value['stackTrace']:
            stacktrace = []
            try:
                for frame in value['stackTrace']:
                    line = self._value_or_default(frame, 'lineNumber', '')
                    file = self._value_or_default(frame, 'fileName', '<anonymous>')
                    if line:
                        file = "%s:%s" % (file, line)
                    meth = self._value_or_default(frame, 'methodName', '<anonymous>')
                    if 'className' in frame:
                        meth = "%s.%s" % (frame['className'], meth)
                    msg = "    at %s (%s)"
                    msg = msg % (meth, file)
                    stacktrace.append(msg)
            except TypeError:
                pass
        if exception_class == ErrorInResponseException:
            raise exception_class(response, message)
        elif exception_class == UnexpectedAlertPresentException and 'alert' in value:
            raise exception_class(message, screen, stacktrace, value['alert'].get('text'))
>       raise exception_class(message, screen, stacktrace)
E       WebDriverException: Message: connection refused

/usr/local/lib/python2.7/dist-packages/selenium/webdriver/remote/errorhandler.py:237: WebDriverException

These tests are running as part of a jenkins plan inside a docker container, to ensure the exact same environment all the time. 这些测试作为docker容器内jenkins计划的一部分运行,以确保始终保持完全相同的环境。 Here is a list of the used packages and their versions: 以下是使用的软件包及其版本的列表:

  • python 2.7.12 python 2.7.12
  • pytest 3.6.1 pytest 3.6.1
  • selenium 3.8.0 硒3.8.0
  • geckodriver 0.19.1 壁虎起子0.19.1
  • firefox 62.0 火狐62.0
  • flaky 3.4.0 片状3.4.0

The error roughly appears in about 1% of all tests. 该错误大约在所有测试的1%中出现。 There are about 15 different tests, and the error seem to appear randomly (ie not always the same test). 大约有15种不同的测试,并且错误似乎随机出现(即,并非总是相同的测试)。

Is this a bug in firefox/selenium/geckodriver? 这是firefox / selenium / geckodriver中的错误吗? And is there a way to fix this? 有办法解决这个问题吗?

The following code snippet is not some code I am using ! 以下代码段不是我正在使用的某些代码 It is just an idea of how to fix my problem described above. 这只是解决上述问题的一种方法。 Is this maybe a good way to solve my original problem, or not? 这也许是解决我原来的问题的好方法吗?

while counter<5:
    try:
        webdriver = webdriver.Firefox(firefox_profile=profile, log_path=logfile) 
        break
    except WebDriverException:
        counter +=1

Is there a better way to do this? 有一个更好的方法吗?

This error message... 此错误消息...

{'status': 500, 'value': '{"value":{"error":"unknown error","message":"connection refused","stacktrace":"stack backtra...s::imp::thread::{{impl}}::new::thread_start\n at /checkout/src/libstd/sys/unix/thread.rs:84"}}'}

...implies that the GeckoDriver was unable to initiate/spawn a new WebBrowsing Session ie Firefox Browser session. ...暗示GeckoDriver无法启动/产生新的WebBrowsing会话,Firefox浏览器会话。

In a comment within the discussion DELETE '/session/{session id}' no longer working @andreastt mentions that: 在讨论中的评论中, DELETE'/ session / {session id}'不再起作用, @ andreastt提到:

geckodriver is implicitly ending the (previous) session when the last window closes. 当最后一个窗口关闭时,geckodriver隐式结束(上一个)会话。 If driver.quit() is called as the subsequent command it will fail because the session has already been implicitly deleted. 如果将driver.quit()作为后续命令调用,它将失败,因为该会话已被隐式删除。

In these cases GeckoDriver should detect that the session has been closed implicitly after driver.close() or ignore the response from driver.quit() in case the session has already been closed. 在这些情况下,GeckoDriver应该检测到在driver.close()之后隐式关闭了会话,或者如果会话已经关闭,则忽略来自driver.quit()的响应。

In such cases the following trace logs are generated: 在这种情况下,将生成以下跟踪日志:

1505753594121   webdriver::server   DEBUG   Last window was closed, deleting session
1505753594121   webdriver::server   DEBUG   Deleting session
1505753594121   geckodriver::marionette DEBUG   Stopping browser process
1505753594364   webdriver::server   DEBUG   <- 200 OK {"value": []}
1505753594523   webdriver::server   DEBUG   -> DELETE /session/a8312282-af00-4931-94d4-0d401abf01c9 
1505753594524   webdriver::server   DEBUG   <- 500 Internal Server Error {"value":{"error":"session not created","message":"Tried to run command without establishing a connection","stacktrace":"stack backtrace:\n   0:           0x4f388c - backtrace::backtrace::trace::h736111741fa0878e\n   1:           0x4f38c2 - backtrace::capture::Backtrace::new::h63b8a5c0787510c9\n   2:           0x442c61 - webdriver::error::WebDriverError::new::hc4fe6a1ced4e57dd\n   3:           0x42a926 - <webdriver::server::Dispatcher<T, U>>::run::hba9181b5aacf8f04\n   4:           0x402c59 - std::sys_common::backtrace::__rust_begin_short_backtrace::h19de262639927233\n   5:           0x40c065 - std::panicking::try::do_call::h6c1659fc4d01af51\n   6:           0x5e38ec - panic_unwind::__rust_maybe_catch_panic\n                        at /checkout/src/libpanic_unwind/lib.rs:98\n   7:           0x420d32 - <F as alloc::boxed::FnBox<A>>::call_box::h953e5f59694972c5\n   8:           0x5dc00b - alloc::boxed::{{impl}}::call_once<(),()>\n                        at /checkout/src/liballoc/boxed.rs:661\n                         - std::sys_common::thread::start_thread\n                        at /checkout/src/libstd/sys_common/thread.rs:21\n                         - std::sys::imp::thread::{{impl}}::new::thread_start\n                        at /checkout/src/libstd/sys/unix/thread.rs:84"}}
1505753594533   webdriver::server   DEBUG   -> DELETE /session/a8312282-af00-4931-94d4-0d401abf01c9 
1505753594542   webdriver::server   DEBUG   <- 500 Internal Server Error {"value":{"error":"session not created","message":"Tried to run command without establishing a connection","stacktrace":"stack backtrace:\n   0:           0x4f388c - backtrace::backtrace::trace::h736111741fa0878e\n   1:           0x4f38c2 - backtrace::capture::Backtrace::new::h63b8a5c0787510c9\n   2:           0x442c61 - webdriver::error::WebDriverError::new::hc4fe6a1ced4e57dd\n   3:           0x42a926 - <webdriver::server::Dispatcher<T, U>>::run::hba9181b5aacf8f04\n   4:           0x402c59 - std::sys_common::backtrace::__rust_begin_short_backtrace::h19de262639927233\n   5:           0x40c065 - std::panicking::try::do_call::h6c1659fc4d01af51\n   6:           0x5e38ec - panic_unwind::__rust_maybe_catch_panic\n                        at /checkout/src/libpanic_unwind/lib.rs:98\n   7:           0x420d32 - <F as alloc::boxed::FnBox<A>>::call_box::h953e5f59694972c5\n   8:           0x5dc00b - alloc::boxed::{{impl}}::call_once<(),()>\n                        at /checkout/src/liballoc/boxed.rs:661\n                         - std::sys_common::thread::start_thread\n                        at /checkout/src/libstd/sys_common/thread.rs:21\n                         - std::sys::imp::thread::{{impl}}::new::thread_start\n                        at /checkout/src/libstd/sys/unix/thread.rs:84"}}
1505753594549   webdriver::server   DEBUG   -> GET /shutdown 
1505753594551   webdriver::server DEBUG <- 404 Not Found {"value":{"error":"unknown command","message":"GET /shutdown did not match a known command","stacktrace":"stack backtrace:\n 0: 0x4f388c - backtrace::backtrace::trace::h736111741fa0878e\n 1: 0x4f38c2 - backtrace::capture::Backtrace::new::h63b8a5c0787510c9\n 2: 0x442d88 - webdriver::error::WebDriverError::new::hea6d4dbf778b2b24\n 3: 0x43c65f - <webdriver::server::HttpHandler<U> as hyper::server::Handler>::handle::hd03629bd67672697\n 4: 0x403a04 - std::sys_common::backtrace::__rust_begin_short_backtrace::h32e6ff325c0d7f46\n 5: 0x40c036 - std::panicking::try::do_call::h5f902dc1eea01ffe\n 6: 0x5e38ec - panic_unwind::__rust_maybe_catch_panic\n at /checkout/src/libpanic_unwind/lib.rs:98\n 7: 0x4209a2 - <F as alloc::boxed::FnBox<A>>::call_box::h032bafb4b576d1cd\n 8: 0x5dc00b - alloc::boxed::{{impl}}::call_once<(),()>\n 

Though the error codes for the error you are seeing is 'status': 500 and the error sample I have provided is 404 Not Found , apparently looks different, the core reason is similar as: 尽管您看到的错误的错误代码是“状态”:500 ,而我提供的错误示例是404 Not Found ,显然看起来有所不同,其核心原因类似于:

"message":"connection refused" 

due to: 由于:

imp::thread::{{impl}}::new::thread_start

from: 从:

/checkout/src/libstd/sys/unix/thread.rs:84

From another perspective, while you use GeckoDriver , Selenium and Firefox ensure that the binaries are compatible as follows: 从另一个角度看,在使用GeckoDriver时SeleniumFirefox确保二进制文件兼容,如下所示:

Geckodriver发布


Analysis 分析

There had been significant changes in the geckodriver binary since the availability of geckodriver 0.19.1 . 自从geckodriver 0.19.1可用以来, geckodriver二进制文件有了重大变化。 A couple of the changes are: 进行了一些更改:

  • GeckoDriver v0.22.0 (2018-09-15): GeckoDriver v0.22.0 (2018-09-15):
    • The HTTP status codes used for [script timeout] and [timeout] errors has changed from Request Timeout (408) to Internal Server Error (500) in order to not break HTTP/1.1 Keep-Alive support, as HTTP clients interpret the old status code to mean they should duplicate the request. 用于[脚本超时]和[超时]错误的HTTP状态代码已从请求超时(408)更改为内部服务器错误(500),以便不破坏HTTP / 1.1 Keep-Alive状态,因为HTTP客户端会解释旧状态代码表示他们应该重复该请求。
    • The HTTP/1.1 Keep-Alive timeout for persistent connections has been increased to 90 seconds. 持久连接的HTTP / 1.1 Keep-Alive超时已增加到90秒。
    • An [invalid session ID] error is now returned when there is no active session. 现在,当没有活动会话时,将返回[无效会话ID]错误。
    • The handshake when geckodriver connects to Marionette has been hardened by killing the Firefox process if it fails. geckodriver连接到木偶时的握手已通过杀死Firefox进程失败而得到加强。
    • The handshake read timeout has been reduced to 10 seconds instead of waiting forever. 握手读取超时已减少至10秒,而不是永远等待。
  • GeckoDriver v0.21.0 (2018-06-15): GeckoDriver v0.21.02018-06-15 ):
    • WebDriver commands that do not have a return value now correctly return {value: null} instead of an empty dictionary. 现在,没有返回值的WebDriver命令可以正确返回{value: null}而不是空字典。
    • Force use of IPv4 network stack. 强制使用IPv4网络堆栈。
    • On certain system configurations, where localhost resolves to an IPv6 address, geckodriver would attempt to connect to Firefox on the wrong IP stack, causing the connection attempt to time out after 60 seconds. 在某些系统配置中,如果localhost解析为IPv6地址,则geckodriver会尝试在错误的IP堆栈上连接到Firefox,从而导致连接尝试在60秒后超时。 We now ensure that geckodriver uses IPv4 consistently to both connect to Firefox and for allocating a free port. 现在,我们确保geckodriver始终使用IPv4来连接Firefox和分配空闲端口。

  • GeckoDriver v0.20.1 (2018-04-06): GeckoDriver v0.20.12018-04-06 ):
    • Avoid attempting to kill Firefox process that has stopped. 避免尝试杀死已停止的Firefox进程。
    • With the change to allow Firefox enough time to shut down in 0.20.0, geckodriver started unconditionally killing the process to reap its exit status. 通过更改使Firefox有足够的时间在0.20.0中关闭,geckodriver开始无条件终止进程以获取退出状态。 This caused geckodriver to inaccurately report a successful Firefox shutdown as a failure. 这导致geckodriver不正确地将Firefox成功关闭报告为失败。

  • GeckoDriver v0.20.0 (2018-03-08): GeckoDriver v0.20.02018-03-08 ):
    • Backtraces from geckodriver no longer substitute for missing Marionette stacktraces. 来自geckodriver的回溯不再替代丢失的木偶堆栈跟踪。
    • The Firefox process is now given ample time to shut down, allowing enough time for the Firefox shutdown hang monitor to kick in. 现在有足够的时间来关闭Firefox进程,从而有足够的时间启动Firefox的关闭挂起监视器。
    • Firefox has an integrated background monitor that observes long-running threads during shutdown. Firefox具有集成的后台监视器,该监视器可在关机期间观察长时间运行的线程。 These threads will be killed after 63 seconds in the event of a hang. 如果挂起,这些线程将在63秒后被杀死。 To allow Firefox to shut down these threads on its own, geckodriver has to wait that time and some additional seconds. 为了允许Firefox自行关闭这些线程,geckodriver必须等待该时间和另外几秒钟。


Solution

  • Upgrade Selenium to current levels Version 3.14.0 . Selenium升级到当前版本3.14.0
  • Upgrade GeckoDriver to GeckoDriver v0.22.0 level. 升级GeckoDriverGeckoDriver v0.22.0水平。
  • Upgrade Firefox version to Firefox v62.0.2 levels. Firefox版本升级到Firefox v62.0.2级别。
  • If your base Web Client version is too old, then uninstall it through Revo Uninstaller and install a recent GA and released version of Web Client . 如果您的基本Web客户端版本太旧,请通过Revo Uninstaller卸载它,并安装最新的GA和Web Client的发行版本。
  • Always invoke driver.quit() within tearDown(){} method to close & destroy the WebDriver and Web Client instances gracefully. 始终在tearDown(){}方法内调用driver.quit() ,以优雅地关闭和破坏WebDriverWeb Client实例。
  • Execute your Test as a non-root user. 以非root用户身份执行Test

Update 更新资料

As per your question update of-coarse you can induce a loop for multiple trials to initialize the selenium webdriver instance as follows: 根据您粗略的问题更新,您可以引发一个多次尝试循环,以初始化selenium webdriver实例,如下所示:

  • Ensure that there is no dangling instances of geckodriver by invoking taskkill command ( WindowsOS specific ) as follows: 通过调用taskkill命令( 特定WindowsOS ),确保没有悬空的geckodriver实例,如下所示:

     os.system("taskkill /f /im geckodriver.exe /T") 
  • Ensure that there is no dangling instances of geckodriver by invoking kill() command ( Cross platform ) as follows: 通过调用kill()命令( Cross platform ),确保没有悬空的geckodriver实例,如下所示:

     from selenium import webdriver import psutil from selenium.common.exceptions import WebDriverException for counter in range(5): try: webdriver = webdriver.Firefox(executable_path=r'C:\\Utility\\BrowserDrivers\\geckodriver.exe') print("WebDriver and WebBrowser initialized ...") break except WebDriverException: #Cross platform PROCNAME = "geckodriver" for proc in psutil.process_iter(): # check whether the process name matches if proc.name() == PROCNAME: proc.kill() print("Retrying ...") print("Out of loop ...") 

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM