Zurück zum Blog
Anleitungen
Raluca PenciucLast updated on Apr 27, 202617 min read

Scrapy Playwright Tutorial: JavaScript-lastige Websites in großem Umfang scrapen

Kurzfassung: Mit Scrapy-Playwright können Sie JavaScript-lastige Seiten direkt in Scrapy-Spidern rendern, indem Sie über Playwright echte Chromium-, Firefox- oder WebKit-Browser steuern. Dieses Tutorial führt Sie durch die Installation, Konfiguration, Seiteninteraktionen, AJAX-Abfang, Anti-Erkennung und eine produktionsreife Projektstruktur, damit Sie dynamische Websites scrapen können, ohne das Scrapy-Ökosystem zu verlassen.

Scrapy eignet sich hervorragend zum schnellen Crawlen von statischem HTML, aber sobald eine Zielseite Inhalte über JavaScript lädt, liefert eine Standard-Scrapy-Anfrage nur eine leere Hülle. Genau dieses Problem löst Scrapy Playwright. Es handelt sich um einen Scrapy-Download-Handler, der das Rendern an Playwright, die Browser-Automatisierungsbibliothek von Microsoft, delegiert, sodass jede Antwort, die Ihr Spider erhält, das vollständig gerenderte DOM enthält. Wenn Sie die Integration von Scrapy Playwright für Ihre eigenen Projekte ins Auge gefasst haben, sich aber nicht sicher waren, wie alle Teile zusammenpassen, deckt dieser Leitfaden jeden Schritt ab: von pip install bis hin zu einem produktionsreifen Spider mit integrierten Items, Pipelines und Anti-Detection-Maßnahmen. Dabei lernst du Wartestrategien, AJAX-Interception, den Umgang mit Infinite Scroll, Proxy-Konfiguration und die Fehlerbehebungsmuster kennen, die lange Crawls stabil halten.

Was ist Scrapy-Playwright und warum sollte man es verwenden?

Scrapy-Playwright (das PyPI-Paket scrapy-playwright) ist ein Scrapy-Download-Handler, der das standardmäßige HTTP-Backend durch einen vollständigen, von Playwright betriebenen Browser ersetzt. Wenn Sie eine Scrapy-Anfrage mit "playwright": True in seinem meta Wörterbuch, startet der Handler eine Browser-Seite, navigiert zur URL, wartet, bis JavaScript fertig ist, und übergibt dann den gerenderten HTML-Code an Ihren parse Callback als normales Scrapy Response.

Warum ist das wichtig? Ein wachsender Anteil des Webs rendert Inhalte clientseitig: React-Dashboards, Vue-Storefronts, Seiten, die hinter Einwilligungsmodalitäten verborgen sind, und Websites, die Produktdaten über API-Aufrufe im Hintergrund laden. Standard-Scrapy ruft nur das anfängliche HTML-Dokument ab, das oft Platzhalter-Tags <div> und ein JavaScript-Bundle enthält, aber keine der Daten, die Sie tatsächlich benötigen. Mit dem JavaScript-Rendering von Scrapy Playwright erhalten Sie dieselbe Ausgabe, die ein echter Browser anzeigen würde, ohne die vertraute Request/Response-Pipeline von Scrapy zu verlassen.

Wann sollten Sie Playwright für eine Anfrage aktivieren? Nicht jede URL benötigt einen vollständigen Browser. Eine nützliche Faustregel:

  • Verwenden Sie eine Standard-Scrapy-Anfrage, wenn die benötigten Daten im rohen HTML vorhanden sind oder über einen direkten API-Endpunkt verfügbar sind, den Sie bereits kennen.
  • Verwenden Sie eine Playwright-Anfrage, wenn Inhalte nach dem Laden der Seite eingefügt werden, wenn Sie klicken oder scrollen müssen, um Daten anzuzeigen, oder wenn die Seite auf Cookies und JavaScript-Weiterleitungen angewiesen ist, die mit einfachem HTTP schwer nachzubilden sind.

Die Kombination beider Modi in einem einzigen Spider ist einfach (und wird empfohlen). Du zahlst den Browser-Overhead nur für die Anfragen, die ihn wirklich benötigen, wodurch dein Crawl für die Seiten, die ihn nicht benötigen, schnell bleibt.

Scrapy-Playwright vs. Scrapy-Splash vs. Scrapy-Selenium

Die Wahl zwischen den Browser-Rendering-Backends für Scrapy hängt vom Wartungsaufwand, der Browser-Genauigkeit und den vorhandenen Tools Ihres Teams ab. Hier ein kurzer Vergleich:

Kriterien

Scrapy-Playwright

Scrapy-Splash

Scrapy-Selenium

Browser-Engine

Chromium, Firefox oder WebKit

Benutzerdefinierter Qt-basierter Renderer

Chrome oder Firefox über WebDriver

Asynchrone Unterstützung

Nativ (asyncio)

Erfordert einen separaten Splash-Server

Standardmäßig synchron; asynchrone Wrapper vorhanden

Wartung

Wird aktiv gepflegt, wachsende Community

Die Entwicklung von Splash hat sich verlangsamt

Stabil, basiert jedoch auf dem WebDriver-Protokoll

JS-Treue

Vollständig moderner Browser

Gut, aber einige Randfälle schlagen fehl

Vollwertiger moderner Browser

Einfache Einrichtung

pip install + playwright install

Docker-Container erforderlich

Verwaltung der WebDriver-Binärdateien

Seiteninteraktionen

Umfangreich (click, fill, evaluate)

Eingeschränkte Lua-Skripting-Funktionen

Vollständige WebDriver-API

Wenn Sie heute ein neues Projekt starten, ist Scrapy Playwright in der Regel die beste Wahl. Es bietet moderne Asynchron-Unterstützung, erstklassige Methoden zur Seiteninteraktion und vermeidet den operativen Aufwand, der mit dem Betrieb eines separaten Rendering-Dienstes verbunden ist. Für einen tieferen Einblick in die Vor- und Nachteile von Scrapy gegenüber Selenium behandelt der Vergleichsleitfaden zu Scrapy vs. Selenium das Thema ausführlich.

Installation und Projekteinrichtung

Um ein Scrapy-Playwright-Projekt zum Laufen zu bringen, sind nur wenige Terminalbefehle erforderlich. Hier ist die Schritt-für-Schritt-Anleitung.

Voraussetzungen: Sie benötigen Python 3.8 oder höher und pip. Eine virtuelle Umgebung wird dringend empfohlen, um Abhängigkeiten voneinander zu trennen.

# Create and activate a virtual environment
python -m venv venv
source venv/bin/activate  # Windows: venv\Scripts\activate

# Install Scrapy and scrapy-playwright
pip install scrapy scrapy-playwright

# Install browser binaries (Chromium is the default)
playwright install chromium

Der playwright install chromium Befehl lädt einen bestimmten Chromium-Build herunter, den Playwright intern verwaltet. Du kannst auch firefox oder webkit installieren, falls Ihr Anwendungsfall eine andere Engine erfordert.

Erstellen Sie als Nächstes ein neues Scrapy-Projekt:

scrapy startproject myproject
cd myproject
scrapy genspider example example.com

Dadurch erhalten Sie die Standard-Verzeichnisstruktur von Scrapy: settings.py, items.py, pipelines.py, middlewares.pysowie einen spiders/ Ordner. Der einzige verbleibende Playwright-spezifische Schritt ist die Aktualisierung settings.py, was wir als Nächstes behandeln.

Eine Anmerkung: scrapy-playwright hängt von der asynchen API von Playwright ab, die wiederum den asyncio-basierten Twisted-Reaktor erfordert. Scrapy unterstützt dies, aber Sie müssen den Reaktor explizit festlegen, bevor Scrapy versucht, seinen Standard zu verwenden. Das Vergessen dieses Schritts ist der häufigste Installationsfehler, den Entwickler begehen.

Konfigurieren der Scrapy-Einstellungen für Playwright

Öffnen Sie die settings.py und fügen Sie Folgendes hinzu:

# settings.py

DOWNLOAD_HANDLERS = {
    "http": "scrapy_playwright.handler.ScrapyPlaywrightDownloadHandler",
    "https": "scrapy_playwright.handler.ScrapyPlaywrightDownloadHandler",
}

TWISTED_REACTOR = "twisted.internet.asyncioreactor.AsyncioSelectorReactor"

# Optional: choose browser type (chromium, firefox, webkit)
PLAYWRIGHT_BROWSER_TYPE = "chromium"

# Optional: global navigation timeout in milliseconds
PLAYWRIGHT_DEFAULT_NAVIGATION_TIMEOUT = 30000

Die DOWNLOAD_HANDLERS dict weist Scrapy an, alle HTTP- und HTTPS-Anfragen über den Playwright-Handler zu leiten. Die TWISTED_REACTOR Zeile schaltet Scrapys Ereignisschleife auf asyncio, was Playwright benötigt.

PLAYWRIGHT_DEFAULT_NAVIGATION_TIMEOUT legt die maximale Zeit (in Millisekunden) fest, die der Browser auf das Laden einer Seite wartet. Der Standardwert beträgt 30 Sekunden, was für die meisten Websites ausreichend ist. Wenn Sie besonders langsame Seiten scrapen, erhöhen Sie diesen Wert. Wenn Sie bei defekten URLs einen schnellen Abbruch wünschen, verringern Sie ihn.

Zwei weitere Einstellungen, die du kennen solltest:

  • PLAYWRIGHT_LAUNCH_OPTIONS: ein Wörterbuch, das direkt an playwright.chromium.launch(). Verwenden Sie es zum Umschalten in den Headless-Modus, für Pfade zu ausführbaren Dateien oder zur globalen Proxy-Konfiguration.
  • PLAYWRIGHT_MAX_PAGES_PER_CONTEXT: begrenzt, wie viele Seiten sich einen einzigen Browserkontext teilen, bevor ein neuer Kontext erstellt wird. Dies kann bei der Speicherverwaltung bei großen Crawls helfen.

Mit diesen Einstellungen wird jede Scrapy-Anfrage, die "playwright": True in ihrem meta , von Playwright gerendert. Anfragen ohne dieses Flag laufen weiterhin über den Standard-Downloader von Scrapy, sodass Sie das Beste aus beiden Welten erhalten.

Rendering von JavaScript-intensiven Seiten

Schreiben wir Ihren ersten Scrapy-Playwright-Spider. Das Ziel: eine Seite aufrufen, die ihren Inhalt mit JavaScript lädt, und Daten aus dem vollständig gerenderten DOM extrahieren.

import scrapy

class QuotesSpider(scrapy.Spider):
    name = "quotes"
    start_urls = ["https://quotes.toscrape.com/js/"]

    def start_requests(self):
        for url in self.start_urls:
            yield scrapy.Request(
                url,
                meta={"playwright": True},
                callback=self.parse,
            )

    def parse(self, response):
        for quote in response.css("div.quote"):
            yield {
                "text": quote.css("span.text::text").get(),
                "author": quote.css("small.author::text").get(),
            }

Die entscheidende Zeile lautet meta={"playwright": True}. Dieses einzelne Flag weist den Download-Handler an, eine Browser-Seite zu starten, zur URL zu navigieren, auf das load Ereignis zu warten und das gerenderte HTML als TextResponse. Innerhalb von parseverwenden Sie dieselben CSS-Selektoren (oder XPath), die Sie bei jedem Scrapy-Spider verwenden würden. Auf der Parsing-Seite ändert sich nichts.

Führen Sie den Spider mit scrapy crawl quotes, und du solltest vollständig extrahierte Zitate sehen, obwohl die Seite auf JavaScript zurückgreift, um diese in das DOM einzufügen. Wenn du dieselbe URL mit einer Standard-Scrapy-Anfrage (ohne das Playwright-Flag) response.css("div.quote") würde eine leere Liste zurückgeben.

Dieses Muster bildet die Grundlage für alles Weitere in diesem Scrapy-Playwright-Tutorial. Jede nachfolgende Technik baut auf demselben meta Wörterbuch auf, um zusätzliche Anweisungen an den Browser zu übergeben.

Seiteninteraktionen: Klicks, Scrollen und Formularübermittlungen

Beim Scraping in der Praxis geht es selten nur darum, eine Seite zu laden. Oft müssen Sie auf Schaltflächen klicken, Suchformulare ausfüllen oder scrollen, um verzögert geladene Inhalte auszulösen. Die Seitenmethoden von Scrapy Playwright regeln all dies über den playwright_page_methods Schlüssel in der Anfrage meta.

Ein PageMethod ist ein Wrapper um eine Playwright-Seitenaktion. Man übergibt eine Liste davon, und der Handler führt jede einzelne nach der anfänglichen Navigation der Reihe nach aus.

Auf eine Schaltfläche klicken:

from scrapy_playwright.page import PageMethod

yield scrapy.Request(
    url,
    meta={
        "playwright": True,
        "playwright_page_methods": [
            PageMethod("click", selector="button#load-more"),
            PageMethod("wait_for_selector", selector="div.new-content"),
        ],
    },
    callback=self.parse,
)

Ausfüllen und Absenden eines Formulars:

yield scrapy.Request(
    url,
    meta={
        "playwright": True,
        "playwright_page_methods": [
            PageMethod("fill", selector="input#search", value="python scrapy"),
            PageMethod("click", selector="button[type=submit]"),
            PageMethod("wait_for_selector", selector="div.results"),
        ],
    },
    callback=self.parse,
)

Zum Ende einer Seite scrollen:

yield scrapy.Request(
    url,
    meta={
        "playwright": True,
        "playwright_page_methods": [
            PageMethod(
                "evaluate",
                "window.scrollTo(0, document.body.scrollHeight)",
            ),
            PageMethod("wait_for_timeout", 2000),
        ],
    },
    callback=self.parse,
)

Beachten Sie das Muster: Sie verketten PageMethod Aufrufe, um eine echte Benutzersitzung zu simulieren. Der Handler verarbeitet sie nacheinander, daher ist die Reihenfolge wichtig. Fügen Sie nach einer Aktion, die neuen Inhalt auslöst (ein Klick, der einen API-Aufruf auslöst, ein Scrollen, das weitere Elemente lädt), immer eine Wartezeit ein, um der Seite Zeit zu geben, sich zu aktualisieren, bevor Scrapy den endgültigen HTML-Code erfasst.

Ein praktischer Tipp: Halte deine playwright_page_methods Liste so kurz wie möglich. Jeder Methodenaufruf verursacht Latenz. Wenn du das gleiche Ergebnis mit weniger Schritten erreichen kannst (zum Beispiel durch direktes Navigieren zu einer gefilterten URL statt das Ausfüllen eines Formulars), wähle den einfacheren Ansatz.

Wartestrategien für dynamische Inhalte

Die Wahl der richtigen Wartestrategie ist entscheidend für ein zuverlässiges Scraping dynamischer Inhalte mit Scrapy Playwright. Wenn Sie zu kurz warten, erhalten Sie unvollständige Daten. Wenn Sie zu lange warten, kommt Ihr Crawl zum Stillstand.

Hier sind die wichtigsten Ansätze:

wait_for_selector ist die präziseste Option. Sie pausiert die Ausführung, bis ein bestimmter CSS-Selektor im DOM erscheint.

PageMethod("wait_for_selector", selector="div.product-list")

Verwende dies, wenn du genau weißt, welches Element signalisiert, dass die Daten geladen sind. Es ist schnell, da es den Moment erfasst, in dem das Element vorhanden ist, anstatt eine beliebige Dauer abzuwarten.

wait_for_load_state wartet auf ein bestimmtes Ereignis im Seitenlebenszyklus:

  • "load": wird ausgelöst, wenn der anfängliche HTML-Code und alle Ressourcen (Bilder, Stylesheets) geladen sind.
  • "domcontentloaded": wird ausgelöst, wenn der HTML-Code geparst ist, noch bevor die Bilder fertig geladen sind.
  • "networkidle": wird ausgelöst, wenn seit mindestens 500 ms keine Netzwerkverbindungen bestehen.
PageMethod("wait_for_load_state", "networkidle")

networkidle ist verlockend, da es die meisten AJAX-Aufrufe abfängt, kann jedoch auf Seiten mit persistenten WebSocket-Verbindungen, Analytics-Pings oder Ad-Trackern, die das Netzwerk beschäftigen, unzuverlässig sein. Es ist zudem tendenziell langsamer als wait_for_selector.

wait_for_timeout ist eine feste Wartezeit, angegeben in Millisekunden.

PageMethod("wait_for_timeout", 3000)

Dies ist das plumpste Werkzeug. Verwenden Sie es nur als letzten Ausweg, zum Beispiel auf Seiten, auf denen kein stabiler Selektor existiert und networkidle unzuverlässig ist. „Hard Sleeps“ verschwenden Zeit auf schnellen Seiten und sind auf langsamen Seiten möglicherweise immer noch nicht lang genug.

Empfehlung: Verwenden Sie standardmäßig wait_for_selector , wann immer möglich. Greifen Sie auf networkidle für Seiten, bei denen du den genauen Selektor nicht kennst. Verwende wait_for_timeout für wirklich unvorhersehbare Seiten und halte den Wert so niedrig wie möglich.

Umgang mit unendlichem Scrollen und Paginierung

Viele moderne Websites verwenden Infinite-Scroll-Muster oder paginierte Navigation, um Inhalte auf mehrere Ansichten aufzuteilen. Die Handhabung beider Funktionen innerhalb von Scrapy erfordert leicht unterschiedliche Strategien.

Infinite Scroll funktioniert in der Regel so, dass bis zum Ende der Seite gescrollt wird, auf das Laden neuer Elemente gewartet wird und dies wiederholt wird, bis keine weiteren Elemente mehr erscheinen. Da playwright_page_methods einmal ausgeführt wird, bevor die Antwort zurückgegeben wird, müssen Sie die Scroll-Schleife innerhalb eines page.evaluate Aufruf oder durch direkten Zugriff auf das Playwright-Seitenobjekt abwickeln.

Der sauberste Ansatz ist die Verwendung des playwright_page Meta-Schlüssel, um die rohe Playwright-Seite abzurufen und die Schleife selbst zu programmieren:

async def parse(self, response):
    page = response.meta["playwright_page"]
    previous_height = 0

    while True:
        await page.evaluate("window.scrollTo(0, document.body.scrollHeight)")
        await page.wait_for_timeout(1500)
        current_height = await page.evaluate("document.body.scrollHeight")
        if current_height == previous_height:
            break
        previous_height = current_height

    # Re-read the fully scrolled page content
    content = await page.content()
    await page.close()

    sel = scrapy.Selector(text=content)
    for item in sel.css("div.item"):
        yield {
            "title": item.css("h3::text").get(),
        }

Beachten Sie, dass wir die Seite explizit mit await page.close(). Dies ist für die Speicherverwaltung unerlässlich; andernfalls sammeln sich Browser-Seiten an und Ihr Prozess belegt immer mehr Speicher.

Die Paginierung (per „Weiter“-Klick oder URL-basiert) ist einfacher. Wenn die Website Abfrageparameter verwendet (?page=2), generieren Sie einfach neue Scrapy-Anfragen mit inkrementierten URLs. Wenn sie auf eine „Weiter“-Schaltfläche setzt, verwenden Sie einen PageMethod click:

def parse(self, response):
    # Extract data from current page
    for product in response.css("div.product"):
        yield {"name": product.css("h2::text").get()}

    # Follow next page if it exists
    next_button = response.css("a.next-page::attr(href)").get()
    if next_button:
        yield response.follow(
            next_button,
            meta={"playwright": True},
            callback=self.parse,
        )

Bei Websites, die reine JavaScript-„Load More“-Schaltflächen verwenden, ohne die URL zu ändern, kombinieren Sie das Klickmuster aus dem Abschnitt „Seiteninteraktionen“ mit einem wait_for_selector , um vor dem Extrahieren der Daten zu überprüfen, ob neue Elemente erschienen sind.

AJAX-Anfragen abfangen

Manchmal ist die sauberste Datenquelle nicht das gerenderte DOM, sondern der API-Aufruf im Hintergrund, den die Seite ausführt, um es zu füllen. Mit der AJAX-Abfangfunktion von Scrapy Playwright können Sie diese Antworten direkt erfassen, was Ihnen oft strukturiertes JSON ohne jegliche HTML-Analyse liefert.

Um Antworten abzufangen, benötigen Sie Zugriff auf das Playwright-Seitenobjekt und dessen response Ereignis:

import json

class AjaxSpider(scrapy.Spider):
    name = "ajax_products"
    captured_data = []

    def start_requests(self):
        yield scrapy.Request(
            "https://example.com/products",
            meta={
                "playwright": True,
                "playwright_include_page": True,
            },
            callback=self.parse,
        )

    async def parse(self, response):
        page = response.meta["playwright_page"]

        async def handle_response(resp):
            if "/api/products" in resp.url:
                body = await resp.json()
                self.captured_data.extend(body.get("items", []))

        page.on("response", handle_response)

        # Trigger the AJAX call (e.g., scroll or click)
        await page.evaluate("window.scrollTo(0, document.body.scrollHeight)")
        await page.wait_for_timeout(3000)
        await page.close()

        for product in self.captured_data:
            yield product

Der page.on("response", ...) Listener wird bei jeder Netzwerkantwort ausgelöst. Sie filtern nach URL-Mustern, um nur die API-Aufrufe zu erfassen, die für Sie relevant sind. Der Antworttext ist bereits geparst (.json() oder .text()), sodass Sie die DOM-Durchquerung komplett überspringen können.

Diese Technik ist besonders leistungsstark für Single-Page-Anwendungen, bei denen das Frontend beim Scrollen mehrere paginierte API-Anfragen stellt. Anstatt komplexes HTML zu parsen, erhalten Sie saubere, strukturierte Daten direkt aus der Quelle.

Ausführen von benutzerdefiniertem JavaScript und Erstellen von Screenshots

Zwei leichtgewichtige, aber nützliche Funktionen von Scrapy Playwright sind die Ausführung von benutzerdefiniertem JavaScript und die Erstellung von Screenshots. Sie dienen unterschiedlichen Zwecken, nutzen jedoch denselben Mechanismus: den direkten Zugriff auf das Playwright-Seitenobjekt.

Das Ausführen von benutzerdefiniertem JavaScript mit page.evaluate können Sie Daten extrahieren, die in JavaScript-Variablen verborgen sind, oder den Seitenstatus manipulieren, bevor Scrapy das HTML liest:

yield scrapy.Request(
    url,
    meta={
        "playwright": True,
        "playwright_page_methods": [
            PageMethod(
                "evaluate",
                "document.querySelectorAll('.popup-overlay')"
                ".forEach(el => el.remove())",
            ),
        ],
    },
    callback=self.parse,
)

Dadurch werden Popup-Overlays entfernt, bevor Scrapy die Seite parst, was bei Websites nützlich ist, die beim ersten Besuch Modals anzeigen.

Das Erstellen eines Scrapy-Playwright-Screenshots ist nützlich für die Fehlerbehebung bei Darstellungsproblemen. Wenn Ihr Spider leere Daten extrahiert, zeigt Ihnen ein Screenshot genau, was der Browser gesehen hat:

yield scrapy.Request(
    url,
    meta={
        "playwright": True,
        "playwright_page_methods": [
            PageMethod("screenshot", path="debug.png", full_page=True),
        ],
    },
    callback=self.parse,
)

Das full_page=True Argument erfasst den gesamten scrollbaren Bereich, nicht nur den Viewport. Während der Entwicklung können Sie Screenshots bedingt aktivieren (beispielsweise nur, wenn ein Parse-Callback keine Elemente findet), um zu vermeiden, dass Ihre Festplatte bei Produktions-Crawls voll wird.

Abbrechen unerwünschter Anfragen für schnellere Crawls

Jede Browserseite lädt standardmäßig Bilder, Schriftarten, CSS, Analyseskripte und Werbetracker. Für das Scraping sind die meisten dieser Ressourcen nur Ballast. Durch deren Blockierung lässt sich die Bandbreitennutzung drastisch reduzieren und das Laden der Seiten beschleunigen.

Scrapy-Playwright unterstützt das Abfangen von Anfragen über die PLAYWRIGHT_ABORT_REQUEST Einstellung. Du definierst eine asynchrone Funktion, die jede Anfrage überprüft und True , um sie abzubrechen:

# settings.py
PLAYWRIGHT_ABORT_REQUEST = "myproject.utils.should_abort"
# myproject/utils.py
from playwright.async_api import Request as PlaywrightRequest

async def should_abort(request: PlaywrightRequest) -> bool:
    blocked_types = {"image", "font", "stylesheet", "media"}
    if request.resource_type in blocked_types:
        return True
    blocked_domains = ["google-analytics.com", "doubleclick.net"]
    if any(domain in request.url for domain in blocked_domains):
        return True
    return False

Allein das Blockieren von Bildern und Schriftarten kann die Ladezeit einer Seite erheblich verkürzen, insbesondere auf medienintensiven E-Commerce-Websites. Achten Sie jedoch darauf, keine JavaScript-Dateien zu blockieren, die für die Darstellung der benötigten Inhalte verantwortlich sind. Wenn Ihre Daten nach der Aktivierung der Anforderungsblockierung verschwinden, fügen Sie "script" wieder zu den zulässigen Typen hinzu und beschränken Sie Ihren Filter stattdessen auf bestimmte Domains.

Verwendung von Proxys mit Scrapy-Playwright

Beim Scraping in großem Umfang sind rotierende Proxys unerlässlich, um IP-Sperren zu vermeiden. Die Proxy-Konfiguration von Scrapy Playwright funktioniert auf zwei Ebenen: global und pro Anfrage.

Der globale Proxy gilt für jede Playwright-Anfrage. Legen Sie ihn in settings.py:

PLAYWRIGHT_LAUNCH_OPTIONS = {
    "proxy": {
        "server": "http://proxy-server:8080",
        "username": "user",
        "password": "pass",
    },
}

Dadurch wird die Proxy-Konfiguration an den Aufruf zum Starten des Browsers übergeben, sodass jede von dieser Browser-Instanz geöffnete Seite über diesen Proxy geleitet wird.

Der Proxy pro Anfrage ermöglicht eine feinere Steuerung. Verwenden Sie playwright_context_kwargs in der Anfrage meta , um einzelnen Anfragen unterschiedliche Proxys zuzuweisen:

yield scrapy.Request(
    url,
    meta={
        "playwright": True,
        "playwright_context_kwargs": {
            "proxy": {
                "server": "http://different-proxy:9090",
            },
        },
        "playwright_context": "proxy_context_1",
    },
    callback=self.parse,
)

Jeder eindeutige playwright_context Name erstellt einen separaten Browserkontext mit eigenem Proxy, eigenen Cookies und eigenem Speicherstatus. Auf diese Weise isolieren Sie Sitzungen, wenn Sie durch einen Proxy-Pool rotieren.

Für Crawls in der Produktion sollten Sie Dienste in Betracht ziehen, die die Proxy-Rotation und das Lösen von CAPTCHAs hinter einem einzigen Endpunkt verwalten, damit Ihre Spider-Logik übersichtlich bleibt. Der entscheidende Punkt ist, dass die Proxy-Unterstützung von Scrapy-Playwright flexibel genug ist, um sich in jede von Ihnen gewählte Rotationsstrategie zu integrieren.

Best Practices für Anti-Erkennung und Stealth

Proxys allein reichen nicht aus. Moderne Anti-Bot-Systeme überprüfen Browser-Fingerabdrücke, User-Agent-Strings und Verhaltensmuster. Hier sind die Anti-Detection-Ebenen, die Sie für Ihre Scrapy-Playwright-Spider in Betracht ziehen sollten.

User-Agent-Rotation: Legen Sie pro Kontext einen realistischen, rotierenden User-Agent fest:

import random

USER_AGENTS = [
    "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 ...",
    "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 ...",
    # Add more real browser UA strings
]

yield scrapy.Request(
    url,
    meta={
        "playwright": True,
        "playwright_context_kwargs": {
            "user_agent": random.choice(USER_AGENTS),
        },
        "playwright_context": f"ctx_{random.randint(1, 100)}",
    },
    callback=self.parse,
)

Fingerabdruck-Reduzierung: Playwrights Chromium verfügt über Standard-WebDriver-Flags, die von Anti-Bot-Skripten erkannt werden. Sie können Ihren Fingerabdruck reduzieren, indem Sie:

  • Übergabe "args": ["--disable-blink-features=AutomationControlled"] in PLAYWRIGHT_LAUNCH_OPTIONS.
  • Verwenden page.evaluate , um die navigator.webdriver Eigenschaft.
  • Festlegen einer realistischen Viewport-Größe anstelle der standardmäßigen Headless-Abmessungen.

Zufällige Verzögerungen: Das Einfügen von Jitter zwischen den Anfragen verhindert, dass Ihr Datenverkehr wie ein Bot wirkt, der den Server mit Maschinen-Geschwindigkeit bombardiert. Verwenden Sie Scrapys DOWNLOAD_DELAY Einstellung in Kombination mit RANDOMIZE_DOWNLOAD_DELAY:

DOWNLOAD_DELAY = 2
RANDOMIZE_DOWNLOAD_DELAY = True

Stealth-Kontext-Einrichtung: Kombinieren Sie alle oben genannten Punkte zu einer wiederverwendbaren Kontextkonfiguration. Eine umfassende Anleitung zur Vermeidung von Sperren finden Sie in den Tipps zur Vermeidung von Sperren oder IP-Bans beim Web-Scraping, die zusätzliche Strategien behandeln, die über Scrapy-Playwright hinausgehen.

Das Fazit: Betrachten Sie Anti-Erkennung als mehrere Schichten statt als eine einzige Lösung. Proxys kümmern sich um die IP-Reputation. Die User-Agent-Rotation kümmert sich um Überprüfungen auf Header-Ebene. Die Fingerabdruck-Reduzierung kümmert sich um Überprüfungen auf JavaScript-Ebene. Verzögerungen kümmern sich um Verhaltensprüfungen. Sie müssen alle zusammenwirken lassen.

Browserkontexte, Sitzungen und Ressourcenverwaltung

Ein Browser-Kontext in Playwright ist eine isolierte Browsersitzung mit eigenen Cookies, lokalem Speicher und Cache. Scrapy-Playwright nutzt Kontexte intensiv, und ihr Verständnis ist der Schlüssel zur Verwaltung von Ressourcen bei großen Crawls.

Standardmäßig teilt sich jede Scrapy-Playwright-Anfrage, die keinen playwright_context Namen angibt, einen Standardkontext. Das bedeutet, dass Cookies über Anfragen hinweg bestehen bleiben, was für Websites, bei denen man angemeldet bleiben muss, in Ordnung ist, aber problematisch wird, wenn man pro Anfrage saubere Sitzungen wünscht.

Mit benannten Kontexten können Sie Sitzungen isolieren:

yield scrapy.Request(
    url,
    meta={
        "playwright": True,
        "playwright_context": "session_a",
    },
    callback=self.parse,
)

Alle Anfragen, die mit "session_a" teilen sich Cookies und den Status. Anfragen, die mit "session_b" erhalten eine völlig separate Sitzung. Dies ist nützlich für parallele Scraping-Workflows, bei denen Sie mehrere unabhängige Benutzer simulieren müssen.

PLAYWRIGHT_MAX_PAGES_PER_CONTEXT steuert, wie viele Seiten gleichzeitig innerhalb eines einzelnen Kontexts geöffnet sein können. Wenn das Limit erreicht ist, wird ein neuer Kontext erstellt. Die Optimierung dieser Einstellung hilft, Speicherüberlastung zu vermeiden:

PLAYWRIGHT_MAX_PAGES_PER_CONTEXT = 4

Tipps zur Speicherverwaltung:

  • Schließen Sie Seiten immer, wenn Sie playwright_include_page. Falls du await page.close() in Ihrer parse Methode vergessen, sammeln sich Seiten an und der Speicherverbrauch steigt linear mit der Anzahl der Anfragen.
  • Verwenden Sie CONCURRENT_REQUESTS , um die Parallelität zu begrenzen. Browser sind ressourcenhungrig; 8 bis 16 gleichzeitige Playwright-Anfragen sind ein vernünftiger Ausgangspunkt auf einem Rechner mit 8 GB RAM.
  • Überwachen Sie den RSS-Speicher Ihres Spiders während der Testläufe. Wenn er stetig ansteigt, prüfen Sie auf nicht geschlossene Seiten oder übermäßige Kontexterstellung.

Für Headless-Browser-Scraping-Workflows im Allgemeinen behandelt der Leitfaden zum Ausführen eines Headless-Browsers mit Python Ressourcenmuster, die die hier behandelten Themen ergänzen.

Fehlerbehebung und Fehlerbehandlung

Selbst gut konfigurierte Scrapy-Playwright-Spider können bei großem Umfang versagen. Hier sind die häufigsten Probleme und umsetzbaren Lösungen.

TimeoutError: Dies ist der Fehler, den Sie am häufigsten sehen werden. Er bedeutet, dass der Browser die Navigation oder eine Wartezeit nicht innerhalb der zulässigen Zeit abschließen konnte.

  • Erhöhen Sie PLAYWRIGHT_DEFAULT_NAVIGATION_TIMEOUT für langsame Websites.
  • Wechseln Sie von networkidle zu wait_for_selector um ein Hängenbleiben bei dauerhaften Verbindungen zu vermeiden.
  • Prüfen Sie, ob die Ziel-Website Sie blockiert (ein Screenshot der Timeout-Seite zeigt oft ein CAPTCHA oder eine Blockierungsseite).

Browser-Verbindungsabbrüche: Wenn der Browser-Prozess während des Crawls abstürzt, wird BrowserError oder Connection closed Ausnahmen.

  • Reduzieren CONCURRENT_REQUESTS. Zu viele parallele Seiten können den Systemspeicher belasten und den Browser zum Absturz bringen.
  • Stellen Sie PLAYWRIGHT_MAX_PAGES_PER_CONTEXT auf einen niedrigeren Wert.
  • Hinzufügen "args": ["--disable-dev-shm-usage"] bei PLAYWRIGHT_LAUNCH_OPTIONS bei Ausführung in Docker, wo /dev/shm oft zu klein ist.

Speicherlecks: Der Speicherverbrauch Ihres Spiders steigt bei langen Crawls an.

  • Stellen Sie sicher, dass Sie alle über playwright_include_page. Jede nicht geschlossene Seite hält ein vollständiges DOM im Speicher.
  • Begrenzen PLAYWRIGHT_MAX_PAGES_PER_CONTEXT und starten Sie Kontexte regelmäßig neu.
  • Verwenden Sie CLOSESPIDER_PAGECOUNT oder eine benutzerdefinierte Erweiterung, um den Spider nach Erreichen eines Schwellenwerts neu zu starten.

Fehlerbehandlungsmuster: Verwenden Sie Scrapys errback , um Fehler elegant zu behandeln, anstatt zuzulassen, dass sie den Spider zum Absturz bringen:

yield scrapy.Request(
    url,
    meta={"playwright": True, "playwright_include_page": True},
    callback=self.parse,
    errback=self.handle_error,
)

async def handle_error(self, failure):
    page = failure.request.meta.get("playwright_page")
    if page:
        await page.close()
    self.logger.error(f"Request failed: {failure.request.url}")

Das entscheidende Detail: Wenn Sie playwright_include_page, müssen Sie die Seite sowohl im Callback als auch im Errback schließen. Andernfalls verursacht eine fehlgeschlagene Anfrage ein Leck des Seitenobjekts. Kombinieren Sie Errbacks mit Scrapys integrierter RETRY_TIMES Einstellung, um vorübergehende Fehler automatisch erneut zu versuchen, bevor aufgegeben wird.

Debugging mit Traces: Playwright unterstützt die Trace-Aufzeichnung, die eine vollständige Zeitleiste von Netzwerkanfragen, DOM-Snapshots und Aktionen erfasst. Aktivieren Sie diese Funktion PLAYWRIGHT_LAUNCH_OPTIONS während der Entwicklung, um genau das nachzustellen, was der Browser auf einer problematischen Seite getan hat.

Erstellen einer produktionsreifen Spider

Tutorials enden oft, nachdem sie Ihnen gezeigt haben, wie man Daten extrahiert. In der Produktion benötigen Sie eine vollständige Projektstruktur mit Items, Pipelines, Middlewares und gut abgestimmten Einstellungen. Hier erfahren Sie, wie Sie alles für ein Scrapy-Playwright-Projekt miteinander verknüpfen.

Definieren Sie Ihre Items:

# items.py
import scrapy

class ProductItem(scrapy.Item):
    name = scrapy.Field()
    price = scrapy.Field()
    url = scrapy.Field()

Verwenden Sie Item Klassen (oder dataclass Items in neueren Scrapy-Versionen) erhalten Sie eine Schemavalidierung und machen Ihren Pipeline-Code übersichtlicher als die Übergabe von rohen Dictionaries.

Schreiben Sie eine Item-Pipeline für die Validierung und Speicherung:

# pipelines.py
class ValidateProductPipeline:
    def process_item(self, item, spider):
        if not item.get("name"):
            raise scrapy.exceptions.DropItem("Missing name")
        item["price"] = float(item["price"].replace("$", "").strip())
        return item

class JsonWriterPipeline:
    def open_spider(self, spider):
        import json
        self.file = open("products.jsonl", "w")

    def close_spider(self, spider):
        self.file.close()

    def process_item(self, item, spider):
        import json
        self.file.write(json.dumps(dict(item)) + "\n")
        return item

Checkliste für die Produktionseinstellungen:

# settings.py (additions for production)
ITEM_PIPELINES = {
    "myproject.pipelines.ValidateProductPipeline": 100,
    "myproject.pipelines.JsonWriterPipeline": 200,
}

CONCURRENT_REQUESTS = 8
DOWNLOAD_DELAY = 1.5
RANDOMIZE_DOWNLOAD_DELAY = True
RETRY_TIMES = 3
LOG_LEVEL = "INFO"

PLAYWRIGHT_MAX_PAGES_PER_CONTEXT = 4
PLAYWRIGHT_DEFAULT_NAVIGATION_TIMEOUT = 30000

Das produktionsreife Muster lautet: Strukturierte Items durchlaufen Validierungspipelines, die Einstellungen begrenzen die Parallelität auf ein Maß, das Ihre Maschine und die Zielseite bewältigen können, und die Wiederholungslogik sowie Errbacks fangen vorübergehende Fehler ab. Scrapys integrierter Statistik-Collector liefert Ihnen Metriken pro Crawl (gescrapte Items, Fehler, Wiederholungsversuche) ohne zusätzlichen Code.

Für Teams, die sich zunächst die Grundlagen des Web-Scrapings mit Scrapy aneignen möchten, bevor sie Playwright darauf aufbauen, bietet der Leitfaden zum Web-Scraping mit Scrapy eine solide Grundlage.

Wichtige Erkenntnisse

  • Aktivieren Sie Playwright selektiv. Markieren Sie Anfragen nur mit "playwright": True , wenn die Seite tatsächlich eine JavaScript-Rendering erfordert; kombinieren Sie für alles andere Standard-Scrapy-Anfragen, um Crawls schnell zu halten.
  • Verwenden Sie wait_for_selector anstelle von networkidle oder Hard Sleeps. Selektorbasiertes Warten ist bei den meisten dynamischen Inhaltsszenarien schneller und zuverlässiger.
  • Fangen Sie AJAX-Aufrufe nach Möglichkeit ab. Das Erfassen von API-Antworten im Hintergrund liefert Ihnen sauberes JSON und vermeidet instabile DOM-Selektoren.
  • Anti-Erkennungsmaßnahmen kombinieren: Proxys, User-Agent-Rotation, Fingerabdruck-Reduzierung und zufällige Verzögerungen sollten zusammenwirken, nicht sich gegenseitig ersetzen.
  • Schließen Sie jede Seite, die Sie öffnen. Speicherlecks durch nicht geschlossene Playwright-Seiten sind die häufigste Ursache für Instabilität bei lang andauernden Scrapy-Playwright-Crawls.

FAQ

Unterstützt Scrapy-Playwright Firefox und WebKit oder nur Chromium?

Ja, alle drei Engines werden unterstützt. Setze PLAYWRIGHT_BROWSER_TYPE auf "firefox" oder "webkit" in deinen Scrapy-Einstellungen und führe playwright install firefox (oder webkit), um die entsprechende Browser-Binärdatei herunterzuladen. Chromium ist der Standard und am umfassendsten getestet, aber Firefox kann für Websites nützlich sein, die speziell nach Chromium suchen.

Wie behebe ich TimeoutError-Ausnahmen in Scrapy-Playwright?

Beginnen Sie damit, PLAYWRIGHT_DEFAULT_NAVIGATION_TIMEOUT über den Standardwert von 30 Sekunden hinaus. Wenn das Timeout weiterhin auftritt, ändern Sie Ihre Wartestrategie von networkidle auf wait_for_selector auf ein bestimmtes Element. Machen Sie außerdem einen Screenshot der fehlerhaften Seite, um zu überprüfen, ob die Website ein CAPTCHA oder eine Blockierungsseite anstelle des erwarteten Inhalts anzeigt.

Kann ich Scrapy-Playwright im Headful-Modus (sichtbarer Browser) zum Debuggen ausführen?

Ja. Fügen Sie "headless": False zu PLAYWRIGHT_LAUNCH_OPTIONS in settings.pyhinzu. Das Browserfenster wird sichtbar geöffnet, sodass Sie jede Navigation und Interaktion in Echtzeit beobachten können. Dies ist für das Debuggen von Seiten-Methoden-Sequenzen von unschätzbarem Wert. Denken Sie daran, vor dem Ausführen von Produktions-Crawls wieder in den Headless-Modus zu wechseln.

Wie viel Speicher benötigt Scrapy-Playwright und wie kann ich den Verbrauch reduzieren?

Jede Chromium-Seite verbraucht je nach Komplexität der Seite etwa 50 bis 150 MB RAM. Um den Speicherbedarf zu reduzieren, senken Sie CONCURRENT_REQUESTS, PLAYWRIGHT_MAX_PAGES_PER_CONTEXT auf einen kleinen Wert (3 bis 5), verzichte auf unnötige Ressourcentypen (Bilder, Schriftarten, Stylesheets) und schließe Seiten immer explizit sowohl in deinen Callback- als auch in deinen Errback-Methoden.

Was ist der Unterschied zwischen Scrapy-Playwright, Scrapy-Splash und Scrapy-Selenium?

Scrapy-Playwright nutzt die moderne asynchrone API von Playwright mit Chromium, Firefox oder WebKit. Scrapy-Splash basiert auf einem separaten Docker-basierten Rendering-Dienst mit eingeschränkter Interaktivität. Scrapy-Selenium nutzt das ältere WebDriver-Protokoll. Für neue Projekte bietet Scrapy-Playwright in der Regel die beste Kombination aus Browser-Treue, asynchroner Leistung und aktiver Wartung.

Fazit

Scrapy Playwright schließt die Lücke zwischen Scrapys leistungsstarker Crawling-Engine und der Realität des heutigen JavaScript-gesteuerten Webs. Durch Hinzufügen eines einzigen Meta-Flags zu Ihren Anfragen erhalten Sie vollständiges Browser-Rendering, ohne auf Scrapys Pipelines, Middleware und das Parallelitätsmodell verzichten zu müssen. Dieses Tutorial deckte das gesamte Spektrum ab: von der Ersteinrichtung und Konfiguration über Seiteninteraktionen, AJAX-Abfang bis hin zu Anti-Detection und Produktionshärtung.

Die hier vorgestellten Techniken sollten die überwiegende Mehrheit dynamischer Scraping-Szenarien abdecken. Für Projekte, bei denen die Verwaltung der Browser-Infrastruktur, Proxy-Rotation und Anti-Detection in großem Maßstab zum Engpass werden und nicht die Scraping-Logik selbst, übernimmt unsere Scraper-API diese Schichten hinter einem einzigen Endpunkt, sodass Sie sich auf die Daten statt auf die technische Umsetzung konzentrieren können.

Unabhängig davon, für welchen Ansatz Sie sich entscheiden, bleibt das Kernprinzip dasselbe: Verwenden Sie Browser-Rendering nur dort, wo es notwendig ist, halten Sie Ihre Spider gut strukturiert und schließen Sie jede Seite, die Sie öffnen.

Über den Autor
Raluca Penciuc, Full-Stack-Entwickler @ WebScrapingAPI
Raluca PenciucFull-Stack-Entwickler

Raluca Penciuc ist Full-Stack-Entwicklerin bei WebScrapingAPI. Sie entwickelt Scraper, verbessert Umgehungsstrategien und findet zuverlässige Wege, um die Erkennung auf Zielwebsites zu verringern.

Los geht’s

Sind Sie bereit, Ihre Datenerfassung zu erweitern?

Schließen Sie sich den über 2.000 Unternehmen an, die WebScrapingAPI nutzen, um Webdaten im Unternehmensmaßstab ohne zusätzlichen Infrastrukturaufwand zu extrahieren.