良い日、私はこことグーグルで多くの検索をしましたが、まだこの問題に対処する解決策を見つけました。
シナリオは次のとおりです。
多くの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にこだわっています。
私はまだこれがなぜ起こっているのか分かりませんが、回避策を見つけたかもしれません。私はいくつかのドキュメントで競合状態があるかもしれません(何については、同じリソースを求めて競合する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に増やすことです。増加以来、この種の障害はありませんでした。
DockerコンテナでSelenium Webdriverを実行しているときにこの問題が発生する他の人は、 コンテナサイズを2gbに増やす 修正 この問題 。
OPがサーバーRAMを2Gbにアップグレードして問題を修正した場合、これは物理マシンにも影響しますが、偶然かもしれません。
buntu 16.04でこれを試してください:
firefox
Sudo apt update
Sudo apt install firefox
firefox
が適切にインストールされていることを確認しますwhich firefox
/usr/bin/firefox
を返します
geckodriver
リリースページに移動します。プラットフォームに対応した最新バージョンのドライバーを見つけてダウンロードします。例えば:wget https://github.com/mozilla/geckodriver/releases/download/v0.24.0/geckodriver-v0.24.0-linux64.tar.gz
tar -xvzf geckodriver*
chmod +x geckodriver
$PATH
に移動し、root
アクセス権を付与しますSudo mv geckodriver /usr/bin/
cd /usr/bin
Sudo chown root:root geckodriver
Selenium
pip3 install Selenium
firefox
とgeckodriver
を$PATH
に追加しますSudo vim ~/.bashrc
2行追加します。
export PATH=$PATH:"/usr/bin/firefox"
export PATH=$PATH:"/usr/bin/geckodriver"
Sudo reboot
これにより、他の貧しい人々の魂が私がこれに費やした時間を節約することを願っています。
firefoxの古いバージョンをダウンロードする (具体的には、私にとってはv66)、そしてそこにSeleniumを向ける:
firefox_binary='/home/user/Downloads/old_firefox/firefox/firefox'
この背後にある本当の問題は、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からの回答を組み合わせてください。