web-dev-qa-db-ja.com

Python / Firefoxヘッドレススクレイピングスクリプトの「マリオネットからの応答をデコードできませんでした」メッセージ

良い日、私はこことグーグルで多くの検索をしましたが、まだこの問題に対処する解決策を見つけました。

シナリオは次のとおりです。

多くのURLをループするPythonスクリプト(2.7)があります(例:Amazonページ、レビューのスクレイピング)。各ページには同じHTMLレイアウトがあり、異なる情報をスクレイピングするだけです。これらのページには情報を取得するために実行する必要があるJavaScriptが含まれているため、私はヘッドレスブラウザでSeleniumを使用します。

このスクリプトをローカルマシン(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ビットサーバーであるため、Chrome v60-62のみをサポートするchromedriver v0.33のみを使用できます。現在、Chromeはv65であり、DigitalOceanでは古いバージョンに戻す簡単な方法がないようです。そのため、Firefoxにこだわっています。

12
Reily Bourne

私はまだこれがなぜ起こっているのか分かりませんが、回避策を見つけたかもしれません。私はいくつかのドキュメントで競合状態があるかもしれません(何については、同じリソースを求めて競合する2つのアイテムが存在するべきではないのでわかりません)。

これを行うためにスクレイピングコードを変更しました。

import time

browserObj.get(url)

time.sleep(3)

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

3秒を選択した具体的な理由はありませんが、この遅延を追加してからMessage: failed to decode response from marionetteスクレイピングするURLリストのいずれかからのエラー。


更新:2018年10月

これは6か月後も引き続き問題となっています。 Firefox、Geckodriver、Selenium、PyVirtualDisplayはすべて最新バージョンに更新されています。このエラーは、パターンなしで自然に繰り返し発生し続けました。時には機能し、時には機能しません。

この問題を修正したのは、サーバー上のRAMを1 GBから2 GBに増やすことです。増加以来、この種の障害はありませんでした。

7
Reily Bourne

DockerコンテナでSelenium Webdriverを実行しているときにこの問題が発生する他の人は、 コンテナサイズを2gbに増やす 修正 この問題

OPがサーバーRAMを2Gbにアップグレードして問題を修正した場合、これは物理マシンにも影響しますが、偶然かもしれません。

7
myol

buntu 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

2行追加します。

export PATH=$PATH:"/usr/bin/firefox"
export PATH=$PATH:"/usr/bin/geckodriver"
  1. 再起動インスタンス
Sudo reboot
0
sashaboulouds

これにより、他の貧しい人々の魂が私がこれに費やした時間を節約することを願っています。

firefoxの古いバージョンをダウンロードする (具体的には、私にとってはv66)、そしてそこにSeleniumを向ける:

firefox_binary='/home/user/Downloads/old_firefox/firefox/firefox'
0
jason m

この背後にある本当の問題は、DOMがまだロードされておらず、次のページで検索をトリガーしていることです。それが、ほとんどの場合、sleep(3)が機能している理由です。適切な修正方法は、待機クラスを使用することです。

これは、Nextcloudの待機機能を使用したテストケースの例です。それは私のdocker-Selenium-firefox-pythonイメージからです: https://hub.docker.com/r/nowsci/Selenium

waitまたはclick呼び出しを囲むgetクラスがどのように呼び出されるかに注意してください。基本的に、これは、ページのロード時に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.set_headless()
        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からの回答を組み合わせてください。

0
Fmstrat