初始化 selenium webdriver 时如何修复 python-selenium 错误“连接被拒绝”?
Posted
技术标签:
【中文标题】初始化 selenium webdriver 时如何修复 python-selenium 错误“连接被拒绝”?【英文标题】:How to fix python-selenium error "connection refused" when initializing a selenium webdriver? 【发布时间】:2019-02-27 17:01:43 【问题描述】:我正在非公开网页上运行非常复杂的 python-selenium 测试。在大多数情况下,这些测试运行良好,但有时其中一个测试在 webdriver 本身初始化期间失败。
提示:尝试初始化 webdriver 时会发生此错误,即执行以下操作时:
# 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()
以下是此类错误的完整示例:
___________ 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
这些测试作为 jenkins 计划的一部分在 docker 容器内运行,以确保始终保持完全相同的环境。以下是使用的软件包及其版本的列表:
python 2.7.12 pytest 3.6.1 硒 3.8.0 geckodriver 0.19.1 火狐62.0 不稳定的 3.4.0该错误大约出现在所有测试的 1% 中。大约有 15 种不同的测试,错误似乎是随机出现的(即并不总是相同的测试)。
这是 firefox/selenium/geckodriver 中的错误吗?有没有办法解决这个问题?
下面的代码sn -p 不是我用的一些代码!这只是如何解决上述问题的一个想法。这可能是解决我最初问题的好方法吗?
while counter<5:
try:
webdriver = webdriver.Firefox(firefox_profile=profile, log_path=logfile)
break
except WebDriverException:
counter +=1
有没有更好的方法来做到这一点?
【问题讨论】:
尝试更新 Selenium (3.14.0) 和 geckodriver (0.22.0)。那里有很多变化,包括初始连接。 不涉及任何异步或多重处理对吧?完成一项测试后,您有close()
和 quit()
网络驱动程序吗?
是的;我不涉及任何多处理,我只使用“退出”...
@Alex 你能用完整的错误堆栈跟踪更新问题吗?
使用quit
只适合firefox(只要找到它)。顺便说一句,我无法通过简单地向 google.com 发送大量请求来重现问题,您确定问题不是来自您的测试页面吗?
【参考方案1】:
此错误消息...
'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"'
...暗示 GeckoDriver 无法启动/产生新的 WebBrowsing 会话,即 Firefox 浏览器 会话。
在comment 的讨论中DELETE '/session/session id' no longer working @andreastt 提到:
geckodriver 在最后一个窗口关闭时隐式结束(前一个)会话。如果
driver.quit()
作为后续命令调用,它将失败,因为会话已被隐式删除。在这些情况下,GeckoDriver 应该检测到会话在
driver.close()
之后隐式关闭,或者在会话已经关闭的情况下忽略来自driver.quit()
的响应。
在这种情况下,会生成以下跟踪日志:
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
虽然您看到的错误的错误代码是 'status': 500 并且我提供的错误示例是 404 Not Found,但显然看起来不同,核心原因类似于:
"message":"connection refused"
由于:
imp::thread::impl::new::thread_start
来自:
/checkout/src/libstd/sys/unix/thread.rs:84
从另一个角度来看,当您使用 GeckoDriver、Selenium 和 Firefox 时,请确保二进制文件的兼容性如下:
分析
自 geckodriver 0.19.1 推出以来,geckodriver 二进制文件发生了重大变化。一些变化是:
GeckoDriver v0.22.0(2018-09-15): 用于 [脚本超时] 和 [超时] 错误的 HTTP 状态代码已从请求超时 (408) 更改为内部服务器错误 (500),以不破坏 HTTP/1.1Keep-Alive
支持,正如 HTTP 客户端所解释的那样旧状态代码表示他们应该重复请求。
持续连接的 HTTP/1.1 Keep-Alive
超时已增加到 90 秒。
现在没有活动会话时会返回 [invalid session ID] 错误。
geckodriver 连接到 Marionette 时的握手已通过在失败时终止 Firefox 进程而得到加强。
握手读取超时已减少到 10 秒,而不是永远等待。
GeckoDriver v0.21.0(2018-06-15):
没有返回值的 WebDriver 命令现在可以正确返回 value: null
而不是空字典。
强制使用 IPv4 网络堆栈。
GeckoDriver v0.20.1(2018-04-06): 避免试图杀死已停止的 Firefox 进程。在某些系统配置中,
localhost
解析为 IPv6 地址,geckodriver 会尝试连接到错误 IP 堆栈上的 Firefox,导致连接尝试在 60 秒后超时。我们现在确保 geckodriver 始终使用 IPv4 来连接到 Firefox 并分配空闲端口。
GeckoDriver v0.20.0(2018-03-08): geckodriver 的回溯不再替代缺失的 Marionette 堆栈跟踪。 Firefox 进程现在有足够的时间关闭,从而有足够的时间让 Firefox 关闭挂起监视器启动。随着在 0.20.0 中允许 Firefox 有足够的时间关闭的更改,geckodriver 开始无条件地终止进程以获取其退出状态。这导致 geckodriver 将成功关闭 Firefox 错误地报告为失败。
Firefox 有一个集成的后台监视器,可以在关机期间观察长时间运行的线程。如果发生挂起,这些线程将在 63 秒后被终止。要让 Firefox 自行关闭这些线程,geckodriver 必须等待这段时间和额外的几秒钟。
解决方案
将Selenium升级到当前级别Version 3.14.0。 将GeckoDriver升级到GeckoDriver v0.22.0级别。 将 Firefox 版本升级到 Firefox v62.0.2 级别。 如果您的基础 Web Client 版本太旧,请通过 Revo Uninstaller 卸载它并安装最新的 GA 和发布版本的 Web Client。 始终在tearDown()
方法中调用 driver.quit()
以优雅地关闭和销毁 WebDriver 和 Web Client 实例。
以非 root 用户身份执行 Test
。
更新
根据您的问题更新粗略,您可以引发一个循环进行多次试验以初始化 selenium webdriver 实例,如下所示:
通过调用taskkill
命令(WindowsOS 特定)确保没有 geckodriver 的悬空实例,如下所示:
os.system("taskkill /f /im geckodriver.exe /T")
通过调用kill()
命令(跨平台)确保没有 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 ...")
【讨论】:
@Alex 从您的代码试验中可以清楚地看出,您正在使用while counter<5
中的循环来启动 webdriver 进程,该进程保证了 clean quit()
没有。该循环代码 sn-p 只是可能解决方案的建议。我在问那个循环代码 sn-p 是否是解决我原来的问题的好方法......
@Alex 我没有符合您要求的 测试环境 selenium 3.8.0, geckodriver 0.19.1 和 firefox 62.0。因此,我们的努力是根据您提供的最少日志和代码试验来构建答案。
是的。我确实已经更新了 selenium 版本和 geckodriver 编号,但很难说它现在可以工作(或更好),因为这个错误只出现在所有情况下的 1% 甚至更少......
在打开新连接之前运行 quit() 解决了我使用 RSelenium 的问题以上是关于初始化 selenium webdriver 时如何修复 python-selenium 错误“连接被拒绝”?的主要内容,如果未能解决你的问题,请参考以下文章
Selenium.WebDriver - 当我尝试在 Chrome 中运行测试时出现错误
Selenium Webdriver 和 PageFactory 初始化 List<WebElement> 元素
零基础学习Selenium自动化测试--WebDriver对浏览器的操作