Selenium fonctionne sur AWS EC2 mais pas sur AWS Lambda

Nov 16 2020

J'ai regardé et essayé presque tous les autres articles sur ce sujet sans succès.

EC2

J'utilise python 3.6donc j'utilise l'AMI suivante amzn-ami-hvm-2018.03.0.20181129-x86_64-gp2(voir ici ). Une fois que je me connecte en SSH à mon EC2, je télécharge Chrome avec:

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 

Et téléchargez et décompressez le Chromedriver correspondant:

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

J'installe python36et seleniumavec:

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

Ensuite, exécutez le script:

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)

Cela marche

Lambda

Je zip ensuite mes fichiers chromedriver et Chrome:

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

Et copiez le fichier compressé dans un S3bucket

Voici ma fonction 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)

Je peux voir mes fichiers décompressés dans le /tmpchemin.

Et mon erreur:

{
  "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)"
    ]
  ]
}

EDIT: Je suis prêt à essayer n'importe quoi à ce stade. Différentes versions de Chrome ou Chromium, Chromedriver, Python ou Selenium.

EDIT2: La réponse ci-dessous n'a pas résolu le problème.

Réponses

5 DebanjanB Nov 18 2020 at 21:13

Ce message d'erreur ...

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

... implique que ChromeDriver n'a pas pu lancer / générer un nouveau contexte de navigation, c'est-à-dire une session du navigateur Chrome .

Il semble que le problème est avec ChromeDriver , de la fonction de sécurité de sandboxing .


Règle du pouce

Une cause fréquente de panne de Chrome au démarrage est l'exécution de Chrome en tant rootqu'utilisateur ( administrator) sous Linux. Bien qu'il soit possible de contourner ce problème en transmettant un --no-sandboxindicateur lors de la création de votre session WebDriver, une telle configuration n'est pas prise en charge et fortement déconseillée. Vous devez configurer votre environnement pour exécuter Chrome en tant qu'utilisateur régulier à la place.


Détails

Un peu plus de détails sur votre cas d'utilisation nous auraient aidé à mieux analyser l'utilisation des arguments que vous avez utilisés et la cause première de l'erreur. Cependant, quelques réflexions:

  • Qu'est-ce que le bac à sable? : Le sandbox est une bibliothèque C ++ qui permet la création de processus sandbox - des processus qui s'exécutent dans un environnement très restrictif. Les seules ressources que les processus sandbox peuvent utiliser librement sont les cycles CPU et la mémoire. Par exemple, les processus sandbox ne peuvent pas écrire sur le disque ou afficher leurs propres fenêtres. Ce qu'ils peuvent faire exactement est contrôlé par une politique explicite. Les moteurs de rendu au chrome sont des processus en bac à sable.
  • Contre quoi protège-t-il et contre quoi? : Le bac à sable limite la gravité des bogues dans le code s'exécutant à l'intérieur du bac à sable. De tels bogues ne peuvent pas installer de logiciels malveillants persistants dans le compte de l'utilisateur (car l'écriture sur le système de fichiers est interdite). De tels bogues ne peuvent pas non plus lire et voler des fichiers arbitraires sur la machine de l'utilisateur. (Dans Chromium, les processus de rendu sont mis en bac à sable et bénéficient de cette protection. Après la suppression de NPAPI, tous les plug-ins restants sont également mis en bac à sable. Notez également que les processus de rendu de Chromium sont isolés du système, mais pas encore du Web. Par conséquent, basés sur le domaine l'isolation des données n'est pas encore fournie.). Le bac à sable ne peut fournir aucune protection contre les bogues dans les composants système tels que le noyau sur lequel il s'exécute.
  • Alors, comment un processus sandbox tel qu'un moteur de rendu peut-il accomplir quelque chose? : Certains canaux de communication sont explicitement ouverts pour les processus sandbox; les processus peuvent écrire et lire à partir de ces canaux. Un processus plus privilégié peut utiliser ces canaux pour effectuer certaines actions au nom du processus sandbox. Dans Chromium, le processus privilégié est généralement le processus du navigateur.

Vous devrez peut-être abandonner l' --no-sandboxoption. Voici le lien vers l' histoire de Sandbox .


Considérations supplémentaires

Quelques considérations supplémentaires:

  • --headlessLorsque vous utilisez l' option, vous ne pourrez pas l'utiliser en --window-size=1280x1696raison de certaines contraintes.

Vous pouvez trouver quelques discussions détaillées pertinentes dans:

  • Plein écran dans Headless Chrome avec Selenium
  • Impossible de maximiser la fenêtre Chrome en mode sans tête
  • L'argument --disable-gpuétait d'activer google-chrome-headless sur la plate - forme Windows . Cela était nécessaire car SwiftShader échouait à une assertion sous Windows en mode sans tête plus tôt. Ce problème a été résolu via Headless: rendre le drapeau --disable-gpu inutile

Vous pouvez trouver une discussion détaillée pertinente dans ERREUR: gpu_process_transport_factory.cc (1007) -Lost UI shared context: lors de l'initialisation du navigateur Chrome via ChromeDriver en mode Headless

  • En outre , vous ne l' avez pas mentionné toute exigence spécifique d'utilisation --disable-dev-shm-usage, --hide-scrollbars, --enable-logging, --log-level=0, --v=99, --single-processet les --remote-debugging-port=9222arguments que vous choisissez d'abandonner pour le moment et les ajouter selon votre spécification de test .

Références

Vous pouvez trouver quelques discussions détaillées pertinentes dans:

  • selenium.common.exceptions.WebDriverException: Message: erreur inconnue: impossible de découvrir les pages ouvertes à l'aide de ChromeDriver via Selenium
  • WebDriverException: erreur inconnue: impossible de découvrir l'erreur de pages ouvertes avec ChromeDriver 80.0.3987.106 et Chrome 80.0.3987.122
CPak Dec 13 2020 at 20:28

J'ai enfin pu le faire fonctionner

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

Chrome sans tête

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

J'ai ajouté du chrome sans tête et un pilote chromé à un Lambda Layer

Autorisations 755pour les deux œuvres

Lambda

La fonction Lambda ressemble à ceci

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)

J'espère que cela aidera quelqu'un au quatrième trimestre 2020 et après.