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?
| Scenariusz | Proste HTTP | Przeglądarka bez głowy |
|---|---|---|
| Statyczne strony HTML | Działa doskonale | Przesada |
| Serwerowane strony z API | Prace (bezpośrednio wciśnij API) | Niepotrzebne |
| SPA (React, Vue, Angular) | Dostaje pustą powłokę | Wymagane |
| Nieskończony zwój / leniwy załadunek | Nie można uruchomić | Wymagane |
| Zawartość za logowaniem + JS | Trudności | Zalecane |
| Strony z kontrolami anty-bot JS | Wykrywanie awarii | Wymagane |
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
| Strategia | Prędkość | Wiarygodność | Use Case |
|---|---|---|---|
domcontentloaded | Szybko | Może przegapić dane async | Strony z danymi inline |
load | Średni | Dobrze. | Większość stron |
networkidle | Powoli | Najwyższy | Ciężkie SPA, nieskończony zwój |
| Określone pokrętło | Zmienna | Najwyższy | Kiedy 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
| Metric | Proste HTTP + Proxy | Przeglądarka bez głowy + Proxy |
|---|---|---|
| Prędkość na stronę | 0.5-2 sekundy | 3- 15 sekund |
| Pamięć na instancję | 50 MB | 200- 500 MB |
| Wykorzystanie procesora | Minimalne | Znaczący |
| Szerokość pasma na stronę | 50- 200 KB | 2- 10 MB (z zasobami) |
| Edycja JavaScript | Nie. | Pełna |
| Bypass anty-bot | Limited | Lepiej (prawdziwa przeglądarka) |
| Równoczesne strony | 100 + | 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.
networkidlejest 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.






