Selenium działa na AWS EC2, ale nie na AWS Lambda

Nov 16 2020

Przeglądałem i próbowałem prawie każdego innego posta na ten temat bez powodzenia.

EC2

Używam, python 3.6więc używam następującego AMI amzn-ami-hvm-2018.03.0.20181129-x86_64-gp2(patrz tutaj ). Po podłączeniu SSH do EC2 pobieram Chrome z:

sudo curl https://intoli.com/install-google-chrome.sh | bash
cp -r /opt/google/chrome/ /home/ec2-user/
google-chrome-stable --version
# Google Chrome 86.0.4240.198 

Pobierz i rozpakuj pasujący Chromedriver:

sudo wget https://chromedriver.storage.googleapis.com/86.0.4240.22/chromedriver_linux64.zip
sudo unzip chromedriver_linux64.zip

Instaluję python36iz selenium:

sudo yum install python36 -y
sudo /usr/bin/pip-3.6 install selenium

Następnie uruchom skrypt:

import os
import selenium
from selenium import webdriver

CURR_PATH = os.getcwd()
chrome_options = webdriver.ChromeOptions()
chrome_options.add_argument('--no-sandbox')
chrome_options.add_argument('--headless')
chrome_options.add_argument('--window-size=1280x1696')
chrome_options.add_argument('--disable-gpu')
chrome_options.add_argument('--disable-dev-shm-usage')
chrome_options.add_argument('--hide-scrollbars')
chrome_options.add_argument('--enable-logging')
chrome_options.add_argument('--log-level=0')
chrome_options.add_argument('--v=99')
chrome_options.add_argument('--single-process')
chrome_options.add_argument('--ignore-certificate-errors')
chrome_options.add_argument('--remote-debugging-port=9222')
chrome_options.binary_location = f"{CURR_PATH}/chrome/google-chrome"
driver = webdriver.Chrome(
    executable_path = f"{CURR_PATH}/chromedriver",
    chrome_options=chrome_options
)
driver.get("https://www.google.com/")
html = driver.page_source
print(html)

To działa

lambda

Następnie spakuję pliki chromedriver i Chrome:

mkdir tmp
mv chromedriver tmp
mv chrome tmp
cd tmp
zip -r9 ../chrome.zip chromedriver chrome

I skopiuj spakowany plik do S3wiadra

To jest moja funkcja lambda:

import os
import boto3
from botocore.exceptions import ClientError
import zipfile
import selenium
from selenium import webdriver

s3 = boto3.resource('s3')

def handler(event, context):
    chrome_bucket = os.environ.get('CHROME_S3_BUCKET')
    chrome_key = os.environ.get('CHROME_S3_KEY')
    # DOWNLOAD HEADLESS CHROME FROM S3
    try:    
        # with open('/tmp/headless_chrome.zip', 'wb') as data:
        s3.meta.client.download_file(chrome_bucket, chrome_key, '/tmp/chrome.zip')
        print(os.listdir('/tmp'))
    except ClientError as e:
        raise e
    # UNZIP HEADLESS CHROME
    try:
        with zipfile.ZipFile('/tmp/chrome.zip', 'r') as zip_ref:
            zip_ref.extractall('/tmp')
        # FREE UP SPACE
        os.remove('/tmp/chrome.zip')
        print(os.listdir('/tmp'))
    except:
        raise ValueError('Problem with unzipping Chrome executable')
    # CHANGE PERMISSION OF CHROME
    try:
        os.chmod('/tmp/chromedriver', 0o775)
        os.chmod('/tmp/chrome/chrome', 0o775)
        os.chmod('/tmp/chrome/google-chrome', 0o775)
    except:
        raise ValueError('Problem with changing permissions to Chrome executable')
    # GET LINKS
    chrome_options = webdriver.ChromeOptions()
    chrome_options.add_argument('--no-sandbox')
    chrome_options.add_argument('--headless')
    chrome_options.add_argument('--window-size=1280x1696')
    chrome_options.add_argument('--disable-gpu')
    chrome_options.add_argument('--disable-dev-shm-usage')
    chrome_options.add_argument('--hide-scrollbars')
    chrome_options.add_argument('--enable-logging')
    chrome_options.add_argument('--log-level=0')
    chrome_options.add_argument('--v=99')
    chrome_options.add_argument('--single-process')
    chrome_options.add_argument('--ignore-certificate-errors')
    chrome_options.add_argument('--remote-debugging-port=9222')
    chrome_options.binary_location = "/tmp/chrome/google-chrome"
    driver = webdriver.Chrome(
        executable_path = "/tmp/chromedriver",
        chrome_options=chrome_options
    )
    driver.get("https://www.google.com/")
    html = driver.page_source
    print(html)

W /tmpścieżce widzę moje rozpakowane pliki .

I mój błąd:

{
  "errorMessage": "Message: unknown error: unable to discover open pages\n",
  "errorType": "WebDriverException",
  "stackTrace": [
    [
      "/var/task/lib/observer.py",
      69,
      "handler",
      "chrome_options=chrome_options"
    ],
    [
      "/var/task/selenium/webdriver/chrome/webdriver.py",
      81,
      "__init__",
      "desired_capabilities=desired_capabilities)"
    ],
    [
      "/var/task/selenium/webdriver/remote/webdriver.py",
      157,
      "__init__",
      "self.start_session(capabilities, browser_profile)"
    ],
    [
      "/var/task/selenium/webdriver/remote/webdriver.py",
      252,
      "start_session",
      "response = self.execute(Command.NEW_SESSION, parameters)"
    ],
    [
      "/var/task/selenium/webdriver/remote/webdriver.py",
      321,
      "execute",
      "self.error_handler.check_response(response)"
    ],
    [
      "/var/task/selenium/webdriver/remote/errorhandler.py",
      242,
      "check_response",
      "raise exception_class(message, screen, stacktrace)"
    ]
  ]
}

EDYCJA: Jestem gotów wypróbować wszystko w tym momencie. Różne wersje Chrome lub Chromium, Chromedriver, Python lub Selenium.

EDIT2: Poniższa odpowiedź nie rozwiązała problemu.

Odpowiedzi

5 DebanjanB Nov 18 2020 at 21:13

Ten komunikat o błędzie ...

"errorMessage": "Message: unknown error: unable to discover open pages\n",
"errorType": "WebDriverException"

... oznacza, że ChromeDriver nie był w stanie zainicjować / odrodzić nowego kontekstu przeglądania, tj . sesji przeglądarki Chrome .

Wydaje się, że problem jest z ChromeDriver , s zabezpieczenia z piaskownicy .


Reguła kciuka

Typową przyczyną awarii Chrome podczas uruchamiania jest uruchomienie Chrome jako rootużytkownik ( administrator) w systemie Linux. Chociaż można obejść ten problem, przekazując --no-sandboxflagę podczas tworzenia sesji WebDriver, taka konfiguracja jest nieobsługiwana i wysoce odradzana. Musisz skonfigurować swoje środowisko, aby zamiast tego uruchamiało Chrome jako zwykły użytkownik.


Detale

Trochę więcej szczegółów na temat twojego przypadku użycia pomogłoby nam w lepszej analizie użycia argumentów, których użyłeś, i głównej przyczyny błędu. Jednak kilka przemyśleń:

  • Co to jest piaskownica? : Piaskownica to biblioteka C ++, która umożliwia tworzenie procesów w piaskownicy - procesów wykonywanych w bardzo restrykcyjnym środowisku. Jedynymi zasobami, z których procesy w piaskownicy mogą swobodnie korzystać, są cykle procesora i pamięć. Na przykład procesy piaskownicy nie mogą zapisywać na dysku ani wyświetlać własnych okien. To, co dokładnie mogą zrobić, jest kontrolowane przez wyraźną politykę. Mechanizmy renderujące Chromium to procesy piaskownicy.
  • Przed czym chroni, a przed czym nie? : Piaskownica ogranicza wagę błędów w kodzie działającym w piaskownicy. Takie błędy nie mogą zainstalować trwałego złośliwego oprogramowania na koncie użytkownika (ponieważ zapisywanie do systemu plików jest zabronione). Takie błędy nie mogą również odczytywać i kraść dowolnych plików z komputera użytkownika. (W Chromium procesy renderowania są w piaskownicy i mają tę ochronę. Po usunięciu NPAPI wszystkie pozostałe wtyczki również są piaskownicą. Pamiętaj też, że procesy renderowania Chromium są odizolowane od systemu, ale jeszcze nie z sieci. Dlatego też oparte na domenach izolacja danych nie jest jeszcze zapewniona). Piaskownica nie zapewnia żadnej ochrony przed błędami w komponentach systemu, takich jak jądro, na którym jest uruchomiony.
  • Jak więc proces w piaskownicy, taki jak mechanizm renderujący, może cokolwiek osiągnąć? : Niektóre kanały komunikacji są jawnie otwarte dla procesów w piaskownicy; procesy mogą zapisywać i czytać z tych kanałów. Bardziej uprzywilejowany proces może używać tych kanałów do wykonywania określonych czynności w imieniu procesu piaskownicy. W Chromium procesem uprzywilejowanym jest zwykle przeglądarka.

Dlatego może być konieczne odrzucenie tej --no-sandboxopcji. Oto link do historii Sandbox .


Dodatkowe uwagi

Jeszcze kilka uwag:

  • Podczas korzystania z --headlessopcji nie będziesz mógł z niej skorzystać z --window-size=1280x1696powodu pewnych ograniczeń.

Kilka odpowiednich szczegółowych dyskusji można znaleźć w:

  • Pełny ekran w Headless Chrome przy użyciu Selenium
  • Nie można zmaksymalizować okna Chrome w trybie bezgłowym
  • Argumentem --disable-gpubyło włączenie google-chrome-headless na platformie Windows . Było to potrzebne, ponieważ SwiftShader nie udaje się wcześniej w systemie Windows w trybie bezgłowym . Ten problem został rozwiązany przez Headless: make --disable-gpu flag niepotrzebne

Odpowiednie szczegółowe omówienie można znaleźć w ERROR: gpu_process_transport_factory.cc (1007) -Lost UI shared context: podczas inicjowania przeglądarki Chrome przez ChromeDriver w trybie Headless

  • Dalej nie wspomnieli żadnego wymogu konkretnego wykorzystania --disable-dev-shm-usage, --hide-scrollbars, --enable-logging, --log-level=0, --v=99, --single-processi --remote-debugging-port=9222argumenty, które zdecydują się spadać na razie i włożyć je z powrotem jak na swoje specyfikacją testów .

Bibliografia

Kilka odpowiednich szczegółowych dyskusji można znaleźć w:

  • selenium.common.exceptions.WebDriverException: Komunikat: nieznany błąd: nie można wykryć otwartych stron za pomocą ChromeDriver przez Selenium
  • WebDriverException: nieznany błąd: nie można wykryć otwartych stron, błąd w ChromeDriver 80.0.3987.106 i Chrome 80.0.3987.122
CPak Dec 13 2020 at 20:28

W końcu udało mi się to uruchomić

Python 3.7
selenium==3.14.0
headless-chromium v1.0.0-55
chromedriver 2.43

Bezgłowy-Chrom

https://github.com/adieuadieu/serverless-chrome/releases/download/v1.0.0-55/stable-headless-chromium-amazonlinux-2017-03.zip

Chromedriver

https://chromedriver.storage.googleapis.com/2.43/chromedriver_linux64.zip

Dodałem bezgłowy chrom i chromowany sterownik do Lambda Layer

Uprawnienia 755do obu prac

lambda

Funkcja Lambda wygląda następująco

import os
import selenium
from selenium import webdriver


def handler(event, context):
    print(os.listdir('/opt'))
    # 
    chrome_options = webdriver.ChromeOptions()
    chrome_options.add_argument('--no-sandbox')
    chrome_options.add_argument('--headless')
    chrome_options.add_argument('--single-process')
    chrome_options.add_argument('--disable-dev-shm-usage')
    chrome_options.binary_location = f"/opt/headless-chromium"
    driver = webdriver.Chrome(
        executable_path = f"/opt/chromedriver",
        chrome_options=chrome_options
    )
    driver.get("https://www.google.com/")
    html = driver.page_source
    driver.close()
    driver.quit()
    print(html)

Mam nadzieję, że to pomoże komuś w czwartym kwartale 2020 roku i później.