Web scraping pode economizar várias horas em comparação com a coleta manual de dados em sites, e o AWS Lambda é uma boa forma de configurar scripts para rodar sob demanda.
Neste artigo, você vai ver o que precisa saber sobre web scraping e AWS Lambda, com o que cada um tem de bom e de ruim. Também vamos analisar o código de um projeto de exemplo que combina essas duas tecnologias, que se encaixam muito bem.
Se você não tem um bom jeito de extrair informação de um site, data scraping é o caminho. Além disso, é muito bom ver um navegador abrindo, clicando em botões e preenchendo formulários sozinho.
O Selenium foi o padrão por 15 anos. O Playwright surgiu em 2020 e vem ganhando espaço de forma consistente. Hoje a pergunta não é mais “devo aprender Selenium?”, e sim “quando Selenium ainda faz sentido?”.
Vamos falar sobre essas tecnologias e passar por um exemplo em Python que resolve esse mistério. Ou, se você não está a fim de ler definições e quer ir direto ao ponto, pule para a seção “O código do projeto”.
O que é data scraping e quando ele se justifica
Data scraping é o processo de extrair informação gerada por outro programa. Web scraping é o caso específico em que essa informação está em um site.
Trate isso como último recurso. APIs existem justamente para dar acesso estruturado e confiável aos dados que você precisa. Consumir uma API é mais rápido, mais estável e não carrega nenhuma das fragilidades legais ou técnicas que o scraping traz. Se existe uma API, use a API.
O scraping se justifica quando não existe API, os dados são de acesso público e a estrutura do site é estável. O método que você escolhe depende do que vai ser coletado.
Quando você não precisa de um navegador: se o dado que você quer já vem na resposta HTML inicial, uma abordagem HTTP leve é imbatível. Sem o custo de subir um navegador, sem execução de JavaScript, sem ChromeDriver para gerenciar.
from bs4 import BeautifulSoup
from urllib.request import urlopen
with urlopen('https://en.wikipedia.org/wiki/Main_Page') as response:
soup = BeautifulSoup(response, 'html.parser')
for anchor in soup.find_all('a'):
print(anchor.get('href', '/'))Code language: JavaScript (javascript)
Essa abordagem, requests ou urllib mais BeautifulSoup para o parsing do HTML, cobre a maioria dos casos simples de scraping e não exige infraestrutura de cloud além de uma função básica.
Quando você precisa de um navegador: páginas renderizadas por JavaScript, single-page applications, conteúdo dinâmico carregado depois do load, fluxos de login, interação com formulários. Tudo isso exige automação de navegador. É aqui que Selenium e Playwright entram, e é aqui que o Lambda vira o alvo certo para o deploy.
Técnicas de web scraping: uma referência rápida
- Copiar e colar: manual. Todo desenvolvedor já fez isso. Não vale automatizar para uma tarefa pontual.
- Casamento de padrões em texto: usar ferramentas como
curlegreppara extrair padrões do HTML bruto. Rápido para alvos simples e estáveis, com markup previsível. - Parsing de HTML: transformar a resposta em uma árvore de elementos navegável. O
BeautifulSoupé a biblioteca padrão em Python. Não precisa de navegador. Serve para conteúdo estático. - Parsing do DOM: um navegador controlado por software de automação interage com a página como um usuário real: abre páginas, clica em botões, preenche formulários, espera o JavaScript executar e então extrai os dados do DOM renderizado. É o território do Selenium e do Playwright. Também é a abordagem que mais consome recursos e a que exige mais infraestrutura para rodar de forma confiável na cloud.
Um pacote conhecido para isso é o Selenium. Essa técnica exige um driver para se comunicar com o navegador instalado (por exemplo, ChromeDriver, GeckoDriver).
Esse driver expõe uma API para que o pacote Selenium consiga controlar o navegador. A versão do driver precisa ser compatível com a versão do navegador instalado.
Veja um exemplo de web scraper que abre um navegador, pega o elemento de cabeçalho da página e imprime o conteúdo dele:
from selenium import webdriver
from selenium.webdriver.common.by import By
driver = webdriver.Chrome() # Open browser
driver.get("http://example.com") # Access page
header_element = driver.find_element(By.CSS_SELECTOR, 'h1') # Find H1 element
print(header_element.text) # Print found element text content
driver.quit() # Close browserCode language: Python (python)Por que usar AWS Lambda para web scraping
Neste artigo, vamos colocar o script do web scraper na cloud. Computação em nuvem disponibiliza serviços como hospedagem e armazenamento pela internet.
Alguns provedores de cloud conhecidos são Amazon AWS, Google Cloud e Microsoft Azure. Usar uma solução em nuvem traz várias vantagens, por exemplo:
- Você não precisa configurar o seu servidor
- Há muitos engenheiros trabalhando para garantir que esses serviços não tenham falhas de segurança
- Normalmente é muito fácil escalar a capacidade do serviço de cloud que você está usando. Em muitos deles, dá para aumentar memória e poder de processamento clicando em alguns botões
Alguns desses serviços de cloud são serverless, ou seja, rodam sob demanda e o provedor cuida da infraestrutura de servidor no lugar do cliente.
Outros exigem o uso de containers, que são execuções de sistemas operacionais virtualizados a partir de imagens (templates). O Docker é a principal plataforma para trabalhar com containers.
Computação em nuvem é ótima, mas não é necessária em todo projeto. Rodar o script na sua máquina pode ser suficiente.
Cada um dos provedores citados tem várias formas de hospedar um script de web scraper. Este artigo do blog da AWS descreve 3 opções.
Resumindo o artigo, as opções são:
- Criar uma máquina virtual com o serviço EC2. É a opção mais primitiva: você precisa configurar a máquina como faria com qualquer outra, e ela fica ligada 24 horas por dia. Também é a solução mais cara das 3 opções.
- Colocar o script em um container e usá-lo no serviço AWS Fargate. O Fargate é uma opção serverless, o que ajuda porque o web scraper só precisa rodar sob demanda. O Fargate também é mais barato que o EC2.
- Usar o AWS Lambda, que também é um serviço serverless e aceita tanto código puro quanto scripts em container. Tem mais limitações que o Fargate, mas resolve na maioria dos casos. É o serviço mais barato, e o seu script pode até caber no free tier.
Aqui vamos usar o AWS Lambda. As principais limitações dele são o timeout, de 15 minutos, e o pacote de deploy, que não pode passar de 250 MB (mas aceita até 10 GB usando containers).
O script de exemplo é bem simples e leva menos de um minuto para rodar. Mas, como já dissemos, o Selenium exige um navegador, e o binário do Chrome tem cerca de 500 MB, o que nos obriga a usar a abordagem de container.
Leia também: Boas práticas de FinOps na AWS: como cortar e otimizar custos de cloud
Como fazer o deploy no AWS Lambda
Existem várias formas de configurar uma função no AWS Lambda. Uma delas, bem simples, é usar o Serverless Framework. Ele ajuda a desenvolver e fazer deploy de funções Lambda com um único arquivo YAML, que declara as funções, a infraestrutura delas e os eventos que vão dispará-las.
O Serverless Framework também permite fazer o deploy das funções lambda com um único comando, o que simplifica bastante o processo.
O Serverless Framework ainda oferece um dashboard opcional, com uma interface para checar a saúde da função, disparar eventos manualmente e ver os logs.
O código do projeto
Nesta seção, vamos analisar alguns arquivos importantes de um projeto de demonstração, que você pode ver aqui. serverless.yml
É neste arquivo que definimos a infraestrutura da aplicação lambda, as funções lambda e os eventos que vão dispará-las.
Na seção provider do arquivo, declaramos que vamos usar uma imagem docker chamada img. A seção functions é onde definimos as funções lambda e a configuração específica de cada uma, como variáveis de ambiente e funções handler.
Repare que aqui adicionamos variáveis de ambiente com o caminho do navegador e do driver. Declaramos que vamos usar a imagem definida antes, que o comando executado quando a função lambda for disparada é o handler do arquivo example.py e que o evento que dispara tudo é um cronjob agendado a cada 6 horas.
O Dockerfile é o template onde configuramos a imagem do nosso container. Esse arquivo cria uma instância Linux capaz de rodar o web scraper. O template instala as dependências do projeto (incluindo o Chrome e o driver do Chrome) e copia os arquivos necessários para a imagem.
Esse arquivo tem uma função que será executada quando o evento for disparado. Ele é bem parecido com o exemplo de Selenium que mostramos antes.
A diferença principal é que configuramos o navegador para não exibir interface (porque o lambda não tem display) e para usar um único processo (porque o lambda tem apenas 1 CPU).
Neste caso, a função handler retorna um dicionário com status code e body, para o caso de você querer trocar o evento de um cronjob para uma requisição HTTP.
Fazendo o deploy
Para fazer o deploy da função lambda, basta rodar um único comando no terminal.

Aqui o Serverless Framework emite um aviso explicando que o dashboard não suporta funções que usam imagens de container. Ou seja, não vamos conseguir ver os logs da função lambda nem dispará-la manualmente pelo dashboard.
Mas ainda dá para fazer isso pelo console da AWS. Abaixo, um print de um log de execução bem-sucedida obtido no AWS Cloudwatch:

Selenium no Lambda: setup completo atualizado para 2026
A arquitetura continua a mesma do artigo original. As atualizações estão no runtime Python (3.12, a versão mais recente suportada pelo Lambda), na sintaxe da flag headless do Chrome (que mudou no Chrome 112) e no Serverless Framework v4.
scraper/
├── Dockerfile
├── handler.py
├── requirements.txt
└── serverless.yml
Dockerfile:
FROM public.ecr.aws/lambda/python:3.12
# Install Chromium and ChromeDriver
RUN dnf install -y chromium chromedriver
# Install Python dependencies
COPY requirements.txt .
RUN pip install -r requirements.txt --target "${LAMBDA_TASK_ROOT}"
# Copy handler
COPY handler.py ${LAMBDA_TASK_ROOT}
CMD ["handler.handler"]Code language: PHP (php)
requirements.txt:
selenium==4.18.1
handler.py:
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.common.by import By
def handler(event, context):
options = Options()
options.add_argument("--headless=new") # Updated: Chrome 112+ syntax
options.add_argument("--no-sandbox")
options.add_argument("--disable-dev-shm-usage")
options.add_argument("--single-process")
options.add_argument("--disable-gpu")
options.add_argument("--window-size=1920,1080")
driver = webdriver.Chrome(options=options)
try:
driver.get(event.get("url", "https://example.com"))
title = driver.title
header = driver.find_element(By.CSS_SELECTOR, 'h1').text
return {"title": title, "header": header}
finally:
driver.quit() # Always close the browserCode language: PHP (php)
Principais mudanças em relação ao original:
A flag --headless foi depreciada no Chrome 112. A nova flag é --headless=new. Usar a flag antiga gera avisos de compatibilidade e pode causar problemas de renderização em versões modernas do Chrome. Em 2026, use sempre --headless=new no seu setup.
O bloco try/finally em volta do driver.quit() também importa: sem ele, um erro no scraping deixa um processo do Chrome rodando dentro do container do Lambda, consumindo memória até o fim da invocação.
serverless.yml (Serverless Framework v4):
service: selenium-scraper
provider:
name: aws
region: us-east-1
timeout: 900 # 15 minutes maximum
functions:
scraper:
image:
uri: <your-ecr-image-uri>
memorySize: 2048 # Chrome needs memory, 2GB is a safe starting point
events:
- schedule: rate(6 hours)Code language: PHP (php)
Build, push e deploy:
# Build and push the container to ECR
aws ecr get-login-password --region us-east-1 | docker login --username AWS --password-stdin <account-id>.dkr.ecr.us-east-1.amazonaws.com
docker build -t selenium-scraper .
docker tag selenium-scraper:latest <account-id>.dkr.ecr.us-east-1.amazonaws.com/selenium-scraper:latest
docker push <account-id>.dkr.ecr.us-east-1.amazonaws.com/selenium-scraper:latest
# Deploy with Serverless Framework
serverless deployCode language: HTML, XML (xml)
Observação: funções Lambda baseadas em container não podem ser gerenciadas pela interface do dashboard do Serverless, mas os logs ficam acessíveis pelo AWS CloudWatch. Monitore lá o tempo de execução e o uso de memória do seu scraper: o Chrome consome muita memória, e o Lambda encerra funções que passam do teto configurado.
Quando usar Playwright no lugar
Se você está começando um projeto de scraping novo em 2026, o Playwright é o padrão mais forte. As vantagens práticas são reais:
A espera automática elimina uma classe inteira de bugs. A causa mais comum de scraper instável é timing: um elemento ainda não está visível, uma requisição de rede não terminou, uma animação em JavaScript ainda está rodando. O Selenium exige chamadas explícitas de WebDriverWait em cada interação. Se você esquecer uma, o scraper quebra de forma intermitente e o debug vira um sofrimento. O Playwright espera automaticamente até que o elemento esteja pronto para receber a interação. Só isso já remove a maioria das falhas ligadas a timing.
O suporte a async permite um throughput muito maior. A API assíncrona do Playwright torna o scraping concorrente direto. Coletar 20 URLs em paralelo são poucas linhas com asyncio. No Selenium, a mesma tarefa exige gerenciar threads e é bem mais difícil de depurar.
A configuração é mais simples. Sem casar versão de ChromeDriver. Sem matriz de compatibilidade entre versões do Chrome e do ChromeDriver. O playwright install baixa os binários corretos do navegador automaticamente e mantém tudo sincronizado.
Playwright no Lambda:
import asyncio
from playwright.async_api import async_playwright
async def scrape(url: str) -> dict:
async with async_playwright() as p:
browser = await p.chromium.launch(
headless=True,
args=["--no-sandbox", "--disable-dev-shm-usage"]
)
page = await browser.new_page()
try:
await page.goto(url, wait_until="networkidle")
title = await page.title()
header = await page.locator("h1").text_content()
return {"title": title, "header": header}
finally:
await browser.close()
def handler(event, context):
url = event.get("url", "https://example.com")
return asyncio.run(scrape(url))Code language: JavaScript (javascript)
A estrutura do Dockerfile para Playwright no Lambda é parecida: função em container com o Playwright e os binários de navegador instalados. O comando playwright install chromium durante o build da imagem cuida de todo o gerenciamento de binários.
Quando Selenium ainda faz sentido
Reescrever código Selenium que funciona não se justifica com “o Playwright é mais novo”. Se você tem uma base de código Selenium que roda de forma confiável, o custo da migração é real e o ganho é marginal para scrapers estáveis.
Fique com o Selenium quando a base de código já está estabelecida e funcionando, quando seu time investiu em Selenium Grid para execução distribuída, ou quando você precisa de Java, C# ou Ruby. Nessas linguagens, os bindings do Playwright são menos maduros que em Python e JavaScript.
Detecção de bots
Tanto Selenium quanto Playwright são detectáveis por sistemas anti-bot modernos. Cloudflare, Akamai, PerimeterX e DataDome fazem fingerprint de ferramentas de automação de navegador em nível de rede e de navegador: inspecionam headers, propriedades de JavaScript, padrões de movimento do mouse e assinaturas de timing.
O Selenium puro é especialmente fácil de detectar porque define navigator.webdriver = true por padrão. Wrappers de stealth como o undetected-chromedriver corrigem isso, mas os sistemas de detecção evoluem e esses patches exigem manutenção.
Para alvos com proteção anti-bot séria, infraestrutura de navegador gerenciada é a escolha mais pragmática. Browserless, Scrapfly e Apify cuidam de pools de navegadores, rotação de proxy e antidetecção no nível de infraestrutura. O custo por requisição é maior do que rodar o seu próprio Lambda, mas a confiabilidade é bem melhor e o esforço de manutenção é bem menor.
Para alvos sem proteção, como ferramentas internas e dados de acesso público sem detecção de bot, um setup simples de Selenium ou Playwright no Lambda funciona de forma confiável.
Leia também: Harness engineering: por que “pronto” não é o agent dizer que terminou
Para fechar
Este é um exemplo bem específico de uso do AWS Lambda com Selenium, mas espero que ele mostre o potencial dessas tecnologias. No lugar de um web scraper, dá para criar funções que rodam testes end-to-end e mandam uma mensagem no Slack em caso de falha. Ou uma API que calcula a distância entre duas strings e devolve o resultado na resposta HTTP. O limite é a sua imaginação.
