Voltar ao blogue
Guias
Raluca PenciucLast updated on Apr 27, 202620 min read

Tutorial do Scrapy Playwright: Extrair sites com muito JavaScript em escala

Resumo: O Scrapy-Playwright permite renderizar páginas com muito JavaScript diretamente dentro dos spiders do Scrapy, controlando navegadores Chromium, Firefox ou WebKit reais através do Playwright. Este tutorial orienta-o passo a passo pela instalação, configuração, interações com páginas, interceção de AJAX, medidas anti-detecção e uma estrutura de projeto pronta para produção, para que possa extrair dados de sites dinâmicos sem sair do ecossistema do Scrapy.

O Scrapy é excelente a rastrear HTML estático a alta velocidade, mas no momento em que um site alvo carrega conteúdo através de JavaScript, um pedido padrão do Scrapy apresenta-lhe uma estrutura vazia. Esse é exatamente o problema que o Scrapy Playwright resolve. Trata-se de um gestor de downloads do Scrapy que delega a renderização ao Playwright, a biblioteca de automação de navegadores da Microsoft, para que todas as respostas que o seu spider recebe contenham o DOM totalmente renderizado. Se tem vindo a considerar a integração do Scrapy Playwright nos seus próprios projetos, mas não tinha a certeza de como todas as peças se encaixam, este guia abrange todos os passos: desde pip install até um spider pronto para produção com itens, pipelines e anti-detecção integrados. Ao longo do caminho, irá aprender estratégias de espera, interceção AJAX, tratamento de rolagem infinita, configuração de proxy e os padrões de resolução de problemas que mantêm os rastreamentos longos estáveis.

O que é o Scrapy-Playwright e por que usá-lo?

O Scrapy-Playwright (o pacote PyPI scrapy-playwright) é um gestor de downloads do Scrapy que substitui o backend HTTP padrão por um navegador completo alimentado pelo Playwright. Quando marca um pedido do Scrapy com "playwright": True no seu meta dicionário, o gestor abre uma página no navegador, navega até ao URL, aguarda que o JavaScript termine e, em seguida, devolve o HTML renderizado ao seu parse callback como um Scrapy normal Response.

Por que é que isto importa? Uma parte crescente da web renderiza conteúdo do lado do cliente: painéis React, lojas Vue, páginas bloqueadas por modais de consentimento e sites que carregam dados de produtos através de chamadas de API em segundo plano. O Scrapy padrão obtém apenas o documento HTML inicial, que frequentemente contém <div> e um pacote de JavaScript, mas nenhum dos dados de que realmente precisa. Com a renderização JavaScript do Scrapy Playwright, obtém o mesmo resultado que um navegador real exibiria, sem sair do familiar pipeline de pedidos/respostas do Scrapy.

Quando deve ativar o Playwright numa solicitação? Nem todas as URLs precisam de um navegador completo. Uma regra prática útil:

  • Use uma solicitação padrão do Scrapy quando os dados de que precisa estiverem presentes no HTML bruto ou disponíveis através de um endpoint de API direto que já conheça.
  • Use uma solicitação Playwright quando o conteúdo for injetado após o carregamento da página, quando precisar clicar ou rolar para revelar dados, ou quando a página depender de cookies e redirecionamentos JavaScript que são difíceis de replicar com HTTP simples.

Misturar ambos os modos num único spider é fácil (e recomendado). Paga a sobrecarga do navegador apenas pelas solicitações que realmente precisam dela, o que mantém o rastreamento rápido para as páginas que não precisam.

Scrapy-Playwright vs Scrapy-Splash vs Scrapy-Selenium

A escolha entre backends de renderização do navegador para o Scrapy resume-se à carga de manutenção, à fidelidade do navegador e às ferramentas existentes da sua equipa. Aqui está uma comparação rápida:

Critérios

Scrapy-Playwright

Scrapy-Splash

Scrapy-Selenium

Motor do navegador

Chromium, Firefox ou WebKit

Renderizador personalizado baseado em Qt

Chrome ou Firefox via WebDriver

Suporte assíncrono

Nativo (asyncio)

Requer um servidor Splash separado

Sincronização por padrão; existem wrappers assíncronos

Manutenção

Manutenção ativa, comunidade em crescimento

O desenvolvimento do Splash abrandou

Estável, mas depende do protocolo WebDriver

Fidelidade ao JS

Navegador moderno completo

Bom, mas alguns casos extremos falham

Navegador moderno completo

Facilidade de configuração

pip install + playwright install

Requer contêiner Docker

Gestão de binários do WebDriver

Interações com a página

Rico (click, fill, evaluate)

Scripting Lua limitado

API WebDriver completa

Se estiver a iniciar um novo projeto hoje, o Scrapy Playwright é geralmente a melhor escolha. Oferece suporte assíncrono moderno, métodos de interação com páginas de primeira classe e evita a sobrecarga operacional de executar um serviço de renderização separado. Para uma análise mais aprofundada das vantagens e desvantagens do Scrapy em comparação com o Selenium, o guia de comparação entre o Scrapy e o Selenium aborda o tema em detalhe.

Instalação e configuração do projeto

Para colocar um projeto Scrapy Playwright a funcionar, bastam alguns comandos no terminal. Aqui está o processo passo a passo.

Pré-requisitos: É necessário o Python 3.8 ou posterior e pip. Recomenda-se vivamente a utilização de um ambiente virtual para manter as dependências isoladas.

# Create and activate a virtual environment
python -m venv venv
source venv/bin/activate  # Windows: venv\Scripts\activate

# Install Scrapy and scrapy-playwright
pip install scrapy scrapy-playwright

# Install browser binaries (Chromium is the default)
playwright install chromium

O playwright install chromium comando descarrega uma compilação específica do Chromium que o Playwright gere internamente. Também pode instalar firefox ou webkit se o seu caso de uso exigir um motor diferente.

Em seguida, crie um novo projeto Scrapy:

scrapy startproject myproject
cd myproject
scrapy genspider example example.com

Isto dá-lhe a estrutura de diretórios padrão do Scrapy: settings.py, items.py, pipelines.py, middlewares.py, e uma spiders/ pasta. O único passo específico do Playwright que resta é atualizar settings.py, que abordaremos a seguir.

Uma coisa que vale a pena notar: scrapy-playwright depende da API assíncrona do Playwright, que, por sua vez, requer o asyncioreactor Twisted baseado em Python. O Scrapy suporta isto, mas deve definir explicitamente o reactor antes de o Scrapy tentar usar o seu padrão. Esquecer este passo é o erro de instalação número um que os programadores cometem.

Configurar as definições do Scrapy para o Playwright

Abra o ficheiro settings.py e adicione o seguinte:

# settings.py

DOWNLOAD_HANDLERS = {
    "http": "scrapy_playwright.handler.ScrapyPlaywrightDownloadHandler",
    "https": "scrapy_playwright.handler.ScrapyPlaywrightDownloadHandler",
}

TWISTED_REACTOR = "twisted.internet.asyncioreactor.AsyncioSelectorReactor"

# Optional: choose browser type (chromium, firefox, webkit)
PLAYWRIGHT_BROWSER_TYPE = "chromium"

# Optional: global navigation timeout in milliseconds
PLAYWRIGHT_DEFAULT_NAVIGATION_TIMEOUT = 30000

O DOWNLOAD_HANDLERS dict diz ao Scrapy para encaminhar todas as solicitações HTTP e HTTPS através do manipulador do Playwright. A TWISTED_REACTOR linha muda o ciclo de eventos do Scrapy para asyncio, o que o Playwright requer.

PLAYWRIGHT_DEFAULT_NAVIGATION_TIMEOUT define o tempo máximo (em milissegundos) que o navegador irá esperar pelo carregamento de uma página. O valor padrão é 30 segundos, o que é adequado para a maioria dos sites. Se estiver a fazer scraping de páginas especialmente lentas, aumente esse valor. Se quiser uma falha rápida em URLs inválidas, diminua-o.

Duas outras configurações que vale a pena conhecer:

  • PLAYWRIGHT_LAUNCH_OPTIONS: um dicionário passado diretamente para playwright.chromium.launch(). Use-o para alternar entre o modo headless, caminhos de executáveis ou configuração global de proxy.
  • PLAYWRIGHT_MAX_PAGES_PER_CONTEXT: limita o número de páginas que partilham um único contexto de navegador antes de ser criado um novo contexto. Isto pode ajudar na gestão de memória em rastreios de grande dimensão.

Com estas configurações definidas, cada pedido do Scrapy que inclua "playwright": True no seu meta será renderizada pelo Playwright. As solicitações sem esse sinalizador continuam a passar pelo downloader padrão do Scrapy, para que obtenha o melhor dos dois mundos.

Renderização de páginas com muito JavaScript

Vamos escrever o seu primeiro spider Scrapy Playwright. O objetivo: visitar uma página que carrega o seu conteúdo com JavaScript e extrair dados do DOM totalmente renderizado.

import scrapy

class QuotesSpider(scrapy.Spider):
    name = "quotes"
    start_urls = ["https://quotes.toscrape.com/js/"]

    def start_requests(self):
        for url in self.start_urls:
            yield scrapy.Request(
                url,
                meta={"playwright": True},
                callback=self.parse,
            )

    def parse(self, response):
        for quote in response.css("div.quote"):
            yield {
                "text": quote.css("span.text::text").get(),
                "author": quote.css("small.author::text").get(),
            }

A linha-chave é meta={"playwright": True}. Esse único sinalizador diz ao manipulador de download para abrir uma página no navegador, navegar até ao URL, aguardar o load evento e devolver o HTML renderizado como um TextResponse. Dentro de parse, utiliza os mesmos seletores CSS (ou XPath) que usaria com qualquer spider do Scrapy. Nada muda no que diz respeito à análise.

Execute o spider com scrapy crawl quotes, e deverá ver as citações totalmente extraídas, mesmo que a página dependa de JavaScript para as inserir no DOM. Se tentasse a mesma URL com um pedido Scrapy padrão (sem o sinalizador Playwright), response.css("div.quote") iria devolver uma lista vazia.

Este padrão é a base para tudo o resto neste tutorial do Scrapy Playwright. Todas as técnicas que se seguem assentam no mesmo meta para passar instruções adicionais ao navegador.

Interações com a página: cliques, deslocamento e envio de formulários

A extração de dados no mundo real raramente envolve apenas carregar uma página. Muitas vezes, é necessário clicar em botões, preencher formulários de pesquisa ou percorrer a página para ativar conteúdo carregado dinamicamente. Os métodos de página do Scrapy Playwright tratam de tudo isto através da playwright_page_methods chave no pedido meta.

Um PageMethod é um wrapper em torno de uma ação de página do Playwright. Passa-se uma lista delas e o manipulador executa cada uma por ordem após a navegação inicial.

Clicar num botão:

from scrapy_playwright.page import PageMethod

yield scrapy.Request(
    url,
    meta={
        "playwright": True,
        "playwright_page_methods": [
            PageMethod("click", selector="button#load-more"),
            PageMethod("wait_for_selector", selector="div.new-content"),
        ],
    },
    callback=self.parse,
)

Preencher e enviar um formulário:

yield scrapy.Request(
    url,
    meta={
        "playwright": True,
        "playwright_page_methods": [
            PageMethod("fill", selector="input#search", value="python scrapy"),
            PageMethod("click", selector="button[type=submit]"),
            PageMethod("wait_for_selector", selector="div.results"),
        ],
    },
    callback=self.parse,
)

Deslizar até ao fundo de uma página:

yield scrapy.Request(
    url,
    meta={
        "playwright": True,
        "playwright_page_methods": [
            PageMethod(
                "evaluate",
                "window.scrollTo(0, document.body.scrollHeight)",
            ),
            PageMethod("wait_for_timeout", 2000),
        ],
    },
    callback=self.parse,
)

Repare no padrão: encadeia PageMethod chamadas para simular uma sessão de utilizador real. O manipulador processa-as sequencialmente, por isso a ordem é importante. Inclua sempre uma espera após uma ação que acione novo conteúdo (um clique que dispara uma chamada à API, uma rolagem que carrega mais itens) para dar tempo à página para atualizar-se antes de o Scrapy capturar o HTML final.

Uma dica prática: mantenha a sua playwright_page_methods lista o mais curta possível. Cada chamada de método adiciona latência. Se conseguir o mesmo resultado com menos passos (por exemplo, navegando diretamente para um URL filtrado em vez de preencher um formulário), opte pela abordagem mais simples.

Estratégias de espera para conteúdo dinâmico

Escolher a estratégia de espera certa é fundamental para uma extração fiável de conteúdo dinâmico com o Scrapy Playwright. Se esperar muito pouco, obtém dados incompletos. Se esperar demasiado, o rastreio fica paralisado.

Aqui estão as principais abordagens:

wait_for_selector é a opção mais precisa. Ela pausa a execução até que um seletor CSS específico apareça no DOM.

PageMethod("wait_for_selector", selector="div.product-list")

Utilize esta opção quando souber exatamente qual o elemento que indica que os dados foram carregados. É rápida porque resolve no momento em que o elemento existe, em vez de esperar por um período de tempo arbitrário.

wait_for_load_state aguarda um evento específico do ciclo de vida da página:

  • "load": dispara quando o HTML inicial e todos os recursos (imagens, folhas de estilo) tiverem sido carregados.
  • "domcontentloaded": dispara quando o HTML é analisado, antes de as imagens terminarem de carregar.
  • "networkidle": dispara quando não há ligações de rede há pelo menos 500 ms.
PageMethod("wait_for_load_state", "networkidle")

networkidle é tentador porque capta a maioria das chamadas AJAX, mas pode não ser fiável em páginas com ligações WebSocket persistentes, pings de análise ou rastreadores de anúncios que mantêm a rede ocupada. Também tende a ser mais lento do que wait_for_selector.

wait_for_timeout é um sono profundo, especificado em milissegundos.

PageMethod("wait_for_timeout", 3000)

Esta é a ferramenta mais bruta. Use-a apenas como último recurso, por exemplo, em páginas onde não existe um seletor estável e networkidle é instável. Os hard sleeps desperdiçam tempo em páginas rápidas e ainda assim podem não ser suficientemente longos nas lentas.

Recomendação: use wait_for_selector sempre que possível. Recorra a networkidle para páginas em que não conheça o seletor exato. Reserve wait_for_timeout para páginas genuinamente imprevisíveis e mantenha o valor o mais baixo possível.

Lidar com rolagem infinita e paginação

Muitos sites modernos utilizam padrões de rolagem infinita do Scrapy Playwright ou navegação paginada para dividir o conteúdo em várias visualizações. O tratamento de ambos no Scrapy requer estratégias ligeiramente diferentes.

A rolagem infinita normalmente funciona rolando até ao fundo da página, aguardando que novos itens sejam carregados e repetindo até que não apareçam mais itens. Uma vez que playwright_page_methods é executado uma vez antes de devolver a resposta, é necessário gerir o ciclo de rolagem dentro de uma page.evaluate chamada ou acedendo diretamente ao objeto de página do Playwright.

A abordagem mais simples é utilizar a playwright_page chave meta para obter a página Playwright em bruto e programar o ciclo você mesmo:

async def parse(self, response):
    page = response.meta["playwright_page"]
    previous_height = 0

    while True:
        await page.evaluate("window.scrollTo(0, document.body.scrollHeight)")
        await page.wait_for_timeout(1500)
        current_height = await page.evaluate("document.body.scrollHeight")
        if current_height == previous_height:
            break
        previous_height = current_height

    # Re-read the fully scrolled page content
    content = await page.content()
    await page.close()

    sel = scrapy.Selector(text=content)
    for item in sel.css("div.item"):
        yield {
            "title": item.css("h3::text").get(),
        }

Repare que fechamos a página explicitamente com await page.close(). Isto é essencial para a gestão da memória; caso contrário, as páginas do navegador acumulam-se e o seu processo fica sobrecarregado na memória.

A paginação (clicar em «Seguinte» ou baseada em URL) é mais simples. Se o site usar parâmetros de consulta (?page=2), basta gerar novas solicitações Scrapy com URLs incrementadas. Se depender de um botão «Seguinte», use um PageMethod clique:

def parse(self, response):
    # Extract data from current page
    for product in response.css("div.product"):
        yield {"name": product.css("h2::text").get()}

    # Follow next page if it exists
    next_button = response.css("a.next-page::attr(href)").get()
    if next_button:
        yield response.follow(
            next_button,
            meta={"playwright": True},
            callback=self.parse,
        )

Para sites que utilizam botões «Carregar mais» apenas em JavaScript sem alterar o URL, combine o padrão de clique da secção de interações da página com um wait_for_selector para confirmar que novos itens apareceram antes de extrair os dados.

Interceptação de solicitações AJAX

Por vezes, a fonte de dados mais limpa não é o DOM renderizado, mas a chamada à API em segundo plano que a página faz para o preencher. A interceção AJAX do Scrapy Playwright permite-lhe capturar essas respostas diretamente, proporcionando-lhe frequentemente JSON estruturado sem qualquer análise de HTML.

Para interceptar respostas, precisa de aceder ao objeto de página do Playwright e ao seu response evento:

import json

class AjaxSpider(scrapy.Spider):
    name = "ajax_products"
    captured_data = []

    def start_requests(self):
        yield scrapy.Request(
            "https://example.com/products",
            meta={
                "playwright": True,
                "playwright_include_page": True,
            },
            callback=self.parse,
        )

    async def parse(self, response):
        page = response.meta["playwright_page"]

        async def handle_response(resp):
            if "/api/products" in resp.url:
                body = await resp.json()
                self.captured_data.extend(body.get("items", []))

        page.on("response", handle_response)

        # Trigger the AJAX call (e.g., scroll or click)
        await page.evaluate("window.scrollTo(0, document.body.scrollHeight)")
        await page.wait_for_timeout(3000)
        await page.close()

        for product in self.captured_data:
            yield product

O page.on("response", ...) listener é disparado para cada resposta de rede. Filtra-se por padrão de URL para capturar apenas as chamadas de API que lhe interessam. O corpo da resposta já está analisado (.json() ou .text()), pelo que se salta completamente a percussão do DOM.

Esta técnica é especialmente poderosa para aplicações de página única, nas quais o front-end faz múltiplas solicitações de API paginadas à medida que se percorre a página. Em vez de analisar HTML complexo, obtém dados limpos e estruturados diretamente da fonte.

Executar JavaScript personalizado e capturar imagens de ecrã

Duas funcionalidades leves, mas úteis, do Scrapy Playwright são a execução de JavaScript personalizado e a captura de capturas de ecrã. Servem propósitos diferentes, mas partilham o mesmo mecanismo: acesso direto ao objeto de página do Playwright.

A execução de JavaScript personalizado com page.evaluate permite extrair dados que estão ocultos em variáveis JavaScript ou manipular o estado da página antes de o Scrapy ler o HTML:

yield scrapy.Request(
    url,
    meta={
        "playwright": True,
        "playwright_page_methods": [
            PageMethod(
                "evaluate",
                "document.querySelectorAll('.popup-overlay')"
                ".forEach(el => el.remove())",
            ),
        ],
    },
    callback=self.parse,
)

Isto remove sobreposições de pop-ups antes de o Scrapy analisar a página, o que é útil para sites que apresentam janelas modais na primeira visita.

Tirar uma captura de ecrã do Scrapy Playwright é útil para depurar problemas de renderização. Se o seu spider extrair dados vazios, uma captura de ecrã mostra-lhe exatamente o que o navegador viu:

yield scrapy.Request(
    url,
    meta={
        "playwright": True,
        "playwright_page_methods": [
            PageMethod("screenshot", path="debug.png", full_page=True),
        ],
    },
    callback=self.parse,
)

O full_page=True argumento captura toda a área rolável, não apenas a janela de visualização. Durante o desenvolvimento, pode ativar capturas de ecrã condicionalmente (apenas quando uma chamada de retorno de análise encontrar zero itens, por exemplo) para evitar encher o disco em rastreios de produção.

Interromper pedidos indesejados para rastreamentos mais rápidos

Por predefinição, todas as páginas do navegador carregam imagens, tipos de letra, CSS, scripts de análise e rastreadores de anúncios. Para o scraping, a maioria destes recursos é peso morto. Bloqueá-los pode reduzir drasticamente a utilização da largura de banda e acelerar o carregamento das páginas.

O Scrapy-Playwright suporta a interceção de pedidos através da PLAYWRIGHT_ABORT_REQUEST configuração. Define-se uma função assíncrona que inspeciona cada pedido e devolve True para a abortar:

# settings.py
PLAYWRIGHT_ABORT_REQUEST = "myproject.utils.should_abort"
# myproject/utils.py
from playwright.async_api import Request as PlaywrightRequest

async def should_abort(request: PlaywrightRequest) -> bool:
    blocked_types = {"image", "font", "stylesheet", "media"}
    if request.resource_type in blocked_types:
        return True
    blocked_domains = ["google-analytics.com", "doubleclick.net"]
    if any(domain in request.url for domain in blocked_domains):
        return True
    return False

Bloquear apenas imagens e fontes pode reduzir significativamente o tempo de carregamento da página, especialmente em sites de comércio eletrónico com grande volume de multimédia. Tenha apenas o cuidado de não bloquear ficheiros JavaScript responsáveis pela renderização do conteúdo de que necessita. Se os seus dados desaparecerem após ativar o bloqueio de pedidos, adicione "script" de volta aos tipos permitidos e restrinja o seu filtro a domínios específicos.

Utilização de proxies com o Scrapy-Playwright

Ao fazer scraping em grande escala, proxies rotativos são essenciais para evitar bloqueios de IP. A configuração de proxy do Scrapy Playwright funciona em dois níveis: global e por solicitação.

O proxy global aplica-se a todas as solicitações do Playwright. Defina-o em settings.py:

PLAYWRIGHT_LAUNCH_OPTIONS = {
    "proxy": {
        "server": "http://proxy-server:8080",
        "username": "user",
        "password": "pass",
    },
}

Isto passa a configuração do proxy para a chamada de inicialização do navegador, de modo que todas as páginas abertas por esta instância do navegador sejam encaminhadas através desse proxy.

O proxy por pedido oferece um controlo mais preciso. Use playwright_context_kwargs na solicitação meta para atribuir proxies diferentes a pedidos individuais:

yield scrapy.Request(
    url,
    meta={
        "playwright": True,
        "playwright_context_kwargs": {
            "proxy": {
                "server": "http://different-proxy:9090",
            },
        },
        "playwright_context": "proxy_context_1",
    },
    callback=self.parse,
)

Cada nome playwright_context cria um contexto de navegador separado com o seu próprio proxy, cookies e estado de armazenamento. É assim que se isolam as sessões ao alternar entre um conjunto de proxies.

Para rastreamentos em produção, considere serviços que gerem a rotação de proxies e a resolução de CAPTCHAs por trás de um único ponto de extremidade, para que a lógica do seu spider permaneça limpa. O ponto-chave é que o suporte a proxies do Scrapy-Playwright é flexível o suficiente para se integrar com qualquer estratégia de rotação que escolher.

Melhores práticas de anti-detecção e camuflagem

Os proxies por si só não são suficientes. Os sistemas anti-bot modernos verificam impressões digitais do navegador, strings de user-agent e padrões de comportamento. Aqui estão as camadas anti-detecção que deve considerar para os seus spiders Scrapy Playwright.

Rotação do user-agent: Defina um user-agent realista e rotativo por contexto:

import random

USER_AGENTS = [
    "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 ...",
    "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 ...",
    # Add more real browser UA strings
]

yield scrapy.Request(
    url,
    meta={
        "playwright": True,
        "playwright_context_kwargs": {
            "user_agent": random.choice(USER_AGENTS),
        },
        "playwright_context": f"ctx_{random.randint(1, 100)}",
    },
    callback=self.parse,
)

Redução de impressão digital: O Chromium do Playwright tem sinalizadores WebDriver padrão que os scripts anti-bot detectam. Pode reduzir a sua impressão digital ao:

  • Passar "args": ["--disable-blink-features=AutomationControlled"] em PLAYWRIGHT_LAUNCH_OPTIONS.
  • Usando page.evaluate para eliminar a navigator.webdriver propriedade.
  • Definir um tamanho de janela de visualização realista em vez das dimensões padrão sem interface gráfica.

Atrasos aleatórios: Adicionar variação entre as solicitações evita que o seu tráfego pareça um bot a bombardear o servidor à velocidade da máquina. Use a configuração DOWNLOAD_DELAY em combinação com RANDOMIZE_DOWNLOAD_DELAY:

DOWNLOAD_DELAY = 2
RANDOMIZE_DOWNLOAD_DELAY = True

configuração de contexto Stealth: Combine tudo o que foi mencionado acima numa configuração de contexto reutilizável. Para um guia abrangente sobre como evitar bloqueios, o recurso «Dicas para evitar ser bloqueado ou ter o IP banido ao fazer web scraping» aborda estratégias adicionais que se aplicam para além do Scrapy-Playwright.

Conclusão: trate a anti-detecção como várias camadas, em vez de uma solução única. Os proxies lidam com a reputação do IP. A rotação do user-agent lida com verificações ao nível do cabeçalho. A redução de impressões digitais lida com verificações ao nível do JavaScript. Os atrasos lidam com verificações comportamentais. É necessário que todos eles funcionem em conjunto.

Contextos de navegador, sessões e gestão de recursos

Um contexto de navegador no Playwright é uma sessão de navegador isolada com os seus próprios cookies, armazenamento local e cache. O Scrapy-Playwright utiliza intensivamente os contextos, e compreendê-los é fundamental para gerir recursos em rastreamentos de grande dimensão.

Por predefinição, todos os pedidos do Scrapy Playwright que não especifiquem um playwright_context nome partilha um contexto padrão. Isto significa que os cookies persistem entre pedidos, o que é adequado para sites onde é necessário permanecer com sessão iniciada, mas problemático se pretender sessões limpas por pedido.

Os contextos nomeados permitem-lhe isolar sessões:

yield scrapy.Request(
    url,
    meta={
        "playwright": True,
        "playwright_context": "session_a",
    },
    callback=self.parse,
)

Todas as solicitações marcadas "session_a" partilham cookies e estado. As solicitações marcadas "session_b" obtêm uma sessão completamente separada. Isto é útil para fluxos de trabalho de scraping paralelos em que é necessário simular vários utilizadores independentes.

PLAYWRIGHT_MAX_PAGES_PER_CONTEXT controla quantas páginas podem ser abertas simultaneamente dentro de um único contexto. Quando o limite é atingido, é criado um novo contexto. Ajustar esta configuração ajuda a evitar o sobrecarregamento da memória:

PLAYWRIGHT_MAX_PAGES_PER_CONTEXT = 4

Dicas de gestão de memória:

  • Feche sempre as páginas ao utilizar playwright_include_page. Se se esquecer await page.close() no seu parse , as páginas acumulam-se e o uso de memória cresce linearmente com o número de pedidos.
  • Use CONCURRENT_REQUESTS para limitar o paralelismo. Os navegadores consomem muitos recursos; 8 a 16 pedidos simultâneos do Playwright é um ponto de partida razoável numa máquina com 8 GB de RAM.
  • Monitorize a memória RSS do seu spider durante as execuções de teste. Se esta aumentar de forma constante, verifique se existem páginas não fechadas ou criação excessiva de contexto.

Para fluxos de trabalho de scraping com navegador headless de forma mais geral, o guia sobre como executar um navegador headless com Python discute padrões de recursos que complementam o que abordamos aqui.

Resolução de problemas e tratamento de erros

Mesmo os spiders Scrapy Playwright bem configurados podem falhar em grande escala. Aqui estão os problemas mais comuns e as soluções práticas.

TimeoutError: Este é o erro que irá ver com mais frequência. Significa que o navegador não conseguiu concluir a navegação ou uma espera dentro do tempo permitido.

  • Aumente PLAYWRIGHT_DEFAULT_NAVIGATION_TIMEOUT para sites lentos.
  • Mude de networkidle para wait_for_selector para evitar o bloqueio em ligações persistentes.
  • Verifique se o site de destino está a bloqueá-lo (uma captura de ecrã da página de tempo limite revela frequentemente um CAPTCHA ou uma página de bloqueio).

Desligamentos do navegador: Se o processo do navegador falhar a meio do rastreio, verá BrowserError ou Connection closed exceções.

  • Reduza CONCURRENT_REQUESTS. Um número excessivo de páginas em paralelo pode esgotar a memória do sistema e fazer com que o navegador falhe.
  • Defina PLAYWRIGHT_MAX_PAGES_PER_CONTEXT um valor mais baixo.
  • Adicione "args": ["--disable-dev-shm-usage"] ao PLAYWRIGHT_LAUNCH_OPTIONS ao executar no Docker, onde /dev/shm é frequentemente demasiado pequeno.

Fugas de memória: A utilização de memória do seu spider aumenta gradualmente durante rastreamentos longos.

  • Verifique se está a fechar todas as páginas obtidas através de playwright_include_page. Cada página não fechada mantém um DOM completo na memória.
  • Limite PLAYWRIGHT_MAX_PAGES_PER_CONTEXT e reinicie periodicamente os contextos.
  • Utilize CLOSESPIDER_PAGECOUNT ou uma extensão personalizada para reiniciar o spider após um limite.

Padrões de erros: Use o errback para lidar com falhas de forma elegante, em vez de deixar que elas causem a falha do spider:

yield scrapy.Request(
    url,
    meta={"playwright": True, "playwright_include_page": True},
    callback=self.parse,
    errback=self.handle_error,
)

async def handle_error(self, failure):
    page = failure.request.meta.get("playwright_page")
    if page:
        await page.close()
    self.logger.error(f"Request failed: {failure.request.url}")

O detalhe fundamental: se solicitou playwright_include_page, deve fechar a página tanto no callback como no errback. Caso contrário, uma solicitação com falha causa uma fuga de objeto de página. Combine errbacks com a configuração integrada do Scrapy RETRY_TIMES para repor automaticamente falhas transitórias antes de desistir.

Depuração com rastreios: O Playwright suporta a gravação de rastreios, que captura uma cronologia completa de pedidos de rede, instantâneos do DOM e ações. Ative-a através de PLAYWRIGHT_LAUNCH_OPTIONS durante o desenvolvimento para reproduzir exatamente o que o navegador fez numa página problemática.

Construir um spider pronto para produção

Os tutoriais costumam parar depois de mostrar como extrair dados. Em produção, precisa de uma estrutura de projeto completa com itens, pipelines, middlewares e configurações bem ajustadas. Veja aqui como interligar tudo para um projeto Scrapy Playwright.

Defina os seus itens:

# items.py
import scrapy

class ProductItem(scrapy.Item):
    name = scrapy.Field()
    price = scrapy.Field()
    url = scrapy.Field()

Usando Item classes (ou dataclass itens nas versões mais recentes do Scrapy) permite a validação do esquema e torna o código do seu pipeline mais limpo do que passar dicionários em bruto.

Escreva um pipeline de itens para validação e armazenamento:

# pipelines.py
class ValidateProductPipeline:
    def process_item(self, item, spider):
        if not item.get("name"):
            raise scrapy.exceptions.DropItem("Missing name")
        item["price"] = float(item["price"].replace("$", "").strip())
        return item

class JsonWriterPipeline:
    def open_spider(self, spider):
        import json
        self.file = open("products.jsonl", "w")

    def close_spider(self, spider):
        self.file.close()

    def process_item(self, item, spider):
        import json
        self.file.write(json.dumps(dict(item)) + "\n")
        return item

Lista de verificação das configurações de produção:

# settings.py (additions for production)
ITEM_PIPELINES = {
    "myproject.pipelines.ValidateProductPipeline": 100,
    "myproject.pipelines.JsonWriterPipeline": 200,
}

CONCURRENT_REQUESTS = 8
DOWNLOAD_DELAY = 1.5
RANDOMIZE_DOWNLOAD_DELAY = True
RETRY_TIMES = 3
LOG_LEVEL = "INFO"

PLAYWRIGHT_MAX_PAGES_PER_CONTEXT = 4
PLAYWRIGHT_DEFAULT_NAVIGATION_TIMEOUT = 30000

O padrão pronto para produção é: itens estruturados fluem através de pipelines de validação, as configurações limitam a concorrência a um nível que a sua máquina e o site de destino possam suportar, e a lógica de repetição, juntamente com os errbacks, deteta falhas transitórias. O coletor de estatísticas integrado do Scrapy fornece métricas por rastreamento (itens extraídos, erros, repetições) sem código adicional.

Para equipas que pretendem adquirir os fundamentos do web scraping com o Scrapy antes de avançarem para o Playwright, o guia sobre web scraping com o Scrapy fornece uma base sólida.

Pontos-chave

  • Ative o Playwright seletivamente. Apenas marque os pedidos com "playwright": True quando a página realmente necessitar de renderização JavaScript; misture pedidos Scrapy padrão para tudo o resto, a fim de manter os rastreamentos rápidos.
  • Use wait_for_selector em vez de networkidle ou pausas forçadas. A espera baseada em seletores é mais rápida e mais fiável para a maioria dos cenários de conteúdo dinâmico.
  • Intercepte chamadas AJAX sempre que possível. Capturar respostas de API em segundo plano proporciona-lhe JSON limpo e evita seletores DOM frágeis.
  • Camadas de anti-detecção: proxies, rotação de user-agent, redução de impressão digital e atrasos aleatórios devem funcionar em conjunto, não substituir-se uns aos outros.
  • Feche todas as páginas que abrir. Fugas de memória de páginas Playwright não fechadas são a causa mais comum de instabilidade em rastreamentos Scrapy Playwright de longa duração.

Perguntas frequentes

O Scrapy-Playwright suporta o Firefox e o WebKit, ou apenas o Chromium?

Sim, os três motores são suportados. Defina PLAYWRIGHT_BROWSER_TYPE para "firefox" ou "webkit" nas suas definições do Scrapy e execute playwright install firefox (ou webkit) para descarregar o binário do navegador correspondente. O Chromium é o padrão e o mais amplamente testado, mas o Firefox pode ser útil para sites que identificam especificamente o Chromium.

Como resolver exceções TimeoutError no Scrapy-Playwright?

Comece por aumentar PLAYWRIGHT_DEFAULT_NAVIGATION_TIMEOUT para além do valor padrão de 30 segundos. Se o tempo limite persistir, altere a sua estratégia de espera de networkidle para wait_for_selector visando um elemento específico. Tire também uma captura de ecrã da página com falha para verificar se o site está a apresentar um CAPTCHA ou uma página de bloqueio em vez do conteúdo esperado.

Posso executar o Scrapy-Playwright no modo headful (navegador visível) para depuração?

Sim. Adicione "headless": False a PLAYWRIGHT_LAUNCH_OPTIONS em settings.py. A janela do navegador abrirá visivelmente, permitindo-lhe observar cada navegação e interação em tempo real. Isto é inestimável para depurar sequências de métodos de página. Lembre-se de voltar ao modo headless antes de executar rastreamentos de produção.

Quanta memória o Scrapy-Playwright utiliza e como posso reduzir o consumo?

Cada página do Chromium consome aproximadamente 50 a 150 MB de RAM, dependendo da complexidade da página. Para reduzir a memória, diminua CONCURRENT_REQUESTS, defina PLAYWRIGHT_MAX_PAGES_PER_CONTEXT para um número pequeno (3 a 5), elimine tipos de recursos desnecessários (imagens, fontes, folhas de estilo) e feche sempre as páginas explicitamente nos seus métodos callback e errback.

Qual é a diferença entre o Scrapy-Playwright, o Scrapy-Splash e o Scrapy-Selenium?

O Scrapy-Playwright utiliza a moderna API assíncrona do Playwright com o Chromium, o Firefox ou o WebKit. O Scrapy-Splash depende de um serviço de renderização separado baseado em Docker com interatividade limitada. O Scrapy-Selenium envolve o protocolo WebDriver mais antigo. Para novos projetos, o Scrapy-Playwright oferece geralmente a melhor combinação de fidelidade do navegador, desempenho assíncrono e manutenção ativa.

Conclusão

O Scrapy Playwright preenche a lacuna entre o poderoso motor de rastreamento do Scrapy e a realidade da web atual, impulsionada por JavaScript. Ao adicionar um único sinalizador meta às suas solicitações, obtém renderização completa do navegador sem abandonar os pipelines, o middleware e o modelo de concorrência do Scrapy. Este tutorial cobriu todo o espectro: desde a instalação e configuração iniciais até interações com páginas, interceção AJAX, anti-detecção e fortalecimento para produção.

As técnicas aqui apresentadas devem dar resposta à grande maioria dos cenários de scraping dinâmico. Para projetos em que a gestão da infraestrutura do navegador, a rotação de proxies e a anti-detecção em escala se tornam o gargalo, em vez da própria lógica de scraping, a nossa API Scraper gere essas camadas por trás de um único ponto de extremidade, para que se possa concentrar nos dados em vez da infraestrutura.

Seja qual for a abordagem que escolher, o princípio fundamental permanece o mesmo: utilize a renderização do navegador apenas quando for necessário, mantenha os seus spiders bem estruturados e feche todas as páginas que abrir.

Sobre o autor
Raluca Penciuc, Desenvolvedor Full-Stack @ WebScrapingAPI
Raluca PenciucDesenvolvedor Full-Stack

Raluca Penciuc é programadora Full Stack na WebScrapingAPI, onde desenvolve scrapers, aperfeiçoa estratégias de evasão e procura formas fiáveis de reduzir a deteção nos sites-alvo.

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.