Saltar para o conteúdo
Voltar ao blogue

Web Scraping com AWS Lambda: Guia para Python e Java 2026

Suciu DanÚltima atualização em 32 min read
Web Scraping com AWS Lambda: Guia para Python e Java 2026
Resumo: O web scraping com o AWS Lambda funciona melhor quando cada invocação é curta, delimitada e pode ser repetida de forma independente. Comece com HTTP direto, AWS SAM e S3; só depois adicione SQS, contentores, renderização no navegador, proxies ou uma camada de recuperação gerida quando a carga de trabalho demonstrar que precisa deles.

O Web Scraping com o AWS Lambda consiste na execução de código de obtenção e extração de páginas como funções AWS de curta duração, em vez de manter servidores de scraping. O Lambda inicia esse código em resposta a agendamentos, filas, chamadas HTTP ou outros eventos da AWS.

Esse modelo operacional é atraente, mas não torna todos os rastreadores compatíveis com a arquitetura sem servidor. O Lambda é adequado para verificações programadas de produtos, tarefas com um único URL, extração orientada por webhooks e trabalhadores de fila. É menos adequado para rastreamentos que exigem horas de tempo de execução ininterrupto, uma sessão de navegador que deve permanecer ativa ou uma expansão descontrolada contra um alvo sensível.

Este guia adota uma abordagem centrada na tomada de decisões. Irá construir um scraper web em Python para o AWS Lambda, ver uma contraparte em Java 21 utilizando o HttpClient e o Jsoup, comparar o empacotamento em ZIP e em contentores, persistir resultados no S3 e escalar o trabalho com URLs através do SQS. Irá também obter um plano de escalonamento conservador para JavaScript e bloqueios, além de fórmulas de cálculo de custos que separam a computação do Lambda das despesas com armazenamento, registo, rede, imagens, proxy e API. Os valores da AWS sensíveis ao tempo estão explicitamente assinalados para verificação junto da documentação oficial associada.

Comece pela decisão de implementação: será o Lambda o ambiente de execução adequado para o Web Scraping com o AWS Lambda?

A resposta curta é sim, quando uma tarefa de web scraping pode ser concluída no âmbito de uma única invocação delimitada ou ser dividida em unidades independentes, tais como um URL, uma página de listagem ou um cursor. O Web Scraping com o AWS Lambda é particularmente eficaz para verificações agendadas e tarefas com picos de carga, uma vez que não existe uma frota de workers a funcionar continuamente entre eventos.

A questão importante não é se o Lambda consegue recuperar uma página. Consegue. A questão é se as falhas, as tentativas de repetição, o estado e o débito permanecem controláveis quando cada invocação é temporária.

Características da carga de trabalho

Boa adequação ao Lambda

Sinal de alerta

Tempo de execução

Segundos ou alguns minutos

Uma execução demora horas

Estado

A entrada contém tudo o que é necessário

Estado do navegador ou de sessão de longa duração

Paralelismo

URLs independentes com um limite claro

Um rastreador descobre trabalho ilimitado

Dependências

Analisador HTTP ou imagem controlada

Pilha de desktop grande e frágil

Saída

Gravado de forma duradoura após cada tarefa

Os resultados existem apenas na memória

Modelo de falha

É possível repetir a tentativa de um URL com segurança

A repetição da tentativa duplica os efeitos secundários

Uma regra de design útil é tornar a função descartável. Se a AWS encerrar um ambiente após um pedido, uma substituição deve ser capaz de processar o mesmo evento sem reconstruir o estado local oculto. Isso transfere pontos de verificação, resultados e credenciais para serviços dedicados, em vez de /tmp ou variáveis globais do módulo.

Adapte HTML estático, rastreamento, renderização do navegador e APIs geridas à tarefa

Escolha o método de recuperação menos complexo que devolva os dados de que necessita. As páginas estáticas requerem normalmente um cliente HTTP e um analisador. O rastreio de múltiplas páginas pode justificar o uso do Scrapy. As aplicações renderizadas pelo cliente podem requerer um navegador, um ponto de extremidade JSON subjacente que esteja autorizado a chamar ou um serviço de renderização alojado.

Requisito

Primeira escolha

Avance para a opção seguinte quando

HTML renderizado no servidor

requests mais BeautifulSoup

O conteúdo não estiver presente na resposta

Rastreamento por links

Scrapy em ZIP ou imagem

As dependências nativas excedem os limites do ZIP

Cliques, deslocamentos, formulários

Playwright num contentor

As operações do navegador dominam o tempo de execução

Elevado risco de bloqueio

Controlo de taxa e, em seguida, um proxy

A reputação do IP ou a localização geográfica são fatores relevantes

Renderização e operações de proxy

API de scraping gerida

A manutenção de navegadores e da lógica de proxy custa mais do que a externalização da recuperação

A renderização, o estado da sessão, a duração da execução, o volume de pedidos e o risco de bloqueio devem orientar esta escolha. Uma arquitetura de navegador «headless» oferece mais controlo, mas também consome mais memória, aumenta o trabalho de arranque a frio e cria mais modos de falha do que a análise de HTML.

Escolha o Fargate, o Batch ou o EC2 para cargas de trabalho que o Lambda não consegue processar

Utilize um serviço de computação de maior duração quando a unidade de trabalho não puder ser delimitada. O Fargate é o próximo passo prático para workers em contentores que necessitam de execuções mais longas sem ter de gerir hosts. O Batch é mais adequado quando os trabalhos estão em fila, exigem grande capacidade de computação e se enquadram naturalmente em submissões em lote. O EC2 oferece o máximo controlo para navegadores persistentes, redes especializadas, caches locais ou rastreadores em execução contínua, mas também devolve à sua equipa a responsabilidade pela aplicação de patches nos hosts e pela gestão da capacidade.

A decisão é qualitativa antes de ser financeira. Se estiver a forçar pontos de verificação a cada poucos minutos apenas para evitar o limite da função, ou a descarregar repetidamente uma pilha de navegadores de grande dimensão, um serviço de contentores pode ser mais simples e mais económico em termos operacionais. O Lambda deve reduzir o trabalho de infraestrutura, não transferi-lo para código de recuperação complexo.

Utilize uma arquitetura de produção que separe o controlo do fluxo de dados

Um scraper de produção é mais fácil de operar quando as suas responsabilidades estão separadas. Trate o gatilho como fluxo de controlo, a função Lambda como um trabalhador substituível, o S3 ou uma base de dados como fluxo de dados durável e o CloudWatch, juntamente com um armazenamento de segredos, como o plano operacional.

Um percurso típico de web scraping com o AWS Lambda tem o seguinte aspeto:

EventBridge schedule or URL producer
                 |
           SQS or Step Functions
                 |
        Lambda scraper workers
          |       |        |
         S3   CloudWatch   SSM/Secrets Manager

Esta separação evita problemas comuns de acoplamento. Uma programação não deve conter lógica de análise. Um trabalhador não deve guardar a única cópia dos seus resultados. Um analisador não deve saber como um segredo é encriptado. A monitorização deve receber factos estruturados sobre cada execução, em vez de os reconstruir a partir de rastreios de pilha não estruturados.

Defina um contrato de evento para cada linguagem e cada gatilho. Um contrato compacto é suficiente:

{
  "url": "https://example.com/catalog",
  "limit": 20,
  "request_id": "job-2026-03-001"
}

Devolver ou armazenar um envelope de resultados correspondente com ok, request_id, url, count, items, e s3_uri. O Python e o Java podem então ser comparados operacionalmente, e as tentativas de repetição do SQS não dependem de formatos de eventos específicos da linguagem.

Escolha o EventBridge, o SQS ou o Step Functions como gatilho

Utilize o EventBridge para a extração de dados da Web agendada na AWS quando a tarefa for pequena e periódica. Uma regra pode invocar uma função a cada hora ou dia, e a função pode gravar o instantâneo resultante no S3. Evite sobreposições tornando a execução curta, adicionando chaves de saída idempotentes e aplicando concorrência reservada caso as execuções simultâneas sejam prejudiciais.

Utilize o SQS quando um produtor puder enumerar URLs ou cursores de página. A fila armazena picos de tráfego, o Lambda consome pequenos lotes e uma URL com falha pode ser repetida sem reiniciar o trabalho bem-sucedido. Esta é a arquitetura predefinida para a recolha de múltiplas URLs, uma vez que a velocidade do produtor já não determina a taxa de pedidos de destino.

Utilize o Step Functions quando a tarefa tiver fases distintas, tais como a obtenção de páginas de listagem, a produção de URLs de detalhes, o enriquecimento de registos e a publicação de um manifesto. Este serviço fornece ramificações explícitas e políticas de repetição, mas as transições de estado acarretam custos adicionais e a necessidade de gerir mais um serviço.

Acionador

Ideal para

Controlo principal

EventBridge

Instantâneos periódicos

Agendamento e concorrência reservada

SQS

Muitos URLs independentes

Tamanho do lote, concorrência da fonte de eventos, DLQ

Step Functions

Fluxos de trabalho em várias etapas

Re-tentativas e ramificações ao nível do estado

API Gateway

Pedidos a pedido

Limites de autenticação, carga útil e tempo de espera

Evento S3

Processamento de sementes carregadas

Convenções de chaves de objeto e tratamento de duplicados

Encaminhar saídas, segredos, registos e métricas para serviços dedicados

O S3 é uma opção predefinida sensata para HTML bruto, registos JSON e manifestos de rastreio, uma vez que cada invocação pode gravar um objeto durável e devolver um URI pequeno. Utilize chaves determinísticas, tais como scrapes/site/date/request-id.json; se a mesma mensagem for repetida, deve substituir o mesmo objeto ou detetar que o resultado já existe.

Mantenha as chaves de API, as palavras-passe de proxy e as credenciais de sessão no SSM Parameter Store ou no Secrets Manager. Coloque apenas o nome do parâmetro ou o ARN do segredo no ambiente Lambda. Conceda à função de execução permissão para ler esse recurso específico, e não todos os segredos da conta.

O CloudWatch recebe registos, métricas e alarmes da função. Registe um ID de correlação, o host normalizado, o código de estado, o tempo decorrido, o número de tentativas, o número de itens e a chave de saída. Evite strings de consulta completas, cabeçalhos de autorização, cookies, corpos de página e valores de segredos. O X-Ray ou um sistema de rastreio equivalente pode ajudar a distinguir o tempo gasto no Lambda da latência do DNS, do destino, do proxy, do S3 ou do armazenamento de segredos.

Essa separação de serviços também proporciona pontos de extensão naturais. Pode adicionar regras de ciclo de vida ao S3, uma tarefa de catalogação após gravações bem-sucedidas ou uma ferramenta de reprodução para uma fila de mensagens perdidas sem alterar o código de extração.

Nomeie funções e recursos de acordo com a responsabilidade, em vez de pelo site de destino. Separe a produção de URLs da recuperação de páginas quando estas escalam de forma diferente e crie versões do esquema de eventos antes que vários produtores dependam dele. Isto evita que as implementações do analisador alterem acidentalmente o comportamento da programação ou da fila.

Conceba tendo em conta as quotas do Lambda antes de codificar

O Web Scraping com o AWS Lambda tem limites rígidos da plataforma, e vários deles são sensíveis ao tempo. Na altura representada pelas fontes fornecidas, os valores mais citados são aproximadamente os que se seguem. Confirme-os para a sua região e conta na página oficial de quotas do AWS Lambda antes da publicação ou implementação.

Restrição

Valor relatado

Consequência para o scraping

Número máximo de invocações

Cerca de 900 segundos

Dividir a paginação longa em unidades enfileiradas

Memória

Aproximadamente 128 MB a 10 240 MB

Mais memória também altera a CPU disponível

Efémera /tmp

Aproximadamente 512 MB a 10 240 MB

Os ficheiros do navegador e os downloads requerem um dimensionamento explícito

Pacote ZIP

Cerca de 50 MB comprimido, 250 MB descompactado

As bibliotecas nativas podem forçar a utilização de camadas ou de uma imagem

Imagem de contentor

Cerca de 10 GB não comprimida

Empacotamento mais fácil, mas compilações mais lentas e downloads maiores

Carga útil síncrona

Cerca de 6 MB em cada sentido

Armazenar os resultados no S3 e devolver uma referência

Concorrência regional

Frequentemente indicada como 1 000 por predefinição

Contas novas ou com restrições podem apresentar valores mais baixos

Não configure o tempo de espera HTTP igual ao tempo de espera da função. Deixe tempo suficiente para a análise, gravações no S3, métricas e uma resposta de erro controlada. Para uma função de 60 segundos, por exemplo, um tempo limite de 35 a 45 segundos para os pedidos é um ponto de partida mais seguro do que 60 segundos, embora o valor correto dependa do destino.

A concorrência também serve para controlar o tráfego de saída. Defina a concorrência reservada para o scraper e, quando suportado, uma concorrência máxima por fonte de eventos para o SQS. Uma quota de serviço não significa permissão para enviar esse número de pedidos simultâneos a um site.

Configure o repositório e a infraestrutura com o AWS SAM

O AWS SAM define funções, permissões IAM, agendamentos, filas e buckets num modelo do CloudFormation. Proporciona ao Web Scraping com o AWS Lambda um fluxo de trabalho local e de implementação repetível, sem necessitar do Docker para uma função leve de HTML estático.

Um repositório compacto pode suportar ambas as linguagens e percursos de empacotamento:

lambda-scraper/
├── template.yaml
├── events/
│   ├── scrape.json
│   └── sqs.json
├── python/
│   ├── app.py
│   └── requirements.txt
├── java/
│   └── pom.xml
├── container/
│   ├── Dockerfile
│   ├── lambda_function.py
│   └── spider.py
└── tests/
    └── fixtures/

Instale uma versão compatível do Python, a AWS CLI, a AWS SAM CLI e o Docker, se pretender utilizar sam local ou imagens de contentor. Configure um perfil AWS com nome e verifique a conta antes da implementação:

aws configure --profile scraper-dev
aws sts get-caller-identity --profile scraper-dev
sam validate --lint

Mantenha os eventos de teste no controlo de código-fonte, mas nunca inclua cookies ativos, credenciais de proxy ou URLs assinadas. Parametrize o bucket de saída e os identificadores secretos por ambiente. Um fluxo de trabalho SAM repetível é validate, build, local invoke, deploye a invocação na nuvem com inspeção de registos.

Escolha o empacotamento ZIP, camadas ou uma imagem de contentor

Utilize um pacote ZIP para requests, BeautifulSoup, Jsoup e grafos de dependências igualmente pequenos. O SAM pode instalar dependências no artefacto de compilação, e os arranques a frio permanecem relativamente leves.

As camadas são úteis quando várias funções partilham um conjunto estável de dependências, mas acrescentam a coordenação de versões. Não eliminam os limites dos pacotes, e uma camada que muda a cada implementação é, normalmente, apenas mais um artefacto para gerir.

Escolha uma imagem de contentor do AWS Lambda quando precisar de pacotes nativos do sistema, dependências do Scrapy que sejam difíceis de compilar num ZIP ou de um navegador sem interface gráfica. Uma imagem melhora a consistência do ambiente, não a adequação ao tempo de execução.

Empacotamento

Melhor opção

Principal compromisso

ZIP

HTML estático, pequenos analisadores

Limites de dependência mais restritos

Camada mais ZIP

Bibliotecas estáveis partilhadas

Acoplamento de versões entre funções

Imagem de contentor

Bibliotecas nativas, Scrapy, Playwright

Sobrecarga de compilação, verificação, armazenamento e arranque a frio

Não opte automaticamente pelo Docker só porque parece mais adequado para produção. Para HTML simples, o artefacto mais pequeno é geralmente mais fácil de corrigir, testar e monitorizar.

Mantenha as diferenças de ambiente nos parâmetros SAM ou nos ficheiros de configuração, e não em modelos editados manualmente. Uma implementação deve ser reproduzível a partir de um commit, de um perfil e de um conjunto de parâmetros, com as saídas da pilha a expor nomes de funções, nomes de buckets, URLs de filas e aliases necessários para os testes de fumo.

Crie o scraper de HTML estático em Python

Para HTML estático, um scraper web em Python para o AWS Lambda necessita apenas de um cliente HTTP, um analisador de HTML e um cliente AWS SDK para uma saída duradoura. O BeautifulSoup analisa a resposta que recebe; não executa JavaScript nem aguarda uma aplicação do lado do cliente.

O manipulador abaixo aceita invocação direta, corpos JSON do API Gateway ou um objeto do EventBridge detail . Valida o URL, reutiliza clientes em invocações «warm», define tempos de espera explícitos, analisa seletores CSS substituíveis, grava o resultado no S3 e devolve erros estruturados.

import json
import os
from datetime import datetime, timezone
from urllib.parse import urljoin, urlparse

import boto3
import requests
from bs4 import BeautifulSoup

SESSION = requests.Session()
SESSION.headers.update({"User-Agent": "catalog-monitor/1.0 (+ops@example.com)"})
S3 = boto3.client("s3")
BUCKET = os.environ["OUTPUT_BUCKET"]
REQUEST_TIMEOUT = (5, 35)

def normalize_event(event):
    payload = event or {}
    if isinstance(payload.get("body"), str):
        payload = json.loads(payload["body"] or "{}")
    elif isinstance(payload.get("body"), dict):
        payload = payload["body"]
    elif isinstance(payload.get("detail"), dict):
        payload = payload["detail"]

    url = str(payload.get("url", "")).strip()
    parsed = urlparse(url)
    if parsed.scheme not in {"http", "https"} or not parsed.netloc:
        raise ValueError("url must be an absolute HTTP or HTTPS URL")

    try:
        limit = max(1, min(int(payload.get("limit", 20)), 100))
    except (TypeError, ValueError):
        raise ValueError("limit must be an integer")

    return {
        "url": url,
        "limit": limit,
        "request_id": str(payload.get("request_id", "")).strip(),
    }

def parse_items(html, base_url, limit):
    soup = BeautifulSoup(html, "html.parser")
    items = []
    for card in soup.select(".product")[:limit]:
        link = card.select_one("a")
        items.append({
            "title": card.select_one(".title").get_text(" ", strip=True)
                     if card.select_one(".title") else None,
            "price": card.select_one(".price").get_text(" ", strip=True)
                     if card.select_one(".price") else None,
            "availability": card.select_one(".availability").get_text(" ", strip=True)
                     if card.select_one(".availability") else None,
            "url": urljoin(base_url, link.get("href")) if link else None,
        })
    return items

def handler(event, context):
    aws_id = getattr(context, "aws_request_id", "local")
    try:
        job = normalize_event(event)
        request_id = job["request_id"] or aws_id

        response = SESSION.get(job["url"], timeout=REQUEST_TIMEOUT)
        response.raise_for_status()
        if not response.encoding or response.encoding.lower() == "iso-8859-1":
            response.encoding = response.apparent_encoding

        items = parse_items(response.text, job["url"], job["limit"])
        result = {
            "ok": True,
            "request_id": request_id,
            "url": job["url"],
            "count": len(items),
            "items": items,
            "fetched_at": datetime.now(timezone.utc).isoformat(),
        }

        key = f"scrapes/{request_id}.json"
        S3.put_object(
            Bucket=BUCKET,
            Key=key,
            Body=json.dumps(result).encode("utf-8"),
            ContentType="application/json",
        )
        result["s3_uri"] = f"s3://{BUCKET}/{key}"
        return result

    except ValueError as exc:
        return {"ok": False, "error": {"type": "validation", "message": str(exc)}}
    except requests.RequestException as exc:
        status = getattr(exc.response, "status_code", None)
        return {"ok": False, "error": {"type": "request", "status": status}}
    except Exception:
        return {"ok": False, "error": {"type": "internal"}}

Substitua os seletores por seletores abrangidos pelos testes de fixture. Se o número zero de itens for inesperado, trate-o como uma falha do analisador, em vez de uma extração vazia bem-sucedida. Decida também se os redirecionamentos são permitidos e se as URLs fornecidas pelo utilizador requerem uma lista de permissões para reduzir o risco de falsificação de pedidos do lado do servidor.

Teste localmente, implemente, execute e grave JSON no S3

Utilize um pequeno ficheiro de dependências:

requests
beautifulsoup4

Os recursos SAM abaixo criam um bucket de saída e concedem apenas gravações de objetos sob o prefixo do scraper. Fixe os ambientes de execução suportados e as versões das dependências no repositório real após a validação.

Resources:
  OutputBucket:
    Type: AWS::S3::Bucket

  PythonScraper:
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: python/
      Handler: app.handler
      Runtime: python3.12
      Architectures: [x86_64]
      MemorySize: 512
      Timeout: 60
      Environment:
        Variables:
          OUTPUT_BUCKET: !Ref OutputBucket
      Policies:
        - Statement:
            - Effect: Allow
              Action: s3:PutObject
              Resource: !Sub "${OutputBucket.Arn}/scrapes/*"

Criar events/scrape.json:

{
  "url": "https://example.com/catalog",
  "limit": 10,
  "request_id": "local-001"
}

Em seguida, execute uma sequência repetível:

sam validate --lint
sam build
sam local invoke PythonScraper -e events/scrape.json
sam deploy --guided
aws lambda invoke \
  --function-name YOUR_STACK_FUNCTION \
  --payload fileb://events/scrape.json response.json

Valide o objeto no S3 e inspecione o fluxo de registos da função no CloudWatch. Um sucesso local comprova a análise, e não as permissões na nuvem, o comportamento do DNS, o acesso ao destino ou a margem de tempo de espera. A validação na nuvem deve confirmar a função exata implementada, as variáveis de ambiente, a arquitetura do artefacto, a chave de saída, o código de estado, a duração e a contagem de itens.

Antes da produção, adicione uma política de tamanho máximo de resposta e rejeite os tipos de conteúdo que não pretende analisar. Isto protege a memória e evita gastar a invocação restante num download de grandes dimensões. Guarde amostras de HTML bruto apenas quando necessário para fins de diagnóstico, com controlos de retenção e acesso.

Empacote o Scrapy ou o Playwright num contentor Lambda

A utilização de um contentor justifica-se quando o gráfico de dependências, as bibliotecas nativas ou o tempo de execução do navegador já não cabem confortavelmente num ficheiro ZIP. Não se trata de uma forma de contornar o modelo de tempo de execução do Lambda. O processo continua a ter uma invocação limitada, armazenamento local efémero e ambientes de execução descartáveis.

Para um trabalhador Scrapy no AWS Lambda, isole cada execução do spider num subprocesso. O reactor do Twisted não foi concebido para ser parado e reiniciado repetidamente no mesmo processo Python, o que pode causar surpresas nas invocações «quentes» do Lambda. Um subprocesso acrescenta sobrecarga de arranque, mas proporciona a cada rastreio um reactor limpo e um limite de tempo de espera bem definido.

Uma imagem mínima orientada para o Lambda pode ter o seguinte aspeto:

FROM public.ecr.aws/lambda/python:3.12

COPY requirements.txt ${LAMBDA_TASK_ROOT}/
RUN pip install --no-cache-dir -r requirements.txt

COPY lambda_function.py spider.py ${LAMBDA_TASK_ROOT}/
CMD ["lambda_function.handler"]

Fixa e hash as dependências na compilação implementada. A imagem base e o tempo de execução aqui apresentados devem ser verificados em relação às imagens base Lambda suportadas no momento da implementação.

Um handler compacto pode executar um spider existente, recolher JSON em /tmpe carregá-lo:

import json
import os
import subprocess
import uuid

import boto3

S3 = boto3.client("s3")
BUCKET = os.environ["OUTPUT_BUCKET"]

def handler(event, context):
    url = event["url"]
    request_id = event.get("request_id") or str(uuid.uuid4())
    output = f"/tmp/{request_id}.json"

    subprocess.run(
        [
            "scrapy", "runspider", "spider.py",
            "-a", f"start_url={url}",
            "-O", output,
        ],
        check=True,
        timeout=720,
    )

    key = f"scrapes/{request_id}.json"
    S3.upload_file(output, BUCKET, key)
    with open(output, encoding="utf-8") as handle:
        count = len(json.load(handle))

    return {
        "ok": True,
        "request_id": request_id,
        "url": url,
        "count": count,
        "s3_uri": f"s3://{BUCKET}/{key}",
    }

O spider deve aceitar start_url, utilizar tempos de espera explícitos para o download, limitar a paginação e produzir registos que correspondam ao esquema de saída em Python e Java. O Scrapy também pode escrever um feed diretamente no S3, mas um upload explícito torna o contrato de saída e os limites de erro mais fáceis de identificar.

A empacotamento do Playwright para o AWS Lambda segue o mesmo princípio de imagem, mas é mais pesado. O Chromium, os tipos de letra, as bibliotecas partilhadas e as caches do navegador têm de corresponder à arquitetura da imagem. Preveja mais memória e tempo de arranque a frio, grave os downloads apenas em /tmp, fechar os contextos em finally, e nunca presuma que um perfil de navegador sobrevive à invocação. Uma configuração Scrapy-Playwright só é adequada quando a renderização do navegador for necessária para os dados.

Compile, envie para o ECR e implemente a imagem com o SAM

Crie uma arquitetura que corresponda à função. O exemplo x86 a seguir utiliza tags versionadas; substitua por uma imagem base e uma região atualmente suportadas.

ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text)
REGION=us-east-1
REPO=lambda-scraper
TAG=2026-03-01

aws ecr create-repository --repository-name "$REPO" 2>/dev/null || true
aws ecr get-login-password --region "$REGION" |
  docker login --username AWS --password-stdin \
  "$ACCOUNT_ID.dkr.ecr.$REGION.amazonaws.com"

docker buildx build \
  --platform linux/amd64 \
  -t "$REPO:$TAG" \
  --load container/

docker tag "$REPO:$TAG" \
  "$ACCOUNT_ID.dkr.ecr.$REGION.amazonaws.com/$REPO:$TAG"
docker push "$ACCOUNT_ID.dkr.ecr.$REGION.amazonaws.com/$REPO:$TAG"

Passe o URI da imagem com versão ao SAM, em vez de implementar uma latest :

Parameters:
  ScraperImageUri:
    Type: String

Resources:
  ContainerScraper:
    Type: AWS::Serverless::Function
    Properties:
      PackageType: Image
      ImageUri: !Ref ScraperImageUri
      Architectures: [x86_64]
      MemorySize: 2048
      EphemeralStorage:
        Size: 2048
      Timeout: 840

Implemente com a tag exata:

sam deploy \
  --parameter-overrides \
  ScraperImageUri="$ACCOUNT_ID.dkr.ecr.$REGION.amazonaws.com/$REPO:$TAG"

Para uma reprodutibilidade mais robusta, registe o resumo da imagem produzido pelo ECR nos metadados de implementação. Ative os controlos de análise e retenção do repositório apenas após verificar as opções atuais do ECR e a política da sua organização. Limpe imagens antigas sem referências, camadas locais, pilhas de teste e objetos de teste do S3 para que as experiências com contentores não se tornem um custo permanente.

Teste a imagem localmente com o ponto de extremidade de tempo de execução do Lambda ou sam local invoke, em seguida, realize um teste de fumaça na nuvem. A arquitetura local do Docker, a arquitetura do Lambda, as permissões do sistema de ficheiros e as bibliotecas do sistema do navegador são fontes comuns de falhas do tipo «funciona na minha máquina».

Torne as compilações de contentores determinísticas, registando o resumo da imagem base, o bloqueio de dependências, a arquitetura de destino e o resumo da imagem gerada na CI. Recompile regularmente para atualizações de segurança, mas promova um resumo testado entre ambientes, em vez de recompilar bytes diferentes para desenvolvimento e produção.

Compile o scraper Java 21 com HttpClient e Jsoup

O scraper web Java para o AWS Lambda deve utilizar o mesmo contrato de eventos e resultados que o Python. HttpClient O `HttpClient` lida com pedidos HTTP delimitados, o `Jsoup` analisa o HTML, o `Jackson` normaliza os corpos de eventos JSON e o SDK da AWS grava o resultado duradouro.

package example;

import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.RequestHandler;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import software.amazon.awssdk.core.sync.RequestBody;
import software.amazon.awssdk.services.s3.S3Client;
import software.amazon.awssdk.services.s3.model.PutObjectRequest;

import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;
import java.time.Instant;
import java.util.*;

public final class Handler
    implements RequestHandler<Map<String, Object>, Map<String, Object>> {

  private static final ObjectMapper JSON = new ObjectMapper();
  private static final HttpClient HTTP = HttpClient.newBuilder()
      .connectTimeout(Duration.ofSeconds(5))
      .followRedirects(HttpClient.Redirect.NORMAL)
      .build();
  private static final S3Client S3 = S3Client.create();
  private static final String BUCKET = System.getenv("OUTPUT_BUCKET");

  @Override
  public Map<String, Object> handleRequest(
      Map<String, Object> event, Context context) {
    try {
      Map<String, Object> input = normalize(event);
      String url = Objects.toString(input.get("url"), "").trim();
      URI uri = URI.create(url);
      if (!Set.of("http", "https").contains(uri.getScheme())) {
        throw new IllegalArgumentException("url must use HTTP or HTTPS");
      }

      int limit = Math.max(1, Math.min(
          Integer.parseInt(Objects.toString(input.getOrDefault("limit", 20))), 100));
      String requestId = Objects.toString(
          input.getOrDefault("request_id", context.getAwsRequestId()));

      HttpRequest request = HttpRequest.newBuilder(uri)
          .timeout(Duration.ofSeconds(35))
          .header("User-Agent", "catalog-monitor/1.0 (+ops@example.com)")
          .GET()
          .build();

      HttpResponse<String> response = HTTP.send(
          request, HttpResponse.BodyHandlers.ofString());
      if (response.statusCode() < 200 || response.statusCode() >= 300) {
        return error("request", requestId, response.statusCode());
      }

      Document doc = Jsoup.parse(response.body(), url);
      List<Map<String, String>> items = new ArrayList<>();
      for (Element card : doc.select(".product")) {
        if (items.size() >= limit) break;
        Element link = card.selectFirst("a");
        Map<String, String> item = new LinkedHashMap<>();
        item.put("title", text(card, ".title"));
        item.put("price", text(card, ".price"));
        item.put("availability", text(card, ".availability"));
        item.put("url", link == null ? null : link.absUrl("href"));
        items.add(item);
      }

      Map<String, Object> result = new LinkedHashMap<>();
      result.put("ok", true);
      result.put("request_id", requestId);
      result.put("url", url);
      result.put("count", items.size());
      result.put("items", items);
      result.put("fetched_at", Instant.now().toString());

      String key = "scrapes/" + requestId + ".json";
      S3.putObject(
          PutObjectRequest.builder().bucket(BUCKET).key(key)
              .contentType("application/json").build(),
          RequestBody.fromString(JSON.writeValueAsString(result)));
      result.put("s3_uri", "s3://" + BUCKET + "/" + key);
      return result;

    } catch (InterruptedException ex) {
      Thread.currentThread().interrupt();
      return error("interrupted", context.getAwsRequestId(), null);
    } catch (Exception ex) {
      return error("internal", context.getAwsRequestId(), null);
    }
  }

  private static String text(Element root, String selector) {
    Element node = root.selectFirst(selector);
    return node == null ? null : node.text();
  }

  private static Map<String, Object> normalize(Map<String, Object> event)
      throws Exception {
    Object body = event.get("body");
    if (body instanceof String text && !text.isBlank()) {
      return JSON.readValue(text, new TypeReference<>() {});
    }
    if (body instanceof Map<?, ?> map) return (Map<String, Object>) map;
    Object detail = event.get("detail");
    if (detail instanceof Map<?, ?> map) return (Map<String, Object>) map;
    return event;
  }

  private static Map<String, Object> error(
      String type, String requestId, Integer status) {
    Map<String, Object> details = new LinkedHashMap<>();
    details.put("type", type);
    if (status != null) details.put("status", status);
    return Map.of("ok", false, "request_id", requestId, "error", details);
  }
}

Reutilize os clientes estáticos durante invocações «a quente», mas não armazene o estado do rastreio, que é crítico para a correção, em campos estáticos. Aplicam-se as mesmas precauções que em Python: limpe os registos, trate as análises inesperadas com zero itens como falhas e mantenha o tempo limite HTTP abaixo do tempo limite da função. Uma referência à análise de HTML em Java com o Jsoup é útil quando os seletores ou o comportamento de links absolutos necessitam de um tratamento mais aprofundado.

Empacote com o Maven e reduza o tempo de arranque com o SnapStart

O projeto Maven necessita das interfaces principais do Lambda, do Jsoup, do Jackson, do SDK do S3 e de uma etapa de compilação que produza um único JAR de implementação. Fixe as versões após verificar as versões atuais e a sua política de dependências.

<dependencies>
  <dependency>
    <groupId>com.amazonaws</groupId>
    <artifactId>aws-lambda-java-core</artifactId>
    <version>${lambda.core.version}</version>
  </dependency>
  <dependency>
    <groupId>org.jsoup</groupId>
    <artifactId>jsoup</artifactId>
    <version>${jsoup.version}</version>
  </dependency>
  <dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>${jackson.version}</version>
  </dependency>
  <dependency>
    <groupId>software.amazon.awssdk</groupId>
    <artifactId>s3</artifactId>
    <version>${aws.sdk.version}</version>
  </dependency>
</dependencies>

Configure maven-shade-plugin durante package, e, em seguida, indique ao SAM o JAR compilado. A configuração esperada do SnapStart utiliza uma versão publicada ou um alias, em vez de $LATEST:

JavaScraper:
  Type: AWS::Serverless::Function
  Properties:
    CodeUri: java/target/scraper.jar
    Handler: example.Handler::handleRequest
    Runtime: java21
    MemorySize: 1024
    Timeout: 60
    AutoPublishAlias: live
    SnapStart:
      ApplyOn: PublishedVersions

Compile e execute com mvn clean package, sam build, sam local invoke JavaScraper -e events/scrape.json, e sam deploy. No momento da redação deste artigo, verifique se o Java 21 e o SnapStart são suportados na região de implementação e confirme quaisquer restrições relativas à inicialização, ligações de rede, entropia e credenciais em cache após a restauração do instantâneo.

Lide com JavaScript, proxies e defesas anti-bot de forma deliberada

O Web Scraping com o AWS Lambda não altera a forma como um alvo apresenta o conteúdo ou avalia o tráfego. Comece com o caminho de pedido autorizado mais simples, observe a falha e escale apenas por um motivo diagnosticado.

  1. HTTP direto: utilize-o quando a resposta contiver os dados. Adicione um agente de utilizador válido, tentativas de repetição limitadas para falhas transitórias, ritmo de acesso sensato e testes de analisador.
  2. Pedido de dados subjacentes: se uma página carregar dados públicos a partir de um ponto de extremidade JSON, utilize esse ponto de extremidade apenas quando os seus termos e autorização o permitirem. Não presuma que um ponto de extremidade não documentado seja estável.
  3. Navegador sem interface gráfica: Utilize o Playwright quando o fluxo de trabalho necessitar efetivamente da execução de JavaScript, de cliques, de deslocamento ou do envio de formulários. A renderização do navegador não garante o acesso.
  4. Proxy explícito: utilize um proxy quando a localização geográfica, a saída estável ou a rotação de IP forem requisitos legítimos. A reputação do proxy, a afinidade de sessão e a política de destino continuam a ser importantes.
  5. Serviço de scraping gerido: Subcontrate a recuperação de dados quando as operações relacionadas com o navegador, o proxy, o CAPTCHA e as tentativas de repetição custarem mais a manter do que a própria lógica de extração.

Os intervalos de endereços dos fornecedores de serviços na nuvem podem ser reconhecidos ou bloqueados por alguns sites. Isso depende do contexto, e a mudança para um proxy ou navegador não é garantia de sucesso. Os cabeçalhos e os plug-ins de camuflagem também podem criar uma falsa sensação de fiabilidade se o verdadeiro problema for a taxa de pedidos, a autorização, o estado da conta ou alterações na marcação.

Uma política prática de web scraping anti-bot classifica os resultados separadamente: falha de transporte, tempo limite esgotado, bloqueio HTTP, página de desafio, necessidade de início de sessão, estado de renderização vazio e incompatibilidade do analisador. Cada categoria tem uma solução diferente. Repetir todas as tentativas de forma idêntica desperdiça dinheiro e pode aumentar a pressão sobre o alvo.

Compare proxies explícitos com uma API de scraping gerida

Um proxy explícito mantém a construção e a análise da solicitação no seu código. Carregue as suas credenciais a partir de um armazenamento secreto, e não a partir do evento ou do repositório:

import boto3
import os
import requests

ssm = boto3.client("ssm")
proxy_url = ssm.get_parameter(
    Name=os.environ["PROXY_PARAMETER"],
    WithDecryption=True,
)["Parameter"]["Value"]

response = requests.get(
    event["url"],
    proxies={"http": proxy_url, "https": proxy_url},
    timeout=(5, 35),
)
response.raise_for_status()

Uma API de scraping gerida pode transferir a recuperação de HTML bruto, a rotação de proxies, o tratamento de CAPTCHA ou a renderização do navegador para fora do Lambda, dependendo das capacidades documentadas do fornecedor. Mantenha a integração genérica até verificar a autenticação e os parâmetros atuais:

token = ssm.get_parameter(
    Name=os.environ["API_TOKEN_PARAMETER"],
    WithDecryption=True,
)["Parameter"]["Value"]

response = requests.get(
    os.environ["SCRAPING_API_URL"],
    params={"url": event["url"]},
    headers={"Authorization": f"Bearer {token}"},
    timeout=(5, 50),
)
response.raise_for_status()

Opção

Você controla

Você mantém

Estrutura de custos

Pedidos diretos

HTTP e análise

Repetidas tentativas, regulação do ritmo, percurso IP

Lambda e redes

Proxies explícitos

Seleção de proxy e sessões

Rotação, falhas, credenciais

Tráfego de proxy e recursos de computação

Conténcer do navegador

Interação completa

Imagem e estabilidade do navegador

Maior capacidade de memória e duração

API gerida

Opções de pedido e análise

Integração com fornecedores e solução alternativa

Por pedido, crédito ou resultado

Um guia sobre a utilização de proxies com pedidos em Python é um complemento natural na implementação de autenticação, rotação e tratamento de erros. Antes de optar por uma via gerida, verifique a latência, o formato da resposta, o tamanho máximo do corpo da mensagem, a unidade de faturação, o encaminhamento regional, o tratamento de dados e o comportamento em caso de falha nos pedidos.

Dimensionar tarefas com múltiplas URLs em segurança com o SQS

A extração baseada em filas coloca uma unidade de trabalho independente em cada mensagem SQS, normalmente um URL, juntamente com um ID de pedido, a versão do analisador e metadados opcionais de rastreamento. O Lambda recebe um pequeno lote, processa cada mensagem e grava cada resultado separadamente. Isto permite que os URLs bem-sucedidos permaneçam completos, enquanto as falhas são repetidas.

Para o Web Scraping com o AWS Lambda, a granularidade das mensagens é uma decisão de controlo de taxa. Uma URL por mensagem proporciona o comportamento mais limpo em termos de repetição de tentativas e idempotência. Um pequeno grupo de URLs intimamente relacionadas pode reduzir a sobrecarga da fila, mas uma única URL lenta atrasa toda a mensagem.

Mantenha as mensagens pequenas. Armazene grandes listas de sementes ou artefactos de sessão no S3 e coloque apenas a referência ao objeto no SQS. Inclua informação suficiente para reproduzir o pedido, mas não inclua palavras-passe de proxy, cookies ou tokens de API.

Alinhe os tempos de espera entre as camadas:

  • O tempo de espera HTTP deve ser inferior ao tempo de espera da função.
  • O tempo limite da função deve deixar tempo para a saída e os diagnósticos.
  • O tempo limite de visibilidade do SQS deve exceder o tempo que um lote pode permanecer em curso, incluindo uma margem adequada para novas tentativas.
  • A retenção de mensagens e a retenção de mensagens não entregues devem ser suficientemente longas para que os operadores possam investigar.
  • A concorrência reservada e a concorrência da fonte de eventos devem refletir uma taxa de pedidos adequada ao destino, e não apenas a capacidade da conta.

As definições da AWS e os valores mínimos impostos podem sofrer alterações; por isso, verifique o comportamento atual da integração na documentação oficial do Lambda com o SQS.

Implemente novas tentativas, falhas parciais de lotes, DLQs, idempotência e limites de concorrência

Ative respostas parciais de lotes para que o Lambda devolva apenas os IDs das mensagens com falha. Sem este comportamento, um registo incorreto pode fazer com que todos os registos do lote reapareçam.

import json
import logging

log = logging.getLogger()
log.setLevel("INFO")

def sqs_handler(event, context):
    failures = []

    for record in event.get("Records", []):
        message_id = record["messageId"]
        try:
            job = json.loads(record["body"])
            scrape_one(job)  # Writes deterministic S3 key
        except Exception as exc:
            log.exception("scrape_failed", extra={"message_id": message_id})
            failures.append({"itemIdentifier": message_id})

    return {"batchItemFailures": failures}

No SAM, declare o modo de resposta na fonte do evento:

Events:
  UrlQueue:
    Type: SQS
    Properties:
      Queue: !GetAtt ScrapeQueue.Arn
      BatchSize: 5
      FunctionResponseTypes:
        - ReportBatchItemFailures

Torne scrape_one idempotente. Uma chave determinística, como scrapes/{job_id}.json, uma gravação condicional no DynamoDB ou uma verificação de resultado existente, evita que mensagens duplicadas produzam efeitos duplicados a jusante.

Associe uma fila de mensagens perdidas através da política de reenvio do SQS e escolha o seu limiar de receção com base no comportamento de repetição que efetivamente pretende. Envie as mensagens esgotadas para lá, para inspeção e reprodução seletiva. Limite a concorrência tanto na função como na fonte do evento, sempre que possível. Esses controlos protegem o site, reservam capacidade para outras funções e impedem que um atraso se transforme num pico de tráfego acidental.

A contrapressão começa no produtor. Se a idade da fila aumentar, pause ou abrande a descoberta de URLs em vez de permitir um atraso ilimitado. Acompanhe cada destino ou anfitrião separadamente quando estes exigirem diferentes políticas de simultaneidade, ritmo, credenciais ou repetição de tentativas.

Adicione observabilidade e diagnóstico de falhas

Um scraper do AWS Lambda deve emitir um resumo estruturado por tentativa. Utilize campos JSON que possam ser consultados sem necessidade de analisar texto:

{
  "event": "scrape_complete",
  "request_id": "job-2026-03-001",
  "host": "example.com",
  "status": 200,
  "duration_ms": 842,
  "retry_count": 0,
  "item_count": 20,
  "output_key": "scrapes/job-2026-03-001.json"
}

Normalize o anfitrião e omita as cadeias de consulta, a menos que se saiba que são seguras. Nunca registe cabeçalhos de autorização, cookies, URLs de proxy com credenciais, corpos HTML completos ou valores secretos.

Acompanhe, pelo menos, estas classes de métricas:

  • Trabalho: tentativas, sucessos, itens extraídos, bytes armazenados.
  • Integridade da solicitação: latência, tempos de espera, erros de ligação, famílias de estados HTTP.
  • Sinais de bloqueio: deteções de páginas de desafio, recusas de acesso, redirecionamentos de início de sessão.
  • Integridade do analisador: resultados com zero itens, campos obrigatórios em falta, falhas no seletor.
  • Integridade da AWS: erros, limitações de tráfego, duração, memória, idade do SQS e profundidade da DLQ.

Alarme em taxas e tendências sustentadas, não em cada falha individual. Um alarme do analisador deve ser distinto de um alarme de bloqueio, pois o manual de procedimentos e o responsável podem ser diferentes. Defina a retenção de registos de forma deliberada, anexe IDs de correlação às mensagens da fila e às chaves S3 e utilize o rastreio sempre que este separe significativamente a latência alvo das chamadas aos serviços da AWS.

O CloudWatch recebe registos Lambda padrão no grupo de registos da função. O X-Ray pode ajudar a visualizar a latência a jusante, mas o rastreio de cada pedido de elevado volume pode aumentar os custos e o ruído. Faça amostragens intencionais e preserve exemplos de falhas suficientes, com o conteúdo sensível removido, para reproduzir incidentes.

Crie painéis de controlo em torno das decisões: se deve abrandar o tráfego, reverter um analisador, rodar credenciais, aumentar a memória ou repetir falhas. Um gráfico que não consiga alterar a próxima ação de um operador é provavelmente ruído.

Teste e implemente alterações através de CI/CD

Trate os analisadores como código determinístico e as redes como limites substituíveis. Armazene exemplos HTML representativos para páginas normais, páginas vazias, marcação alterada, páginas de desafio e respostas malformadas. Os testes unitários devem chamar diretamente as funções de análise e verificar os campos obrigatórios, os URLs absolutos e o comportamento em caso de falha.

Simule respostas HTTP, gravações no S3, leituras no SSM e tempos de espera nos testes de manipuladores. Adicione testes de contrato que executem o mesmo url, limite request_id evento em Python e Java e compare o conjunto de resultados, e não os detalhes internos específicos da linguagem.

Um pipeline prático executa:

  1. Formatter, linter, verificações de tipo ou compilação e testes unitários.
  2. sam validate --lint além de verificações de políticas do CloudFormation.
  3. Verificações de dependências e vulnerabilidades dos contentores.
  4. sam build ou a compilação de uma imagem específica da arquitetura.
  5. Execução local de testes de fumaça com um servidor de fixtures.
  6. Implantação numa pilha que não seja de produção e num ambiente «canary» controlado.
  7. Promoção gradual para produção com um caminho de reversão imediata.

Não torne a CI dependente de um site público não controlado para cada commit. Utilize um servidor de teste local para garantir a repetibilidade e agende testes de integração separados para verificar o comportamento pretendido. Verifique se as funções de implementação não podem alargar a função do scraper, se os segredos nunca aparecem nos registos de compilação e se as imagens de contentores antigas e os buckets de teste são eliminados.

Calcule o custo total, não apenas a computação do Lambda

O custo do scraping do AWS Lambda começa com os pedidos e a duração, mas isso é apenas a linha de computação. Calcule a utilização antes de aplicar um preço regional:

GB-seconds = invocations × average duration in seconds × configured memory in GB
request units = total invocations, including retries

Uma função diária que utiliza 256 MB durante 3 segundos consome cerca de 22,5 GB-segundos ao longo de 30 dias. Com um milhão de páginas, um analisador estático que utiliza 256 MB durante 2 segundos consome 500 000 GB-segundos. Um «browser worker» que utiliza 2 GB durante 10 segundos consome 20 000 000 GB-segundos. Estes valores de utilização são mais comparáveis do que uma estimativa em dólares, uma vez que as tarifas, os descontos de arquitetura, a elegibilidade para o nível gratuito e as regiões podem variar.

O material de referência fornecido cita uma cota mensal gratuita aproximada de um milhão de pedidos e 400 000 GB-segundos, estimando depois cerca de 1,33 dólares para o exemplo estático e 261,33 dólares para o exemplo do navegador, com base nas suas premissas. Considere esses valores em dólares como referências de planeamento não verificadas, e não como cotações para 2026. Recalcule-os a partir da página oficial de preços do AWS Lambda para a região de implementação.

Adicione todas as linhas acessórias:

Área de custo

Fator determinante típico

S3

Gravações de objetos, armazenamento, leituras, ciclo de vida

CloudWatch

Ingestão de registos, retenção, métricas, alarmes

ECR

Armazenamento de imagens, digitalização, transferência entre regiões

Redes

Transferência de dados e eventual processamento por gateway NAT

SQS ou Step Functions

Pedidos, transições, novas tentativas

Execução no navegador

Mais memória, duração e tamanho da imagem

Proxy ou API gerida

Tráfego, pedidos, créditos ou resultados bem-sucedidos

O NAT pode sobrecarregar uma carga de trabalho pequena se o Lambda for colocado numa VPC apenas para garantir uma saída estável. Simule também novas tentativas e respostas bloqueadas. Estas consomem recursos de computação e tráfego de terceiros, mesmo quando não produzem dados.

Lista de verificação para o lançamento em produção: segurança, conformidade e rastreamento respeitoso

Antes de lançar o «Web Scraping com AWS Lambda», analise o sistema tanto como uma carga de trabalho da AWS como um coletor de dados automatizado.

  • IAM: Atribua a cada função apenas o prefixo S3, a fila, o espaço de nomes de métricas e o ARN secreto de que necessita. Separe as permissões de implementação das permissões de tempo de execução.
  • Segredos: Armazene credenciais de proxy, API e de início de sessão no SSM Parameter Store ou no Secrets Manager. Faça a rotação destas credenciais e evite que valores descodificados entrem nos registos.
  • Criptografia: utilize criptografia para o S3, filas, registos e configurações sensíveis, de acordo com a sua classificação de dados e política organizacional.
  • Controlos de entrada: Valide esquemas e anfitriões. Se os chamadores fornecerem URLs, considere uma lista de permissões e proteções contra pedidos de metadados, endereços privados ou endereços «link-local».
  • Controlos de tráfego: Defina a simultaneidade reservada, limites de fila, tempos limite de pedidos, limites de tentativas e um «kill switch» global. Um sinalizador de configuração que bloqueia novos pedidos é mais rápido do que uma implementação de código de emergência.
  • Minimização de dados: Recolha apenas os campos necessários para a finalidade declarada, defina o período de retenção e restrinja o acesso a HTML bruto que possa conter dados pessoais ou sensíveis.
  • Análise do alvo: Verifique o ficheiro robots.txt, os termos e condições, a autorização, as orientações de frequência, as regras da conta, as obrigações de privacidade e a legislação aplicável antes da recolha. Estes sinais têm significados jurídicos diferentes, pelo que uma estrutura de conformidade jurídica para a extração de dados da Web e aconselhamento jurídico qualificado são adequados para riscos significativos.
  • Responsabilidade operacional: Atribua alertas, um procedimento de repetição do DLQ, um manual de alterações do analisador e um contacto para reclamações do alvo.
  • Segurança da implementação: Comece com baixa simultaneidade, observe o estado e as métricas do analisador e, em seguida, aumente a taxa de processamento de forma deliberada.

Estas são orientações de engenharia, não aconselhamento jurídico. A admissibilidade da recolha depende dos dados, da jurisdição, do método de acesso, da relação contratual e da utilização pretendida. Não considere a acessibilidade técnica como autorização.

O que implementar primeiro

Comece com o sistema útil mais pequeno: HTTP direto em Python, empacotado pelo SAM, acionado por um evento de teste e a gravar JSON no S3. Essa linha de base comprova as permissões, a rede, a análise, o armazenamento e a observabilidade com o mínimo de componentes móveis.

Adicione o EventBridge para uma pequena programação. Adicione o SQS quando as URLs se tornarem itens de trabalho independentes. Mude para um contentor apenas para dependências que o justifiquem e utilize um navegador apenas quando os dados exigirem renderização ou interação. Avance para proxies ou recuperação gerida apenas após avaliar o modo de falha. Esta sequência mantém o Web Scraping com o AWS Lambda compreensível à medida que cresce.

Pontos-chave

  • Utilize o Lambda quando cada processamento for curto, delimitado e puder ser repetido de forma independente; opte por recursos de computação de maior duração para sessões persistentes ou rastreamentos com duração de várias horas.
  • Mantenha um único contrato de eventos e resultados em Python, Java, agendamentos, filas e testes locais.
  • Armazene os resultados no S3, as credenciais num repositório de segredos gerido e os dados operacionais em registos e métricas estruturados.
  • Adicione falhas parciais do SQS, DLQs, idempotência e limites de concorrência antes de aumentar o volume de URLs.
  • Calcule os custos de computação, armazenamento, registos, imagens, rede, novas tentativas, proxies e serviços geridos como rubricas de custo separadas.

Perguntas frequentes

Um scraper do AWS Lambda precisa de ser executado dentro de uma VPC?

Não. Uma função Lambda pode aceder a sites públicos sem estar ligada à sua VPC. Utilize uma VPC apenas quando for necessário aceder a recursos privados, utilizar inspeção de rede controlada ou enviar tráfego através de um gateway NAT com um IP público fixo. A ligação à VPC implica uma configuração de rede adicional e pode acarretar custos de NAT, pelo que deve responder a um requisito específico.

Como é que o Lambda pode utilizar um IP de saída estável quando um proxy ou destino requer inclusão numa lista de permissões?

Encaminhe as funções Lambda ligadas a uma VPC através de sub-redes privadas e de um gateway NAT associado a um IP elástico, ou utilize um ponto de extremidade de proxy com um endereço estável. Configure uma rede redundante se a disponibilidade for importante. Um gateway NAT acrescenta custos por hora e de processamento de dados, enquanto um proxy acrescenta as suas próprias preocupações em termos de tráfego e autenticação.

Os cookies e as sessões de início de sessão podem persistir entre invocações do Lambda?

Não de forma fiável na memória. Um ambiente de execução «quente» pode ser reutilizado, mas a AWS não garante que a próxima invocação aceda ao mesmo ambiente. Mantenha um «cookie jar» encriptado ou o estado da sessão num armazenamento durável adequado, aplique controlos de expiração e de acesso e conceba o sistema para atualizações simultâneas. Confirme se o início de sessão automatizado e a utilização de credenciais estão autorizados.

Como devem ser armazenados os resultados de scraping que excedam o limite de carga útil síncrona do Lambda?

Grave o corpo no S3 e devolva uma pequena chave de objeto, um URI, uma soma de verificação e um envelope de metadados. Para o processamento a jusante, publique essa referência através do SQS, do EventBridge ou de um registo na base de dados, em vez de passar o resultado completo. Utilize compressão e uploads multiparte quando apropriado e restrinja qualquer URL pré-assinada em termos de tempo de vida e permissões.

Como deve a paginação ser dividida quando um rastreio pode exceder o tempo de execução máximo do Lambda?

Representa cada número de página, cursor ou token de continuação como uma tarefa separada na fila. Armazene o trabalho da página seguinte descoberta no SQS e persista um ID de rastreio juntamente com um ponto de verificação, para que as tentativas de repetição permaneçam idempotentes. Limite a descoberta de páginas, detete cursores repetidos e utilize o Step Functions apenas quando o estado explícito do fluxo de trabalho for mais valioso do que um simples padrão produtor-e-fila.

Conclusão

Um scraper sem servidor em produção é, acima de tudo, um exercício de limites. Mantenha cada invocação curta, torne o evento autónomo, grave a saída de forma duradoura e parta do princípio de que qualquer mensagem pode ser entregue mais do que uma vez. HTTP direto, juntamente com um analisador, é a opção padrão adequada para páginas estáticas, enquanto o SQS oferece o caminho mais simples para uma escalabilidade controlada de múltiplas URLs.

Os contentores são úteis para o Scrapy, pacotes nativos e dependências do navegador, mas não eliminam os limites de tempo de execução e de estado do Lambda. O Java 21 pode seguir o mesmo contrato que o Python, com HttpClient, Jsoup, S3 e uma configuração SnapStart verificada. No caso de páginas bloqueadas ou com muito JavaScript, diagnostique a falha real antes de adicionar um proxy, navegador ou serviço gerido, e nunca considere essas opções como acesso garantido.

Por fim, calcule o custo total do sistema e verifique todos os valores da AWS sensíveis ao tempo na sua região. Se o bloqueio de pedidos se tornar o principal fardo de engenharia, a WebScrapingAPI pode funcionar como uma camada gerida de obtenção de HTML bruto que lida com a rotação de proxies, bloqueios e CAPTCHAs, enquanto a sua função Lambda mantém a responsabilidade pela análise, validação e armazenamento. Recorra a essa escalada apenas quando esta reduzir a complexidade operacional total.

Sobre o autor

Suciu Dan, Co-fundador @ WebScrapingAPI

Suciu Dan

Co-fundador

Suciu Dan é cofundador da WebScrapingAPI e escreve guias práticos, voltados para programadores, sobre web scraping em Python, web scraping em Ruby e infraestruturas de proxy.

Como utilizar o GoSpider: rastrear, limpar URLs e extrair dados
Guias

Como utilizar o GoSpider: rastrear, limpar URLs e extrair dados

Resumo: O GoSpider é um rastreador de linha de comandos destinado a descobrir URLs, não um extrator completo de dados estruturados. Este guia sobre como utilizar o GoSpider mostra como realizar um rastreio limitado, gerir resultados de forma organizada, fazer a transferência de dados do Colly para CSV e seguir um percurso de diagnóstico para respostas 403 ou páginas que exijam renderização em JavaScript.

Suciu Dan23 min read
Ler artigo
Como fazer o Scrape Redfin: Guia Python para Dados de Propriedade
Guias

Como fazer o Scrape Redfin: Guia Python para Dados de Propriedade

TL;DR: A Redfin expõe pontos de extremidade de API ocultos que retornam JSON estruturado para listagens de propriedades, tornando possível ignorar totalmente a análise HTML frágil. Este guia orienta-o na construção de um scraper Python que extrai dados de aluguer e venda, pesquisa por localização, monitoriza novas listagens através de sitemaps XML e exporta resultados limpos para CSV ou JSON.

Suciu Dan14 min read
Ler artigo
XPath Web Scraping: Um guia prático com exemplos em Python
Guias

XPath Web Scraping: Um guia prático com exemplos em Python

TL;DR: XPath é uma linguagem de consulta para navegar em árvores HTML/XML por caminho, atributo ou conteúdo de texto. Este guia aborda a sintaxe, os eixos e as funções XPath e, em seguida, mostra scrapers Python funcionais com lxml e Selenium. Você também terá uma folha de dicas consolidada e uma seção de solução de problemas para os erros mais comuns do XPath.

Suciu Dan11 min read
Ler artigo

Comece a construir

Pronto para expandir a sua recolha de dados?

Junte-se a mais de 2.000 empresas que utilizam a WebScrapingAPI para extrair dados da Web à escala empresarial, sem quaisquer custos de infraestrutura.