【问题标题】:"Failed to decode response from marionette" message in Python/Firefox headless scraping scriptPython/Firefox 无头抓取脚本中的“无法解码木偶的响应”消息
【发布时间】:2018-09-18 23:14:34
【问题描述】:

美好的一天,我在这里和谷歌上进行了多次搜索,但尚未找到解决此问题的解决方案。

场景是:

我有一个循环遍历多个 URL 的 Python 脚本 (2.7)(例如,想想亚马逊页面、抓取评论)。每个页面都有相同的 HTML 布局,只是抓取不同的信息。我将 Selenium 与无头浏览器一起使用,因为这些页面具有需要执行以获取信息的 javascript。

我在本地机器 (OSX 10.10) 上运行此脚本。 Firefox 是最新的 v59。 Selenium 版本为 3.11.0,使用 geckodriver v0.20。

这个脚本在本地没有问题,它可以遍历所有的 URL 并毫无问题地抓取页面。

现在,当我将脚本放在我的服务器上时,唯一的区别是它是 Ubuntu 16.04(32 位)。我使用了适当的 geckodriver(仍然是 v0.20),但其他一切都是一样的(Python 2.7、Selenium 3.11)。它似乎随机使无头浏览器崩溃,然后所有 browserObjt.get('url...') 不再工作。

错误信息说:

消息:无法解码来自木偶的响应

对页面的任何进一步 selenium 请求都会返回错误:

消息:试图在未建立连接的情况下运行命令


显示一些代码:

当我创建驱动程序时:

    options = Options()
    options.set_headless(headless=True)

    driver = webdriver.Firefox(
        firefox_options=options,
        executable_path=config.GECKODRIVER
    )

driver 作为参数browserObj 传递给脚本的函数,然后用于调用特定页面,然后在加载后将其传递给 BeautifulSoup 进行解析:

browserObj.get(url)

soup = BeautifulSoup(browserObj.page_source, 'lxml')

错误可能指向使浏览器崩溃的 BeautifulSoup 行。

可能是什么原因造成的,我可以做些什么来解决这个问题?


编辑:添加指向同一事物的堆栈跟踪:

Traceback (most recent call last):
  File "main.py", line 164, in <module>
    getLeague
  File "/home/ps/dataparsing/XXX/yyy.py", line 48, in BBB
    soup = BeautifulSoup(browserObj.page_source, 'lxml')
  File "/home/ps/AAA/projenv/local/lib/python2.7/site-packages/selenium/webdriver/remote/webdriver.py", line 670, in page_source
    return self.execute(Command.GET_PAGE_SOURCE)['value']
  File "/home/ps/AAA/projenv/local/lib/python2.7/site-packages/selenium/webdriver/remote/webdriver.py", line 312, in execute
    self.error_handler.check_response(response)
  File "/home/ps/AAA/projenv/local/lib/python2.7/site-packages/selenium/webdriver/remote/errorhandler.py", line 242, in check_response
    raise exception_class(message, screen, stacktrace)
WebDriverException: Message: Failed to decode response from marionette

注意:此脚本用于 Chrome。因为服务器是32位服务器,所以我只能使用chromedriver v0.33,它只支持Chrome v60-62。目前 Chrome 是 v65,在 DigitalOcean 上我似乎没有简单的方法来恢复到旧版本 - 这就是我坚持使用 Firefox 的原因。

【问题讨论】:

  • 使用错误堆栈跟踪更新问题。
  • 为每个请求添加了堆栈跟踪。

标签: python selenium firefox web-scraping geckodriver


【解决方案1】:

这个错误可能是浏览器启动次数过多而没有正确关闭造成的。

如果是 OP 的 Python 代码 driver.quit(),则应在运行成功或失败后调用。必须捕获所有异常。

无论您的系统有多少内存,如果浏览器没有正确关闭,后续运行最终将无法启动另一个。在那种情况下,添加更多内存只会推迟那个时刻。

  1. 要检查孤立的浏览器进程,请在 Linux 上(或在 Windows 上的 Powershell 中)使用 ps aux 命令。
  2. 要终止此类进程,请使用 killall firefox 命令或在使用 Firefox 扩展支持版本时使用 killall firefox-esr)。这可能需要 sudo。

参考文献:

【讨论】:

    【解决方案2】:

    这背后可能的真正问题是 DOM 尚未加载,而您正在触发下一页上的搜索。这就是为什么sleep(3) 在大多数情况下都能正常工作的原因。正确的解决方法是使用等待类。

    这是一个使用 Nextcloud 的等待函数的示例测试用例。它来自我的 docker-selenium-firefox-python 图像:https://hub.docker.com/r/nowsci/selenium

    注意wait 类是如何围绕任何clickget 调用的。基本上,这样做是利用 selenium 在页面加载时更改 HTML 标记的 ID 的事实。等待函数检查新 ID 是否与旧 ID 不同,如果不同,则 DOM 已加载。

    import time
    from selenium.webdriver import Firefox
    from selenium.webdriver.firefox.options import Options
    from selenium.webdriver.common.keys import Keys
    
    class wait(object):
    
        def __init__(self, browser):
            self.browser = browser
    
        def __enter__(self):
            self.old_page = self.browser.find_element_by_tag_name('html')
    
        def page_has_loaded(self):
            new_page = self.browser.find_element_by_tag_name('html')
            return new_page.id != self.old_page.id
    
        def __exit__(self, *_):
            start_time = time.time()
            while time.time() < start_time + 5:
                if self.page_has_loaded():
                    return True
                else:
                    time.sleep(0.1)
            raise Exception('Timeout waiting for page load.')
    
    def test():
        try:
            opts = Options()
            opts.headless = True
            assert opts.headless  # Operating in headless mode
            browser = Firefox(options=opts)
        except Exception as e:
            print("  -=- FAIL -=-: Browser setup - ", e)
            return
    
        # Test title
        try:
            with wait(browser):
                browser.get('https://nextcloud.mydomain.com/index.php/login')
            assert 'Nextcloud' in browser.title
        except Exception as e:
            print("  -=- FAIL -=-: Initial load - ", e)
            return
        else:
            print("  Success: Initial load")
    
        try:
            # Enter user
            elem = browser.find_element_by_id('user')
            elem.send_keys("MYUSER")
    
            # Enter password
            elem = browser.find_element_by_id('password')
            elem.send_keys("MYPASSWORD")
    
            # Submit form
            elem = browser.find_element_by_id('submit')
            with wait(browser):
                elem.click()
    
            # Check title for success
            assert 'Files' in browser.title
        except Exception as e:
            print("  -=- FAIL -=-: Login - ", e)
            return
        else:
            print("  Success: Login")
    
        print("  Finished.")
    
    print("Testing nextcloud...")
    test()
    

    如果您使用的是 Docker,请将其与 @myol 的答案结合起来。

    【讨论】:

      【解决方案3】:

      此错误消息...

      Message: failed to decode response from marionette
      

      ...表示 GeckoDriverMarionette 之间的通信中断/中断。


      此问题的一些原因和解决方案如下:

      步骤:配置SHM size in the docker container

      • 同样,在本地主机中执行测试时,建议保留以下(最低)配置:

        --memory 1024mb
        

      其他注意事项

      由于您使用的二进制文件版本之间不兼容,也会出现此问题。

      解决方案:

      • JDK升级到最新级别JDK 8u341
      • Selenium升级到当前级别Version 3.141.59
      • GeckoDriver升级到GeckoDriver v0.26.0级别。
      • Firefox 版本升级到 Firefox v72.0 级别。
      • 以非 root 用户身份执行 Test

      GeckoDriver, Selenium and Firefox Browser compatibility chart


      tl;博士

      [e10s] Crash in libyuv::ARGBSetRow_X86


      参考

      您可以在以下位置找到相关的详细讨论:

      【讨论】:

        【解决方案4】:

        Ubuntu 16.04 上试试这个:

        1. 安装firefox
        sudo apt update
        sudo apt install firefox
        
        1. 检查firefox 是否安装正确
        which firefox
        

        将返回/usr/bin/firefox

        1. 转到geckodriver 发布页面。为您的平台找到最新版本的驱动程序并下载。例如:
        wget https://github.com/mozilla/geckodriver/releases/download/v0.24.0/geckodriver-v0.24.0-linux64.tar.gz
        
        1. 解压文件:
        tar -xvzf geckodriver*
        
        1. 使其可执行:
        chmod +x geckodriver
        
        1. 将其移至$PATH 并授予root 访问权限
        sudo mv geckodriver /usr/bin/
        cd /usr/bin
        sudo chown root:root geckodriver
        
        1. 安装selenium
        pip3 install selenium
        
        1. firefoxgeckodriver 添加到$PATH
        sudo vim ~/.bashrc
        

        添加两行:

        export PATH=$PATH:"/usr/bin/firefox"
        export PATH=$PATH:"/usr/bin/geckodriver"
        
        1. 重启您的实例
        sudo reboot
        

        【讨论】:

          【解决方案5】:

          我希望这可以节省我刚刚花在这上面的时间。

          Download an old version of firefox(特别是对我来说是 v66),并在那里指出 selenium:

          firefox_binary='/home/user/Downloads/old_firefox/firefox/firefox'
          

          【讨论】:

            【解决方案6】:

            问题是你没有关闭驱动。我犯了同样的错误。在 Linux 中使用 Whit htop,我注意到我占用了我的 pc whit 未关闭进程的所有 26 GB。

            【讨论】:

            • 是的!这也是我的问题,我注意到有很多 geckodriver.exe 进程仍在运行,所以杀死它们修复了它。
            【解决方案7】:

            对于在 Docker 容器中运行 selenium webdriver 时遇到此问题的任何其他人,increasing the container size to 2gb 修复了this issue

            如果 OP 通过将服务器 RAM 升级到 2Gb 来解决问题,我猜这也会影响物理机,但这可能是巧合。

            【讨论】:

            • 如果有办法给你多个支持,我支持!花了几个小时弄清楚这一点!非常感谢!
            • 谢谢,这正是我的问题。显然,default is only 64mb...
            • 我在尝试通过window.resize_to 然后driver.screenshot_as 进行完整内容截图时遇到了这样的问题,关于我可以缩放浏览器窗口的大小,“免费内存”似乎与你所说的一样.
            • 太棒了!
            • 非常感谢,我会被卡很久
            【解决方案8】:

            我仍然不知道为什么会发生这种情况,但我可能已经找到了解决方法。我在一些文档中读到可能存在竞争条件(我不确定,因为不应该有两个项目竞争相同的资源)。

            我改变了抓取代码来做到这一点:

            import time
            
            browserObj.get(url)
            
            time.sleep(3)
            
            soup = BeautifulSoup(browserObj.page_source, 'lxml')
            

            我没有选择 3 秒的具体原因,但是自从添加了这个延迟后,我的任何要抓取的 URL 列表中都没有出现 Message: failed to decode response from marionette 错误。


            更新:2018 年 10 月

            六个月后,这仍然是一个问题。 Firefox、Geckodriver、Selenium 和 PyVirtualDisplay 都已更新到最新版本。此错误不断自发地重复发生,没有规律:有时有效,有时无效。

            解决此问题的方法是将我的服务器上的 RAM 从 1 GB 增加到 2 GB。自增加以来,没有出现过此类失败。

            【讨论】:

            • 您的问题解决了吗?我也随机收到相同的错误。 stackoverflow.com/questions/49831841/…
            • 可能是因为这个bug
            • 在我发现一种在 DOM 上进行适当等待的方法之前,我被这个问题困扰了一段时间。如果您尚未解决此问题,请参阅我的答案。希望对您有所帮助!
            • 确认将我的 Droplet 实例的 RAM 从 1GB 增加到 2GB 也解决了这个问题。
            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2018-04-16
            • 1970-01-01
            • 2023-04-02
            • 2019-10-27
            • 1970-01-01
            • 2013-09-30
            • 1970-01-01
            相关资源
            最近更新 更多