Saltar para o conteúdo
Voltar ao blogue

JavaScript com o Scrapy: os dados em primeiro lugar, renderização apenas quando necessário

Mihai MaximÚltima atualização em 13 min read
JavaScript com o Scrapy: os dados em primeiro lugar, renderização apenas quando necessário
Resumo: A utilização de JavaScript com o Scrapy nem sempre requer um navegador. Em primeiro lugar, analise a resposta em bruto, os pedidos de rede e o estado da página incorporada; adicione o `scrapy-playwright` apenas quando os dados pretendidos dependerem efetivamente da execução no navegador e, em seguida, controle deliberadamente as esperas, as interações, a limpeza e a concorrência.

Utilizar JavaScript com o Scrapy significa adicionar a execução no navegador para páginas cujos dados necessários só aparecem após a execução do código do lado do cliente. O objetivo prático não é renderizar todas as páginas, mas identificar o caminho mais económico e fiável para os dados: HTML bruto, uma solicitação JSON, um objeto JavaScript incorporado ou, apenas quando necessário, um DOM construído pelo navegador.

Essa distinção é importante porque o downloader do Scrapy recebe a resposta do servidor, enquanto o Chrome ou o Firefox podem efetuar pedidos adicionais e alterar o documento posteriormente. Uma página em React, Vue ou Angular pode, portanto, parecer completa no DevTools, mesmo que response.text contenha pouco mais do que uma estrutura de aplicação.

Este guia começa com um diagnóstico que deixa o navegador para o fim e, em seguida, constrói um fluxo de trabalho focado no «scrapy-playwright» para as solicitações que realmente precisam de ser executadas. Verá também como utilizar esperas baseadas em condições, gerir cliques e o estado da sessão, comparar opções de renderização e manter os spiders dinâmicos estáveis em produção. O resultado é um fluxo de trabalho que preserva o eficiente pipeline HTTP do Scrapy sempre que possível, sem fingir que todos os sites modernos podem ser raspados apenas a partir de marcação estática.

Encontre a fonte de dados antes da renderização

Antes de tentar executar JavaScript com o Scrapy, analise o que o servidor já devolveu. As orientações oficiais do Scrapy sobre conteúdo dinâmico recomendam localizar primeiro a fonte subjacente. Siga esta ordem: resposta bruta, pedido de rede, estado incorporado e, por fim, um navegador sem interface gráfica.

Compare a resposta do Scrapy com o DOM renderizado

Guarde ou procure response.text um valor distintivo que consiga ver no navegador, como um ID de produto em vez de um preço formatado. Em seguida, compare-o com o painel «Elementos» do DevTools. Se o valor existir apenas no DOM renderizado, provavelmente foi inserido pelo JavaScript, mas isso ainda não prova que a renderização seja necessária. A aplicação pode ter obtido o mesmo valor a partir de um ponto de extremidade acessível.

Um guia mais abrangente sobre web scraping com o Scrapy é uma referência interna útil neste contexto, especialmente para a inspeção de respostas, seletores e depuração de pedidos.

Reproduza a solicitação XHR ou fetch

Abra o DevTools, selecione «Rede», atualize a página e filtre por «Fetch/XHR». Inspecione as respostas candidatas até encontrar os campos de que necessita. Registe o URL, o método HTTP, a string de consulta ou o corpo da solicitação, os cabeçalhos relevantes e quaisquer cookies ou tokens associados à sessão.

Reproduza a menor solicitação válida no Scrapy, em vez de copiar todos os cabeçalhos do navegador:

def parse(self, response):
    yield scrapy.Request(
        "https://example.com/api/products?page=1",
        headers={"Accept": "application/json"},
        callback=self.parse_products,
    )

def parse_products(self, response):
    payload = response.json()
    for row in payload["results"]:
        yield {"id": row["id"], "name": row["name"]}

Esta abordagem evita o tempo de execução do DOM, o consumo de memória do navegador e alterações nos seletores visuais. Se o ponto final utilizar paginação, valores de cursor ou dados POST, siga esses parâmetros diretamente. Quando houver autenticação envolvida, preserve os cookies ou o fluxo de tokens necessários, em vez de codificar manualmente uma credencial de curta duração.

Analise JSON e dados incorporados em scripts

Por vezes, o HTML inicial já contém o estado da aplicação dentro de um <script> elemento. Extraia esse script com um seletor CSS ou XPath. Utilize json.loads() quando o conteúdo for JSON válido; utilize chompjs quando se tratar de sintaxe de objeto JavaScript com chaves sem aspas, vírgulas finais ou diferenças semelhantes.

import chompjs

script = response.css("script[data-page-state]::text").get()
state = chompjs.parse_js_object(script) if script else {}
items = state.get("products", [])

Dê preferência a um analisador estruturado em vez de uma expressão regular abrangente. As expressões regulares podem isolar uma atribuição claramente delimitada, mas tornam-se instáveis quando se trata de objetos aninhados e cadeias de caracteres com caracteres de escape. Combinar pedidos JSON diretos com a análise de tags de script resolve frequentemente o conteúdo dinâmico do Scrapy sem ter de abrir um navegador.

Adicione renderização JavaScript com o scrapy-playwright

Quando os dados dependem de comportamentos exclusivos do navegador, tais como código de aplicações, APIs Web ou estados orientados pela interação, adicione a renderização de forma seletiva. Este exemplo de JavaScript com o Scrapy utiliza o scrapy-playwright como principal via de integração, mantendo as solicitações normais no downloader padrão do Scrapy.

Instalar e configurar a integração

Um padrão de configuração comum instala o plugin e um executável do navegador Playwright no mesmo ambiente de projeto:

python -m pip install scrapy-playwright
python -m playwright install chromium

Adicione o reator asyncio e os manipuladores de download do Playwright a settings.py:

TWISTED_REACTOR = "twisted.internet.asyncioreactor.AsyncioSelectorReactor"

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

PLAYWRIGHT_BROWSER_TYPE = "chromium"

Fixar versões compatíveis de dependências em projetos implementáveis e testar o mesmo ficheiro de bloqueio em desenvolvimento e produção. A documentação fornecida não valida a superfície de configuração atual do plugin; por isso, verifique a documentação atual do projeto antes de considerar este modelo pronto para lançamento.

Renderize uma solicitação e analise-a com seletores do Scrapy

Marque apenas a solicitação que necessita de um navegador. Uma espera significativa do seletor garante que a função de retorno receba HTML após o componente alvo aparecer:

import scrapy
from scrapy_playwright.page import PageMethod

class ProductsSpider(scrapy.Spider):
    name = "products"

    def start_requests(self):
        yield scrapy.Request(
            "https://example.com/products",
            meta={
                "playwright": True,
                "playwright_page_methods": [
                    PageMethod(
                        "wait_for_selector",
                        "[data-product-card]",
                    )
                ],
            },
        )

    def parse(self, response):
        for card in response.css("[data-product-card]"):
            yield {
                "name": card.css("[data-name]::text").get(),
                "price": card.css("[data-price]::text").get(),
            }

A função de retorno de chamada continua a receber uma resposta do Scrapy, pelo que os padrões de extração CSS e XPath existentes continuam a ser úteis. Mantenha as páginas de categorias estáticas, as chamadas à API e os recursos em pedidos normais. Um tutorial dedicado ao Scrapy-Playwright para sites com grande utilização de JavaScript é a próxima referência interna natural quando precisar de múltiplos contextos, eventos de página ou uma configuração mais aprofundada do projeto.

Aguarde e interaja com conteúdo dinâmico

Um evento de navegação concluído não significa que os dados da aplicação estejam prontos. A renderização do JavaScript do Scrapy só se torna fiável quando o spider aguarda um estado relacionado com os campos que pretende extrair.

Prefira esperas baseadas no estado a atrasos fixos

Aguarde por um seletor estável, uma resposta de API conhecida, uma transição de URL ou um sinalizador da aplicação. Um seletor como [data-results-loaded="true"] expressa a intenção melhor do que ficar inativo durante três segundos. Os atrasos fixos podem ser demasiado curtos numa execução lenta e desperdiçar tempo numa rápida.

Utilize tempos de espera limitados e registe qual a condição que falhou. Se não existir nenhum sinal estável, uma espera fixa curta pode servir de alternativa, mas estabeleça um limite máximo para todo o pedido do navegador. Isso torna a espera do Scrapy por uma falha de JavaScript observável, em vez de deixar as páginas abertas indefinidamente.

Clicar, percorrer, paginar e manter o estado da sessão

Os métodos de página podem realizar uma interação controlada antes de a resposta ser devolvida:

bplaywright_page_methods": [
    PageMethod("click", "button.load-more"),
    PageMethod("wait_for_selector", "[data-page='2']"),
]

Para a rolagem infinita, repita uma ação de rolagem apenas enquanto o número de itens aumentar e pare após um número definido de passagens sem alterações. Para a paginação, dê preferência ao pedido de dados do site, se existir; caso contrário, clique no controlo seguinte e aguarde que um marcador de página ou o ID da primeira linha mude.

Mantenha os pedidos autenticados relacionados no mesmo contexto de navegador nomeado, para que os cookies e o armazenamento local possam persistir. Com playwright_include_page=True, feche a página ativa num finally bloco. Feche um contexto dedicado após a sua última solicitação de sessão, e não enquanto outras solicitações ainda dependerem dele:

async def parse_last_session_page(self, response):
    page = response.meta["playwright_page"]
    context = page.context
    try:
        return {"title": await page.title()}
    finally:
        await page.close()
        await context.close()

Como as APIs de inclusão e limpeza podem mudar, confirme os metadados da solicitação atual e as regras do ciclo de vida antes de implementar. Páginas com fugas acabam por esgotar o limite de páginas do navegador e fazem com que um spider JavaScript com o Scrapy, que de outra forma estaria correto, pareça ficar bloqueado.

Compare as opções de JavaScript do Scrapy

A melhor integração depende de se precisa de um navegador real, do nível de interação que o fluxo exige e de quem irá operar a infraestrutura de renderização. Não escolha apenas com base na rapidez com que a primeira demonstração parece funcionar.

Playwright, Selenium, Splash e APIs de renderização hospedadas

Opção

Melhor adequação

Principais vantagens e desvantagens

scrapy-playwright

Renderização seletiva no navegador dentro de um projeto Scrapy, incluindo interações modernas

Custo de CPU e memória do navegador, gestão assíncrona do ciclo de vida e detalhes de integração sensíveis à versão

Integração baseada em Selenium

Equipas com automação WebDriver ou código de teste já estabelecidos

Gestão de drivers e navegadores, maior integração com o Scrapy e compatibilidade que deve ser verificada para o pacote escolhido

Integração baseada no Splash

Um serviço de renderização HTTP ou fluxos de trabalho existentes baseados em Lua

Um serviço separado para operar, além de um comportamento JavaScript que pode diferir do de um navegador completo atual

API de renderização alojada

Descarregamento da infraestrutura do navegador, do proxy e das tentativas de repetição

Limites específicos do fornecedor, semântica de tempo de espera, concorrência, preços e menor controlo local

Uma comparação específica entre o Scrapy e o Selenium pode ajudar quando já mantém código WebDriver. Um tutorial do Scrapy Splash é mais útil quando a sua arquitetura privilegia um serviço de renderização autónomo. Para JavaScript com o Scrapy, utilize a opção que satisfaça a interação mínima necessária, mantendo os pedidos simples livres do navegador.

A renderização não é uma garantia anti-bot. Um navegador completo ainda pode receber uma resposta 403, CAPTCHA, limite de taxa ou desafio de conta; por isso, avalie os controlos de acesso separadamente da execução do DOM.

Execute spiders renderizados de forma fiável em produção

Um navegador sem interface gráfica com o Scrapy é mais lento e mais pesado do que uma solicitação HTTP normal. A fiabilidade em produção advém da limitação do trabalho do navegador, da contenção de falhas e da medição de recursos.

Controle os custos, a concorrência e os recursos do navegador

Renderize apenas URLs que comprovadamente o exijam. Armazene em cache páginas representativas durante o desenvolvimento do analisador, bloqueie meios de comunicação desnecessários com cautela e defina limites conservadores para contextos de navegador e páginas. Aumente a concorrência gradualmente, mantendo-se atento à memória, à CPU, aos tempos de espera e às páginas abertas.

Utilize limites de tempo de espera separados para a navegação e para as esperas pós-carregamento. Repita tentativas em caso de falhas transitórias de rede ou do processo do navegador, mas limite o número de tentativas e evite repetir tentativas em caso de falhas determinísticas, como um seletor removido para sempre. Feche as páginas em caso de sucesso ou de exceções e recicle os «workers» do navegador que não estejam a funcionar corretamente. Estes controlos impedem que o JavaScript com o Scrapy transforme um alvo lento numa contrapressão que afete todo o rastreador.

Um guia sobre web scraping sem ser bloqueado é um complemento interno útil para cabeçalhos, taxas de pedidos, proxies e diagnósticos de acesso, que são aspetos distintos da correção da renderização.

Resolver problemas de conteúdo em falta, lento ou bloqueado

Sintoma

Verifique a seguir

O campo existe no navegador, mas não response.text

Encontre a solicitação Fetch/XHR ou o script incorporado antes de adicionar a renderização

A resposta renderizada ainda apresenta dados em falta

Aguarde por um seletor específico do campo ou por uma condição de rede e, em seguida, verifique o seletor em relação ao DOM final

A primeira página funciona, mas as páginas seguintes estão vazias

Preserve cookies, tokens, estado do contexto e parâmetros de paginação

O spider fica mais lento com o tempo

Conte as páginas e contextos abertos, confirme a limpeza e reduza a simultaneidade de renderização

Timeouts repetidos

Separar a navegação das esperas da aplicação, capturar dados de diagnóstico e limitar as tentativas de repetição

403 ou CAPTCHA

Trate-o como um problema de controlo de acesso, não como prova de que o JavaScript falhou

Quando um seletor falhar, guarde o HTML renderizado e compare-o com a sessão do navegador utilizada nos testes manuais. Se as respostas estiverem bloqueadas, inspecione os códigos de estado e as páginas de verificação antes de alterar a lógica de espera. Este ciclo de «sintoma-ação» mantém a depuração baseada em evidências, em vez de adicionar tempos de espera mais longos a cada pedido.

Pontos-chave

  • Procure HTML bruto, pontos de extremidade JSON e o estado incorporado da aplicação antes de adicionar um navegador.
  • Ative o `scrapy-playwright` apenas em pedidos que exijam execução e, em seguida, continue a utilizar os seletores do Scrapy na resposta renderizada.
  • Aguarde até que o estado da página seja observável, limite os tempos de espera e feche todas as páginas incluídas ou contextos dedicados.
  • Trate a renderização, o dimensionamento e o acesso anti-bot como problemas de engenharia distintos, com diagnósticos diferentes.

Perguntas frequentes

É possível que um spider do Scrapy misture pedidos padrão com pedidos renderizados em JavaScript?

Sim. Mantenha os objetos scrapy.Request no downloader predefinido e adicione os metadados de renderização apenas às URLs que necessitem de um navegador. Ambos os tipos de pedido podem alimentar o mesmo pipeline de itens. Este modelo seletivo preserva o rendimento do Scrapy para páginas estáticas, permitindo simultaneamente que um subconjunto mais pequeno utilize a execução no navegador.

Como posso executar JavaScript personalizado ou clicar num elemento com o scrapy-playwright?

Utilize uma entrada «page-method» para operações que possam ser executadas antes do callback, tais como um clique ou uma evaluate chamada. Para lógica condicional ou valores devolvidos por JavaScript personalizado, inclua o objeto de página ativo nos metadados da resposta e utilize um callback assíncrono. Feche sempre essa página no finally, mesmo quando a extração gerar uma exceção.

Como posso capturar uma captura de ecrã ou inspecionar o HTML renderizado durante a depuração?

Inclua a página do Playwright na resposta e, em seguida, chame page.screenshot(path="debug.png", full_page=True) dentro de um callback assíncrono. Guarde await page.content() juntamente com a captura de ecrã quando o comportamento do seletor não for claro. Utilize nomes de ficheiro únicos que contenham um ID de pedido e evite manter estes diagnósticos ativados em grande escala, pois as capturas de ecrã aumentam a sobrecarga de E/S e de armazenamento.

Um navegador sem interface gráfica impede respostas 403, CAPTCHAs ou bloqueios?

Não. Um navegador sem interface gráfica executa JavaScript, mas os sites continuam a poder avaliar a reputação do IP, a taxa de pedidos, os cookies, o comportamento da conta, as impressões digitais do navegador e os padrões de navegação. Diagnostique o estado devolvido e o conteúdo do desafio separadamente. A renderização pode tornar a página funcional sem que o tráfego seja considerado fiável ou autorizado.

O scrapy-playwright pode ser executado no Docker ou num ambiente de CI?

Sim. O contentor deve incluir o binário do navegador selecionado e as suas dependências do sistema operativo, e a versão do Playwright deve corresponder ao pacote Python que instalar. Fixe as dependências, teste a imagem sem um servidor de visualização, forneça memória partilhada suficiente e execute um pequeno teste de fumaça renderizado antes de iniciar o spider completo.

Conclusão: JavaScript com o Scrapy na prática

A forma fiável de lidar com páginas dinâmicas é avançar do mais simples para o mais complexo. Compare a resposta bruta do Scrapy com o DOM renderizado, localize o pedido ou o estado incorporado que fornece os campos em falta e analise os dados estruturados diretamente sempre que possível. Só então deve adicionar a execução no navegador.

Quando a renderização for necessária, mantenha-a seletiva. Aguarde um estado vinculado aos dados, não um atraso arbitrário; preserve o contexto da sessão apenas quando o fluxo de trabalho o exigir; e feche páginas e contextos de forma previsível. Em produção, a concorrência do navegador, os limites de tempo de espera, as tentativas de repetição e as métricas de recursos são tão importantes quanto os seletores. Um navegador que renderiza corretamente num portátil pode ainda assim tornar-se o gargalo do rastreador ou deparar-se com controlos de acesso em grande escala.

Se o bloqueio na camada de pedidos se tornar a restrição, em vez da lógica de interação, a WebScrapingAPI oferece uma API de Scraper que devolve HTML bruto, ao mesmo tempo que gere a rotação de proxies e CAPTCHAs nos bastidores. Isso permite-lhe manter a análise e o pipeline de itens do Scrapy, ao mesmo tempo que descarrega a camada de recuperação instável. Comece pelo caminho mais simples que conseguir verificar, implemente-o e adicione complexidade do navegador apenas quando as evidências indicarem que é necessário.

Sobre o autor

Mihai Maxim, Desenvolvedor Full Stack @ WebScrapingAPI

Mihai Maxim

Desenvolvedor Full Stack

Mihai Maxim é um programador Full Stack na WebScrapingAPI, contribuindo em todas as áreas do produto e ajudando a criar ferramentas e funcionalidades fiáveis para a plataforma.

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

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

Resumo: A extração de dados da Web 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 e, só depois, adicione SQS, contentores, renderização no navegador, proxies ou uma camada de recuperação gerida, apenas quando a carga de trabalho demonstrar que precisa deles.

Suciu Dan32 min read
Ler artigo
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

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.