Sie sammeln Daten aus einem Shop, der seine Preise per JavaScript nachlädt. Das HTML, das über Requests ankommt, ist leer, also wechseln Sie zu Playwright, und die Seite öffnet sich ohne Probleme. Wenn das Projekt wächst, muss der Traffic über einen Proxy laufen, und der erste Versuch endet mit net::ERR_TUNNEL_CONNECTION_FAILED oder „Browser does not support socks5 proxy authentication“. Die meisten Anleitungen behandeln Playwright als Werkzeug für Testautomatisierung, deshalb wird die Proxy-Seite oft aus Forenbeiträgen und Issue-Trackern zusammengesucht.
In diesem Beitrag definieren wir Playwright kurz, zeigen die Installation für Python und Node.js nebeneinander und gehen dann zum Proxy über: der Unterschied zwischen Proxy auf Browser-Ebene und auf Context-Ebene, Authentifizierung mit Benutzername und Passwort, das Verhalten von Chromium bei SOCKS5, die Wahl zwischen Rotating und Sticky, Traffic sparen durch das Blockieren von Bild- und Font-Anfragen, die Prüfung der IP und eine Fehlertabelle. Alle Beispiele in diesem Beitrag haben wir mit Playwright 1.63 und Chromium über einen lokalen Test-Proxy mit Authentifizierung ausgeführt.
Was ist Playwright?
Playwright ist eine von Microsoft entwickelte Open-Source-Bibliothek für Browserautomatisierung. Sie öffnet einen echten Browser aus dem Code heraus, ruft eine Adresse auf, klickt, füllt Formulare aus und liest die Elemente der Seite. Die Engines Chromium, Firefox und WebKit werden über dieselbe API gesteuert; offizielle Versionen gibt es für Node.js, Python, Java und .NET.
Der Ausgangspunkt des Werkzeugs sind End-to-End-Tests, und die meisten Anleitungen stellen es von dieser Seite vor. Für Datenteams liegt der Wert woanders: Playwright führt eine per JavaScript erzeugte Seite in einem echten Browser aus, wartet von selbst, bis ein Element erscheint, und lässt Sie die API-Aufrufe mitlesen, die die Seite im Hintergrund macht, sowie unnötige Anfragen abbrechen. Ob eine Seite wirklich einen Browser braucht, klären Sie zuerst mit unserem Beitrag Statische und dynamische Seiten beim Web Scraping; liegen die Daten im Seitenquelltext oder hinter einem JSON-Endpunkt, ist ein Browser unnötiger Aufwand.
Die Aufgaben, die mit Playwright erledigt werden, lassen sich grob in vier Gruppen fassen:
- Daten aus dynamischen Seiten sammeln (Produktlisten, Preise, Lagerbestand, Anzahl der Bewertungen).
- Testen, wie Ihre eigene Website oder App aus verschiedenen Ländern aussieht.
- Screenshots und PDFs erzeugen.
- KI-Agenten einen Browser bedienen lassen. Die Einzelheiten zu diesem letzten Punkt stehen in unserem Beitrag über Playwright MCP, Einrichtung und Proxy-Einstellungen.
Die architektonischen Unterschiede zu Selenium und die Frage, welches Werkzeug zu welchem Projekt passt, behandeln wir in einem eigenen Beitrag: Playwright und Selenium im Vergleich.
Wie wird Playwright installiert?
Die Installation besteht aus zwei Schritten: zuerst die Bibliothek, dann die Browser-Binärdateien. Playwright verwendet nicht das auf dem System installierte Chrome, sondern Browser, die es selbst herunterlädt und auf eine Version festlegt. Der Fehler „Bibliothek installiert, aber Browser nicht gefunden“ entsteht, wenn der zweite Schritt ausgelassen wird.
Legen Sie auf der Python-Seite eine virtuelle Umgebung an und installieren Sie die Bibliothek. Die offizielle Dokumentation der Python-Bibliothek nennt dieselben Schritte auch für poetry und uv.
python -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install playwright
playwright install chromiumAuf der Node.js-Seite:
npm init -y
npm install playwright
npx playwright install chromiumWenn Sie dem Befehl install keinen Browsernamen mitgeben, werden alle drei Engines heruntergeladen. Für Scraping-Aufgaben reicht in der Regel Chromium allein. Wenn Sie Tests schreiben wollen, werden für Python das Paket pytest-playwright und für Node.js @playwright/test empfohlen; für die Datenerfassung genügt die schlanke Bibliotheksinstallation oben.
In Python gibt es zwei APIs: sync_api und async_api. In Skripten, die Seiten nacheinander öffnen, ist die synchrone Variante besser lesbar. Wenn Sie mehrere Seiten gleichzeitig verarbeiten, nutzen Sie die auf asyncio basierende Variante; das vollständige Beispiel am Ende des Beitrags ist so geschrieben.
Wie wird ein Proxy in Playwright definiert?
Der Abschnitt HTTP Proxy in der Netzwerk-Dokumentation von Playwright beschreibt zwei Ebenen: Der Proxy wird entweder für den gesamten Browser oder für jeden Context einzeln angegeben. In beiden Fällen wird dasselbe Objekt verwendet:
| Feld | Pflicht? | Bedeutung |
|---|---|---|
server | Ja | http://pr.proxynet.io:8000 oder socks5://host:port. Ohne Schema gilt die Angabe als HTTP Proxy |
username | Nein | Benutzername für die Authentifizierung am HTTP Proxy |
password | Nein | Passwort für die Authentifizierung am HTTP Proxy |
bypass | Nein | Domains, die nicht über den Proxy laufen, durch Komma getrennt (.beispiel.de, api.ihrefirma.de) |
In Python sieht die Definition auf Browser-Ebene so aus:
from playwright.sync_api import sync_playwright
PROXY = {
"server": "http://pr.proxynet.io:8000",
"username": "user",
"password": "pass",
}
with sync_playwright() as p:
browser = p.chromium.launch(proxy=PROXY)
page = browser.new_page()
page.goto("https://httpbin.org/ip")
print(page.inner_text("body")) # Ausgangs-IP des Proxys
browser.close()Das Gegenstück in Node.js:
import { chromium } from "playwright";
const browser = await chromium.launch({
proxy: {
server: "http://pr.proxynet.io:8000",
username: "user",
password: "pass",
},
});
const page = await browser.newPage();
await page.goto("https://httpbin.org/ip");
console.log(await page.innerText("body")); // Ausgangs-IP des Proxys
await browser.close();Zu beachten ist, wo die Zugangsdaten stehen. Aus Gewohnheit von cURL oder Requests http://user:pass@pr.proxynet.io:8000 zu schreiben, funktioniert hier nicht: Playwright übernimmt aus dem Wert server nur Schema, Host und Port und gibt Benutzername und Passwort aus der Adresse nicht an den Browser weiter. Zugangsdaten gehören immer in die Felder username und password. Das hat einen Nebeneffekt: Enthält das Passwort Zeichen wie @ oder :, müssen Sie sich nicht mit URL-Kodierung befassen. Die allgemeine Logik der beiden Verfahren erklärt unser Beitrag Proxy-Authentifizierung: User:Pass oder IP-Whitelist.
Worin unterscheiden sich Proxy auf Browser-Ebene und auf Context-Ebene?
Ein Context ist in Playwright eine von den anderen isolierte Browsersitzung: mit eigenen Cookies, eigenem Cache und eigenem lokalen Speicher. Er ähnelt einem Inkognito-Fenster, aber in einem einzigen Browserprozess können Dutzende davon gleichzeitig geöffnet sein, und das Öffnen kostet deutlich weniger als der Start eines neuen Browsers.
Wenn Sie den Proxy nicht an launch(), sondern an new_context() übergeben, gilt die Einstellung nur für diesen Context:
from playwright.sync_api import sync_playwright
PROXIES = [
{"server": "http://pr.proxynet.io:8000", "username": "user", "password": "pass"},
{"server": "http://pr.proxynet.io:8001", "username": "user", "password": "pass"},
]
with sync_playwright() as p:
browser = p.chromium.launch() # der Browser wird einmal gestartet, ohne Proxy
for proxy in PROXIES:
context = browser.new_context(proxy=proxy) # jeder Context mit eigenem Proxy
page = context.new_page()
page.goto("https://httpbin.org/ip")
print(proxy["server"], page.inner_text("body"))
context.close() # Cookies und Cache werden zusammen mit dem Context gelöscht
browser.close()Als wir dieses Beispiel mit zwei getrennten lokalen Proxys ausführten, landete die Anfrage jedes Contexts im Log seines eigenen Proxys; ein dritter Context ohne Proxy verband sich direkt. In älteren Anleitungen heißt es, unter Windows müsse man für Chromium einen Platzhalter-Proxy wie http://per-context in den Aufruf launch() schreiben. Das war eine Einschränkung älterer Playwright-Versionen und wurde im August 2024 aus dem Code entfernt; auch die aktuelle Dokumentation enthält keinen solchen Hinweis mehr. Mit Playwright 1.63 unter Windows 11 lief das Beispiel ohne Platzhalter.
Browser-Ebene (launch) | Context-Ebene (new_context) | |
|---|---|---|
| Geltungsbereich | Alle Contexts und Seiten | Nur dieser Context |
| Verschiedene IPs in einem Browser | Nein | Ja, eigener Proxy pro Context |
| Proxy wechseln | Browser schließen und neu starten | Context schließen und einen neuen öffnen |
| Cookies und Sitzung | Contexts bleiben trotzdem getrennt | Werden zusammen mit dem Proxy isoliert |
| Geeignet für | Skripte und Tests, denen ein Ausgangspunkt genügt | Viele unabhängige Sitzungen, Ländervergleiche |
Die praktische Regel lautet: eine Sitzung, ein Context, eine IP. Innerhalb desselben Contexts lässt sich der Proxy nicht wechseln, und das ist gut so; eine Sitzung, deren Cookies gleich bleiben, während sich die IP ändert, ist aus Sicht der Zielseite ein widersprüchlicher Besucher.
Funktioniert Playwright mit einem SOCKS5 Proxy?
Ja, aber ohne Benutzername und Passwort. Wenn Sie in server eine Adresse mit dem Schema socks5:// angeben und username hinzufügen, meldet Playwright diesen Fehler, ohne den Browser überhaupt zu starten:
BrowserType.launch: Browser does not support socks5 proxy authenticationDieselbe Prüfung gilt für new_context(). Der Grund ist Chromium selbst: Die Proxy-Dokumentation von Chromium hält ausdrücklich fest, dass für SOCKSv5 kein Authentifizierungsverfahren unterstützt wird. Wir haben das am Tag des Schreibens mit einem lokalen SOCKS5-Server ausprobiert. Das einzige Verfahren, das Chromium in seiner Handshake-Nachricht anbot, war 0x00, also „keine Authentifizierung“; das Verfahren mit Benutzername und Passwort (0x02) aus RFC 1929 stand nicht auf der Liste. Auch die Zugangsdaten in die Adresse einzubetten (socks5://user:pass@...) änderte nichts: Playwright verwarf diesen Teil, und weil der Server eine Authentifizierung verlangte, wurde die Verbindung mit net::ERR_SOCKS_CONNECTION_FAILED geschlossen. Bei einem SOCKS5-Server ohne Authentifizierung öffnete sich die Seite ohne Probleme.
Die Lösung besteht darin, sich nicht mit einem Passwort, sondern mit der eigenen IP-Adresse auszuweisen. Sie tragen die Ausgangs-IP des Servers, auf dem das Skript läuft, im Proxy-Dashboard in die IP-Whitelist ein und geben nur das Feld server an:
from playwright.sync_api import sync_playwright
with sync_playwright() as p:
# Kein Benutzername, kein Passwort: Ihre Ausgangs-IP muss im Dashboard auf der IP-Whitelist stehen
browser = p.chromium.launch(proxy={"server": "socks5://pr.proxynet.io:1080"})
page = browser.new_page()
page.goto("https://httpbin.org/ip")
print(page.inner_text("body"))
browser.close()In unseren Testprotokollen schickte Chromium dem SOCKS5-Server keinen IP-Wert, sondern den Domainnamen; die DNS-Auflösung findet also auf der Proxy-Seite statt, und die von cURL bekannte Unterscheidung socks5h:// ist hier nicht nötig. Zum Öffnen von Seiten reicht meistens ein HTTP Proxy, weil HTTPS-Traffic ohnehin durch einen CONNECT-Tunnel läuft. Wann SOCKS5 die bessere Wahl ist, erklären wir in SOCKS und HTTP Proxy im Vergleich: was wählen?; die Produktseite dazu ist SOCKS5-Proxy.
Rotating oder Sticky: was für welche Aufgabe?
Ein Browser verhält sich anders als ein Skript, das eine einzelne HTTP-Anfrage sendet. Beim Laden einer Seite werden für das Hauptdokument, Skripte, Stylesheets, Bilder und API-Aufrufe getrennte Verbindungen zu verschiedenen Domains geöffnet. An einem Gateway, das bei jeder Verbindung die Ausgangs-IP wechselt, können diese Verbindungen über verschiedene IPs hinausgehen. Beim Crawlen voneinander unabhängiger Seiten ist das kein Problem. In Abläufen mit Login, Warenkorb oder mehrstufigem Formular kann ein IP-Wechsel mitten in der Sitzung jedoch dazu führen, dass die Website die Sitzung beendet.
- Unabhängige Seiten (Produktliste, Kategorie, Suchergebnis): Rotierender Proxy passt hier. Die Rotation übernimmt das Gateway, Sie führen im Code keine Proxy-Liste. Wie Rotation funktioniert, steht in unserem Beitrag über IP-Rotation.
- Abläufe, die eine Sitzung brauchen (Login mit dem eigenen Konto, mehrstufige Vorgänge): Mit Sticky-Proxy bleibt dieselbe IP für die Lebensdauer des Contexts erhalten. Die Angaben zur Sticky Sitzung entnehmen Sie dem Dashboard und tragen sie im Feld
proxydes Contexts ein. - Inhalte, die je nach Land abweichen: Sie öffnen für jedes Land einen eigenen Context und geben ihm den Ausgangspunkt dieses Landes; ein Browser, direkter Vergleich nebeneinander.
Ein Proxy pro Context ist für sich genommen schon ein Rotationsschema: Wenn Sie den Context schließen und einen neuen öffnen, werden Cookies und IP gemeinsam erneuert.
Wie lässt sich Traffic durch das Blockieren von Bildern und Fonts senken?
Residential-Proxy wird pro GB abgerechnet, und ein Browser lädt alles herunter, was ein schlichter HTTP-Client nicht lädt: Produktbilder, Webfonts, Videos. Wenn Sie Preis und Titel brauchen, hat es keinen Sinn, für diese Bytes zu zahlen. Der route-Mechanismus von Playwright fängt eine Anfrage ab, bevor sie ins Netz geht; Sie können sie nach Ressourcentyp abbrechen.
from playwright.sync_api import sync_playwright
BLOCKED = {"image", "media", "font"}
def filter_resources(route):
if route.request.resource_type in BLOCKED:
route.abort()
else:
route.continue_()
with sync_playwright() as p:
browser = p.chromium.launch(proxy={
"server": "http://pr.proxynet.io:8000",
"username": "user",
"password": "pass",
})
page = browser.new_page()
page.route("**/*", filter_resources)
page.goto("https://books.toscrape.com/")
print(page.locator("article.product_pod h3 a").first.get_attribute("title"))
browser.close()Wie groß die Ersparnis ist, hängt von der Seite ab; eine allgemeine Prozentzahl wäre irreführend. Um sie für Ihr eigenes Ziel zu messen, öffnen Sie dieselbe Seite mit und ohne Filter und vergleichen den Traffic-Zähler im Proxy-Dashboard. Zwei Warnungen: Das Blockieren von Stylesheets (stylesheet) und Skripten (script) kann verhindern, dass die Seite ihren Inhalt aufbaut; beschränken Sie die Liste deshalb auf Bilder, Medien und Fonts. Diese Technik dient dem Sparen von Traffic, sie wird nicht eingesetzt, um die Schutzskripte einer Website herauszufiltern.
Die größere Ersparnis steckt oft in dem Traffic, den der Browser mitliest. Dynamische Seiten holen ihre Daten meist von einem JSON-Endpunkt, und Playwright lässt Sie diese Antwort direkt lesen:
from playwright.sync_api import sync_playwright
with sync_playwright() as p:
browser = p.chromium.launch(proxy={
"server": "http://pr.proxynet.io:8000",
"username": "user",
"password": "pass",
})
page = browser.new_page()
with page.expect_response("**/api/quotes?page=1") as info:
page.goto("https://quotes.toscrape.com/scroll")
data = info.value.json()
for quote in data["quotes"][:3]:
print(quote["author"]["name"], "-", quote["text"][:60])
browser.close()Statt HTML mit Selektoren zu zerlegen, erhalten Sie strukturierte Daten. Verlangt der Endpunkt keine Anmeldung, können Sie im nächsten Schritt ganz auf den Browser verzichten und diese Adresse mit einem schlichten HTTP-Client aufrufen.
Wie prüft man, ob der Proxy funktioniert?
Drei Prüfungen genügen:
- Öffnen Sie
https://httpbin.org/ipmit Playwright und vergleichen Sie die zurückgegebene IP mit Ihrer eigenen. Weicht sie ab, läuft der Traffic über den Proxy. - Öffnen Sie dieselbe Adresse mit einem Context ohne Proxy. Sind beide Ergebnisse gleich, wurde das
proxy-Objekt dem falschen Aufruf übergeben oder die Domain steht auf derbypass-Liste. - Wenn Sie ein Land ansteuern, prüfen Sie den Standort der IP in einem Geolokalisierungsdienst. Warum Datenbanken voneinander abweichen, erklären wir in IP-Standort falsch? Was eine IP-Adresse wirklich verrät.
Den Proxy unabhängig von Playwright zu testen, zeigt, ob das Problem im Code oder im Netz liegt. Die Tests für die Kommandozeile stehen in Funktioniert mein Proxy? So testen Sie einen Proxy.
Vollständiges Beispiel: parallele Contexts, Wiederholungen und Ressourcen-Blocking
Das folgende Skript crawlt sechs per JavaScript erzeugte Seiten. Es hält höchstens drei Contexts gleichzeitig offen, verwendet bei jedem Versuch einen frischen Context und eine frische Proxy-Verbindung, blockiert Bilder und Fonts und wiederholt vorübergehende Fehler mit exponentieller Wartezeit plus Zufallsanteil. Bei Fehlern, die zeigen, dass der Proxy gar nicht erreichbar ist, wiederholt es nicht, denn diese Lage behebt nicht das Warten, sondern die Konfiguration.
import asyncio
import random
from playwright.async_api import Error, async_playwright
PROXY = {
"server": "http://pr.proxynet.io:8000",
"username": "user",
"password": "pass",
}
URLS = [f"https://quotes.toscrape.com/js/page/{n}/" for n in range(1, 7)]
BLOCKED = {"image", "media", "font"}
CONCURRENCY = 3 # Anzahl gleichzeitig offener Contexts
ATTEMPTS = 3
# Fehler, an denen eine Wiederholung nichts ändert: zuerst die Konfiguration korrigieren
FATAL = ("ERR_PROXY_CONNECTION_FAILED", "ERR_TUNNEL_CONNECTION_FAILED", "ERR_SOCKS_CONNECTION_FAILED")
async def filter_resources(route):
if route.request.resource_type in BLOCKED:
await route.abort()
else:
await route.continue_()
async def scrape(browser, url, limit):
async with limit:
for attempt in range(ATTEMPTS):
context = await browser.new_context(proxy=PROXY) # frische Sitzung bei jedem Versuch
try:
page = await context.new_page()
await page.route("**/*", filter_resources)
response = await page.goto(url, timeout=30_000)
if response is None or response.status >= 400:
raise Error(f"HTTP {response.status if response else 'keine Antwort'}")
quotes = page.locator("div.quote span.text")
await quotes.first.wait_for(timeout=10_000) # warten, bis JavaScript den Inhalt erzeugt hat
return url, await quotes.all_inner_texts()
except Error as exc: # TimeoutError leitet sich ebenfalls von Error ab
if any(code in str(exc) for code in FATAL):
raise
if attempt == ATTEMPTS - 1:
raise
await asyncio.sleep(2**attempt + random.random())
finally:
await context.close()
async def main():
async with async_playwright() as p:
browser = await p.chromium.launch()
limit = asyncio.Semaphore(CONCURRENCY)
results = await asyncio.gather(
*(scrape(browser, url, limit) for url in URLS), return_exceptions=True
)
await browser.close()
for url, result in zip(URLS, results):
if isinstance(result, Exception):
print(url, "FEHLER:", str(result).splitlines()[0])
else:
print(url, len(result[1]), "Zitate")
asyncio.run(main())Auf unserem lokalen Test-Proxy kamen alle sechs Seiten mit je zehn Zitaten zurück; als wir den Proxy-Port absichtlich falsch schrieben, stoppte das Skript ohne Wiederholung mit ERR_PROXY_CONNECTION_FAILED. Für den echten Einsatz ergänzen Sie zwei Dinge. Die Wiederholungslogik, die bei 429 und 503 den Header Retry-After beachtet, übernehmen Sie aus dem Beispiel in HTTP-Statuscodes beim Web Scraping: 403, 407, 429, 503; wir wiederholen sie hier nicht. Richten Sie außerdem die Parallelität am Arbeitsspeicher aus: Jeder Context und jede Seite belegt Speicher, starten Sie CONCURRENCY deshalb niedrig und erhöhen Sie den Wert, während Sie Ihren Server beobachten. Die Einzelheiten zu Wartestrategien (warum man auf ein Element statt auf networkidle wartet) stehen im oben genannten Beitrag über statische und dynamische Seiten.
Fehlertabelle: ERR_PROXY_CONNECTION_FAILED, ERR_TUNNEL_CONNECTION_FAILED, 407
Jede der folgenden Zeilen haben wir auf dem lokalen Test-Proxy absichtlich herbeigeführt.
| Was Sie sehen | Was es bedeutet | Was zu tun ist |
|---|---|---|
net::ERR_PROXY_CONNECTION_FAILED | Zum Proxy-Server kam keine TCP-Verbindung zustande: Adresse oder Port falsch, oder eine Firewall sperrt ausgehenden Traffic | Wert von server und Port prüfen, dieselbe Adresse mit cURL testen |
net::ERR_TUNNEL_CONNECTION_FAILED | Der Proxy wurde erreicht, aber auf die CONNECT-Anfrage kam eine andere Antwort als 200: Authentifizierung abgelehnt (407) oder der Proxy erreichte das Ziel nicht (502) | Zuerst die Zugangsdaten, dann die Zieladresse prüfen |
Zeitüberschreitung bei goto auf einer HTTPS-Adresse, 407 im Proxy-Log | Benutzername oder Passwort falsch oder gar nicht angegeben. In unserem Versuch meldete das Ereignis requestfailed den Fehler ERR_TUNNEL_CONNECTION_FAILED, aber goto wartete bis zur Zeitüberschreitung, statt einen Fehler auszulösen | Den echten Fehler mit page.on("requestfailed") sichtbar machen, die Felder username und password korrigieren |
Antwortcode 407 auf einer HTTP-Adresse (unverschlüsselt) | Gleiche Ursache; ohne Tunnel kommt die Antwort des Proxys als Seitenantwort an | response.status prüfen, Zugangsdaten korrigieren |
Browser does not support socks5 proxy authentication | Zusammen mit einer socks5://-Adresse wurde username angegeben | Zugangsdaten entfernen und IP-Whitelist nutzen oder auf HTTP Proxy wechseln |
net::ERR_SOCKS_CONNECTION_FAILED | Der SOCKS5-Server verlangt eine Authentifizierung oder Ihre IP steht nicht auf der Whitelist | Ihre Ausgangs-IP im Dashboard in die Liste eintragen |
| Die Seite öffnet sich, aber die IP ist Ihre eigene | Das proxy-Objekt wurde gar nicht angewendet oder die Domain steht auf der bypass-Liste | Prüfen, ob das Objekt an launch oder new_context übergeben wurde |
Die dritte Zeile kostet am meisten Zeit: Das Skript meldet keinen Fehler, es wartet nur dreißig Sekunden und läuft in die Zeitüberschreitung, und das Problem wird der langsamen Zielseite zugeschrieben. Während der Entwicklung verkürzen diese zwei Zeilen die Diagnose:
page.on("requestfailed", lambda r: print("FEHLGESCHLAGEN:", r.url, r.failure))
page.on("response", lambda r: print(r.status, r.url) if r.status >= 400 else None)Die allgemeine Bedeutung des Codes 407 und sein Erscheinungsbild in anderen Bibliotheken finden Sie in unserem Beitrag über HTTP-Statuscodes; für Fehler der Art „Proxyserver antwortet nicht“ außerhalb der Browserautomatisierung lesen Sie unseren Beitrag über Proxy-Fehler und die Meldung „Proxyserver antwortet nicht“.
Einsatzbereiche
- Dynamische Shop- und Katalogseiten: per JavaScript geladene Preis- und Bestandsdaten. Den allgemeinen Aufbau zeigt unsere Seite zur Data-Scraping-Lösung.
- Regelmäßiges Crawlen vieler Seiten: Seitenerkennung und Warteschlangenverwaltung stehen auf der Seite zur Web-Crawler-Lösung; Muster für die Seitennummerierung in unserem Beitrag über Pagination beim Web Scraping.
- Ihre App aus verschiedenen Ländern testen: ein Context pro Land, jeweils mit dem Ausgangspunkt dieses Landes. Einzelheiten auf der Seite App-Tests.
- Preisbeobachtung bei Wettbewerbern: ein täglicher Crawl mit Ressourcen-Blocking, der Ratenlimits einhält. Die geschäftliche Seite behandelt unser Beitrag über Preisbeobachtung der Konkurrenz im E-Commerce.
Bei jeder Aufgabe gilt derselbe Rahmen: Halten Sie die Regeln der robots.txt und die Nutzungsbedingungen der Website ein, ziehen Sie eine offizielle API vor, wenn es eine gibt, und halten Sie Ihre Anfragerate auf einem Niveau, das die Website verkraftet. Wie man eine robots.txt liest, steht in Was ist robots.txt und wie liest man die Datei?.
Häufige Fehler
- Zugangsdaten in die
server-Adresse einbetten. Playwright ignoriert diesen Teil; das Ergebnis ist407oder eine Zeitüberschreitung. - Benutzername und Passwort mit SOCKS5 versuchen. Chromium unterstützt das nicht; nutzen Sie eine IP-Whitelist oder einen HTTP Proxy.
- Für jede Seite einen neuen Browser starten. Der Browser wird einmal gestartet, Isolation und Proxy-Wechsel laufen über Contexts.
- Einen Ablauf mit Sitzung über ein rotierendes Gateway schicken. Die Verbindungen gehen über verschiedene IPs hinaus, die Sitzung bricht ab. Nutzen Sie Sticky.
- Contexts nicht schließen. Jeder offene Context hält Speicher; schließen Sie ihn im
finally-Block. - Skripte und Stylesheets blockieren. Die Seite kann ihren Inhalt nicht aufbauen, Ihr Selektor liefert nichts.
- Eine Zeitüberschreitung bei
gotoder Zielseite zuschreiben. Sehen Sie zuerst auf das Ereignisrequestfailedund auf die Proxy-Zugangsdaten. - Eine
200-Antwort akzeptieren, ohne den Inhalt anzusehen. Prüfen Sie, ob das erwartete Element auf der Seite steht; auch Verifizierungsseiten können200liefern.
Entscheidungshilfe
| Bedarf | Empfehlung |
|---|---|
| Skript oder Test, dem ein Ausgangspunkt genügt | launch(proxy=...), HTTP Proxy |
| Mehrere unabhängige Sitzungen in einem Browser | new_context(proxy=...), eigener Proxy pro Context |
| Massenhaftes Crawlen unabhängiger Seiten | Rotating Proxy, eine einzige Gateway-Adresse |
| Mehrstufiger Ablauf mit Login | Sticky Proxy, ein Context eine IP |
| SOCKS5 ist Pflicht | IP-Whitelist, ohne username und password |
| Nach GB abgerechneter Traffic | Bild-, Medien- und Font-Anfragen mit route blockieren; wenn möglich die JSON-Antwort lesen |
| Daten im Seitenquelltext oder hinter einem JSON-Endpunkt | Schlichter HTTP-Client statt Playwright |
| Einen KI-Agenten den Browser bedienen lassen | Playwright MCP |
Häufige Fragen
Ist Playwright kostenlos?
Ja. Es ist ein Open-Source-Projekt unter der Lizenz Apache 2.0; für die Bibliothek und die heruntergeladenen Browser-Binärdateien fallen keine Kosten an. Kosten entstehen durch die Serverressourcen, die die Browser verbrauchen, und durch den genutzten Proxy-Traffic.
Sollte man Playwright mit Python oder mit Node.js nutzen?
Proxy-Einstellung und Browserverhalten sind in beiden Sprachen gleich, weil beide mit demselben Treiber sprechen. Entscheiden sollte die Sprache Ihres Teams und Ihrer Datenverarbeitung. Einen Vergleich der beiden Sprachen aus Scraping-Sicht finden Sie in Web Scraping: JavaScript oder Python?.
Kann ich für jede Seite einen eigenen Proxy verwenden?
Der Proxy hängt am Context, nicht an der Seite. Wenn Sie für jede Seite eine eigene IP wollen, öffnen Sie jede Seite in ihrem eigenen Context. Mit einem rotierenden Gateway ist auch das nicht nötig; die Rotation übernimmt das Gateway.
Funktioniert die SOCKS5-Authentifizierung mit Firefox oder WebKit?
Wird zusammen mit einer socks5://-Adresse ein Benutzername angegeben, meldet Playwright den Fehler in seinem eigenen Validierungsschritt, noch bevor der Browser startet, und diese Prüfung unterscheidet nicht nach Browsertyp. Wir haben es nur mit Chromium ausprobiert; gehen Sie auch bei den anderen Engines von der IP-Whitelist aus.
Verhält sich der Proxy im Headless-Modus anders?
Nein. Das proxy-Objekt wird mit und ohne Fenster auf dieselbe Weise angewendet. Wenn Sie das Problem mit eigenen Augen sehen wollen, öffnen Sie das Fenster mit launch(headless=False) und führen dasselbe Skript aus.
Lässt ein Proxy Verifizierungsseiten verschwinden?
Nein. Ein Proxy ändert nur, über welche IP die Anfrage hinausgeht; Anfragerate, Browsersignale und die Konsistenz der Sitzung bleiben gleich. Warum diese Seiten erscheinen, erklären wir in Puppeteer und CAPTCHA: warum, und wie seltener?; das dort Gesagte gilt auch für Playwright. Erweiterungen zum Umgehen der Erkennung empfehlen wir nicht: Der dauerhafte Weg sind eine vernünftige Rate, eine konsistente Sitzung und, wo vorhanden, eine offizielle API.
Fazit
In Playwright ist der Proxy ein einzelnes Objekt: server, username, password. Übergeben Sie es an launch(), geht der gesamte Browser über den Proxy hinaus; übergeben Sie es an new_context(), nur diese Sitzung. Der zweite Weg bedeutet eine eigene IP pro Context in einem einzigen Browser. Zugangsdaten werden nicht in die Adresse geschrieben, bei SOCKS5 tritt die IP-Whitelist an die Stelle des Passworts, Abläufe mit Sitzung bekommen Sticky, unabhängige Seiten Rotating. Wenn Sie Traffic pro GB bezahlen, blockieren Sie Bilder und Fonts; wenn Sie Zeitüberschreitungen sehen, prüfen Sie zuerst das Ereignis requestfailed. Die zu Ihrer Aufgabe passenden Proxy-Typen finden Sie bei unseren Proxy-Diensten.




