---
title: "Páginas estáticas e dinâmicas no web scraping"
description: "Páginas dinâmicas carregam o conteúdo depois com JavaScript, então uma requisição simples volta vazia. Explicamos quando um navegador headless é necessário."
url: https://proxynet.io/pt-br/blog/static-vs-dynamic-pages
date: 2026-09-13
author: "Acar Diveroli"
category: "Comparativos, Web scraping"
lang: pt-BR
---

# Páginas estáticas e dinâmicas no web scraping

Quase todo mundo que começa no scraping passa pelo mesmo momento: produtos, preços e avaliações estão ali no navegador, mas, quando você busca o mesmo endereço no Python com `requests`, os seletores não encontram nada. Olhando o HTML da página, você vê um texto "Carregando..." e algumas tags `<script>` em vez de uma lista de produtos. O problema não é o seu código; a página é **dinâmica**, e o JavaScript que roda no navegador busca o conteúdo depois.

Neste artigo, explicamos a diferença entre páginas estáticas e dinâmicas, como saber em poucos minutos se uma página é dinâmica e o que é um navegador headless. A parte em que mais nos concentramos é como chegar aos dados sem navegador headless na maioria dos casos: encontrando a requisição de API que a página faz em segundo plano e lendo o JSON embutido no HTML. Quando um navegador é realmente necessário, também mostramos as estratégias de espera certas no Playwright e formas de reduzir o custo. Rodamos os exemplos contra uma página de teste local que carrega o conteúdo com JavaScript.

> **Nota: Resposta rápida**
>
> Em uma página estática, o servidor envia todo o conteúdo em HTML e uma requisição HTTP simples basta. Em uma página dinâmica, o servidor envia um esqueleto e o JavaScript do navegador busca o conteúdo depois, então uma requisição HTTP volta vazia. Antes de partir para um navegador headless, procure no painel Network das ferramentas do desenvolvedor a requisição JSON que a página faz, ou dados embutidos no HTML; os dados costumam estar ali e são muito mais rápidos de obter. Um navegador headless só é necessário quando não há outro jeito de chegar aos dados.

## O que é uma página estática?

Em uma página estática, tudo o que o navegador mostra está no HTML da primeira resposta do servidor. O servidor pode ler esse HTML de um arquivo pronto ou gerá-lo a partir de um banco de dados a cada requisição; o que importa para o scraping é que o conteúdo **já está dentro do HTML quando chega ao navegador**.

Você não precisa de navegador para obter dados de uma página estática:

```python
import requests
from bs4 import BeautifulSoup

html = requests.get("https://example.com/deals", timeout=20).text
cards = BeautifulSoup(html, "html.parser").select("div.product")
print("Cards de produto encontrados:", len(cards))
```

Sites de notícias, blogs, muitos sites corporativos e lojas online renderizadas no servidor fazem parte desse grupo.

## O que é uma página dinâmica (carregada com JavaScript)?

Em uma página dinâmica, a primeira resposta do servidor é um esqueleto: o layout da página, um contêiner de lista vazio e arquivos JavaScript. O conteúdo chega depois, nestas etapas:

1. **O navegador recebe o esqueleto HTML** e desenha na tela um placeholder como "Carregando...".
2. **Os arquivos JavaScript são baixados e executados.** Muitas vezes um framework como React ou Vue.
3. **O JavaScript envia uma requisição de API.** Por exemplo, com `fetch` para `/api/products?category=headphones&page=1`.
4. **O servidor devolve os dados em JSON.** Nomes de produto, preços, informações de estoque.
5. **O JavaScript monta o HTML a partir desse JSON e o insere na página.** Os cards de produto só aparecem nesta etapa.
6. **Rolar ou clicar dispara novas requisições.** Rolagem infinita, um botão "Ver mais", filtros.

Um cliente HTTP como o `requests` do Python só faz a primeira etapa; ele não executa JavaScript. Por isso o HTML recebido não tem cards de produto. Na nossa página de teste local, o código `requests` acima encontrou zero cards; a mesma página mostrava dois produtos no navegador. Com imagens acontece o mesmo; mostramos por que as que só carregam ao rolar a página ficam de fora de um download em massa em [como baixar todas as imagens de um site](/pt-br/blog/download-all-images-from-website).

Existe também uma **estrutura híbrida**: frameworks como Next.js e Nuxt renderizam no servidor o primeiro estado da página e também embutem os mesmos dados no HTML como um bloco JSON para o JavaScript usar. Nessas páginas, os dados estão no HTML sem executar JavaScript, mas às vezes dentro de uma tag `<script>`, e não nos cards visíveis.

## Como saber se uma página é dinâmica?

Alguns minutos de diagnóstico antes de montar um navegador headless ajudam a escolher o caminho certo:

1. **Veja o código-fonte.** `Ctrl + U` na página (`Cmd + Option + U` no macOS) mostra o HTML bruto que o servidor enviou. Procure ali um nome de produto ou um preço que aparece na página. Se encontrar, a página provavelmente é estática.
2. **Compare com o painel Elements.** O painel Elements das ferramentas do desenvolvedor mostra a página depois que o JavaScript rodou. Se o texto está em Elements, mas não no código-fonte, o conteúdo veio por JavaScript.
3. **Desative o JavaScript e recarregue.** Nas ferramentas do desenvolvedor do Chrome, abra o menu de comandos com `Ctrl + Shift + P`, digite "Disable JavaScript" e recarregue a página. Se o conteúdo sumir, a página é dinâmica.
4. **Verifique com código.** Procure no HTML obtido com `requests` um texto que você espera. Se não estiver lá e o HTML for muito curto, você recebeu um esqueleto.
5. **Olhe o filtro Fetch/XHR no painel Network.** Se, ao recarregar a página, aparecerem respostas JSON nesse filtro, os dados provavelmente estão em uma dessas requisições.

A etapa quatro tem uma armadilha: se o conteúdo não está no HTML, o motivo nem sempre é JavaScript. O site pode ter devolvido uma página de verificação ou outro conteúdo. Confira o código de status e o conteúdo da resposta; explicamos esse diagnóstico em detalhe em [Como fazer web scraping sem ser bloqueado](/pt-br/blog/web-scraping-without-getting-blocked).

## O que é um navegador headless?

Um navegador headless é um navegador de verdade que roda sem abrir janela na tela. O mecanismo do Chromium, do Firefox ou do WebKit carrega a página como um navegador normal, executa JavaScript, envia requisições de API e monta a página; você lê essa página com código. As ferramentas mais comuns são Playwright, Puppeteer e Selenium.

Um navegador headless faz mais do que um cliente HTTP, mas também custa mais:

- **CPU e memória.** Cada aba de navegador usa muitas vezes os recursos de uma requisição HTTP. O número de páginas que você roda ao mesmo tempo na mesma máquina é limitado por núcleos e memória, não pela rede.
- **Tempo.** Baixar e executar todo o JavaScript de uma página pode levar segundos.
- **Número de requisições e banda.** Uma única página gera dezenas de requisições extras de imagens, fontes, folhas de estilo, scripts de analytics e anúncios. Isso aumenta tanto o seu tráfego quanto a carga do site de destino.
- **Manutenção.** Versões de navegador, drivers e lógica de espera adicionam complexidade.

Por isso o navegador headless deve ser o último recurso, não a primeira escolha. Para comparar as ferramentas, veja [Web scraping: JavaScript ou Python?](/pt-br/blog/web-scraping-javascript-vs-python), e para configurar o Selenium, [Como usar proxy com Selenium](/pt-br/blog/selenium).

## Primeiro, encontre a requisição de API/XHR

Os dados de uma página dinâmica chegam ao navegador de algum lugar em JSON. Se você encontrar essa requisição, obtém os dados direto, sem renderizar a página. A [referência do painel Network](https://developer.chrome.com/docs/devtools/network/reference) do Chrome explica os filtros e as opções de cópia em detalhe; o caminho curto é:

1. Abra as ferramentas do desenvolvedor (F12) e vá para a aba **Network**.
2. Marque **Fetch/XHR** na barra de filtros.
3. Recarregue a página ou faça a ação que carrega os dados (rolar a página, escolher um filtro, ir para a próxima página).
4. Clique nas requisições cujo nome contém palavras como `api`, `graphql`, `search` ou `products` e veja o JSON na aba **Response**.
5. Ao encontrar a requisição com os dados de que precisa, anote o endereço, os parâmetros e os cabeçalhos necessários na aba **Headers**. Dá para testá-la na linha de comando clicando com o botão direito na requisição e escolhendo **Copy > Copy as cURL**. No artigo [POST com JSON no Python Requests: equivalentes do cURL](/pt-br/blog/python-requests-post-json), explicamos quais cabeçalhos remover ao passar o comando copiado para o Python e se o corpo vai em `json=` ou em `data=`.

Repetir no Python a requisição encontrada costuma levar poucas linhas:

```python
import requests

session = requests.Session()
session.headers.update({
    "User-Agent": "ExamplePriceBot/1.0 (+https://example.com/about-our-bot)",
    "Accept": "application/json",
})

response = session.get(
    "https://example.com/api/products",
    params={"category": "headphones", "page": 1},
    timeout=20,
)
response.raise_for_status()
for product in response.json()["products"]:
    print(product["name"], product["price"])
```

As vantagens desse caminho são grandes: os dados chegam estruturados, você não precisa de seletores HTML, o seu código não quebra quando o design da página muda e uma única requisição é uma fração pequena da carga de uma página inteira. A paginação geralmente é feita com um parâmetro `page`, `offset` ou `cursor`. Se o endpoint devolver uma página HTML em vez de JSON quando o seu script o chamar, a chamada `.json()` falha com JSONDecodeError: Expecting Value; mostramos como achar a causa em [Como corrigir JSONDecodeError: Expecting Value no Python](/pt-br/blog/jsondecodeerror-expecting-value).

Pontos de atenção:

- **Autenticação e tokens.** Algumas requisições de API precisam de cookies, tokens de sessão ou assinaturas de curta duração. Copiar isso à mão funciona por um tempo e depois quebra.
- **Termos do site.** Uma API interna que o site usa no próprio front-end não é uma API pública. Aplique também a essas requisições os termos de uso e as regras de robots.txt do site; se houver uma API oficial, prefira-a. Para o enquadramento legal, veja [Web scraping é legal?](/pt-br/blog/is-data-web-scraping-legal).
- **Velocidade.** Como as requisições de API são leves, dá para enviá-las muito rápido, o que também facilita ultrapassar os limites de um site. Aplique as mesmas regras de velocidade.

## Ler o JSON embutido no HTML

Em sites feitos com Next.js, Nuxt e frameworks parecidos, os dados costumam estar prontos em uma tag `<script>` dentro do HTML. Veja o código-fonte e procure `__NEXT_DATA__`, `__NUXT__`, `__INITIAL_STATE__` ou `application/ld+json`.

```python
import json

import requests
from bs4 import BeautifulSoup

html = requests.get("https://example.com/deals", timeout=20).text
soup = BeautifulSoup(html, "html.parser")

data = json.loads(soup.select_one("script#__NEXT_DATA__").string)
for product in data["props"]["pageProps"]["products"]:
    print(product["name"], product["price"])
```

Os blocos `application/ld+json` trazem dados estruturados como nome do produto, preço, estoque e avaliação no formato schema.org e, como são preparados para os buscadores, costumam ser estáveis. Tratamos de como tornar seletores robustos contra mudanças de design em [Seletor CSS ou XPath](/pt-br/blog/css-selector-vs-xpath).

## Estratégias de espera no Playwright

Se os dados não estão nem em uma API nem em JSON embutido, ou se chegar ao conteúdo exige interações como clicar, rolar e preencher formulários, usa-se um navegador headless. O erro mais comum nesse caso é **o momento de ler**: se você lê assim que a página abre, o conteúdo ainda não chegou; se espera um tempo fixo, ou perde tempo ou, com uma resposta lenta, recebe um resultado vazio do mesmo jeito.

As ferramentas de espera certas do Playwright são:

- **A espera automática dos locators.** Operações como `page.locator("div.product h2").inner_text()` esperam o elemento aparecer na página, até o timeout padrão. A [documentação de verificações de ação](https://playwright.dev/python/docs/actionability) do Playwright lista o que é esperado.
- **Esperar um elemento específico.** `page.locator("div.product").first.wait_for()` espera o primeiro card de produto aparecer.
- **Esperar uma resposta específica.** `page.expect_response(...)` espera a requisição de dados da página voltar e entrega o JSON direto.
- **Não usar esperas fixas.** `page.wait_for_timeout(5000)` é útil em testes, mas pouco confiável em produção.

A documentação do Playwright desaconselha o estado de carregamento `networkidle`, ou seja, esperar o tráfego de rede parar por um tempo; em páginas cujos scripts de analytics e anúncios continuam enviando requisições, esse estado pode nunca chegar.

O exemplo abaixo abre a página por um proxy, bloqueia imagens e fontes para reduzir a carga, captura a resposta da requisição de dados e depois lê os cards renderizados:

```python
from playwright.sync_api import sync_playwright

PROXY = {"server": "http://pr.proxynet.io:8000", "username": "user", "password": "pass"}

with sync_playwright() as p:
    browser = p.chromium.launch(proxy=PROXY)
    page = browser.new_page()

    # Não baixar imagens e fontes, para economizar tempo e tráfego
    page.route("**/*.{png,jpg,jpeg,webp,gif,woff2}", lambda route: route.abort())

    with page.expect_response(lambda r: "/api/products" in r.url and r.ok) as response:
        page.goto("https://example.com/deals")
    api_data = response.value.json()           # pegar o JSON direto

    page.locator("div.product").first.wait_for()  # ou esperar os cards renderizados
    names = page.locator("div.product h2").all_inner_texts()

    print(len(api_data["products"]), names)
    browser.close()
```

Neste exemplo, o JSON capturado com `expect_response` muitas vezes basta sozinho; nem é preciso ler os cards. No fim, você usa o navegador só para fazer a página enviar essa requisição com os parâmetros e cookies certos.

No Playwright, os dados do proxy são informados ao iniciar o navegador, com usuário e senha em campos separados. Para fluxos em que o mesmo IP precisa ser mantido durante a sessão, pode-se usar um endereço de saída fixo; para muitas páginas independentes, um [Proxies rotativos](https://proxynet.io/pt-br/rotating-proxy), que dá um IP diferente a cada conexão. Explicamos por que telas de verificação aparecem na automação de navegador em [Puppeteer e CAPTCHA](/pt-br/blog/puppeteer-captcha).

## Custo: quando o navegador headless é desnecessário?

| Abordagem | Quando funciona | Velocidade e recursos | Fragilidade | Atenção a |
|---|---|---|---|---|
| Cliente HTTP + HTML | A página é estática, o conteúdo está no código-fonte | Muito rápida, muito leve | Sensível a mudanças de design | Ancorar seletores em atributos estáveis |
| JSON embutido no HTML | Páginas híbridas como Next.js, Nuxt | Muito rápida, leve | A estrutura de dados pode mudar | Tratamento de erro que confira o caminho do JSON |
| Requisição de API em segundo plano | O conteúdo chega em JSON | Muito rápida, a mais leve | Independe do design, a API pode mudar | Termos do site, tokens, limites de taxa |
| Navegador headless | Há interação, os dados não são acessíveis de outro jeito | Lenta, pesada | Sensível à lógica de espera | Bloquear imagens, manter a concorrência baixa |

Casos típicos em que o navegador headless é desnecessário:

- O conteúdo já está no código-fonte da página.
- Os dados vêm de uma requisição JSON que pode ser repetida com parâmetros simples.
- Os dados estão em um bloco `__NEXT_DATA__` ou JSON-LD.
- O JavaScript só é usado para navegar entre páginas, e o conteúdo vem do servidor em cada página.

Casos em que o navegador headless é realmente necessário:

- O conteúdo só carrega com rolagem, clique ou interação com formulário, e a requisição de API correspondente não pode ser repetida.
- As requisições precisam de assinaturas ou tokens de curta duração gerados pelo JavaScript da página.
- A saída visual da página (uma captura de tela, uma verificação de layout) é o próprio trabalho.

Se usar navegador headless, ajuste o número de páginas simultâneas aos recursos da máquina; tratamos disso em [Concorrência e paralelismo](/pt-br/blog/concurrency-vs-parallelism).

## Casos de uso

- **Monitoramento de preços no e-commerce:** a página de categoria é dinâmica, mas a lista de produtos vem de uma única requisição `/api/products`. A requisição de API em vez do navegador headless; para ver preços em países diferentes, um [Proxies residenciais](https://proxynet.io/pt-br/residential-proxy) que sai por conexões domésticas reais. A configuração está na nossa página de [solução de proxy para e-commerce](/pt-br/e-commerce-proxy).
- **Um site de classificados feito com Next.js:** os dados estão em `__NEXT_DATA__`. Um cliente HTTP e a análise do JSON bastam.
- **Uma lista de avaliações com rolagem infinita:** encontra-se a requisição de paginação enviada ao rolar e percorre-se em loop com o parâmetro `cursor`.
- **Um painel de conta que exige interação (a sua própria conta):** login, escolha de filtros e download de relatórios com navegador headless; o mesmo IP durante a sessão. A configuração geral está na nossa página de [solução de extração de dados](/pt-br/data-scraping).

## Erros comuns

- **Partir primeiro para o navegador headless.** A requisição JSON no painel Network muitas vezes entrega os mesmos dados de forma muito mais barata.
- **Esperar um tempo fixo.** `time.sleep(5)` desperdiça tempo em respostas rápidas e ainda dá resultado vazio nas lentas.
- **Esperar por `networkidle`.** Pode nunca chegar em páginas que continuam enviando requisições.
- **Baixar todos os recursos.** Imagens, fontes e vídeos não são necessários para os dados; bloqueá-los economiza tempo e tráfego.
- **Confundir resultado vazio com página dinâmica.** Uma página de verificação ou um bloqueio também dá resultado vazio; confira o código de status e o conteúdo.
- **Usar uma API interna como se fosse pública.** Os termos e os limites de taxa do site também valem para essas requisições.
- **Recorrer a ferramentas de evasão de detecção.** Plugins que tentam fazer o navegador "parecer humano" escondem a origem do problema; revisar velocidade, escopo e permissão é um caminho mais sólido. Discutimos por que essas ferramentas são problemáticas em [Como usar o undetected ChromeDriver para web scraping](/pt-br/blog/how-to-use-undetected-chromedriver-for-web-scraping).

## Guia de decisão

| Resultado do diagnóstico | Recomendação |
|---|---|
| O texto está no código-fonte da página | Cliente HTTP + análise de HTML |
| O código-fonte tem `__NEXT_DATA__` ou JSON-LD | Cliente HTTP + análise de JSON |
| O painel Network tem uma requisição JSON com os dados | Repetir a requisição diretamente |
| A requisição JSON precisa de assinatura de curta duração | Navegador headless + `expect_response` |
| O conteúdo só chega com interação | Navegador headless + espera com locators |
| O navegador headless está lento e pesado | Bloquear imagens, reduzir a concorrência |
| Sem conteúdo no HTML e código de status 403/429 | Não é dinâmica, é bloqueio; diagnostique |

## Perguntas frequentes

### Por que o requests não traz o conteúdo que vejo no navegador?

Porque o `requests` só busca o primeiro HTML que o servidor envia e não executa JavaScript. Se o conteúdo da página é carregado depois com JavaScript, esse HTML só contém o esqueleto. Procure no painel Network a requisição de onde vêm os dados, ou JSON embutido no HTML.

### Qual é a diferença entre navegador headless e navegador normal?

O mecanismo é o mesmo; a diferença é que no modo headless nenhuma janela é desenhada na tela. O carregamento da página, a execução de JavaScript e as requisições de rede funcionam como em um navegador normal. Alguns sites conseguem distinguir navegadores headless por pequenas diferenças.

### Playwright ou Selenium?

Os dois rodam páginas dinâmicas. O Playwright oferece em uma única API recursos que ajudam no scraping, como espera automática, captura de respostas de rede e bloqueio de requisições; o Selenium é mais antigo, multilinguagem e tem um ecossistema amplo. Continuar com o que o seu projeto atual já usa costuma ser o mais prático.

### Usar uma requisição de API escondida é legal?

Uma requisição ser tecnicamente pública não torna o uso dela irrestrito. Considere os termos de uso do site, as regras do robots.txt e as leis de proteção de dados pessoais; se houver API oficial, prefira-a. Se tiver dúvida, busque orientação jurídica.

### Como usar navegador headless com proxy?

No Playwright, o proxy é informado com o parâmetro `proxy` ao iniciar o navegador; endereço do servidor, usuário e senha são campos separados. No Puppeteer e no Selenium, ele é passado ao navegador como argumento de inicialização, e a autenticação é tratada separadamente.

### Como obter dados de páginas com rolagem infinita?

Primeiro, encontre no painel Network a requisição de paginação enviada ao rolar. Ela geralmente traz um número de página ou um valor `cursor` e pode ser repetida em loop. Se a requisição não puder ser repetida, role a página aos poucos em um navegador headless e espere, a cada passo, a chegada de novos cards.

## Em resumo

Em uma página estática, o conteúdo está pronto no HTML e uma requisição HTTP simples basta; em uma página dinâmica, o conteúdo chega depois por JavaScript e uma requisição HTTP devolve um esqueleto. Antes de partir para um navegador headless, veja o código-fonte, procure no painel Network a requisição JSON com os dados e confira se há dados embutidos no HTML. Quando o navegador for realmente necessário, use a espera com locators e `expect_response` em vez de esperas fixas, bloqueie imagens e mantenha a concorrência baixa. Tipos de proxy adequados ao seu trabalho de coleta de dados estão nos nossos [serviços de proxy](/pt-br/proxy).
