Wissen

Residential Proxies mit Playwright verwenden

Eine praktische Anleitung zu Residential Proxies in Playwright: Authentifizierung, Rotation pro Kontext, Geo-Targeting, Bandbreite senken, und die Chromium-Fallstricke.

Chris Collins

Chris Collins

6. Juli 2026 · 10 Min. Lesezeit

Einen Residential Proxy in Playwright einzubinden, sind ein paar Zeilen, sobald man es einmal gesehen hat. Die Reibung ist nie die Launch-Option, es sind die Details, die niemand aufschreibt: wie die Zugangsdaten das Targeting kodieren, der Chromium-Fallstrick, der Proxies pro Kontext bricht, wie Rotation auf Browser-Kontexte abgebildet wird, und die Tatsache, dass jedes Bild, das der Browser lädt, Bandbreite ist, für die Sie zahlen.

Dies ist der Begleiter zu Residential Proxies mit Python verwenden, für die Browser-Seite. Copy-Paste-Beispiele, die tatsächlich laufen, plus die Teile, die aus einem funktionierenden Snippet eine Automatisierung machen, die die Produktion übersteht.

Alles unten nutzt das Shifter Residential-Gateway: ein Endpunkt, p.shifter.io:443, mit dem gesamten Targeting kodiert im Benutzernamen. Bei einem anderen Anbieter ist die Form dieselbe; tauschen Sie Host und Zugangsdaten-Format. Die Beispiele sind Node/JavaScript; ein Python-Playwright-Snippet folgt gegen Ende.

Das Eine, das Sie zuerst verstehen müssen

An einem Residential-Gateway trägt der Proxy-Benutzername Ihre Authentifizierung und Ihr Targeting. Sie wechseln nicht den Endpunkt, um Land oder Session zu ändern, Sie ändern die Benutzernamen-Zeichenfolge. Ein Benutzername sieht so aus:

customer-USERNAME-country-us-sid-abc123-ttl-600

Von links nach rechts gelesen: Konto-ID, dann Flags. country-us zielt auf die USA. sid-abc123 fixiert eine Sticky Session. ttl-600 hält diese IP für 600 Sekunden. Lassen Sie sid/ttl weg, und jede neue Verbindung rotiert zu einer neuen IP. Das Passwort ist konstant. Das ist das gesamte mentale Modell, der Rest ist, dies in Playwrights Proxy-Slot zu stecken.

Bewahren Sie Zugangsdaten in Umgebungsvariablen auf, nie hartkodiert:

const USER = process.env.SHIFTER_USER; // der Benutzername Ihres Kontos
const PASS = process.env.SHIFTER_PASS;
const GATEWAY = "http://p.shifter.io:443";

Ein Playwright-spezifischer Hinweis vorab: Setzen Sie Benutzername und Passwort nicht inline in die Proxy-URL. Chromium ignoriert inline Proxy-Zugangsdaten, sodass http://user:pass@host bei der Authentifizierung stillschweigend fehlschlägt. Playwright hat dedizierte Felder username und password am Proxy-Objekt, verwenden Sie diese.

Die 10-Zeilen-Version

Für einen einzelnen Proxy über den ganzen Browser setzen Sie proxy bei launch():

const { chromium } = require("playwright");
const USER = process.env.SHIFTER_USER;
const PASS = process.env.SHIFTER_PASS;
(async () => {
const browser = await chromium.launch({
proxy: {
server: "http://p.shifter.io:443",
username: `${USER}-country-us`, // das Targeting lebt hier
password: PASS,
},
});
const page = await browser.newPage();
await page.goto("https://api.ipify.org?format=json");
console.log(await page.textContent("body")); // {"ip":"<eine US-Residential-IP>"}
await browser.close();
})();

Das Feld username ist der Ort, an dem das gesamte Gateway-Modell lebt. ${USER}-country-us zielt auf die USA; fügen Sie sid/ttl für eine Sticky-IP hinzu, city/asn, um weiter einzugrenzen. Das Passwort ändert sich nie.

Der Chromium-Fallstrick: Proxies pro Kontext

Die Einzel-Proxy-Version ist gut für eine Identität. Aber der eigentliche Grund, Playwright zu nutzen, ist das Ausführen vieler Identitäten, und dafür wollen Sie einen anderen Proxy pro Browser-Kontext (jeder Kontext ist seine eigenen isolierten Cookies/Speicher, also sein eigener “Nutzer”).

Hier ist die Falle: In Chromium funktionieren Proxies pro Kontext nur, wenn Sie den Browser mit einem bereits gesetzten Proxy starten. Starten Sie ohne Proxy, und jedes newContext({ proxy }) wird stillschweigend ignoriert. Die Lösung ist ein Platzhalter-Server beim Start:

const browser = await chromium.launch({
proxy: { server: "per-context" }, // Platzhalter, aktiviert Proxying pro Kontext
});
// Jetzt kann jeder Kontext seinen eigenen echten Proxy + Targeting tragen
const ctxUS = await browser.newContext({
proxy: {
server: "http://p.shifter.io:443",
username: `${USER}-country-us-sid-a1-ttl-600`,
password: PASS,
},
});
const ctxDE = await browser.newContext({
proxy: {
server: "http://p.shifter.io:443",
username: `${USER}-country-de-sid-b2-ttl-600`,
password: PASS,
},
});

ctxUS surft auf einer US-IP, ctxDE auf einer deutschen, gleichzeitig, in einem Browser. Das ist das Muster, auf dem Sie aufbauen. (Firefox und WebKit brauchen den Platzhalter nicht zwingend, aber ihn zu setzen ist harmlos und hält Ihren Code Cross-Browser.)

Rotierend vs sticky, auf Playwright-Art

In einem rohen HTTP-Scraper rotieren Sie oft bei jeder Anfrage. In einem Browser ist das meist falsch: Ein echter Nutzer wechselt nicht mitten in der Session die IP, also ist eine IP, die zwischen Seitenaufrufen springt, während die Cookies konstant bleiben, ein Erkennungssignal. Die idiomatische Zuordnung lautet:

  • Ein Kontext = eine Sticky-Identität. Geben Sie jedem Kontext eine eindeutige sid und eine ttl, die die Session abdeckt, sodass alle seine Seitenaufrufe eine IP teilen, wie ein echter Nutzer.
  • Rotieren Sie, indem Sie neue Kontexte erstellen. Ein frischer Kontext mit einer neuen sid (oder ohne sid) bekommt eine neue IP. Schließen Sie den alten, wenn Sie fertig sind.
async function contextForSession(browser, sessionId, country = "us") {
return browser.newContext({
proxy: {
server: "http://p.shifter.io:443",
username: `${USER}-country-${country}-sid-${sessionId}-ttl-600`,
password: PASS,
},
});
}
// 50 Ziele verarbeiten, jedes mit eigener IP-stabiler Identität
for (let i = 0; i < 50; i++) {
const ctx = await contextForSession(browser, `job-${i}`, "us");
const page = await ctx.newPage();
await page.goto("https://example.com/item/" + i);
// ... scrapen ...
await ctx.close(); // fertig mit dieser Identität
}

Wählen Sie die sid selbst, irgendeine eindeutige Zeichenfolge pro logischer Session. Dieselbe Zeichenfolge gibt dieselbe IP zurück, bis die TTL abläuft. Führen Sie Kontexte nebenläufig aus (ein kleiner Pool) statt 50 auf einmal; mehr Browser sind nicht mehr Tempo, und ein Schwall von IPs, der ein Ziel bombardiert, löst weiterhin Verhaltenserkennung aus. Werden Sie konsistent statt sporadisch blockiert, liegt das Problem an IP-Qualität oder Verhalten, nicht an der Rotation, siehe wie man beim Scrapen nicht blockiert wird.

Geo-Targeting, und den Browser mit der IP in Einklang bringen

Weil das Targeting im Benutzernamen lebt, ist Geo nur ein Flag: country-de, fügen Sie city-berlin oder asn-3320 hinzu, um einzugrenzen. Aber mit einem echten Browser gibt es eine zweite Hälfte, die man übersieht: Ihr Browser-Fingerprint muss mit Ihrer IP übereinstimmen. Eine deutsche Residential-IP gepaart mit einem en-US-Locale und einer New Yorker Zeitzone ist ein Widerspruch, den Anti-Bot-Systeme sofort markieren. (Siehe warum Scraper blockiert werden.)

Playwright lässt Sie alles am Kontext ausrichten:

const ctx = await browser.newContext({
proxy: {
server: "http://p.shifter.io:443",
username: `${USER}-country-de-city-berlin-sid-de1-ttl-600`,
password: PASS,
},
locale: "de-DE",
timezoneId: "Europe/Berlin",
geolocation: { latitude: 52.52, longitude: 13.405 },
permissions: ["geolocation"],
});

Städtenamen nutzen das Gateway-Format (Kleinbuchstaben); kombinieren Sie Flags frei, die Reihenfolge ist dem Gateway egal. locale/timezoneId/geolocation an das Proxy-Land anzupassen, ist eine der wirkungsvollsten Maßnahmen, um wie ein echter lokaler Nutzer auszusehen.

Senken Sie Ihre Bandbreite (Sie zahlen pro GB)

Dies ist browser-spezifisch und es zählt, weil Residential Proxies pro Gigabyte abgerechnet werden. Ein Headless-Browser, sich selbst überlassen, lädt jedes Bild, jede Schrift, jedes Video und jedes Tracking-Skript der Seite herunter, das meiste davon brauchen Sie zum Scrapen nicht. Sie abzubrechen kann die Bandbreite (und die Kosten) um weit mehr als die Hälfte senken und beschleunigt Ihre Läufe.

Nutzen Sie context.route(), um schwere Ressourcentypen zu blockieren, bevor sie den Proxy erreichen:

await ctx.route("**/*", (route) => {
const type = route.request().resourceType();
if (["image", "media", "font"].includes(type)) {
return route.abort(); // nie heruntergeladen, nie abgerechnet
}
return route.continue();
});

Behalten Sie document, script, xhr und fetch (die eigentlichen Daten und das JS, das sie rendert); verwerfen Sie den Rest. Lädt die Seite Inhalt lazy hinter Bildern nach, testen Sie, dass Ihr Scrape noch funktioniert, aber für die meisten Jobs ist das geschenktes Geld. Es passt gut zum Pro-GB-Modell aus nach Bandbreite abgerechnete Residential Proxies.

Verifizieren, dass es tatsächlich funktioniert

Bevor Sie einer Konfiguration trauen, bestätigen Sie zwei Dinge: Die IP ist im richtigen Land (Geo) und sie ändert sich zwischen Kontexten (Rotation). Ein schneller Check:

const page = await ctx.newPage();
await page.goto("http://ip-api.com/json");
const data = JSON.parse(await page.textContent("body"));
console.log(data.query, data.countryCode); // erwarte eine DE-IP

Starten Sie zwei Kontexte mit unterschiedlichen sids, und Sie sollten zwei verschiedene IPs sehen, beide im richtigen Land. Ist das Land falsch, prüfen Sie die Schreibweise Ihrer Flags. Ändert sich die IP nie zwischen Kontexten, haben Sie wahrscheinlich den Platzhalter beim Start vergessen und Chromium ignoriert den Proxy pro Kontext.

Die Fehler, die tatsächlich auftreten

Playwright bringt Proxy-Probleme als Navigationsfehler zutage, nicht als HTTP-Statuscodes. Die drei, denen Sie begegnen werden:

  • net::ERR_TUNNEL_CONNECTION_FAILED Proxy-Authentifizierung fehlgeschlagen, oder ein nicht erkanntes Flag (ein Tippfehler in country oder asn, oder inline Zugangsdaten, die Chromium ignoriert hat). Korrigieren Sie die Felder username/password; Wiederholen hilft nicht.
  • net::ERR_PROXY_CONNECTION_FAILED das Gateway war nicht erreichbar, oder keine IP passte zu einem zu engen Filter (wie country + city + asn). Prüfen Sie die Konnektivität, dann lockern Sie ein Flag.
  • Eine Seite, die lädt, aber eine Sperre/CAPTCHA zeigt das Ziel, nicht der Proxy, weist Sie ab. Rotieren Sie zu einem neuen Kontext/IP, verlangsamen Sie, und richten Sie den Fingerprint wie oben aus.

Umhüllen Sie die Navigation mit einem kleinen Retry, das bei Fehler einen frischen Kontext erstellt:

async function gotoWithRetry(browser, url, country = "us", tries = 3) {
for (let attempt = 0; attempt < tries; attempt++) {
const ctx = await contextForSession(browser, `r-${Date.now()}-${attempt}`, country);
const page = await ctx.newPage();
try {
await page.goto(url, { waitUntil: "domcontentloaded", timeout: 30000 });
return { ctx, page }; // der Aufrufer schließt ctx bei Fertigstellung
} catch (e) {
await ctx.close();
if (attempt === tries - 1) throw e;
await new Promise((r) => setTimeout(r, 2 ** attempt * 1000)); // 1s, 2s
}
}
}

Ein neuer Kontext bedeutet eine neue IP, sodass ein Retry tatsächlich eine andere Route bekommt, nicht dieselbe fehlschlagende.

Ein Hinweis zu SOCKS5

Das Gateway spricht SOCKS5, aber es gibt eine Playwright-Einschränkung, die man kennen sollte: Playwright/Chromium unterstützt keine authentifizierten SOCKS-Proxies. Da das Gateway per Benutzernamen authentifiziert (dort lebt Ihr Targeting), ist SOCKS5 hier nicht praktikabel, nutzen Sie den oben gezeigten HTTP-Proxy, der ohnehin der richtige Standard für Browser-Scraping ist. Die SOCKS-Abwägungen finden sich in SOCKS5-Proxies für Automatisierung.

Dasselbe in Python

Playwrights Python-API spiegelt die JavaScript-Version; das Proxy-Objekt ist identisch:

from playwright.sync_api import sync_playwright
import os
USER, PASS = os.environ["SHIFTER_USER"], os.environ["SHIFTER_PASS"]
with sync_playwright() as p:
browser = p.chromium.launch(proxy={"server": "per-context"})
ctx = browser.new_context(proxy={
"server": "http://p.shifter.io:443",
"username": f"{USER}-country-us-sid-a1-ttl-600",
"password": PASS,
})
page = ctx.new_page()
page.goto("https://api.ipify.org")
print(page.text_content("body"))
browser.close()

Dieselbe Platzhalter-beim-Start-Regel, dasselbe im Benutzernamen kodierte Targeting. Für Nicht-Browser-HTTP-Arbeit deckt die Python-Anleitung requests, httpx und Scrapy ab.

FAQ

Warum funktioniert http://user:pass@host in Playwright nicht? Chromium ignoriert inline Proxy-Zugangsdaten. Setzen Sie Benutzername und Passwort stattdessen in die dedizierten Felder username/password des proxy-Objekts. Das ist der häufigste Grund für “der Proxy funktioniert nicht” in Playwright.

Warum werden meine Proxies pro Kontext ignoriert? Weil Sie Chromium ohne Proxy gestartet haben. Proxying pro Kontext aktiviert sich nur, wenn der Browser mit einem gestartet wurde, übergeben Sie einen Platzhalter proxy: { server: "per-context" } an launch(), dann setzen Sie echte Proxies an jedem Kontext.

Sollte ich die IP bei jeder Anfrage in einem Browser rotieren? Nein. Eine Browser-Session sollte eine IP halten, wie ein echter Nutzer. Nutzen Sie eine Sticky-sid pro Kontext und rotieren Sie, indem Sie neue Kontexte erstellen, nicht durch IP-Wechsel mitten in der Session.

Wie führe ich verschiedene Länder gleichzeitig aus? Ein Browser, mehrere Kontexte, jeder mit einem anderen country-<cc> in seinem Proxy-Benutzernamen. Sie laufen nebenläufig und unabhängig.

Funktioniert das auch mit Puppeteer oder Selenium? Das Gateway ja. Puppeteer nimmt --proxy-server beim Start und authentifiziert über page.authenticate(); Selenium nutzt eine Proxy-Capability oder eine Erweiterung für die Auth. Das im Benutzernamen kodierte Targeting ist identisch; nur die Verdrahtung unterscheidet sich. Playwrights Kontext-Modell ist das sauberste für Multi-Identitäts-Arbeit.

Meine Läufe verbrennen Bandbreite schnell. Was tue ich? Blockieren Sie Bilder, Media und Schriften mit context.route() wie oben gezeigt. Browser laden standardmäßig alles, und bei einem Pro-GB-Tarif ist das echtes Geld. Schwere Ressourcen abzubrechen senkt den Verbrauch oft um mehr als die Hälfte.

Zum Abschluss

Das Muster: Starten Sie Chromium mit einem per-context-Platzhalter, geben Sie jedem Kontext einen Proxy, dessen username country/sid/ttl kodiert, richten Sie locale/timezone/geolocation am Land aus, und brechen Sie Bilder und Schriften ab, um Bandbreite zu sparen. Rotieren Sie durch neue Kontexte, nicht durch IP-Wechsel. Fehler erscheinen als net::-Navigationsfehler, und ein Retry, der einen frischen Kontext baut, verschafft Ihnen eine frische IP.

Beginnen Sie mit den obigen Snippets, richten Sie sie auf das Residential-Gateway, und Sie haben in einem Nachmittag produktionsreife Browser-Automatisierung. Tarife und Pro-GB-Preise stehen auf der Preisseite, und die vollständige Flag-Referenz lebt in den Gateway-Docs.

Bereit, loszulegen?

Testen Sie Shifters Residential-Proxys, 205M+ IPs, 195+ Länder, ab $0.75/GB.

Jetzt starten