连接重置错误:[Errno 104]同级连接重置,以及通过Selenium在Heroku上进行移动测试时的ERR_NAME_NOT_RESOLVED问题。

9
我希望能够使用Selenium和Chrome测试多个移动用户代理。我正在使用Python 3.6,并部署到Heroku。基于http://chromedriver.chromium.org/mobile-emulation
您可以在以下链接中下载我的项目,适用于Windows和Heroku使用:
https://github.com/kc1/mobiletest

记住,如果你部署到Heroku,你必须将FLASK_CONFIG设置为生产环境。另请注意,项目中的代码与本问题中略有不同,因为我在过去的一周里一直在调试代码。
我有:
def some_long_calculation():
    driver = create_chromedriver('kkk')
    # driver = create_chromedriver()

    driver.get("https://www.yahoo.com/")
    .....

和:

def create_chromedriver(ua=False):
    options = webdriver.ChromeOptions()
    CHROMEDRIVER_PATH = os.getenv('$HOME') or basedir+'/chromedriver.exe'
    FLASK_CONFIG = os.getenv('FLASK_CONFIG')

    if ua:

        mobile_emulation = {"deviceName": "Nexus 5"}
        options.add_experimental_option("mobileEmulation", mobile_emulation)


    if FLASK_CONFIG and FLASK_CONFIG == "production":
        CHROMEDRIVER_PATH = '/app/.chromedriver/bin/chromedriver'
        GOOGLE_CHROME_SHIM = os.getenv('$GOOGLE_CHROME_SHIM') or 'no path found'
        options.binary_location = '/app/.apt/usr/bin/google-chrome-stable'

        options.add_argument('--disable-gpu')
        options.add_argument('--no-sandbox')

    return webdriver.Chrome(executable_path=CHROMEDRIVER_PATH, options=options)  

如果我在本地运行并启用移动浏览器,则它将按预期工作:

enter image description here

如果我在启用移动浏览器的情况下在Heroku上运行它:

enter image description here

然后我在Heroku上尝试,禁用移动用户后得到以下结果:

enter image description here

所以至少我知道设置已经在Chrome和Chromedriver方面工作了。
Heroku日志:
2018-07-15T17:37:53.967643+00:00 app[web.1]:     driver = create_chromedriver('kkk')
2018-07-15T17:37:53.967637+00:00 app[web.1]:     png = some_long_calculation()
2018-07-15T17:37:53.967645+00:00 app[web.1]:   File "/app/app/main/cl.py", line 120, in create_chromedriver
2018-07-15T17:37:53.967640+00:00 app[web.1]:   File "/app/app/main/cl.py", line 123, in some_long_calculation
2018-07-15T17:37:53.967648+00:00 app[web.1]:     return webdriver.Chrome(executable_path=CHROMEDRIVER_PATH, options=options)
2018-07-15T17:37:53.967651+00:00 app[web.1]:   File "/app/.heroku/python/lib/python3.6/site-packages/selenium/webdriver/chrome/webdriver.py", line 75, in __init__
2018-07-15T17:37:53.967654+00:00 app[web.1]:     desired_capabilities=desired_capabilities)
2018-07-15T17:37:53.967656+00:00 app[web.1]:   File "/app/.heroku/python/lib/python3.6/site-packages/selenium/webdriver/remote/webdriver.py", line 156, in __init__
2018-07-15T17:37:53.967659+00:00 app[web.1]:     self.start_session(capabilities, browser_profile)
2018-07-15T17:37:53.967661+00:00 app[web.1]:   File "/app/.heroku/python/lib/python3.6/site-packages/selenium/webdriver/remote/webdriver.py", line 251, in start_session
2018-07-15T17:37:53.967669+00:00 app[web.1]:     response = self.command_executor.execute(driver_command, params)
2018-07-15T17:37:53.967664+00:00 app[web.1]:     response = self.execute(Command.NEW_SESSION, parameters)
2018-07-15T17:37:53.967667+00:00 app[web.1]:   File "/app/.heroku/python/lib/python3.6/site-packages/selenium/webdriver/remote/webdriver.py", line 318, in execute
2018-07-15T17:37:53.967672+00:00 app[web.1]:   File "/app/.heroku/python/lib/python3.6/site-packages/selenium/webdriver/remote/remote_connection.py", line 472, in execute
2018-07-15T17:37:53.967674+00:00 app[web.1]:     return self._request(command_info[0], url, body=data)
2018-07-15T17:37:53.967677+00:00 app[web.1]:   File "/app/.heroku/python/lib/python3.6/site-packages/selenium/webdriver/remote/remote_connection.py", line 496, in _request
2018-07-15T17:37:53.967679+00:00 app[web.1]:     resp = self._conn.getresponse()
2018-07-15T17:37:53.967682+00:00 app[web.1]:   File "/app/.heroku/python/lib/python3.6/http/client.py", line 1331, in getresponse
2018-07-15T17:37:53.967685+00:00 app[web.1]:     response.begin()
2018-07-15T17:37:53.967687+00:00 app[web.1]:   File "/app/.heroku/python/lib/python3.6/http/client.py", line 297, in begin
2018-07-15T17:37:53.967695+00:00 app[web.1]:     line = str(self.fp.readline(_MAXLINE + 1), "iso-8859-1")
2018-07-15T17:37:53.967690+00:00 app[web.1]:     version, status, reason = self._read_status()
2018-07-15T17:37:53.967698+00:00 app[web.1]:   File "/app/.heroku/python/lib/python3.6/socket.py", line 586, in readinto
2018-07-15T17:37:53.967692+00:00 app[web.1]:   File "/app/.heroku/python/lib/python3.6/http/client.py", line 258, in _read_status
2018-07-15T17:37:53.967700+00:00 app[web.1]:     return self._sock.recv_into(b)
2018-07-15T17:37:53.967712+00:00 app[web.1]: ConnectionResetError: [Errno 104] Connection reset by peer

我该如何解决这个问题?

编辑:

感谢您详细的回答。我已经更改了代码以包含您提到的标志。 Chrome版本为67.0.3396.99。 Chromedriver为2.40,selenium为3.13。不幸的是,结果没有改变。我仍然收到相同的错误。关于您的第二和第三阶段建议。我目前正在部署到heroku,因此我无法完全控制环境变量。有没有办法使用python进行这些更改?

编辑2:

当我在https://sites.google.com/a/chromium.org/chromedriver/mobile-emulation上思考更多时,示例使用了

from selenium import webdriver
mobile_emulation = { "deviceName": "Nexus 5" }
chrome_options = webdriver.ChromeOptions()
chrome_options.add_experimental_option("mobileEmulation", mobile_emulation)
driver = webdriver.Remote(command_executor='http://127.0.0.1:4444/wd/hub',
                  desired_capabilities = chrome_options.to_capabilities())

您是在暗示浏览器位于 'http://127.0.0.1:4444/wd/hub' 吗?

2个回答

18

ConnectionResetError: [Errno 104] Connection reset by peer

一般情况下,如果客户端突然终止连接而没有正确关闭连接,则底层操作系统的TCP/IP协议栈会发送一个RST数据包。Python将其转换为一个异常,该异常显示为Connection reset by peer。根据您的错误跟踪信息,意味着一旦调用了self._read_status()(在内部),Python就认为会收到某些内容,但连接突然断开,Python通过引发异常来通知您这个错误:

ConnectionResetError: [Errno 104] Connection reset by peer

这种情况有点类似于这个表达式

 

“Connection reset by peer”是TCP/IP界的挂电话相当于。 它比仅仅不回答、让人等待更礼貌。 但它并不是真正礼貌的TCP/IP会话者所期望的FIN-ACK。

这个错误背后可能有多个原因,例如:


解决方案阶段A

一个快速而精确的解决方案是在现有的ChromeOptions之外,添加一些推荐的ChromeOptions,如下:

options.add_argument("start-maximized"); // open Browser in maximized mode
options.add_argument("disable-infobars"); // disabling infobars
options.add_argument("--disable-extensions"); // disabling extensions
options.add_argument("--no-sandbox")
options.add_argument("--disable-dev-shm-usage"); // overcome limited resource problems

然后

return webdriver.Chrome(executable_path=CHROMEDRIVER_PATH, options=options) 

注意:你需要删除参数options.add_argument('--disable-gpu'),因为它只适用于Windows操作系统。


解决方案B阶段

几点建议:

  • 根据Python Chrome Mobile Emulation中的文档,键和值对似乎是"deviceName": "Google Nexus 5"(而不是 "deviceName": "Nexus 5"
  • 您可以调整代码以通过以下任一方式调用Remote():

    • 通过DesiredCapabilities()调用Remote():

      from selenium import webdriver
      # import DesiredCapabilities was missing in your program
      from selenium.webdriver.common.desired_capabilities import DesiredCapabilities
      
      mobile_emulation = { "deviceName": "Google Nexus 5" }
      chrome_options = webdriver.ChromeOptions()
      chrome_options.add_experimental_option("mobileEmulation", mobile_emulation)
      capabilities = DesiredCapabilities.CHROME
      capabilities = options.to_capabilities()
      driver = webdriver.Remote(command_executor='http://127.0.0.1:4444/wd/hub', desired_capabilities = capabilities)
      
    • 如何将Selenium Chrome选项添加到'desiredCapabilities'中?上找到了有关通过ChromeOptions()调用Remote()的类似讨论。

    • 通过ChromeOptions()调用Remote()

    • from selenium import webdriver
      mobile_emulation = { "deviceName": "Google Nexus 5" }
      chrome_options = webdriver.ChromeOptions()
      chrome_options.add_experimental_option("mobileEmulation", mobile_emulation)
      driver = webdriver.Remote(command_executor='http://127.0.0.1:4444/wd/hub', options=chrome_options)
      
    • 此处,您可以找到有关通过ChromeOptions()调用Remote()的类似讨论。

  • 此处提供有关Webdriver.android的文档链接,供您参考。
  • 此处提供有关Getting started with Selendroid的文档链接,供您参考。

解决方案 C

如果您仍然看到错误,请执行以下升级/清理任务:


@user61629,请查看我的更新答案并告诉我状态。 - undetected Selenium
"deviceName": "Google Nexus 5" - 不起作用,日志中显示selenium错误,表示找不到设备。如果您不介意查看我的 GitHub 存储库,请将其放在问题的顶部。 - user1592380
1
感谢您再次提供详细的答案。我将授予您奖励,但很遗憾我仍然无法使其正常工作。 - user1592380
1
docker run --sysctl 运行Docker容器时,如果遇到问题,可以使用这些sysctl.conf更改(参见https://dev59.com/tVQI5IYBdhLWcg3w-Qv_#54845286)。我必须设置`--sysctl net.ipv4.tcp_window_scaling=0`才能使其正常工作。现在正在尝试调整tcp_rmem/wmem设置,看看是否有其他解决方法。非常好的答案。寻找了两个星期。使用gunicorn和geventwebsockets以及websocket-client Python库,在一些Debian主机VM和基于Ubuntu 18.04的Docker镜像上运行时遇到此问题。 - dwagnerkc

2
具体的错误信息是:
android 服务器的 IP 地址无法找到。
在我看来,这意味着浏览器正在尝试查找一个 DNS 条目,而 android 不是有效的 TLD。是否偶然间浏览器试图访问 http://android,而不是像 https://www.google.com 这样的网址?通过在地址栏中键入 http://android,我可以在自己的 Chrome 中复制相同的错误消息。这使我相信为浏览器指定一个正确的 URL 应该可以解决问题。

谢谢您查看这个问题,Josh。这是一个有趣的想法。不过我不明白如何“为浏览器指定正确的URL”。你能给我举个例子吗? - user1592380
如果您感兴趣的话,我已经创建了一个精简版的 Github 项目仓库,您可以在本地 Windows 上运行它,并且很快就可以部署到 Heroku。详情请参见问题说明。 - user1592380

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接