Como Fazer Scraping de Sites Pesados em JavaScript

Conteúdo renderizado em JavaScript com navegadores sem cabeça e proxies. Guias de configuração Puppeteer, Playwright e cromedp com otimização de desempenho e estratégias de interceptação de API.

Como Fazer Scraping de Sites Pesados em JavaScript

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?

Quando você precisa de um navegador sem cabeça?
CenárioHTTP simplesNavegador sem cabeça
Páginas HTML estáticasFunciona perfeitamente.Excedente
Páginas renderizadas ao servidor com APIFunciona (hit a API diretamente)Não necessário
SPA (React, Vue, Angular)Obtém a shell vaziaRequerido
Rolagem infinita / carregamento preguiçosoIncapaz de activarRequerido
Conteúdo por trás do login + JSDifícilRecomendado
Páginas com verificações JS anti-botDetecção de falhasRequerido
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

2. Use a estratégia de espera certa
EstratégiaVelocidadeConfiabilidadeCaso de Uso
domcontentloadedRápidoPode perder os dados do assyncPáginas com dados em linha
loadMédioBom.A maioria das páginas
networkidleDevagarMaiorSPA pesados, pergaminho infinito
Selector específicoVariávelMaiorQuando 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

Navegador sem Cabeça vs Comparação HTTP
MétricoHTTP simples + ProxyNavegador + Proxy sem Cabeça
Velocidade por página0,5-2 segundos3-15 segundos
Memória por instância~ 50 MB200- 500 MB
Utilização da CPUMínimoSignificativo
Largura de banda por página50- 200 KB2-10 MB (com recursos)
Tradução de JavaScriptNãoCompleto
Anti-bot bypassLimitadoMelhor (navegador real)
Páginas simultâneas100+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.

Pronto para começar?

Acesse mais de 50M de IPs residenciais em mais de 148 países com filtragem por IA.

Ver preçosProxies residenciais
← Voltar ao Blog