O desafio do conteúdo gerado pelo JavaScript
Os sites modernos dependem cada vez mais do JavaScript para renderizar conteúdo. Aplicações de página única (SPAs) construídas com React, Vue ou Angular carregam uma shell HTML mínima, então obtêm e renderizam dados do lado do cliente. Quando você faz uma simples solicitação HTTP para estes sites, você recebe uma página vazia ou incompleta porque o conteúdo só existe após a execução do JavaScript.
Raspar sites pesados de JavaScript requer Navegadores sem cabeça — motores de navegador reais que funcionam sem uma janela visível que pode executar JavaScript, renderizar DOM e interagir com elementos de página. Combinado com proxies, navegadores sem cabeça desbloqueiam dados dos sites mais dinâmicos.
Este guia faz parte do nosso Guia completo de Web Raspando Proxies. Para evitar a detecção ao usar navegadores sem cabeça, consulte Como sistemas antibot detectam proxies.
Quando você precisa de um navegador sem cabeça?
| Cenário | HTTP simples | Navegador sem cabeça |
|---|---|---|
| Páginas HTML estáticas | Funciona perfeitamente. | Excedente |
| Páginas renderizadas ao servidor com API | Funciona (hit a API diretamente) | Não necessário |
| SPA (React, Vue, Angular) | Obtém a shell vazia | Requerido |
| Rolagem infinita / carregamento preguiçoso | Incapaz de activar | Requerido |
| Conteúdo por trás do login + JS | Difícil | Recomendado |
| Páginas com verificações JS anti-bot | Detecção de falhas | Requerido |
Verifique sempre se o site tem uma API ou renderização do lado do servidor antes de procurar um navegador sem cabeça. Muitos sites "JavaScript-heavy" realmente têm terminais API que retornam JSON limpo — muito mais rápido e mais barato para raspar.
Puppeteer + Proxies (Node.js)
O Puppeteer controla programaticamente o Chrome/Chromium. É a ferramenta de navegador sem cabeça mais madura para Node.js.
Configuração Básica com o ProxyHat
const puppeteer = require('puppeteer');
async function scrapeWithPuppeteer(url) {
const browser = await puppeteer.launch({
headless: 'new',
args: [
'--proxy-server=http://gate.proxyhat.com:8080',
'--no-sandbox',
'--disable-setuid-sandbox',
'--disable-dev-shm-usage',
],
});
const page = await browser.newPage();
// Authenticate with proxy
await page.authenticate({
username: 'USERNAME',
password: 'PASSWORD',
});
// Set realistic viewport and user agent
await page.setViewport({ width: 1920, height: 1080 });
await page.setUserAgent(
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 ' +
'(KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'
);
try {
await page.goto(url, { waitUntil: 'networkidle2', timeout: 60000 });
// Wait for specific content to render
await page.waitForSelector('.product-list', { timeout: 10000 });
const content = await page.content();
const data = await page.evaluate(() => {
return Array.from(document.querySelectorAll('.product-item')).map(el => ({
name: el.querySelector('.product-name')?.textContent?.trim(),
price: el.querySelector('.product-price')?.textContent?.trim(),
url: el.querySelector('a')?.href,
}));
});
return { html: content, data };
} finally {
await browser.close();
}
}
// Usage
const result = await scrapeWithPuppeteer('https://example.com/products');
console.log(`Found ${result.data.length} products`);Raspamento Multi-Page otimizado
const puppeteer = require('puppeteer');
class PuppeteerScraper {
constructor(concurrency = 3) {
this.concurrency = concurrency;
this.browser = null;
}
async init() {
this.browser = await puppeteer.launch({
headless: 'new',
args: [
'--proxy-server=http://gate.proxyhat.com:8080',
'--no-sandbox',
'--disable-setuid-sandbox',
'--disable-dev-shm-usage',
'--disable-gpu',
'--disable-extensions',
],
});
}
async scrapePage(url) {
const page = await this.browser.newPage();
await page.authenticate({ username: 'USERNAME', password: 'PASSWORD' });
await page.setViewport({ width: 1920, height: 1080 });
// Block unnecessary resources to speed up loading
await page.setRequestInterception(true);
page.on('request', (req) => {
const type = req.resourceType();
if (['image', 'stylesheet', 'font', 'media'].includes(type)) {
req.abort();
} else {
req.continue();
}
});
try {
await page.goto(url, { waitUntil: 'networkidle2', timeout: 30000 });
const content = await page.content();
return { url, status: 'success', html: content };
} catch (err) {
return { url, status: 'error', error: err.message };
} finally {
await page.close();
}
}
async scrapeMany(urls) {
const results = [];
for (let i = 0; i < urls.length; i += this.concurrency) {
const batch = urls.slice(i, i + this.concurrency);
const batchResults = await Promise.all(
batch.map(url => this.scrapePage(url))
);
results.push(...batchResults);
console.log(`Progress: ${results.length}/${urls.length}`);
}
return results;
}
async close() {
if (this.browser) await this.browser.close();
}
}
// Usage
const scraper = new PuppeteerScraper(3);
await scraper.init();
const results = await scraper.scrapeMany(urls);
await scraper.close();dramaturgo + Proxies (Python)
Playwright é uma nova alternativa que suporta Chromium, Firefox e WebKit. Sua API Python é limpa e adequada para raspar.
Configuração Básica
from playwright.sync_api import sync_playwright
def scrape_with_playwright(url: str) -> dict:
"""Scrape a JavaScript-heavy page using Playwright with ProxyHat proxy."""
with sync_playwright() as p:
browser = p.chromium.launch(
headless=True,
proxy={
"server": "http://gate.proxyhat.com:8080",
"username": "USERNAME",
"password": "PASSWORD",
}
)
context = browser.new_context(
viewport={"width": 1920, "height": 1080},
user_agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
"AppleWebKit/537.36 (KHTML, like Gecko) "
"Chrome/120.0.0.0 Safari/537.36",
)
page = context.new_page()
try:
page.goto(url, wait_until="networkidle", timeout=60000)
# Wait for dynamic content
page.wait_for_selector(".product-list", timeout=10000)
# Extract data using page.evaluate
products = page.evaluate("""() => {
return Array.from(document.querySelectorAll('.product-item')).map(el => ({
name: el.querySelector('.product-name')?.textContent?.trim(),
price: el.querySelector('.product-price')?.textContent?.trim(),
url: el.querySelector('a')?.href,
}));
}""")
return {"url": url, "products": products, "html": page.content()}
finally:
browser.close()Async dramaturgo para raspagem paralela
import asyncio
from playwright.async_api import async_playwright
async def scrape_batch(urls: list[str], concurrency: int = 3) -> list[dict]:
"""Scrape multiple JS-heavy pages in parallel using Playwright."""
results = []
async with async_playwright() as p:
browser = await p.chromium.launch(
headless=True,
proxy={
"server": "http://gate.proxyhat.com:8080",
"username": "USERNAME",
"password": "PASSWORD",
}
)
semaphore = asyncio.Semaphore(concurrency)
async def scrape_one(url: str) -> dict:
async with semaphore:
context = await browser.new_context(
viewport={"width": 1920, "height": 1080},
)
page = await context.new_page()
# Block heavy resources
await page.route("**/*.{png,jpg,jpeg,gif,svg,css,woff,woff2}",
lambda route: route.abort())
try:
await page.goto(url, wait_until="networkidle", timeout=30000)
html = await page.content()
return {"url": url, "status": "success", "html": html}
except Exception as e:
return {"url": url, "status": "error", "error": str(e)}
finally:
await context.close()
tasks = [scrape_one(url) for url in urls]
results = await asyncio.gather(*tasks)
await browser.close()
return results
# Usage
urls = [f"https://example.com/product/{i}" for i in range(50)]
results = asyncio.run(scrape_batch(urls, concurrency=5))Ir: Usando cromedp com Proxies
package main
import (
"context"
"fmt"
"log"
"time"
"github.com/chromedp/chromedp"
)
func scrapeJSPage(targetURL string) (string, error) {
// Configure proxy
opts := append(chromedp.DefaultExecAllocatorOptions[:],
chromedp.ProxyServer("http://gate.proxyhat.com:8080"),
chromedp.Flag("headless", true),
chromedp.Flag("disable-gpu", true),
chromedp.Flag("no-sandbox", true),
chromedp.UserAgent("Mozilla/5.0 (Windows NT 10.0; Win64; x64) "+
"AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"),
)
allocCtx, cancel := chromedp.NewExecAllocator(context.Background(), opts...)
defer cancel()
ctx, cancel := chromedp.NewContext(allocCtx)
defer cancel()
ctx, cancel = context.WithTimeout(ctx, 60*time.Second)
defer cancel()
var htmlContent string
err := chromedp.Run(ctx,
chromedp.Navigate(targetURL),
chromedp.WaitVisible(".product-list", chromedp.ByQuery),
chromedp.OuterHTML("html", &htmlContent),
)
if err != nil {
return "", fmt.Errorf("scrape failed: %w", err)
}
return htmlContent, nil
}
func main() {
html, err := scrapeJSPage("https://example.com/products")
if err != nil {
log.Fatal(err)
}
fmt.Printf("Got %d bytes of rendered HTML\n", len(html))
}Estratégias de otimização de desempenho
Navegadores sem cabeça são 10-50x mais lentos do que simples solicitações HTTP. Aqui estão estratégias para minimizar a lacuna de desempenho:
1. Bloquear recursos desnecessários
Imagens, CSS, fontes e arquivos de mídia não são necessários para extração de dados. Bloqueá-los acelera dramaticamente cargas de página:
# Playwright resource blocking
async def fast_scrape(page, url):
# Block images, CSS, fonts, media
await page.route("**/*.{png,jpg,jpeg,gif,svg,css,woff,woff2,mp4,webm}",
lambda route: route.abort())
# Also block tracking scripts
await page.route("**/*google-analytics*", lambda route: route.abort())
await page.route("**/*facebook*", lambda route: route.abort())
await page.goto(url, wait_until="domcontentloaded") # Faster than networkidle
return await page.content()2. Use a estratégia de espera certa
| Estratégia | Velocidade | Confiabilidade | Caso de Uso |
|---|---|---|---|
domcontentloaded | Rápido | Pode perder os dados do assync | Páginas com dados em linha |
load | Médio | Bom. | A maioria das páginas |
networkidle | Devagar | Maior | SPA pesados, pergaminho infinito |
| Selector específico | Variável | Maior | Quando você conhece o elemento alvo |
3. Reutilizar instâncias de navegador
O lançamento de um navegador leva 1-3 segundos. Para raspar em lote, inicie uma vez e crie novas páginas/contextos para cada URL:
from playwright.sync_api import sync_playwright
class BrowserPool:
"""Reusable browser pool for efficient headless scraping."""
def __init__(self, pool_size: int = 3):
self.pool_size = pool_size
self.playwright = None
self.browsers = []
def start(self):
self.playwright = sync_playwright().start()
for _ in range(self.pool_size):
browser = self.playwright.chromium.launch(
headless=True,
proxy={
"server": "http://gate.proxyhat.com:8080",
"username": "USERNAME",
"password": "PASSWORD",
}
)
self.browsers.append(browser)
def get_browser(self, index: int):
return self.browsers[index % self.pool_size]
def stop(self):
for browser in self.browsers:
browser.close()
self.playwright.stop()
# Usage
pool = BrowserPool(pool_size=3)
pool.start()
for i, url in enumerate(urls):
browser = pool.get_browser(i)
context = browser.new_context()
page = context.new_page()
page.goto(url, wait_until="networkidle")
html = page.content()
context.close()
pool.stop()4. Interceptar chamadas API em vez de processar DOM
Muitos SPA obtêm dados de APIs. Intercepte essas chamadas API diretamente — você fica limpo JSON sem analisar HTML:
const puppeteer = require('puppeteer');
async function interceptAPIData(url) {
const browser = await puppeteer.launch({
headless: 'new',
args: ['--proxy-server=http://gate.proxyhat.com:8080'],
});
const page = await browser.newPage();
await page.authenticate({ username: 'USERNAME', password: 'PASSWORD' });
const apiResponses = [];
// Intercept XHR/fetch responses
page.on('response', async (response) => {
const url = response.url();
if (url.includes('/api/') || url.includes('/graphql')) {
try {
const json = await response.json();
apiResponses.push({ url, data: json });
} catch {
// Not JSON, skip
}
}
});
await page.goto(url, { waitUntil: 'networkidle2' });
await browser.close();
return apiResponses;
}
// Get clean API data instead of scraping DOM
const data = await interceptAPIData('https://example.com/products');
console.log(`Intercepted ${data.length} API calls`);Navegador sem Cabeça vs Comparação HTTP
| Métrico | HTTP simples + Proxy | Navegador + Proxy sem Cabeça |
|---|---|---|
| Velocidade por página | 0,5-2 segundos | 3-15 segundos |
| Memória por instância | ~ 50 MB | 200- 500 MB |
| Utilização da CPU | Mínimo | Significativo |
| Largura de banda por página | 50- 200 KB | 2-10 MB (com recursos) |
| Tradução de JavaScript | Não | Completo |
| Anti-bot bypass | Limitado | Melhor (navegador real) |
| Páginas simultâneas | 100+ | 3-10 por máquina |
Melhores Práticas
- Tente sempre HTTP primeiro. Verifique se existem endpoints de API, conteúdo renderizado ao servidor ou JSON incorporado no HTML antes de usar um navegador sem cabeça.
- Bloquear recursos desnecessários. Imagens, CSS e fontes adicionam tempo de carga sem fornecer dados.
- Use selectores específicos para esperar.
networkidleé seguro, mas lento. Espere pelo elemento específico que precisa. - Reutilizar instâncias do navegador. Lançar uma vez, criar novos contextos por página.
- Interceptar chamadas da API. Muitos SPA carregam dados via APIs — interceptam o JSON diretamente.
- Limitar a concorrência. Navegadores sem cabeça são intensivos em memória. 3-5 páginas simultâneas por GB de RAM é uma boa regra.
- Use proxies residenciais. ProxyHat proxies residenciais fornecer as maiores pontuações de confiança, reduzindo a detecção ao executar navegadores sem cabeça.
Para lidar com CAPTCHAs que navegadores sem cabeça encontrar, consulte Tratamento dos CAPTCHA Ao Raspar. Para raspar o navegador sem cabeça, leia Como escalar a infraestrutura de raspagem.
Comece com o Python SDK, Nó SDK, ou Ir SDK para integração de proxy, e explorar ProxyHat para raspar Web.
Perguntas Frequentes
Preciso sempre de um navegador sem cabeça para sites JavaScript?
Não. Muitos sites cheios de JavaScript carregam dados dos endpoints da API. Verifique a guia Rede do navegador para solicitações XHR/fetch — se os dados vêm de uma API, você pode chamar essa API diretamente com solicitações HTTP simples através de um proxy, que é muito mais rápido.
Puppeteer ou dramaturgo — o que é melhor para raspar?
O dramaturgo é geralmente recomendado para novos projetos. Ele suporta vários motores de navegador (Chromium, Firefox, WebKit), tem melhor espera automática, suporte nativo async em Python, e configuração de proxy incorporada. Puppeteer é mais maduro e tem um ecossistema maior se você estiver no mundo Node.js.
Quantas páginas de navegador sem cabeça posso executar simultaneamente?
Cada página consome 200-500 MB de RAM. Em uma máquina com 8 GB de RAM, 3-10 páginas concorrentes é realista. Use o bloqueio de recursos (imagens, CSS) para reduzir a memória. Para maior concorrência, distribua através de várias máquinas usando uma arquitetura baseada em fila.
Por que usar proxies com navegadores sem cabeça?
Mesmo com um navegador real, pedidos repetidos do mesmo IP são bloqueados. Proxies gire seu IP para que cada carga de página pareça vir de um usuário diferente. Proxies residenciais através do ProxyHat fornecem os maiores escores de confiança, minimizando blocos e CAPTCHAs.






