如何修复python-selenium错误“连接被拒绝"初始化 selenium webdriver 时? [英] How to fix python-selenium error "connection refused" when initializing a selenium webdriver?

查看:47
本文介绍了如何修复python-selenium错误“连接被拒绝"初始化 selenium webdriver 时?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在非公共网页上运行非常复杂的 python-selenium 测试.在大多数情况下,这些测试运行良好,但有时这些测试之一会在 webdriver 本身的初始化期间失败.

I am running very complex python-selenium tests on non-public webpages. In most cases these tests run fine, but sometimes one of these tests fail during the initialization of the webdriver itself.

提示:尝试初始化网络驱动程序时会发生此错误,即执行以下操作时:

Hint: This error happens when trying to initialize a webdriver, i.e. 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
                        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

这些测试作为 jenkins 计划的一部分在 docker 容器内运行,以确保始终保持完全相同的环境.以下是所用软件包及其版本的列表:

These tests are running as part of a jenkins plan inside a docker container, to ensure the exact same environment all the time. Here is a list of the used packages and their versions:

  • python 2.7.12
  • pytest 3.6.1
  • 硒 3.8.0
  • geckodriver 0.19.1
  • Firefox 62.0
  • 片状 3.4.0

错误大约出现在所有测试的 1% 左右.大约有 15 种不同的测试,错误似乎是随机出现的(即并不总是相同的测试).

The error roughly appears in about 1% of all tests. There are about 15 different tests, and the error seem to appear randomly (i.e. not always the same test).

这是 firefox/selenium/geckodriver 中的错误吗?有没有办法解决这个问题?

Is this a bug in 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?

推荐答案

此错误信息...

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

...表示 GeckoDriver 无法启动/生成新的 WebBrowsing 会话,即 Firefox 浏览器 会话.

...implies that the GeckoDriver was unable to initiate/spawn a new WebBrowsing Session i.e. Firefox Browser session.

在讨论中的评论DELETE '/session/{session id}' 不再有效 @andreastt 提到:

In a comment within the discussion DELETE '/session/{session id}' no longer working @andreastt mentions that:

geckodriver 在最后一个窗口关闭时隐式结束(上一个)会话.如果 driver.quit() 作为后续命令调用,它将失败,因为会话已经被隐式删除.

geckodriver is implicitly ending the (previous) session when the last window closes. If driver.quit() is called as the subsequent command it will fail because the session has already been implicitly deleted.

在这些情况下,GeckoDriver 应该检测到会话在 driver.close() 之后隐式关闭或忽略来自 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.

在这种情况下,会生成以下跟踪日志:

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:
   0:           0x4f388c - backtrace::backtrace::trace::h736111741fa0878e
   1:           0x4f38c2 - backtrace::capture::Backtrace::new::h63b8a5c0787510c9
   2:           0x442c61 - webdriver::error::WebDriverError::new::hc4fe6a1ced4e57dd
   3:           0x42a926 - <webdriver::server::Dispatcher<T, U>>::run::hba9181b5aacf8f04
   4:           0x402c59 - std::sys_common::backtrace::__rust_begin_short_backtrace::h19de262639927233
   5:           0x40c065 - std::panicking::try::do_call::h6c1659fc4d01af51
   6:           0x5e38ec - panic_unwind::__rust_maybe_catch_panic
                        at /checkout/src/libpanic_unwind/lib.rs:98
   7:           0x420d32 - <F as alloc::boxed::FnBox<A>>::call_box::h953e5f59694972c5
   8:           0x5dc00b - alloc::boxed::{{impl}}::call_once<(),()>
                        at /checkout/src/liballoc/boxed.rs:661
                         - std::sys_common::thread::start_thread
                        at /checkout/src/libstd/sys_common/thread.rs:21
                         - std::sys::imp::thread::{{impl}}::new::thread_start
                        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:
   0:           0x4f388c - backtrace::backtrace::trace::h736111741fa0878e
   1:           0x4f38c2 - backtrace::capture::Backtrace::new::h63b8a5c0787510c9
   2:           0x442c61 - webdriver::error::WebDriverError::new::hc4fe6a1ced4e57dd
   3:           0x42a926 - <webdriver::server::Dispatcher<T, U>>::run::hba9181b5aacf8f04
   4:           0x402c59 - std::sys_common::backtrace::__rust_begin_short_backtrace::h19de262639927233
   5:           0x40c065 - std::panicking::try::do_call::h6c1659fc4d01af51
   6:           0x5e38ec - panic_unwind::__rust_maybe_catch_panic
                        at /checkout/src/libpanic_unwind/lib.rs:98
   7:           0x420d32 - <F as alloc::boxed::FnBox<A>>::call_box::h953e5f59694972c5
   8:           0x5dc00b - alloc::boxed::{{impl}}::call_once<(),()>
                        at /checkout/src/liballoc/boxed.rs:661
                         - std::sys_common::thread::start_thread
                        at /checkout/src/libstd/sys_common/thread.rs:21
                         - std::sys::imp::thread::{{impl}}::new::thread_start
                        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:
 0: 0x4f388c - backtrace::backtrace::trace::h736111741fa0878e
 1: 0x4f38c2 - backtrace::capture::Backtrace::new::h63b8a5c0787510c9
 2: 0x442d88 - webdriver::error::WebDriverError::new::hea6d4dbf778b2b24
 3: 0x43c65f - <webdriver::server::HttpHandler<U> as hyper::server::Handler>::handle::hd03629bd67672697
 4: 0x403a04 - std::sys_common::backtrace::__rust_begin_short_backtrace::h32e6ff325c0d7f46
 5: 0x40c036 - std::panicking::try::do_call::h5f902dc1eea01ffe
 6: 0x5e38ec - panic_unwind::__rust_maybe_catch_panic
 at /checkout/src/libpanic_unwind/lib.rs:98
 7: 0x4209a2 - <F as alloc::boxed::FnBox<A>>::call_box::h032bafb4b576d1cd
 8: 0x5dc00b - alloc::boxed::{{impl}}::call_once<(),()>
 

尽管您看到的错误的错误代码是 'status': 500 并且我提供的错误示例是 404 Not Found,但显然看起来不同,核心原因类似于:

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:

"message":"connection refused" 

由于:

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

来自:

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

从另一个角度来看,当您使用 GeckoDriverSeleniumFirefox 时,请确保二进制文件如下兼容:

From another perspective, while you use GeckoDriver, Selenium and Firefox ensure that the binaries are compatible as follows:

geckodriver 0.19.1 发布以来,geckodriver 二进制文件发生了重大变化.一些变化是:

There had been significant changes in the geckodriver binary since the availability of geckodriver 0.19.1. A couple of the changes are:

  • GeckoDriver v0.22.0 (2018-09-15):
    • 用于 [脚本超时] 和 [超时] 错误的 HTTP 状态代码已从请求超时 (408) 更改为内部服务器错误 (500),以免破坏 HTTP/1.1 Keep-Alive 支持,因为 HTTP 客户端将旧状态代码解释为意味着他们应该复制请求.
    • 持久连接的 HTTP/1.1 Keep-Alive 超时已增加到 90 秒.
    • 现在没有活动会话时会返回 [无效会话 ID] 错误.
    • geckodriver 连接到 Marionette 时的握手已通过在失败时终止 Firefox 进程而得到加强.
    • 握手读取超时已减少到 10 秒,而不是永远等待.
    • 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.
      • The HTTP/1.1 Keep-Alive timeout for persistent connections has been increased to 90 seconds.
      • An [invalid session ID] error is now returned when there is no active session.
      • The handshake when geckodriver connects to Marionette has been hardened by killing the Firefox process if it fails.
      • The handshake read timeout has been reduced to 10 seconds instead of waiting forever.
      • 没有返回值的 WebDriver 命令现在可以正确返回 {value: null} 而不是空字典.
      • 强制使用 IPv4 网络堆栈.
      • WebDriver commands that do not have a return value now correctly return {value: null} instead of an empty dictionary.
      • Force use of IPv4 network stack.

      在某些系统配置中,localhost 解析为 IPv6 地址,geckodriver 会尝试连接到错误 IP 堆栈上的 Firefox,导致连接尝试在 60 秒后超时.我们现在确保 geckodriver 始终使用 IPv4 来连接到 Firefox 并分配空闲端口.

      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. We now ensure that geckodriver uses IPv4 consistently to both connect to Firefox and for allocating a free port.

      • 避免试图杀死已停止的 Firefox 进程.

      随着 0.20.0 中允许 Firefox 有足够时间关闭的更改,geckodriver 开始无条件地终止进程以获取其退出状态.这导致 geckodriver 错误地将成功关闭 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. This caused geckodriver to inaccurately report a successful Firefox shutdown as a failure.

      • 来自 geckodriver 的回溯不再替代丢失的 Marionette 堆栈跟踪.
      • Firefox 进程现在有足够的时间关闭,让 Firefox 关闭挂起监视器有足够的时间启动.

      Firefox 有一个集成的后台监视器,可以在关闭期间观察长时间运行的线程.如果挂起,这些线程将在 63 秒后终止.为了让 Firefox 自行关闭这些线程,geckodriver 必须等待该时间和一些额外的秒数.

      Firefox has an integrated background monitor that observes long-running threads during shutdown. These threads will be killed after 63 seconds in the event of a hang. To allow Firefox to shut down these threads on its own, geckodriver has to wait that time and some additional seconds.

      • Selenium 升级到当前级别版本 3.14.0.
      • GeckoDriver 升级到 GeckoDriver v0.22.0 级别.
      • Firefox 版本升级到 Firefox v62.0.2 级别.
      • 如果您的基本Web Client 版本太旧,请通过Revo Uninstaller 并安装 Web Client 的最新 GA 和发布版本.
      • 总是在 tearDown(){} 方法中调用 driver.quit() 来关闭 &优雅地销毁 WebDriverWeb Client 实例.
      • 以非 root 用户身份执行您的 Test.
      • Upgrade Selenium to current levels Version 3.14.0.
      • Upgrade GeckoDriver to GeckoDriver v0.22.0 level.
      • Upgrade Firefox version to Firefox v62.0.2 levels.
      • 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.
      • Always invoke driver.quit() within tearDown(){} method to close & destroy the WebDriver and Web Client instances gracefully.
      • Execute your Test as a non-root user.

      根据您的粗略更新问题,您可以为多次试验引入一个循环来初始化 selenium webdriver 实例,如下所示:

      As per your question update of-coarse you can induce a loop for multiple trials to initialize the selenium webdriver instance as follows:

      • 通过调用 taskkill 命令(WindowsOS 特定)确保没有 geckodriver 的悬空实例,如下所示:

      • Ensure that there is no dangling instances of geckodriver by invoking taskkill command (WindowsOS specific) as follows:

      os.system("taskkill /f /im geckodriver.exe /T") 
      

    • 通过调用 kill() 命令(跨平台)确保没有 geckodriver 的悬空实例,如下所示:

    • Ensure that there is no dangling instances of geckodriver by invoking kill() command (Cross platform) as follows:

      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:UtilityBrowserDriversgeckodriver.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 ...")
      

    • 这篇关于如何修复python-selenium错误“连接被拒绝"初始化 selenium webdriver 时?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆