Jak Scrape JavaScript- Ciężkie Strony internetowe

Scrape JavaScript- renderowane zawartość z przeglądarek bez głowy i proxy. Puppeteer, Playwright i chromedp prowadnice z optymalizacją wydajności i strategii przechwytywania API.

Jak Scrape JavaScript- Ciężkie Strony internetowe

Wyzwanie JavaScript- Rendered Content

Nowoczesne strony internetowe coraz częściej polegają na JavaScript do renderowania treści. Aplikacje jednostronicowe (SPA) zbudowane za pomocą React, Vue lub Angular obciążają minimalną powłokę HTML, następnie pobierają i renderują stronę klienta danych. Po wykonaniu prostego żądania HTTP na tych stronach, otrzymasz pustą lub niekompletną stronę, ponieważ zawartość istnieje dopiero po wykonaniu JavaScript.

Scraping JavaScript- ciężkich stron internetowych wymaga Przeglądarki bez głowy - prawdziwe silniki przeglądarki uruchomione bez widocznego okna, które mogą wykonać JavaScript, renderować DOM i współdziałać z elementami strony. W połączeniu z proxy, przeglądarki bez głowy odblokowują dane nawet z najbardziej dynamicznych stron internetowych.

Ten przewodnik jest częścią naszego Kompletny przewodnik do Web Scraping ProxiesAby uniknąć wykrycia podczas korzystania z przeglądarek bez głowy, zobacz Jak systemy Anti- Bot wykrywają efekty.

Kiedy potrzebujesz przeglądarki bez głowy?

ScenariuszProste HTTPPrzeglądarka bez głowy
Statyczne strony HTMLDziała doskonalePrzesada
Serwerowane strony z APIPrace (bezpośrednio wciśnij API)Niepotrzebne
SPA (React, Vue, Angular)Dostaje pustą powłokęWymagane
Nieskończony zwój / leniwy załadunekNie można uruchomićWymagane
Zawartość za logowaniem + JSTrudnościZalecane
Strony z kontrolami anty-bot JSWykrywanie awariiWymagane
Zawsze sprawdzić, czy strona ma API lub server- strony renderowania przed sięgnięciem po przeglądarkę bez głowy. Wiele stron "JavaScript- heavy" rzeczywiście mają punkty końcowe API, które zwracają czysty JSON - znacznie szybciej i taniej do skrobania.

Puppeteer + Proxies (Node.js)

Puppeteer kontroluje chrom / chrom programowo. Jest to najbardziej dojrzałe narzędzie przeglądarki bez głowy dla Node.js.

Podstawowe ustawienia z 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`);

Zoptymalizowane rozdrabnianie wielu stron

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();

Playwright + Proxies (Python)

Playwright jest nowszą alternatywą, która obsługuje Chromium, Firefox i WebKit. Jego Python API jest czysty i dobrze nadaje się do skrobania.

Podstawowe ustawienia

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 Playwright for Parallel Scraping

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))

Idź: Korzystanie chromedp z 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))
}

Strategie optymalizacji wydajności

Bezgłowe przeglądarki są 10- 50x wolniejsze niż proste żądania HTTP. Oto strategie w celu zminimalizowania luki wydajności:

1. Blokowanie niepotrzebnych zasobów

Obrazy, CSS, czcionki i pliki multimedialne nie są potrzebne do ekstrakcji danych. Blokowanie ich dramatycznie przyspiesza ładunki stron:

# 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. Użyj prawej strategii oczekiwania

StrategiaPrędkośćWiarygodnośćUse Case
domcontentloadedSzybkoMoże przegapić dane asyncStrony z danymi inline
loadŚredniDobrze.Większość stron
networkidlePowoliNajwyższyCiężkie SPA, nieskończony zwój
Określone pokrętłoZmiennaNajwyższyKiedy znasz element docelowy

3. Ponowne użycie sytuacji przeglądarki

Uruchomienie przeglądarki zajmuje 1-3 sekundy. Dla zeskrobywania partii, uruchom raz i utworzyć nowe strony / konteksty dla każdego 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. Intercept API połączenia zamiast Parsing DOM

Wiele SPA pobiera dane z API. Interceptuj te rozmowy API bezpośrednio - otrzymasz czysty JSON bez parsowania 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`);

Bezgłowy przeglądarka vs porównanie HTTP

MetricProste HTTP + ProxyPrzeglądarka bez głowy + Proxy
Prędkość na stronę0.5-2 sekundy3- 15 sekund
Pamięć na instancję50 MB200- 500 MB
Wykorzystanie procesoraMinimalneZnaczący
Szerokość pasma na stronę50- 200 KB2- 10 MB (z zasobami)
Edycja JavaScriptNie.Pełna
Bypass anty-botLimitedLepiej (prawdziwa przeglądarka)
Równoczesne strony100 +3- 10 na maszynę

Najlepsze praktyki

  • Zawsze najpierw spróbuj HTTP. Przed użyciem przeglądarki bez głowy należy sprawdzić punkty końcowe API, zawartość w serwerze lub JSON osadzone w HTML.
  • Zablokuj niepotrzebne zasoby. Obrazy, CSS i czcionki dodają czas obciążenia bez dostarczania danych.
  • Do oczekiwania należy używać specjalnych selektorów. networkidle jest bezpieczny, ale powolny. Poczekaj na konkretny element, którego potrzebujesz.
  • Ponowne użycie instancji przeglądarki. Uruchom raz, tworzyć nowe konteksty na stronie.
  • Intercept API dzwoni. Wiele SPA załadować dane przez API - przechwycić JSON bezpośrednio.
  • Ograniczona zbieżność. Bezgłowe przeglądarki są pamięciowo intensywne. 3-5 stron równoległych na GB pamięci RAM to dobra zasada.
  • Użyj proxy mieszkalnych. ProxyHat proxy mieszkalne zapewnić najwyższe wyniki zaufania, zmniejszając wykrywalność podczas uruchamiania przeglądarek bez głowy.

Do obsługi CAPTCHA, że przeglądarki bez głowy napotkać, patrz Obsługa CAPTCHA Podczas skracania. Do skalowania bez głowy przeglądarki scrating, przeczytaj Jak rozdrabniać infrastrukturę.

Zacznij od Python SDK, Węzeł SDKlub Go SDK dla integracji proxy, i zbadać ProxyHat do skracania stron internetowych.

Często zadawane pytania

Czy zawsze potrzebuję przeglądarki bez głowy dla stron JavaScript?

Nie. Wiele stron JavaScript- ciężkie wczytywanie danych z punktów końcowych API. Sprawdź zakładkę Sieć przeglądarki dla żądań XHR / pobrać - jeśli dane pochodzą z API, możesz nazwać to API bezpośrednio z prostymi żądaniami HTTP za pośrednictwem proxy, który jest znacznie szybszy.

Puppeteer czy Playwright - co jest lepsze do skrobania?

Playwright jest ogólnie zalecane dla nowych projektów. Obsługuje wiele silników przeglądarki (Chromium, Firefox, WebKit), ma lepsze auto- oczekiwania, natywne wsparcie async w Python, i built- w konfiguracji proxy. Puppeteer jest bardziej dojrzały i ma większy ekosystem, jeśli jesteś w świecie Node.js.

Ile stron przeglądarki bez głowy mogę uruchomić jednocześnie?

Każda strona zużywa 200- 500 MB pamięci RAM. Na maszynie z 8 GB RAM, 3- 10 jednoczesnych stron jest realistyczne. Użyj blokady zasobów (obrazy, CSS), aby zmniejszyć pamięć. Dla wyższej współzależności, dystrybuować na wielu maszynach za pomocą architektury opartej na kolejkach.

Po co używać proxy z przeglądarkami bez głowy?

Nawet z prawdziwą przeglądarką, powtarzające się żądania z tego samego IP zostają zablokowane. Proxies obracają Twój IP tak, że każde obciążenie strony wydaje się pochodzić od innego użytkownika. Proxy mieszkaniowe poprzez ProxyHat zapewniają najwyższe wyniki zaufania, minimalizując bloki i CAPTCHA.

Gotowy, aby zacząć?

Dostęp do ponad 50 mln rezydencjalnych IP w ponad 148 krajach z filtrowaniem AI.

Zobacz cenyProxy rezydencjalne
← Powrót do Bloga