Rendering JavaScript em web scraping: quando você precisa e quanto custa
Como saber se uma página precisa de um navegador de verdade, por que o rendering custa mais em todo lugar, e os multiplicadores de crédito, agosto de 2026.

Um fetch HTTP simples te dá o HTML que o servidor mandou. Cada vez mais, essa não é a página que um visitante vê. O espaço entre os dois é o rendering JavaScript, e é o principal motivo pelo qual um scraper que funciona em um site falha silenciosamente no próximo.
Como saber se uma página precisa de rendering?
Duas checagens, rápidas, antes de escrever qualquer código de scraping:
Compare o view-source com o DOM. No navegador, o view-source (Ctrl+U ou Cmd+Option+U) mostra exatamente o que um fetch simples receberia: o HTML bruto do servidor, antes de qualquer script rodar. Agora abra as dev tools e olhe o painel Elements, que mostra o DOM depois que o JavaScript rodou. Se o conteúdo que você quer, preços, listagens, avaliações, está presente em Elements mas ausente ou substituído por um esqueleto de carregamento no view-source, a página está construindo esse conteúdo do lado do cliente.
Faça o fetch você mesmo, sem navegador. A mesma checagem, via código, sem precisar de dev tools:
import requests
resp = requests.get("https://example.com/products", timeout=30)
print(len(resp.text))
print("add-to-cart" in resp.text) # troque por uma string que você sabe que está no conteúdo real
Se uma string marcadora que você vê na página ao vivo está ausente de resp.text, um fetch simples não está te trazendo o conteúdo, e você precisa de algo que execute JavaScript antes de ler a página.
Um motivo rápido para isso acontecer mais do que antes: frameworks modernos (React, Vue, Svelte e companhia) entregam um shell HTML quase vazio e constroem a página de verdade no navegador. É uma escolha arquitetural deliberada do site, não uma medida anti-scraping, mas produz o mesmo resultado para você de qualquer forma.
Por que o rendering custa mais, em todo lugar?
Um fetch simples é um socket, uma requisição, uma resposta. Rendering significa colocar de pé algo mais parecido com um navegador completo: carregar o DOM, executar scripts, esperar as chamadas de rede se estabilizarem, às vezes rolar a página para ativar conteúdo lazy-loaded. Isso é CPU e memória reais por página, ocupadas durante toda a duração do rendering, não os microssegundos que um fetch estático leva.
Essa estrutura de custo não é específica de nenhum fornecedor. Todo provedor que oferece tanto um fetch estático quanto um fetch renderizado cobra mais caro pelo renderizado, porque é infraestrutura genuinamente mais cara de rodar em escala: um pool de processos de navegador reais em vez de um connection pool. Se um orçamento para scraping com rendering parece ter o mesmo preço do estático, olhe com cuidado o que “renderizado” significa na documentação deles.
Quanto custa o rendering na FastAF
/scrape tem preços em quatro níveis, e o multiplicador acompanha o custo do que está de fato rodando por trás da requisição:
| Modo | Parâmetro | Créditos |
|---|---|---|
| Fetch estático | (padrão) | 1 |
| Rendering JS | dynamic=true |
5 |
| Proxies premium, sem rendering | premium=true |
10 |
| Rendering JS + proxies premium | dynamic=true&premium=true |
25 |
Do estático, é um salto de 5x para adicionar rendering, e um salto de 25x para adicionar rendering e roteamento por proxy premium juntos. Esse é o custo real da infraestrutura extra, não uma margem arbitrária, e é por isso que vale a pena checar se você realmente precisa de rendering antes de usá-lo por padrão.
A estratégia: tente estático primeiro, escale só se falhar
A maioria dos jobs de scraping mistura páginas que precisam de rendering com páginas que não precisam, mesmo dentro do mesmo site. Pagar dynamic=true em toda requisição quando só algumas páginas precisam é a forma mais comum de gastar demais nesse endpoint:
import requests
API_KEY = "faf_live_..." # sua chave FastAF
def scrape(url, dynamic=False, premium=False, wait=None):
params = {"url": url}
if dynamic:
params["dynamic"] = "true"
if premium:
params["premium"] = "true"
if wait:
params["wait"] = wait
resp = requests.get(
"https://api.fastaf.co/v1/scrape",
params=params,
headers={"Authorization": f"Bearer {API_KEY}"},
timeout=90,
)
resp.raise_for_status()
return resp.text
def scrape_smart(url, marker):
html = scrape(url)
if marker in html:
return html # estático foi suficiente, 1 crédito gasto
html = scrape(url, dynamic=True) # escalar, 5 créditos gastos
return html
marker é uma string que você sabe que aparece no conteúdo real, um título de produto, um formato de preço, qualquer coisa que estaria ausente de uma página shell. Se o fetch estático já tem, você economizou 4 créditos nessa URL. Se não tem, você escala uma vez, não por palpite.
Use premium=true separadamente: é sobre qualidade de proxy e superar bloqueios baseados em IP, não sobre JavaScript. Um site pode precisar de um, dos dois, ou de nenhum. wait (em milissegundos) vale a pena definir explicitamente nas requisições dynamic=true para páginas que carregam conteúdo depois que uma chamada de API termina, em vez de assumir que a espera padrão é suficiente.
Resumo
Compare o view-source com o DOM renderizado, ou faça o fetch da página você mesmo, antes de assumir que precisa de um navegador. Estático é 1 crédito, rendering é 5, proxies premium são 10, os dois juntos são 25: os multiplicadores existem porque infraestrutura de navegador real genuinamente custa mais para rodar. Tente estático primeiro e escale para dynamic=true só nas URLs onde o conteúdo marcador está de fato ausente, e você vai pagar o sobrepreço do rendering só onde ele é merecido.