Puppeteer mit Proxy: Authentifizierung, Rotation und SOCKS5

Veröffentlicht:

14 Min. Lesezeit

Acar Diveroli
Autor: Acar Diveroli
Proxynet-Wortmarke und weißes Puppeteer-Logo nebeneinander in einem technischen Rahmen, verbunden durch ein dünnes Kreuz

Sie schreiben --proxy-server=http://user:pass@host:port in die Startargumente, so wie Sie einen Proxy für cURL angeben würden, und der erste page.goto() bricht mit net::ERR_NO_SUPPORTED_PROXIES ab. Sie nehmen die Zugangsdaten heraus, und der Fehler lautet nun net::ERR_INVALID_AUTH_CREDENTIALS. Puppeteer hat keine eigene Proxy-Option. Es startet Chrome, und Chrome entscheidet, wie ein Proxy verwendet wird; die meisten Antworten stecken deshalb in den Regeln von Chrome und nicht in der API von Puppeteer.

Dieser Beitrag behandelt das Start-Flag und page.authenticate(), einen Proxy pro Browser-Context, Sticky- und Rotating-Sitzungen, SOCKS5, die Bypass-Liste, die Prüfung der Ausgangs-IP, die typischen Fehler und ein vollständiges Skript mit Wiederholungen. Jedes Beispiel lief am 6. Oktober 2026 mit Puppeteer 25.12.0 und dem Chrome-154-Build, den es herunterlädt, gegen einen lokalen HTTP-Proxy mit Benutzername und Passwort sowie gegen einen lokalen SOCKS5-Server. Die zitierten Fehlermeldungen sind genau die, die wir erhalten haben.

Wie leitet Puppeteer den Datenverkehr über einen Proxy?

Puppeteer ist eine Node.js-Bibliothek, die einen echten Browser aus dem Code heraus steuert. Sie öffnet selbst keine Netzwerkverbindungen; das tut der Browser. Ein Proxy ist deshalb eine Chrome-Einstellung, die entweder als Kommandozeilen-Flag beim Browserstart oder als Option beim Anlegen eines Browser-Contexts übergeben wird. Ein Browser-Context ist eine isolierte Sitzung innerhalb eines Browsers mit eigenen Cookies, eigenem Cache und eigenem Speicher, ähnlich einem Inkognitofenster.

Die Anmeldung am Proxy ist ein eigener Schritt, weil Chrome keine Zugangsdaten aus der Proxy-Adresse liest. Unser Test-Proxy hat für eine HTTPS-Seite diesen Ablauf protokolliert:

  1. Chrome sendet CONNECT httpbin.org:443 und bittet den Proxy damit, einen Tunnel zur Website zu öffnen. Bei einer http://-Seite sendet es direkt die Anfrage selbst.
  2. Die Anfrage enthält keine Zugangsdaten, also antwortet der Proxy mit 407 Proxy Authentication Required. RFC 9110 definiert 407 als Gegenstück zu 401 auf der Seite des Proxys.
  3. Puppeteer fängt diese Aufforderung ab und antwortet mit dem Benutzernamen und dem Passwort, die page.authenticate() übergeben wurden.
  4. Chrome wiederholt die Anfrage mit einem Proxy-Authorization-Header, der Proxy antwortet mit 200, und die verschlüsselte Verbindung zur Website wird innerhalb des Tunnels aufgebaut. Der Proxy sieht den Hostnamen, nicht den Seiteninhalt.
  5. Chrome legt die Zugangsdaten im Authentifizierungs-Cache des Contexts ab und sendet sie bei späteren Verbindungen sofort mit.

Wegen Schritt 5 gehören die Zugangsdaten am Ende zum Context, wie die Tests weiter unten zeigen.

Wie wird ein Proxy mit --proxy-server und page.authenticate() gesetzt?

npm i puppeteer installiert die Bibliothek und lädt ein passendes Chrome herunter; puppeteer-core ist dieselbe API ohne diesen Download. Die minimale Einrichtung:

js
import puppeteer from "puppeteer";

const browser = await puppeteer.launch({
  args: ["--proxy-server=http://pr.proxynet.io:8000"],
});
const page = await browser.newPage();
await page.authenticate({ username: "user", password: "pass" });

await page.goto("https://httpbin.org/ip");
console.log(await page.$eval("body", (el) => el.innerText)); // die Ausgangs-IP, die die Website sieht

await browser.close();

Das http:// im Flag beschreibt die Verbindung zum Proxy, nicht die Seiten. Ein HTTP-Proxy transportiert auch https://-Websites durch den Tunnel, und ihr Inhalt bleibt zwischen Chrome und der Website verschlüsselt. Die Referenz zu page.authenticate() ergänzt, dass Puppeteer dafür im Hintergrund das Abfangen von Anfragen (Request Interception) einschaltet, was etwas Leistung kosten kann; mit null schalten Sie es wieder ab.

In unseren Tests gingen drei Dinge schief, und aus jedem folgt eine feste Regel:

  • Rufen Sie page.authenticate() vor dem ersten goto() auf. Erst danach aufgerufen, scheiterte die erste Navigation mit net::ERR_INVALID_AUTH_CREDENTIALS; erst die nächste kam durch.
  • Lassen Sie die Zugangsdaten aus dem Flag heraus. Mit user:pass@ in der Adresse lehnte Chrome 154 den gesamten Wert mit net::ERR_NO_SUPPORTED_PROXIES ab. Ältere Anleitungen beschreiben an dieser Stelle einen 407; heute erreicht die Anfrage den Proxy gar nicht.
  • Senden Sie Proxy-Authorization nicht selbst. Als wir den Header mit page.setExtraHTTPHeaders() setzten, verweigerte Chrome die Navigation mit net::ERR_INVALID_ARGUMENT.
Wert von --proxy-serverErgebnis in Chrome 154
http://pr.proxynet.io:8000Funktioniert; HTTPS-Seiten laufen durch einen CONNECT-Tunnel
pr.proxynet.io:8000Funktioniert; ohne Schema nimmt Chrome einen HTTP-Proxy an
http://user:pass@pr.proxynet.io:8000net::ERR_NO_SUPPORTED_PROXIES
socks5://host:portFunktioniert nur, wenn der Server kein Passwort verlangt
socks5h://host:portnet::ERR_NO_SUPPORTED_PROXIES; Chrome kennt dieses Schema nicht

Wie bekommt jeder Browser-Context einen eigenen Proxy?

Puppeteer 22 hat createIncognitoBrowserContext(), den Namen aus vielen älteren Anleitungen, in browser.createBrowserContext() umbenannt. Dessen BrowserContextOptions enthalten zwei Proxy-Felder, proxyServer und proxyBypassList. Der Browser kann ohne Proxy starten, während jeder Context seinen eigenen bekommt:

js
import puppeteer from "puppeteer";

const PROXY = "http://pr.proxynet.io:8000";
// Eine Sticky-Sitzung pro Context: gleiche Sitzungs-ID -> gleiche Ausgangs-IP für bis zu 600 Sekunden
const sessions = ["a1b2c3", "d4e5f6"];

const browser = await puppeteer.launch(); // der Browser selbst startet ohne Proxy
for (const id of sessions) {
  const context = await browser.createBrowserContext({ proxyServer: PROXY });
  const page = await context.newPage();
  await page.authenticate({ username: `user-session-${id}-ttl-600`, password: "pass" });

  for (let i = 0; i < 2; i++) {
    await page.goto("https://httpbin.org/ip");
    console.log(id, await page.$eval("body", (el) => el.innerText));
  }
  await context.close(); // Cookies, Cache und Proxy-Einstellung verschwinden mit dem Context
}
await browser.close();

Unser Test-Proxy vergibt die Ausgänge nach Sitzungs-ID, so wie es ein Gateway tut. Die beiden Contexts gingen über zwei verschiedene Adressen hinaus, und jeder behielt seine Adresse bei der zweiten Anfrage. Zwei weitere Ergebnisse sind für echte Aufgaben wichtig:

  • Zugangsdaten gehören zum Context, nicht zur Seite. Eine zweite Seite im selben Context rief page.authenticate() mit einem anderen Benutzernamen auf, und trotzdem trug jede ihrer Anfragen weiterhin den ersten Benutzernamen. Eine Seite, die die Methode nie aufgerufen hatte, kam mit denselben zwischengespeicherten Zugangsdaten durch.
  • Ein Context ohne proxyServer erbt den Proxy des Start-Flags, behält aber seine eigenen Zugangsdaten. Mit --proxy-server am Browser gingen zwei Contexts mit eigenen Sitzungs-IDs trotzdem über zwei verschiedene Ausgänge hinaus.
Flag --proxy-servercreateBrowserContext({ proxyServer })
GeltungsbereichDer gesamte BrowserNur dieser Context
Getrennte Ausgänge in einem BrowserJa, ein Benutzername pro ContextJa, sogar getrennte Proxy-Hosts
Proxy wechselnBrowser neu startenContext schließen, einen neuen öffnen
Geeignet fürEin Skript, ein AusgangViele unabhängige Sitzungen parallel

Die Arbeitsregel lautet: eine Sitzung, ein Context, ein Benutzername.

Rotating oder Sticky: Welcher Sitzungstyp passt zu einem Browser?

Ein Browser sendet nicht eine Anfrage pro Seite. Eine Produktseite lädt das HTML-Dokument, Skripte, Stylesheets, Bilder und API-Aufrufe im Hintergrund, oft von mehreren Hosts über mehrere Verbindungen. An einem rotierenden Gateway kann jede neue Verbindung eine neue Ausgangs-IP bekommen; in unserem Test ging sogar das Favicon einer Seite über eine andere Adresse hinaus als die Seite selbst. Für voneinander unabhängige Seiten ist das harmlos. Bei einem Login, einem Warenkorb oder einem mehrstufigen Formular sieht die Website dagegen einen Besucher, der zwischen Adressen springt, und beendet die Sitzung womöglich.

Das Gateway liest den Sitzungstyp aus dem Benutzernamen, den der Endpoint-Generator im Dashboard für Sie zusammensetzt. Seine Teile sind leicht zu lesen: -country-de wählt das Ausgangsland (die Stadt wählen Sie im selben Generator), -session-a1b2c3-ttl-600 hält für diese Sitzungs-ID einen Ausgang für die angegebene Zahl an Sekunden, irgendwo zwischen 1 und 60 Minuten, und ein Benutzername ohne Sitzungsteil rotiert.

Für unabhängige Seiten wie Kategorielisten oder Suchergebnisse, die in kurzlebigen Contexts geöffnet werden, eignet sich ein Rotierender Proxy.

Für alles, was einen Zustand hält, eignet sich ein Sticky-Proxy: ein Context, eine Sitzungs-ID, länger gehalten, als der Ablauf dauert. Wenn die Aufgabe endet, schließen Sie den Context, damit Cookies und IP gemeinsam enden.

Rotation verteilt unabhängige Arbeit auf verschiedene Ausgänge; sie ist kein Weg um die Limits einer Website herum. Ein 429 Too Many Requests oder 403 Forbidden fordert Sie auf, langsamer zu werden, und eine neue IP ändert an dieser Antwort nichts.

Funktioniert Puppeteer mit einem SOCKS5-Proxy?

Ja, aber ohne Benutzername und Passwort. Die Proxy-Dokumentation von Chromium hält fest, dass für SOCKSv5 kein Authentifizierungsverfahren unterstützt wird. Unser lokaler SOCKS5-Server hat das von der anderen Seite gesehen: Die Begrüßung von Chrome bot nur die Methode 0x00 an, „keine Authentifizierung“. Ein Server, der die Methode mit Benutzername und Passwort (0x02) verlangte, musste dieses Angebot ablehnen, und die Navigation scheiterte mit net::ERR_SOCKS_CONNECTION_FAILED. page.authenticate() änderte daran nichts, weil es bei SOCKS5 keine Aufforderung nach Art von 407 gibt, auf die Puppeteer antworten könnte.

Die Lösung besteht darin, sich über die IP-Adresse auszuweisen. Tragen Sie die öffentliche IP des Rechners, auf dem Chrome läuft, im Dashboard in die IP-Whitelist ein (bis zu 10 Adressen), nehmen Sie den SOCKS5-Port, den der Endpoint-Generator anzeigt, und starten Sie ohne Zugangsdaten:

js
import puppeteer from "puppeteer";

// Kein Benutzername, kein Passwort: Die öffentliche IP dieses Rechners muss auf der IP-Whitelist stehen.
// SOCKS5_PORT: der SOCKS5-Port, den der Endpoint-Generator anzeigt.
const { SOCKS5_PORT } = process.env;

const browser = await puppeteer.launch({
  args: [`--proxy-server=socks5://pr.proxynet.io:${SOCKS5_PORT}`],
});
const page = await browser.newPage();
await page.goto("https://httpbin.org/ip");
console.log(await page.$eval("body", (el) => el.innerText));
await browser.close();

Chrome schickte dem SOCKS5-Server Hostnamen, keine IP-Adressen. DNS wird also am Proxy aufgelöst, und die von cURL bekannte Gewohnheit socks5h:// ist hier nicht nötig. Für Webseiten ist ein HTTP-Proxy meist die einfachere Wahl, weil er mit page.authenticate() funktioniert.

Wie werden bestimmte Adressen vom Proxy ausgenommen?

Das Flag --proxy-bypass-list oder proxyBypassList als Array an einem Context listet Hosts auf, die Chrome direkt erreicht. Im Flag werden die Regeln durch Semikolons oder Kommas getrennt: --proxy-bypass-list=*.internal.example;192.168.0.0/16. In unserem Test umging ein gelisteter Host den Proxy vollständig und wurde auf dem lokalen Rechner aufgelöst.

Chrome hat außerdem implizite Regeln: localhost, *.localhost, 127.0.0.1/8 und [::1] nutzen nie den Proxy, auch bei leerer Liste. Wenn Sie gegen einen lokalen Server testen und das Proxy-Log stumm bleibt, liegt es daran. Die Sonderregel <-loopback> hebt diese impliziten Regeln auf; damit lief unsere Anfrage an 127.0.0.1 über den Proxy.

Wie wird die Ausgangs-IP geprüft?

Vergleichen Sie im selben Browser einen direkten Context mit einem Context, der über den Proxy läuft. Wenn sich die Adressen unterscheiden, geht der Datenverkehr über den Proxy:

js
import puppeteer from "puppeteer";

async function exitIp(context, credentials) {
  const page = await context.newPage();
  if (credentials) await page.authenticate(credentials);
  await page.goto("https://httpbin.org/ip");
  const { origin } = JSON.parse(await page.$eval("body", (el) => el.innerText));
  await page.close();
  return origin;
}

const browser = await puppeteer.launch();
const direct = await browser.createBrowserContext();
const proxied = await browser.createBrowserContext({ proxyServer: "http://pr.proxynet.io:8000" });

const own = await exitIp(direct);
const viaProxy = await exitIp(proxied, { username: "user", password: "pass" });
console.log({ own, viaProxy, proxyWorks: own !== viaProxy });
await browser.close();

Wenn Sie ein bestimmtes Land ansteuern, schlagen Sie die Ausgangs-IP zusätzlich in einem Geolocation-Dienst nach. Schlägt die Prüfung fehl, nehmen Sie Chrome aus der Gleichung: Funktioniert mein Proxy? So testen Sie einen Proxy enthält die Kommandozeilentests, die ein Netzwerkproblem von einem Codeproblem unterscheiden.

Welche Fehler treten auf, und was bedeuten sie?

Jeden dieser Fehler haben wir im lokalen Testaufbau selbst erzeugt:

Was Sie sehenWas passiert istWas zu tun ist
net::ERR_PROXY_CONNECTION_FAILEDChrome konnte den Proxy nicht erreichen: falscher Host oder Port, oder ausgehender Datenverkehr wird blockiertAdresse und Port prüfen; Wiederholen hilft nicht
net::ERR_INVALID_AUTH_CREDENTIALSDer Proxy verlangte eine Anmeldung, und Chrome hatte keine Zugangsdatenpage.authenticate() vor dem ersten goto() aufrufen
goto() liefert Status 407Das Passwort war falsch; Puppeteer versuchte es einmal und gab aufresponse.status() prüfen, Zugangsdaten korrigieren
net::ERR_TUNNEL_CONNECTION_FAILEDDie Anmeldung klappte, aber der Proxy konnte den Tunnel zur Website nicht öffnenZieladresse prüfen
net::ERR_NO_SUPPORTED_PROXIESChrome lehnte den Wert von --proxy-server abuser:pass@ entfernen, http:// oder socks5:// verwenden
net::ERR_SOCKS_CONNECTION_FAILEDDer SOCKS5-Server will ein Passwort, oder Ihre IP steht nicht auf der WhitelistIP auf die Whitelist setzen oder den HTTP-Port nutzen
Seite lädt, aber die IP ist Ihre eigeneEine Bypass-Regel oder ein Loopback-Host hat den Proxy umgangenBypass-Liste und <-loopback> prüfen

Die Zeile mit dem falschen Passwort bleibt am längsten unentdeckt: Nichts wirft einen Fehler, und das Skript arbeitet mit der 407-Seite des Proxys weiter. In einem Desktop-Browser hat der Tunnelfehler mehr Ursachen, vom HTTPS-Scan eines Virenschutzprogramms bis zu einem veralteten System-Proxy; dazu mehr in err_tunnel_connection_failed in Chrome und Edge beheben. Während der Entwicklung zeigen zwei Listener den echten Fehler hinter einem Timeout:

js
page.on("requestfailed", (req) => console.log("FEHLGESCHLAGEN", req.url(), req.failure()?.errorText));
page.on("response", (res) => res.status() >= 400 && console.log(res.status(), res.url()));

Vollständiges Beispiel: parallele Contexts, Sticky-Sitzungen und Wiederholungen

Das Skript öffnet sechs per JavaScript gerenderte Seiten mit höchstens drei Contexts gleichzeitig. Jeder Versuch bekommt einen frischen Context und eine frische Sticky-Sitzung, sodass Cookies und Ausgangs-IP gemeinsam wechseln. Bilder, Medien und Fonts werden blockiert, um Datenverkehr zu sparen; page.setRequestInterception() erledigt das, ohne page.authenticate() zu stören. Timeouts und Tunnelfehler werden mit exponentiellem Backoff wiederholt, während Konfigurationsfehler und ein 403 oder 429 der Website die Aufgabe beenden.

js
import puppeteer from "puppeteer";

const PROXY = "http://pr.proxynet.io:8000";
const USER = "user";
const PASS = "pass";
const URLS = Array.from({ length: 6 }, (_, i) => `https://quotes.toscrape.com/js/page/${i + 1}/`);
const CONCURRENCY = 3; // gleichzeitig offene Contexts
const ATTEMPTS = 3;
const BLOCKED = new Set(["image", "media", "font"]);
// Konfigurationsfehler: Warten und Wiederholen beheben sie nicht
const FATAL = ["ERR_PROXY_CONNECTION_FAILED", "ERR_NO_SUPPORTED_PROXIES", "ERR_INVALID_AUTH_CREDENTIALS"];

const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));

async function scrape(browser, url) {
  for (let attempt = 1; attempt <= ATTEMPTS; attempt++) {
    // Ein Context pro Versuch: eigene Cookies und eigene Sticky-Sitzung (eine Ausgangs-IP)
    const context = await browser.createBrowserContext({ proxyServer: PROXY });
    try {
      const page = await context.newPage();
      const session = crypto.randomUUID().slice(0, 8);
      await page.authenticate({ username: `${USER}-session-${session}-ttl-300`, password: PASS });
      await page.setRequestInterception(true);
      page.on("request", (req) => (BLOCKED.has(req.resourceType()) ? req.abort() : req.continue()));

      const res = await page.goto(url, { waitUntil: "domcontentloaded", timeout: 30_000 });
      const status = res ? res.status() : 0;
      if (status === 403 || status === 429) {
        // Die Website lehnt ab oder bremst Sie: Eine neue IP ist nicht die Antwort
        throw Object.assign(new Error(`HTTP ${status}: anhalten und die Anfragerate senken`), { fatal: true });
      }
      if (status >= 400) throw new Error(`HTTP ${status}`);

      await page.waitForSelector("div.quote span.text", { timeout: 10_000 }); // per JavaScript gerendert
      return await page.$$eval("div.quote span.text", (els) => els.map((el) => el.textContent));
    } catch (err) {
      err.fatal ||= FATAL.some((code) => err.message.includes(code));
      if (err.fatal || attempt === ATTEMPTS) throw err;
      await sleep(2 ** attempt * 1000 + Math.random() * 1000); // vor dem nächsten Versuch warten
    } finally {
      await context.close();
    }
  }
}

const browser = await puppeteer.launch();
const queue = [...URLS];
const results = new Map();
await Promise.all(
  Array.from({ length: CONCURRENCY }, async () => {
    while (queue.length) {
      const url = queue.shift();
      try {
        results.set(url, `${(await scrape(browser, url)).length} Zitate`);
      } catch (err) {
        results.set(url, `FEHLER ${err.message.split("\n")[0]}`);
        if (err.fatal) queue.length = 0; // die ganze Aufgabe stoppen, nicht nur diese Seite
      }
    }
  }),
);
await browser.close();
for (const url of URLS) console.log(url, results.get(url) ?? "übersprungen");

Über den lokalen Test-Proxy kamen alle sechs Seiten in weniger als fünf Sekunden mit je zehn Zitaten zurück. Mit absichtlich falsch eingetipptem Proxy-Port stoppten die drei laufenden Seiten sofort mit net::ERR_PROXY_CONNECTION_FAILED, und die anderen drei wurden übersprungen; eine lokale Testseite, die mit 429 antwortete, beendete die Aufgabe auf dieselbe Weise. Beginnen Sie mit einem niedrigen Wert für CONCURRENCY: Jeder Context belegt Speicher, und auch die Zielseite hat Grenzen.

Einsatzbereiche

  • Per JavaScript gerenderte Katalog- und Preisseiten, die ein einfacher HTTP-Client leer sieht.
  • Prüfen, wie Ihre eigene Website, Ihre Preise oder Ihre Consent-Banner aus einem anderen Land aussehen, mit einem Context pro Land.
  • Screenshots und PDFs öffentlicher Seiten, so wie ein Besucher in einem bestimmten Land sie sieht.
  • Überwachung Ihrer eigenen Seiten und Checkout-Abläufe von außerhalb Ihres Netzwerks.

Für alle gilt derselbe Rahmen: Halten Sie sich an die robots.txt und die Nutzungsbedingungen der Website, nutzen Sie eine offizielle API, wo es eine gibt, und halten Sie die Anfragerate auf einem Niveau, das die Website tragen kann.

Häufige Fehler

  • user:pass@ in --proxy-server schreiben. Chrome lehnt den Wert mit net::ERR_NO_SUPPORTED_PROXIES ab.
  • Einen neuen Benutzernamen pro Seite erwarten. Chrome behält die ersten Zugangsdaten für den gesamten Context; nutzen Sie einen Context pro Sitzung.
  • Ein Passwort für SOCKS5 versuchen. Chrome bietet nur „keine Authentifizierung“ an; nutzen Sie eine IP-Whitelist oder den HTTP-Port.
  • Gegen localhost testen und dem Ergebnis trauen. Loopback-Adressen umgehen den Proxy, solange Sie nicht <-loopback> hinzufügen.
  • Eine 407-Antwort wie eine normale Seite behandeln. Ein falsches Passwort wirft keinen Fehler; prüfen Sie response.status().
  • Nach einem 429 oder 403 die IP rotieren. Werden Sie stattdessen langsamer; das Limit betrifft Ihr Verhalten, nicht Ihre Adresse.
  • Contexts offen lassen. Jeder belegt Speicher; schließen Sie sie in einem finally-Block.

Entscheidungshilfe

BedarfEmpfehlung
Ein Skript, ein AusgangFlag --proxy-server plus page.authenticate()
Viele unabhängige Sitzungen in einem BrowsercreateBrowserContext({ proxyServer }), eine Sitzungs-ID pro Context
Viele Seiten, die nicht voneinander abhängenRotierender Benutzername, kurzlebige Contexts
Login, Warenkorb oder mehrstufiges FormularSticky-Sitzung, die länger hält als der Ablauf, ein Context
SOCKS5 ist PflichtIP-Whitelist, keine Zugangsdaten
Ein Werkzeug, das nur das Start-Flag annimmtIP-Whitelist oder ein lokaler Forwarder wie proxy-chain
Interne Hosts müssen direkt erreichbar bleiben--proxy-bypass-list oder proxyBypassList

Häufige Fragen

Hat puppeteer.launch() eine Proxy-Option?

Nein. Der Proxy kommt als Chrome-Flag --proxy-server in args, die Anmeldung in page.authenticate(). Die eigene API von Puppeteer hat Proxy-Felder nur bei browser.createBrowserContext(): proxyServer und proxyBypassList.

Kann jede Seite ihren eigenen Proxy haben?

Nicht innerhalb eines Contexts. Proxy und zwischengespeicherte Zugangsdaten gehören zum Context, deshalb nutzte in unserem Test eine zweite Seite mit anderen Zugangsdaten trotzdem die ersten. Geben Sie jeder Seite, die einen eigenen Ausgang braucht, einen eigenen Context; ein Context ist weit günstiger als ein neuer Browser.

Was ist proxy-chain, und brauchen Sie es?

proxy-chain ist ein Open-Source-Paket für Node.js, dessen Funktion anonymizeProxy() einen lokalen Proxy ohne Passwort startet und den Datenverkehr an einen Upstream-Proxy mit Zugangsdaten weiterleitet, sodass Chrome eine einfache Adresse http://127.0.0.1:<port> bekommt. In unserem Test funktionierte das sowohl mit einem HTTP- als auch mit einem SOCKS5-Upstream, der ein Passwort verlangte. Mit page.authenticate() oder einer IP-Whitelist brauchen Sie es nicht.

Lässt sich der Proxy ohne Neustart des Browsers wechseln?

Ja, über Contexts. Schließen Sie den Context, öffnen Sie einen neuen mit einem anderen proxyServer oder einer anderen Sitzungs-ID, und rufen Sie page.authenticate() erneut auf. Nur das Flag --proxy-server ist für die gesamte Lebensdauer des Browsers festgelegt.

Warum zeigt das Proxy-Log Anfragen, die Sie nicht gestellt haben?

Der eigene Hintergrund-Traffic von Chrome, etwa Update-Prüfungen und Anfragen an Google-Dienste, geht an denselben Proxy. In unserem Log kam der Großteil davon ohne Zugangsdaten an und erhielt einen 407, weil page.authenticate() nur auf Aufforderungen aus dem Datenverkehr der Seiten antwortet. Ihre Seiten beeinflusst das nicht.

Verhindert ein Proxy CAPTCHAs in Puppeteer?

Nein. Ein Proxy ändert, woher der Datenverkehr kommt, während CAPTCHAs auch auf die Anfragerate, auf Browsersignale und auf widersprüchliche Sitzungen reagieren. Die Ursachen und die legitimen Wege, sie seltener zu machen, stehen in Puppeteer und CAPTCHA: warum, und wie seltener?.

Fazit

In Puppeteer ist der Proxy eine Chrome-Einstellung: das Flag --proxy-server für den gesamten Browser oder proxyServer für einen einzelnen Context, mit der Anmeldung über page.authenticate() vor der ersten Navigation. Zugangsdaten werden pro Context zwischengespeichert; bauen Sie deshalb jede Sitzung als eigenen Context, wählen Sie Sticky oder Rotating über den Benutzernamen und nutzen Sie für SOCKS5 eine IP-Whitelist. Prüfen Sie response.status() und halten Sie die Anfragerate höflich. Für Seiten, die sich über gewöhnliche Heimanschlüsse öffnen sollen, liefert ein Residential-Proxy die Ausgänge.

ChatGPT fragenClaude fragen