Skip to content
Advanced / 8 min read

ProxyScrape-Web-Scraping mit Playwright: Headless Chromium über HTTP- und SOCKS5-Proxies leiten

Konfigurieren Sie Playwright so, dass headless Chromium über ProxyScrape ausgeht, verifizieren Sie die Exit-IP vor dem Scraping, rotieren Sie HTTP- oder SOCKS5-Endpunkte pro Browserkontext und klassifizieren Sie Proxy-Fehler korrekt.

Linux HTTP(S) Web Scraping

Überblick

Headless-Browser platzieren die Proxy-Konfiguration im Browser, nicht in Ihrer HTTP-Bibliothek. Das ändert das Setup: Sie konfigurieren den Proxy beim Starten eines Browsers oder beim Erstellen eines Browserkontexts, Sie müssen die Proxy-Authentifizierung behandeln, und die Konfiguration pro Kontext ermöglicht Rotation, ohne Chromium für jede Anfrage neu zu starten.

Dieses Tutorial verwendet Playwright mit Chromium und ProxyScrape-Endpunkten (HTTP oder SOCKS5). Es behandelt den Start mit einem einzelnen Endpunkt, eine Exit-IP-Prüfung, Rotation pro Kontext über mehrere Endpunkte, Proxy-Fehlerbehandlung und die Ausführung auf Linux-CI, ohne den Speicher zu erschöpfen.

Wann ein Headless-Browser das richtige Werkzeug ist

  • Das Ziel rendert Inhalte clientseitig, sodass eine einfache HTTP-Anfrage eine leere Hülle zurückgibt.
  • Sie müssen interagieren: Formulare ausfüllen, klicken, scrollen oder auf Netzwerk-Idle warten.
  • Sie benötigen Browserverhalten wie Cookies, localStorage oder Service Worker.
  • Sie validieren, wie sich eine Website für eine bestimmte Region verhält.

Bleiben Sie bei einem einfachen HTTP-Client, wenn die Seite serverseitig gerendert wird; das ist deutlich günstiger und schneller.

Voraussetzungen

  • Node.js 18 oder neuer und npm.
  • Playwright mit Chromium installiert.
  • ProxyScrape-Anmeldedaten sowie Host und Port für http und/oder socks5. Residential-, Datacenter- und Mobile-Proxies funktionieren hier alle; wählen Sie den Typ, der zu den Abwehrmechanismen des Ziels passt.
  • Linux, macOS oder Windows. Die CI-Hinweise gehen von Debian- oder Ubuntu-Runnern aus.

Schritte

Schritt 1 — Playwright und Chromium installieren

mkdir proxyscrape-playwright
cd proxyscrape-playwright
npm init -y
npm install playwright
npx playwright install --with-deps chromium

--with-deps installiert die Betriebssystembibliotheken, die Chromium auf Linux-Images und CI-Runnern benötigt.

Schritt 2 — Entscheiden, ob der Proxy für den Browser oder den Kontext gilt

Bereich Wo er gesetzt wird Wofür er verwendet wird Einschränkungen
Browser chromium.launch({ proxy }) Ein einzelner Endpunkt für den gesamten Lauf Jeder Kontext teilt sich eine Exit-IP
Kontext browser.newContext({ proxy }) Rotation: ein Endpunkt pro Kontext, parallel Engine-Unterstützung unterscheidet sich; Chromium ist die sichere Standardwahl — bestätigen Sie die Proxy-Unterstützung pro Kontext für Ihre Playwright-Version, bevor Sie sich in Firefox oder WebKit darauf verlassen

Zwei praktische Regeln:

  • Übergeben Sie Anmeldedaten als separate Felder username und password. Chromium ignoriert Anmeldedaten, die in die Proxy-Server-URL eingebettet sind.
  • Der server-Wert benötigt ein Schema: http://host:port für die HTTP-Endpunkte von ProxyScrape und socks5://host:port für SOCKS5.

Schritt 3 — Über einen Endpunkt starten und die Exit-IP prüfen

// launch-proxy.mjs
import { chromium } from 'playwright';

const proxy = {
  server: process.env.PROXYSCRAPE_SERVER, // http://HOST:PORT or socks5://HOST:PORT
  username: process.env.PROXYSCRAPE_USERNAME,
  password: process.env.PROXYSCRAPE_PASSWORD,
};

const browser = await chromium.launch({ proxy, headless: true });
try {
  const context = await browser.newContext();
  const page = await context.newPage();
  await page.goto('https://api.ipify.org?format=json', { waitUntil: 'domcontentloaded' });
  console.log('exit ip:', await page.textContent('body'));
} finally {
  await browser.close();
}
export PROXYSCRAPE_SERVER="http://host-from-your-plan:port-from-your-plan"
export PROXYSCRAPE_USERNAME="your-username"
export PROXYSCRAPE_PASSWORD="your-password"
node launch-proxy.mjs

Wenn dies eine IP ausgibt, funktioniert der Proxy-Pfad durchgängig. Wenn es hängt oder einen Fehler wirft, springen Sie zur Fehlerbehebungstabelle, bevor Sie weiteren Code schreiben.

Schritt 4 — Schnell fehlschlagen, wenn der Datenverkehr nicht über den Proxy läuft

Eine stille Fehlkonfiguration — zum Beispiel ein Tippfehler in der Server-Zeichenfolge — kann dazu führen, dass Sie von Ihrer eigenen IP aus scrapen. Erfassen Sie Ihre direkte IP einmal und prüfen Sie dann dagegen.

// exit-ip.mjs
export async function getExitIp(page) {
  await page.goto('https://api.ipify.org?format=json', { waitUntil: 'domcontentloaded' });
  const body = await page.textContent('body');
  return JSON.parse(body).ip;
}

export function assertProxied(exitIp, directIp) {
  if (directIp && exitIp === directIp) {
    throw new Error(`Traffic was not proxied: exit IP ${exitIp} equals the direct IP`);
  }
}

Geordnetes Vorgehen:

  1. Führen Sie ein einmaliges Skript ohne konfigurierten Proxy aus und zeichnen Sie das Ergebnis als DIRECT_IP auf.
  2. Führen Sie das Skript erneut mit Proxy aus.
  3. Rufen Sie assertProxied(exitIp, process.env.DIRECT_IP) vor jedem echten Scraping auf und lassen Sie den Lauf fehlschlagen, wenn die Zusicherung auslöst.

Schritt 5 — Endpunkte über Browserkontexte rotieren

Ein Browser, viele Kontexte, jeweils ein Endpunkt.

// rotate-contexts.mjs
import { chromium } from 'playwright';

const ENDPOINTS = (process.env.PROXYSCRAPE_ENDPOINTS ?? '')
  .split(',')
  .map((entry) => entry.trim())
  .filter(Boolean)
  .map((entry) => {
    const [scheme, host, port, username, password] = entry.split('|');
    return { server: `${scheme}://${host}:${port}`, username, password };
  });

if (ENDPOINTS.length === 0) {
  throw new Error('Set PROXYSCRAPE_ENDPOINTS to http|HOST|PORT|USER|PASS,socks5|HOST|PORT|USER|PASS');
}

const TARGETS = (process.env.TARGETS ?? 'https://example.com,https://example.org').split(',');
const CONCURRENCY = Number(process.env.CONCURRENCY ?? 3);
const USER_AGENT =
  'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0 Safari/537.36';

function isProxyError(error) {
  const message = String(error && error.message ? error.message : '');
  return /ERR_PROXY|ERR_TUNNEL|ERR_PROXY_AUTH/i.test(message);
}

async function runBatch(browser, proxy, urls) {
  const context = await browser.newContext({ proxy, userAgent: USER_AGENT });
  context.setDefaultTimeout(30000);
  const results = [];
  try {
    const page = await context.newPage();
    for (const url of urls) {
      try {
        const response = await page.goto(url, { waitUntil: 'domcontentloaded' });
        results.push({ url, endpoint: proxy.server, status: response ? response.status() : null });
      } catch (error) {
        results.push({
          url,
          endpoint: proxy.server,
          error: isProxyError(error) ? `proxy: ${error.message}` : error.message,
        });
      }
    }
  } finally {
    await context.close();
  }
  return results;
}

const browser = await chromium.launch({ headless: true });
try {
  const queue = ENDPOINTS.map((proxy, index) => ({
    proxy,
    urls: TARGETS.filter((_, targetIndex) => targetIndex % ENDPOINTS.length === index),
  }));
  const workers = Array.from({ length: Math.min(CONCURRENCY, queue.length) }, async () => {
    const collected = [];
    while (queue.length > 0) {
      const job = queue.shift();
      collected.push(...(await runBatch(browser, job.proxy, job.urls)));
    }
    return collected;
  });
  const results = (await Promise.all(workers)).flat();
  for (const result of results) {
    console.log(JSON.stringify(result));
  }
} finally {
  await browser.close();
}
export PROXYSCRAPE_ENDPOINTS="http|HOST|PORT|USER|PASS,socks5|HOST|PORT|USER|PASS"
export TARGETS="https://example.com,https://example.org,https://example.net"
export CONCURRENCY=3
node rotate-contexts.mjs

Warum diese Form funktioniert:

  • Der Browser startet einmal; Kontexte sind günstig und jeder trägt seinen eigenen Endpunkt.
  • Der Worker-Pool begrenzt, wie viele Kontexte gleichzeitig existieren, was der wichtigste Speicherhebel in Playwright ist.
  • Jedes Ergebnis zeichnet den Endpunkt auf, der es bedient hat, sodass Fehler ohne Raten zugeordnet werden können.

Schritt 6 — Proxy-Fehler als Endpunktfehler behandeln, nicht als Seitenfehler

  • net::ERR_PROXY_CONNECTION_FAILED — der Proxy-Host oder -Port ist falsch oder nicht erreichbar.
  • net::ERR_TUNNEL_CONNECTION_FAILED — der CONNECT-Tunnel zu einem HTTPS-Ziel wurde abgelehnt; bestätigen Sie, dass das konfigurierte Protokoll zum Endpunkt passt.
  • net::ERR_PROXY_AUTH_REQUESTED — Anmeldedaten wurden abgelehnt; überprüfen Sie Benutzername und Passwort erneut und übergeben Sie sie als Felder statt innerhalb der Server-URL.
  • TimeoutError — der Endpunkt ist langsam oder das Ziel ist schwer; erhöhen Sie das Timeout und verringern Sie die Parallelität.

Wenn einer dieser Fehler auftritt, entfernen Sie diesen Endpunkt für den Rest des Laufs aus der Warteschlange und fahren Sie mit den anderen fort. Wiederholtes erneutes Versuchen desselben defekten Endpunkts kostet nur Zeit.

Schritt 7 — Auf Linux-CI ausführen, ohne Ressourcen zu erschöpfen

  1. Installieren Sie Browser-Abhängigkeiten in der Pipeline mit npx playwright install --with-deps chromium.
  2. Halten Sie die Parallelität bei zwei oder drei Kontexten pro vCPU auf einem CI-Runner; jeder Kontext trägt eine gerenderte Seite.
  3. Starten Sie den Browser einmal pro Prozess und schließen Sie Kontexte in finally-Blöcken.
  4. Bevorzugen Sie Datacenter-Proxies für Ziele mit hohem Volumen und geringer Abwehr bei geplanten Jobs und behalten Sie Residential- oder Mobile-Endpunkte für die schwierigeren Ziele.
  5. Achten Sie auf die Stückkosten: Pay-as-you-go-Abrechnung erleichtert den Start stoßweiser CI-Läufe, während ein monatlicher Pauschaltarif für einen festen nächtlichen Job normalerweise günstiger ist.

Fehlerbehebung

Symptom Wahrscheinliche Ursache Behebung
Seiten laden, aber von Ihrer eigenen IP Proxy ignoriert (Tippfehler in server oder die Option wurde nicht an den Kontext übergeben) Protokollieren Sie die Exit-IP mit dem Helfer aus Schritt 4 und lösen Sie einen Fehler aus, wenn sie mit der direkten IP übereinstimmt
ERR_PROXY_CONNECTION_FAILED Falscher Host oder Port oder CI-Egress blockiert Testen Sie denselben Endpunkt mit curl und prüfen Sie dann die Netzwerkrichtlinie
ERR_PROXY_AUTH_REQUESTED Anmeldedaten falsch oder in der URL eingebettet Übergeben Sie die Felder username und password, niemals user:pass@host
ERR_TUNNEL_CONNECTION_FAILED bei HTTPS Protokollkonflikt zwischen dem Endpunkt und dem von Ihnen konfigurierten Schema Probieren Sie den HTTP-Endpunkt, dann den SOCKS5-Endpunkt, und behalten Sie den, der funktioniert
Jeder Kontext meldet dieselbe Exit-IP Proxy auf Browser-Ebene statt auf Kontext-Ebene verwendet oder die Engine unterstützt keine Proxies pro Kontext Verwenden Sie chromium.launch() und setzen Sie proxy bei jedem newContext()
Der Lauf bringt den CI-Runner um Zu viele gleichzeitige Kontexte Verringern Sie CONCURRENCY auf 2 und messen Sie den Speicher erneut

Zusammenfassung

  • In Playwright gehört der Proxy zum Startaufruf oder zum Browserkontext, nicht zu einzelnen Anfragen.
  • Verifizieren Sie die Exit-IP vor dem Scraping; ein falsch konfigurierter Proxy kann Sie stillschweigend auf Ihrer eigenen IP lassen.
  • Rotieren Sie, indem Sie jedem Kontext seinen eigenen ProxyScrape-HTTP- oder SOCKS5-Endpunkt geben und begrenzen, wie viele Kontexte gleichzeitig laufen.
  • Klassifizieren Sie ERR_PROXY*- und ERR_TUNNEL*-Fehler als Endpunktprobleme, verwerfen Sie den Endpunkt und lassen Sie den Lauf weiterlaufen.