Selenium funciona no AWS EC2, mas não no AWS Lambda

Nov 16 2020

Eu olhei e tentei quase todas as outras postagens sobre este tópico sem sorte.

EC2

Estou usando, python 3.6então estou usando o seguinte AMI amzn-ami-hvm-2018.03.0.20181129-x86_64-gp2(veja aqui ). Depois de fazer SSH em meu EC2, faço o download do Chrome com:

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 

E baixe e descompacte o Chromedriver correspondente:

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

Eu instalo python36e seleniumcom:

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

Em seguida, execute o 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)

Isso funciona

Lambda

Em seguida, compacto meus arquivos do chromedriver e do Chrome:

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

E copie o arquivo compactado para um S3balde

Esta é minha função 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)

Consigo ver meus arquivos descompactados no /tmpcaminho.

E meu erro:

{
  "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: Estou disposto a experimentar qualquer coisa neste momento. Diferentes versões do Chrome ou Chromium, Chromedriver, Python ou Selenium.

EDIT2: A resposta abaixo não resolveu o problema.

Respostas

5 DebanjanB Nov 18 2020 at 21:13

Esta mensagem de erro ...

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

... implica que o ChromeDriver não conseguiu iniciar / gerar um novo Contexto de Navegação, ou seja, uma sessão do navegador Chrome

Parece que o problema é com o recurso de segurança do ChromeDriver do Sandboxing .


Regra do polegar

Uma causa comum de travamento do Chrome durante a inicialização é a execução do Chrome como rootusuário ( administrator) no Linux. Embora seja possível contornar esse problema passando o --no-sandboxsinalizador ao criar sua sessão do WebDriver, essa configuração não tem suporte e é altamente desencorajada. Você precisa configurar seu ambiente para executar o Chrome como um usuário regular.


Detalhes

Um pouco mais de detalhes sobre o seu caso de uso nos teria ajudado a analisar melhor o uso dos argumentos que você usou e a causa raiz do erro. No entanto, alguns pensamentos:

  • Qual é a sandbox? : A sandbox é uma biblioteca C ++ que permite a criação de processos em área restrita - processos executados em um ambiente muito restritivo. Os únicos recursos que os processos em área restrita podem usar livremente são os ciclos da CPU e a memória. Por exemplo, processos de sandboxes não podem gravar em disco ou exibir suas próprias janelas. O que exatamente eles podem fazer é controlado por uma política explícita. Os renderizadores do Chromium são processos em área restrita.
  • Contra o que protege e não protege? : A sandbox limita a gravidade dos bugs no código executado dentro da sandbox. Esses bugs não podem instalar malware persistente na conta do usuário (porque a gravação no sistema de arquivos é proibida). Esses bugs também não podem ler e roubar arquivos arbitrários da máquina do usuário. (No Chromium, os processos de renderização são colocados em sandbox e têm essa proteção. Após a remoção do NPAPI, todos os plug-ins restantes também são colocados em sandbox. Observe também que os processos de renderização do Chromium são isolados do sistema, mas ainda não da web. Portanto, com base em domínio o isolamento de dados ainda não foi fornecido.). A sandbox não pode fornecer nenhuma proteção contra bugs nos componentes do sistema, como o kernel em que está sendo executado.
  • Então, como um processo em área restrita, como um renderizador, pode realizar alguma coisa? : Certos canais de comunicação são explicitamente abertos para os processos em área restrita; os processos podem escrever e ler nesses canais. Um processo mais privilegiado pode usar esses canais para realizar certas ações em nome do processo em área restrita. No Chromium, o processo privilegiado geralmente é o processo do navegador.

Portanto, você pode precisar descartar a --no-sandboxopção. Aqui está o link para a história do Sandbox .


Considerações adicionais

Mais algumas considerações:

  • Ao usar a --headlessopção, você não poderá usar --window-size=1280x1696devido a certas restrições.

Você pode encontrar algumas discussões detalhadas relevantes em:

  • Tela cheia no Headless Chrome usando Selenium
  • Não é possível maximizar a janela do Chrome no modo headless
  • O argumento --disable-gpuera habilitar google-chrome-headless na plataforma Windows . Ele era necessário, pois o SwiftShader falhava em uma declaração no Windows no modo headless anteriormente. Este problema foi resolvido através do Headless: make --disable-gpu flag desnecessário

Você pode encontrar uma discussão detalhada relevante em ERROR: gpu_process_transport_factory.cc (1007) -Lost UI shared context: durante a inicialização do navegador Chrome através do ChromeDriver no modo Headless

  • Além disso, você não mencionou qualquer exigência específica de usar --disable-dev-shm-usage, --hide-scrollbars, --enable-logging, --log-level=0, --v=99, --single-processe --remote-debugging-port=9222argumentos que você optar por deixar cair por enquanto, e adicioná-los de volta como por sua especificação de teste .

Referências

Você pode encontrar algumas discussões detalhadas relevantes em:

  • selenium.common.exceptions.WebDriverException: Mensagem: erro desconhecido: não foi possível descobrir páginas abertas usando ChromeDriver por meio do Selenium
  • WebDriverException: erro desconhecido: não foi possível descobrir o erro de páginas abertas com ChromeDriver 80.0.3987.106 e Chrome 80.0.3987.122
CPak Dec 13 2020 at 20:28

Eu finalmente consegui fazer funcionar

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

Headless-Chromium

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

Eu adicionei cromo sem cabeça e cromo-driver a um Lambda Layer

Permissões 755para ambas as obras

Lambda

A função Lambda se parece com isto

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)

Espero que isso ajude alguém no quarto trimestre de 2020 e depois.