Selenium działa na AWS EC2, ale nie na AWS Lambda
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
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
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.