Zurück zum Blog
Anleitungen
Robert SfichiLast updated on Apr 29, 202627 min read

Web Scraping mit Selenium: Python-Schritt-für-Schritt-Tutorial

Web Scraping mit Selenium: Python-Schritt-für-Schritt-Tutorial
Kurzfassung: Mit Selenium können Sie JavaScript-lastige Websites scrapen, indem Sie einen echten Browser über Python-Code steuern. Dieses Tutorial führt Sie durch alle Schritte: Installation von Selenium, Konfiguration von Chrome, Auffinden von Elementen und Interaktion mit ihnen, Umgang mit Wartezeiten und Paginierung, Exportieren sauberer Daten sowie Skalierung Ihres Scrapers mithilfe von Proxys, Selenium Grid und API-basierten Alternativen.

Selenium ist ein Framework zur Browser-Automatisierung, das eine echte Browser-Instanz (Chrome, Firefox, Edge und andere) per Code steuert. Obwohl es ursprünglich zum Testen von Webanwendungen entwickelt wurde, hat es sich zu einem der am häufigsten verwendeten Tools für das Web-Scraping mit Selenium entwickelt, insbesondere auf Websites, auf denen JavaScript die benötigten Inhalte rendert.

Wenn Sie bereits versucht haben, eine Single-Page-Anwendung oder einen Infinite-Scroll-Feed mit requests und BeautifulSoup zu scrapen, kennen Sie das Problem bereits: Der heruntergeladene HTML-Code ist eine leere Hülle. Die eigentlichen Daten werden erst nach Ausführung von JavaScript geladen, und ein einfacher HTTP-Client führt dieses JavaScript niemals aus. Selenium löst dieses Problem, indem es einen vollständigen Browser startet, die Seite genau so lädt, wie es ein menschlicher Besucher tun würde, und Ihnen dann programmatischen Zugriff auf das resultierende DOM gewährt.

Dieses Tutorial behandelt jeden praktischen Schritt des Selenium-Web-Scrapings in Python: Einrichten der Umgebung, Strategien zur Elementlokalisierung, Warten auf dynamische Inhalte, Scrollen, Paginierung, Datenexport, Proxy-Integration und Leistungsoptimierung. Am Ende verfügen Sie über einen funktionierenden End-to-End-Scraper und ein klares Bild davon, wann Selenium die richtige Wahl ist und wann leichtere Alternativen vorzuziehen sind.

Was ist Selenium und warum sollte man es für das Web-Scraping verwenden?

Selenium begann 2004 als Test-Framework und hat sich seitdem über mehrere Hauptversionen weiterentwickelt. Heute kommuniziert Selenium WebDriver mit Browsern über das W3C-WebDriver-Protokoll, eine standardisierte API, die alle großen Browser-Anbieter nativ implementieren. Sie schreiben Anweisungen in Python (oder Java, JavaScript, Ruby, C# und mehreren anderen Sprachen), und der WebDriver übersetzt diese Anweisungen in Aktionen innerhalb einer echten Browsersitzung. Der Browser rendert HTML, führt JavaScript aus, wendet CSS an und sendet Netzwerkanfragen, genau wie es ein Mensch tun würde, der vor einer Tastatur sitzt.

Genau diese vollständige Rendering-Pipeline macht Selenium für das Scraping so wertvoll. Herkömmliche HTTP-Bibliotheken wie requests laden das rohe HTML-Dokument, das der Server zurücksendet. Wenn die Seite auf clientseitiges JavaScript angewiesen ist, um eine Produkttabelle zu füllen, Bewertungen zu laden oder ein Dashboard zusammenzustellen, erhalten Sie keine dieser Daten. Selenium hingegen wartet darauf, dass das JavaScript ausgeführt wird, und liefert Ihnen das vollständig gerenderte DOM.

Selenium unterstützt zudem alle gängigen Browser (Chrome, Firefox, Edge, Opera, Safari) und funktioniert systemübergreifend. Diese browserübergreifende Unterstützung ist wichtig, wenn sich eine Zielseite je nach Browser-Engine unterschiedlich verhält. Und da Selenium echtes Nutzerverhalten nachahmt (Klicken, Tippen, Scrollen), kann es interaktive Workflows durchlaufen, die statische Scraper einfach nicht erreichen können.

Der Nachteil sind die Ressourcenkosten. Das Ausführen eines vollständigen Browsers beansprucht echte CPU-Leistung und RAM und ist spürbar langsamer als das Absenden einer schlanken HTTP-Anfrage. Selenium wurde als Testtool konzipiert, daher verursachen einige seiner Abstraktionen (der Overhead des WebDriver-Protokolls, das Fehlen einer integrierten automatischen Wartefunktion) Reibungsverluste bei reinen Scraping-Anwendungsfällen. Dennoch machen seine riesige Community, die umfangreiche Dokumentation und die breite Sprachunterstützung es zu einem der sichersten Einstiegspunkte für alle, die sich mit browserbasierter Datenextraktion beschäftigen. Wir werden später im Tutorial auf Leistungsaspekte und Alternativen eingehen.

Wann Selenium das richtige Scraping-Tool ist (und wann nicht)

Selenium ist die richtige Wahl, wenn die Seite, die Sie scrapen möchten, die Ausführung von JavaScript erfordert, um ihren Inhalt darzustellen. Denken Sie an Single-Page-Anwendungen, die mit React, Angular oder Vue erstellt wurden; an Websites hinter Anmeldeformularen; an Seiten mit unendlichem Scrollen; und an Dashboards, bei denen Daten nach dem ersten Laden der Seite über AJAX-Aufrufe geladen werden.

Es ist nicht die beste Wahl für das groß angelegte Crawling statischer HTML-Seiten. Für solche Aufgaben requests in Kombination mit BeautifulSoup oder einem Framework wie Scrapy schneller, speicherfreundlicher und leichter zu skalieren. Der Vergleich zwischen Scrapy und Selenium läuft darauf hinaus, ob Sie überhaupt einen Browser benötigen. Neuere Browser-Automatisierungsbibliotheken wie Playwright und Puppeteer sollten ebenfalls in Betracht gezogen werden, wenn Sie moderne asynchrone APIs mit integrierten Auto-Wait-Funktionen wünschen.

Hier ist eine kurze Entscheidungshilfe:

  • Statisches HTML, kein JS erforderlich: Verwenden Sie requests + BeautifulSoup.
  • Großes Crawling, statische Seiten: Verwenden Sie Scrapy.
  • JS-gerenderte Seiten, mehrsprachiges Team: Verwenden Sie Selenium.
  • JS-gerenderte Seiten, nur Python/JS, moderne API gewünscht: Ziehen Sie Playwright in Betracht.

Eine praktische Faustregel: Beginnen Sie mit dem leichtesten Tool, das funktioniert. Wenn requests die benötigten Daten abruft, belassen Sie es dabei. Wenn der Inhalt JavaScript-gerendert ist und Sie umfassende Sprachunterstützung sowie ein ausgereiftes Ökosystem wünschen, ist Web-Scraping mit Selenium ein solider Mittelweg.

Voraussetzungen und Einrichten der Umgebung

Bevor du auch nur eine Zeile Scraping-Code schreibst, musst du drei Dinge installieren: Python 3.8 oder höher, das Selenium-Paket und einen Browser-Treiber, der zu deiner Browserversion passt.

Python und Selenium installieren

Öffnen Sie ein Terminal und überprüfen Sie Ihre Python-Version:

python --version

Installieren Sie dann Selenium über pip:

pip install selenium

Ab Version 4.6 enthält Selenium ein integriertes Tool namens selenium-manager, das den richtigen Browser-Treiber automatisch für Sie herunterladen und konfigurieren kann. In früheren Versionen musstest du ChromeDriver (oder GeckoDriver für Firefox) manuell herunterladen und zu deinem System-PATH hinzufügen. Wenn du Selenium 4.6 oder höher verwendest, kannst du die manuelle Treiberverwaltung in den meisten Fällen überspringen, es lohnt sich jedoch zu überprüfen, ob deine Chrome- und ChromeDriver-Versionen übereinstimmen, falls Startfehler auftreten.

Überprüfen Sie die Installation

Erstellen Sie ein kurzes Testskript, um zu überprüfen, ob alles funktioniert:

from selenium import webdriver

driver = webdriver.Chrome()
driver.get("https://example.com")
print(driver.title)  # Should print "Example Domain"
driver.quit()

Wenn sich ein Chrome-Fenster öffnet, die Seite lädt und den Titel in Ihrer Konsole ausgibt, ist Ihre Umgebung bereit. Falls nicht, überprüfen Sie, ob Chrome installiert ist und ob Ihre Selenium-Version mit der Hauptversion von Chrome auf Ihrem Rechner übereinstimmt.

Virtuelle Umgebungen

Es empfiehlt sich, in einer virtuellen Umgebung zu arbeiten, damit Selenium und seine Abhängigkeiten nicht mit anderen Projekten in Konflikt geraten:

python -m venv scraper-env
source scraper-env/bin/activate   # On Windows: scraper-env\Scripts\activate
pip install selenium beautifulsoup4 pandas

BeautifulSoup übernimmt das schnelle Parsen von HTML, sobald Selenium den Quellcode der gerenderten Seite abgerufen hat, und pandas vereinfacht die Datenbereinigung und den CSV-Export. Beide sind optional, kommen aber in realen Web-Scraping-Workflows mit Selenium Python häufig zum Einsatz, sodass es Zeit spart, sie von Anfang an installiert zu haben.

Zu diesem Zeitpunkt ist Ihre Umgebung bereit. Der nächste Schritt besteht darin, Chrome so zu konfigurieren, dass es sich während einer Scraping-Sitzung wie gewünscht verhält.

Konfigurieren der Chrome-Optionen für das Scraping

Selenium startet Chrome mit den Standardeinstellungen, doch diese sind für das Scraping nicht ideal. Mit der ChromeOptions Klasse ermöglicht es Ihnen, die Browsersitzung vor dem Start anzupassen.

from selenium import webdriver

options = webdriver.ChromeOptions()
options.add_argument("--headless=new")          # Run without a visible window
options.add_argument("--disable-gpu")           # Prevents GPU-related issues in headless
options.add_argument("--window-size=1920,1080") # Consistent viewport for element visibility
options.add_argument("--no-sandbox")            # Required in some CI/Docker environments
options.add_argument("--disable-dev-shm-usage") # Avoids shared memory issues in containers
options.add_argument("user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
                     "AppleWebKit/537.36 (KHTML, like Gecko) "
                     "Chrome/125.0.0.0 Safari/537.36")

driver = webdriver.Chrome(options=options)

Im Headless-Modus wird Chrome ohne Darstellung eines grafischen Fensters ausgeführt. Dies reduziert den Ressourcenverbrauch und beschleunigt die Ausführung, was wichtig ist, wenn Sie Scraper auf einem Server oder in einer CI-Pipeline ausführen. Das --headless=new Flag hat das ältere --headless Flag in Chrome 109 und bietet eine bessere Funktionsparität mit dem Head-Modus. Für weitere Hintergrundinformationen zu Headless-Browsern und deren Einsatzmöglichkeiten ist eine spezielle Einführung in die Architektur von Headless-Browsern eine hilfreiche Referenz.

Das Festlegen einer benutzerdefinierten User-Agent-Zeichenkette hilft dabei, Ihre Anfragen in den regulären Browser-Verkehr einzubinden, anstatt zu verraten, dass sie von einem automatisierten Tool stammen. Kombinieren Sie dies mit einer realistischen Fenstergröße, damit responsive Layouts die Desktop-Version der Seite anzeigen, die Sie analysieren möchten.

Weitere nützliche Flags sind --disable-extensions (überspringt das Laden von Erweiterungen, die die Startzeit verlängern), --disable-infobars (blendet das Banner „Chrome wird gesteuert“ aus) und --disable-notifications (blockiert Pop-ups, die Elemente verdecken können). Zusammen ergeben diese Flags ein schlankes Browserprofil, das eher für die Datenextraktion als für interaktives Surfen optimiert ist.

Einen Browser starten und zu einer URL navigieren

Wenn Ihre Optionen konfiguriert sind, sind zum Starten eines Browsers und zum Aufrufen einer Seite nur zwei Zeilen erforderlich:

driver = webdriver.Chrome(options=options)
driver.get("https://books.toscrape.com/")

driver.get() wartet, bis der Browser sein load Ereignis auslöst, d. h. wenn das anfängliche HTML, CSS und synchrone JavaScript geladen sind. Es wartet nicht auf AJAX-Aufrufe, die nach dem Load-Ereignis ausgelöst werden, sodass Sie möglicherweise weiterhin explizit auf dynamisch eingefügte Inhalte warten müssen (siehe Abschnitt „Warten“ weiter unten).

Einige nützliche Eigenschaften direkt nach der Navigation:

print(driver.title)        # Page <title> text
print(driver.current_url)  # Final URL after any redirects

Rufen Sie immer driver.quit() , wenn Sie fertig sind, entweder in einem finally Block oder durch die Verwendung von Selenium als Kontextmanager. Wenn Browserprozesse weiterlaufen, kommt es schnell zu Speicherlecks, insbesondere in Schleifen. Ein einfaches Muster sieht wie folgt aus:

try:
    driver = webdriver.Chrome(options=options)
    driver.get("https://example.com")
    # ... scraping logic ...
finally:
    driver.quit()

Dies stellt sicher, dass der Browser heruntergefahren wird, selbst wenn Ihr Code während der Ausführung eine Ausnahme auslöst. Wenn Sie sich frühzeitig mit dieser Art der Ressourcenverwaltung vertraut machen, vermeiden Sie, dass verwaisten Chrome-Prozesse in der Produktion den Speicher Ihres Servers belasten. Überprüfen Sie auf einem Entwicklungsrechner regelmäßig Ihren Task-Manager, um sicherzustellen, dass keine veralteten chrome oder chromedriver veraltete Prozesse von abgestürzten Skripten zurückbleiben.

Elemente auf der Seite finden

Das Auffinden von Elementen ist der Kern jedes Selenium-Scraping-Tutorials. Selenium bietet zwei Hauptmethoden: find_element() (gibt die erste Übereinstimmung zurück) und find_elements() (gibt eine Liste aller Treffer zurück), von denen jede eine By Lokalisierungsstrategie akzeptiert.

Gängige Locator-Strategien

from selenium.webdriver.common.by import By

# By ID (fastest, most reliable when available)
driver.find_element(By.ID, "search-input")

# By class name
driver.find_elements(By.CLASS_NAME, "product-card")

# By CSS selector
driver.find_element(By.CSS_SELECTOR, "div.results > a.item-link")

# By XPath
driver.find_element(By.XPATH, "//table[@id='data']//tr")

# By tag name
driver.find_elements(By.TAG_NAME, "tr")

# By name attribute
driver.find_element(By.NAME, "email")

ID-basierte Locators sind in der Regel am schnellsten, da der Browser direkt zum Element springen kann, ohne den DOM-Baum zu durchlaufen. Wenn das Zielelement keine ID hat, sind CSS-Selektoren für die meisten Anwendungsfälle die nächstbeste Option: Sie sind kompakt, gut lesbar und werden gut unterstützt.

XPath vs. CSS-Selektoren

XPath ist besonders geeignet, wenn Sie im DOM nach oben navigieren (Elternachse), nach Textinhalt suchen oder komplexe Prädikate verwenden müssen. Zum Beispiel //div[contains(text(), 'Price')] ist in XPath einfach, hat aber kein direktes CSS-Äquivalent. CSS-Selektoren sind bei einfachen Mustern wie div.card > h3.title. Verwenden Sie XPath, wenn CSS die von Ihnen benötigte Beziehung nicht ausdrücken kann; bleiben Sie bei allem anderen bei CSS. Es lohnt sich, die Vor- und Nachteile von XPath und CSS-Selektoren genauer zu betrachten, wenn Sie Selektoren für komplexe Seitenstrukturen erstellen.

find_element vs. find_elements

find_element() löst einen NoSuchElementException , wenn nichts übereinstimmt, während find_elements() eine leere Liste zurückgibt. Beim Scraping find_elements() ist in der Regel sicherer, da Sie die Listenlänge vor der Verarbeitung überprüfen können und so try/except-Blöcke für fehlende Elemente vermeiden.

cards = driver.find_elements(By.CSS_SELECTOR, ".product-card")
if cards:
    for card in cards:
        title = card.find_element(By.CSS_SELECTOR, "h3").text
        price = card.find_element(By.CSS_SELECTOR, ".price").text
        print(title, price)

Beachten Sie, dass Sie find_element Aufrufe an ein WebElement verketten (nicht nur den Treiber). Dadurch wird die Suche auf den Teilbaum dieses Elements beschränkt, was sowohl schneller als auch präziser ist, wenn Sie wiederholte Strukturen wie Karten oder Tabellenzeilen durchlaufen.

Der Web-Scraping-Workflow mit Selenium Find Element läuft im Wesentlichen wie folgt ab: Bestimme die Locator-Strategie, teste sie in der DevTools-Konsole des Browsers und codiere sie anschließend in dein Python-Skript. Mit Chrome DevTools kannst du CSS-Selektoren mit $$() und XPath $x() direkt in der Konsole, was die Entwicklung von Selektoren erheblich beschleunigt.

Interaktion mit Seitenelementen

Sobald Sie ein Element gefunden haben, bietet Selenium Ihnen Methoden, um darauf zu reagieren, genau wie es ein menschlicher Benutzer tun würde. Das WebElement-Objekt stellt Aktionen wie Klicken, Tippen, Lesen von Text und Abrufen von Attributwerten bereit.

Klicken, Eingeben und Lesen

from selenium.webdriver.common.keys import Keys

# Click a button
driver.find_element(By.ID, "load-more").click()

# Type into an input field
search_box = driver.find_element(By.NAME, "q")
search_box.clear()
search_box.send_keys("web scraping with selenium")
search_box.send_keys(Keys.RETURN)

# Read text content
heading = driver.find_element(By.TAG_NAME, "h1").text

# Read an attribute value
link = driver.find_element(By.CSS_SELECTOR, "a.detail-link")
href = link.get_attribute("href")

Die .text Eigenschaft gibt den sichtbaren Text eines Elements zurück, während .get_attribute() jedes HTML-Attribut (href, src, data-* usw.) ausliest. Diese beiden Methoden sind Ihre wichtigsten Werkzeuge zur Datenextraktion auf Elementebene.

Arbeiten mit Dropdown-Menüs

Für <select> -Elemente bietet Selenium eine Komfortklasse:

from selenium.webdriver.support.ui import Select

dropdown = Select(driver.find_element(By.ID, "sort-by"))
dropdown.select_by_visible_text("Price: Low to High")

Sie können auch nach Wert (select_by_value("price_asc")) oder anhand eines nullbasierten Index (select_by_index(2)). Moderne Webanwendungen verwenden zunehmend benutzerdefinierte Dropdown-Komponenten anstelle von nativen <select> -Elemente. In diesem Fall müssen Sie auf den Auslöser des Dropdowns klicken und anschließend auf die gewünschte Option als separate find_element und click Aufrufe.

Verkettung von Aktionen

Die ActionChains API ermöglicht es Ihnen, komplexe Interaktionen wie Hover, Drag-and-Drop und Rechtsklick zu erstellen:

from selenium.webdriver.common.action_chains import ActionChains

menu = driver.find_element(By.ID, "mega-menu")
ActionChains(driver).move_to_element(menu).perform()

Dies ist nützlich, um Mega-Menüs, Tooltips und andere Elemente zu scrapen, die nur beim Hover erscheinen. Sie können mehrere Aktionen verketten, bevor Sie .perform() , um sie nacheinander auszuführen.

Jede dieser Interaktionsmethoden wartet darauf, dass das Element im DOM vorhanden ist, muss jedoch nicht unbedingt sichtbar oder anklickbar sein. Kombinieren Sie sie mit expliziten Wartezeiten (die im Folgenden behandelt werden), um Race-Conditions auf Seiten zu vermeiden, die Inhalte asynchron laden.

Wartestrategien: Implizite, explizite und fließende Wartezeiten

Dynamische Seiten laden Inhalte asynchron, und einer der häufigsten Fehler beim Web-Scraping mit Selenium ist der Versuch, ein Element zu lokalisieren, bevor es im DOM vorhanden ist. Hardcoding time.sleep(5) funktioniert technisch gesehen, verschwendet aber Zeit auf schnellen Seiten und schlägt auf langsamen Seiten fehl. Selenium bietet drei intelligentere Alternativen.

Implizite Wartezeiten

Eine implizite Wartezeit weist den Treiber an, das DOM für eine bestimmte Anzahl von Sekunden abzufragen, bevor ein NoSuchElementException:

driver.implicitly_wait(10)  # Applies globally to all find_element calls

Dies ist einfach einzurichten, gilt jedoch für jede Suche, was Leistungsprobleme verschleiern und Ihren Scraper verlangsamen kann, wenn Elemente tatsächlich nicht vorhanden sind. Außerdem können Sie die Bedingung nicht anpassen: Es wird nur das Vorhandensein geprüft, nicht die Sichtbarkeit oder Klickbarkeit.

Explizite Wartezeiten

Explizite Wartezeiten sind der empfohlene Ansatz für Best Practices im Selenium-Scraping-Tutorial. Sie geben eine Bedingung und ein Timeout an, und der Treiber fragt so lange ab, bis die Bedingung erfüllt ist:

from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

element = WebDriverWait(driver, 15).until(
    EC.visibility_of_element_located((By.CSS_SELECTOR, ".results-container"))
)

Zu den gängigen Bedingungen gehören:

  • presence_of_element_located: Das Element ist im DOM vorhanden (kann ausgeblendet sein)
  • visibility_of_element_located: Element ist sowohl vorhanden als auch sichtbar
  • element_to_be_clickable: Element ist sichtbar und aktiviert
  • text_to_be_present_in_element: Element enthält bestimmten Text
  • staleness_of: Element ist nicht mehr im DOM vorhanden (nützlich nach der Navigation)

Damit können Sie genau auf das warten, was Sie benötigen, und auf nichts anderes – so bleibt Ihr Scraper schnell, ohne an Zuverlässigkeit einzubüßen.

Fluent Waits

Ein Fluent Wait ist ein explizites Warten mit zusätzlicher Feinabstimmung: Sie legen das Abfrageintervall fest und bestimmen, welche Ausnahmen während der Abfrage ignoriert werden sollen.

from selenium.webdriver.support.ui import WebDriverWait
from selenium.common.exceptions import NoSuchElementException

wait = WebDriverWait(
    driver, timeout=20, poll_frequency=0.5,
    ignored_exceptions=[NoSuchElementException]
)
element = wait.until(
    EC.presence_of_element_located((By.ID, "dynamic-table"))
)

Verwenden Sie Fluent Waits, wenn das standardmäßige Abfrageintervall von 500 ms für ein Ziel mit Ratenbegrenzung oder langsamer Ladezeit zu aggressiv ist. In den meisten Web-Scraping-Szenarien mit Selenium deckt eine standardmäßige explizite Wartezeit WebDriverWait deckt Ihre Anforderungen ab. Die wichtigste Erkenntnis lautet: Bevorzugen Sie immer WebDriverWait vor time.sleep(). Es ist sowohl schneller als auch zuverlässiger.

Ausführung von JavaScript und Scroll-Techniken

Manche Interaktionen sind durch die direkte Ausführung von JavaScript einfacher (oder nur möglich). Seleniums execute_script() -Methode führt beliebige JavaScript-Schnipsel im Browserkontext aus und gibt das Ergebnis an Ihren Python-Code zurück.

Grundlegende Skriptausführung

page_height = driver.execute_script("return document.body.scrollHeight;")
print(f"Total page height: {page_height}px")

Sie können Python-Objekte als Argumente an das Skript übergeben und sie über arguments[0], arguments[1]usw. innerhalb der JavaScript-Zeichenkette darauf verweisen. Rückgabewerte werden automatisch in ihre Python-Entsprechungen (Dicts, Listen, Zeichenketten, Zahlen) konvertiert.

Zum Ende der Seite scrollen

Der häufigste Anwendungsfall beim Scraping ist das unendliche Scrollen. Hier ist ein zuverlässiges Schleifenmuster für Selenium-Scraping-Szenarien auf dynamischen Websites:

import time

last_height = driver.execute_script("return document.body.scrollHeight")

while True:
    driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")
    time.sleep(2)  # Allow content to load
    new_height = driver.execute_script("return document.body.scrollHeight")
    if new_height == last_height:
        break
    last_height = new_height

Die Schleife vergleicht die Scrollhöhe vor und nach jedem Scrollvorgang. Wenn sich die Höhe nicht mehr ändert, werden keine neuen Inhalte mehr geladen und die Schleife wird beendet. Möglicherweise möchten Sie eine maximale Iterationsgrenze festlegen, um Endlosschleifen auf Seiten zu vermeiden, die kontinuierlich Inhalte laden.

Zu einem bestimmten Element scrollen

Manchmal müssen Sie ein bestimmtes Element in den Sichtbereich bringen (zum Beispiel ein verzögert geladenes Bild oder eine Schaltfläche „Mehr laden“):

element = driver.find_element(By.ID, "footer-section")
driver.execute_script("arguments[0].scrollIntoView({behavior: 'smooth'});", element)

Auslösen versteckter Aktionen

Die Ausführung von JavaScript ist auch nützlich, um auf Elemente zu klicken, die durch Overlays verdeckt sind, um Sticky-Header zu entfernen, die Screenshots stören, oder um Daten zu extrahieren, die in JavaScript-Variablen eingebettet sind:

data = driver.execute_script("return window.__INITIAL_STATE__;")

Wenn die Website strukturierte Daten in einem globalen JS-Objekt speichert (wie es in React- und Next.js-Anwendungen üblich ist), ist das direkte Abrufen schneller als das Parsen des gerenderten DOM. Sie erhalten ein Python-Dict zurück von execute_script, das Sie sofort und ohne HTML-Parsing verarbeiten können.

Screenshots zum Debuggen

Wenn ein Scraper stillschweigend fehlschlägt oder unerwartete Ergebnisse liefert, ist ein Screenshot des Seitenzustands zum Zeitpunkt des Fehlers von unschätzbarem Wert.

driver.save_screenshot("debug_screenshot.png")

Sie können auch ein bestimmtes Element erfassen:

element = driver.find_element(By.ID, "captcha-container")
element.screenshot("captcha_element.png")

Speichern Sie Screenshots in Ihren Fehlerbehandlungsblöcken, damit Sie visuell überprüfen können, was der Browser gerendert hat, als etwas schiefgelaufen ist. Im Headless-Modus ist dies besonders wichtig, da es kein sichtbares Fenster gibt, das man überfliegen kann. Kombinieren Sie Screenshots mit der Protokollierung von driver.current_url und driver.page_source[:500] , um einen vollständigen Debugging-Snapshot zu erhalten.

Bei lang andauernden Scraping-Pipelines sollten Sie erwägen, Screenshots mit Zeitstempel an wichtigen Kontrollpunkten zu speichern (nach der Anmeldung, nach der Paginierung, vor der Datenextraktion), damit Sie genau nachverfolgen können, an welcher Stelle ein Lauf vom erwarteten Pfad abgewichen ist.

Umgang mit Paginierung über mehrere Seiten

Die meisten Websites verteilen Daten auf mehrere Seiten, und ein Scraper für den Produktionsbetrieb muss diesen Paginierungslinks automatisch folgen. Es gibt zwei gängige Muster: Paginierung über URL-Parameter und klickbasierte Paginierung.

Paginierung über URL-Parameter

Wenn die Website Abfragezeichenfolgen wie ?page=2, können Sie URLs direkt erstellen:

all_results = []
for page_num in range(1, 50):
    driver.get(f"https://example.com/listings?page={page_num}")
    WebDriverWait(driver, 10).until(
        EC.presence_of_element_located((By.CSS_SELECTOR, ".listing-card"))
    )
    items = driver.find_elements(By.CSS_SELECTOR, ".listing-card")
    if not items:
        break  # No results means we passed the last page
    for item in items:
        all_results.append({
            "title": item.find_element(By.CSS_SELECTOR, "h2").text,
            "price": item.find_element(By.CSS_SELECTOR, ".price").text,
        })

Die if not items: break Prüfung ist die einfachste Methode zur Erkennung der letzten Seite: Wenn eine Seite null Ergebnisse liefert, hörst du auf. Ein alternativer Ansatz besteht darin, zu prüfen, ob die aktuelle Seitenzahl die im Paginierungs-Widget angegebene Gesamtzahl überschreitet, oder nach einem „Nächste Seite“-Link zu suchen und aufzuhören, wenn dieser verschwindet.

Klickbasierte Paginierung

Einige Websites laden die nächste Seite über JavaScript, wenn Sie auf eine „Weiter“-Schaltfläche klicken:

from selenium.common.exceptions import NoSuchElementException

all_results = []
while True:
    items = driver.find_elements(By.CSS_SELECTOR, ".listing-card")
    for item in items:
        all_results.append(item.find_element(By.CSS_SELECTOR, "h2").text)

    try:
        next_btn = driver.find_element(By.CSS_SELECTOR, "a.next-page")
        if "disabled" in next_btn.get_attribute("class"):
            break
        next_btn.click()
        WebDriverWait(driver, 10).until(EC.staleness_of(items[0]))
    except NoSuchElementException:
        break

Die staleness_of Bedingung wartet, bis die alten Elemente aus dem DOM entfernt wurden, was bestätigt, dass die neue Seite geladen wurde. Die Überprüfung auf eine „disabled“-Klasse bei der „Weiter“-Schaltfläche sorgt für eine elegante Behandlung der letzten Seite.

Beide Muster benötigen eine klare Beendigungsbedingung. Ohne diese wird Ihr Scraper entweder endlos in einer Schleife laufen oder auf der letzten Seite abstürzen. Testen Sie Ihre Paginierungslogik immer bis an die Grenze: Was passiert auf der allerletzten Seite?

Daten von mehreren URLs scrapen

Wenn sich Ihre Zieldaten auf separaten Detailseiten befinden (z. B. eine Produktliste, die auf einzelne Produktseiten verlinkt), führen Sie das Scraping in der Regel in zwei Durchgängen durch: Sammeln Sie die URLs aus der Liste und rufen Sie dann jede einzelne auf.

# Pass 1: Collect detail URLs from the listing page
driver.get("https://example.com/catalog")
links = driver.find_elements(By.CSS_SELECTOR, "a.product-link")
detail_urls = [link.get_attribute("href") for link in links]

# Pass 2: Visit each detail URL and extract data
products = []
for url in detail_urls:
    try:
        driver.get(url)
        WebDriverWait(driver, 10).until(
            EC.presence_of_element_located((By.CSS_SELECTOR, ".product-detail"))
        )
        products.append({
            "name": driver.find_element(By.CSS_SELECTOR, "h1.product-name").text,
            "description": driver.find_element(By.CSS_SELECTOR, ".description").text,
            "url": url,
        })
    except Exception as e:
        print(f"Skipped {url}: {e}")
        continue

driver.quit()

Verwenden Sie durchgehend dieselbe WebDriver-Instanz. Das Starten eines neuen Browsers für jede URL verschwendet Startzeit und Speicher. Das Umschließen der inneren Schleife mit einem try/except stellt sicher, dass eine einzelne fehlerhafte Seite nicht Ihren gesamten Scraping-Vorgang zum Absturz bringt.

Dieses Master-Detail-Muster ist einer der praktischsten Python-Selenium-Workflows zum Scrapen von Websites. Es lässt sich gut mit der Paginierungsschleife aus dem vorherigen Abschnitt kombinieren, um einen gesamten Katalog zu crawlen. Sammeln Sie zunächst alle URLs der Auflistungsseiten über die Paginierung hinweg und durchlaufen Sie dann in einem zweiten Durchgang die Detailseiten.

Bei großen URL-Listen sollten Sie eine kurze Verzögerung zwischen den Anfragen (0,5 bis 2 Sekunden) einfügen, um die Ratenbeschränkungen der Website einzuhalten und das Risiko zu verringern, Anti-Bot-Abwehrmechanismen auszulösen. Sie können die Verzögerung leicht variieren, um Ihr Anfragemuster weniger vorhersehbar zu machen.

Extrahieren und Parsen von HTML-Tabellen

Tabellarische Daten sind eines der am besten strukturierten (und daher einfachsten) Ziele für das Web-Scraping mit Selenium. Hier ist ein generisches Muster, das bei jeder Standard-HTML-Tabelle funktioniert:

table = driver.find_element(By.CSS_SELECTOR, "table#stats")

# Extract headers
headers = [th.text for th in table.find_elements(By.CSS_SELECTOR, "thead th")]

# Extract rows
rows = []
for tr in table.find_elements(By.CSS_SELECTOR, "tbody tr"):
    cells = [td.text for td in tr.find_elements(By.TAG_NAME, "td")]
    rows.append(dict(zip(headers, cells)))

Dadurch erhalten Sie eine Liste von Wörterbüchern, in denen jeder Schlüssel eine Spaltenüberschrift und jeder Wert den entsprechenden Zelltext darstellt. Es handelt sich um eine übersichtliche, vorhersehbare Struktur, die sich direkt in Ihre Export-Pipeline einbinden lässt.

Wenn die Tabelle groß ist oder Sie weitere Analysen planen, ist die Übergabe an pandas effizienter:

import pandas as pd

html = driver.page_source
tables = pd.read_html(html, attrs={"id": "stats"})
df = tables[0]

pd.read_html verarbeitet colspan, rowspan und verschachtelte Tabellen eleganter als die manuelle Elementdurchlauf. Verwenden Sie das Selenium-gesteuerte page_source , um ihm den vollständig gerenderten HTML-Code zu übergeben, der alle von JavaScript eingefügten Daten enthält.

Achten Sie auf Tabellen, die Zeilen beim Scrollen dynamisch laden. In diesen Fällen müssen Sie den Tabellen-Container (nicht nur die Seite) scrollen, um das verzögerte Laden auszulösen, bevor Sie die Zeilen extrahieren. Einige Finanz- und Analyse-Websites verwenden virtualisierte Tabellen, die nur sichtbare Zeilen rendern, was ein schrittweises Scrollen erfordert, um den vollständigen Datensatz zu erfassen.

Kombination von Selenium und BeautifulSoup für schnelleres Parsen

Selenium ist hervorragend beim Rendern von Seiten, aber seine Methoden zur Elementsuche sind relativ langsam, da jeder Aufruf die Grenze des WebDriver-Protokolls überschreitet. Ein gängiger Trick zur Leistungsoptimierung besteht darin, Selenium das Rendern übernehmen zu lassen und den fertigen HTML-Code anschließend zur Analyse an BeautifulSoup zu übergeben.

from bs4 import BeautifulSoup

driver.get("https://example.com/products")
WebDriverWait(driver, 10).until(
    EC.presence_of_element_located((By.CSS_SELECTOR, ".product-grid"))
)

soup = BeautifulSoup(driver.page_source, "html.parser")
cards = soup.select(".product-card")

for card in cards:
    title = card.select_one("h3").get_text(strip=True)
    price = card.select_one(".price").get_text(strip=True)
    print(title, price)

Dieses Selenium-BeautifulSoup-Web-Scraping-Muster bietet Ihnen das Beste aus beiden Welten: Selenium führt JavaScript aus, sodass das DOM vollständig ist, und BeautifulSoup parst den statischen HTML-Snapshot im Speicher ohne Netzwerk-Roundtrips. Auf Seiten mit Hunderten von Elementen ist der Geschwindigkeitsunterschied deutlich spürbar.

Der Arbeitsablauf ist einfach: Verwenden Sie Selenium für die Navigation und die Ausführung von JavaScript und wechseln Sie dann zu BeautifulSoup (oder lxml), sobald Sie das page_source. Dadurch reduziert sich die Anzahl der WebDriver-Aufrufe von Hunderten auf einen. Wenn Sie noch keine Erfahrung mit BeautifulSoup haben, ist eine Anleitung zum Extrahieren und Parsen von Webdaten mit BeautifulSoup eine nützliche Ergänzung zu diesem Tutorial.

Exportieren von gescrapten Daten in CSV und JSON

Rohdaten in einer Python-Liste sind nur nützlich, solange Ihr Skript läuft. Sie werden sie fast immer dauerhaft speichern wollen.

CSV-Export

import csv

fieldnames = ["title", "price", "url"]
with open("products.csv", "w", newline="", encoding="utf-8") as f:
    writer = csv.DictWriter(f, fieldnames=fieldnames)
    writer.writeheader()
    writer.writerows(all_results)

JSON-Export

import json

with open("products.json", "w", encoding="utf-8") as f:
    json.dump(all_results, f, indent=2, ensure_ascii=False)

Pandas-Abkürzung

Wenn Sie bereits Pandas verwenden, lässt sich mit einem einzigen Befehl beide Formate verarbeiten:

import pandas as pd

df = pd.DataFrame(all_results)
df.to_csv("products.csv", index=False)
df.to_json("products.json", orient="records", indent=2)

Wählen Sie CSV, wenn nachgelagerte Tools flache tabellarische Daten erwarten (Tabellenkalkulationen, SQL-Importe). Wählen Sie JSON, wenn Ihre Daten verschachtelt sind oder Sie Typen wie Arrays und Objekte beibehalten müssen. Bei sehr großen Datensätzen sollten Sie in Betracht ziehen, Zeilen schrittweise zu schreiben, anstatt zunächst alles im Speicher anzusammeln. Der csv.DictWriter Ansatz unterstützt inkrementelles Schreiben von Natur aus, da Sie nach jeder Zeile einen Flush durchführen können.

Bereinigung und Validierung von gescrapten Daten

Scraped-Daten sind selten direkt aus dem Browser heraus analysefertig. Doppelte Zeilen, fehlende Felder und inkonsistente Formatierungen sind die Regel, nicht die Ausnahme.

import pandas as pd

df = pd.DataFrame(all_results)

# Remove exact duplicate rows
df.drop_duplicates(inplace=True)

# Drop rows where critical fields are missing
df.dropna(subset=["title", "price"], inplace=True)

# Normalize price strings to floats
df["price"] = (df["price"]
               .str.replace(r"[^0-9.]", "", regex=True)
               .astype(float))

# Strip extra whitespace from text fields
df["title"] = df["title"].str.strip()

Dieser Bereinigungsschritt schließt die Lücke zwischen den rohen Scraping-Ergebnissen und Daten, die tatsächlich für Analysen, Berichte oder die Einspeisung in eine Datenbank nützlich sind. Die Durchführung dieser Prüfungen vor dem Export deckt Probleme frühzeitig auf, anstatt fehlerhafte Daten erst später zu entdecken.

Zu den gängigen Validierungsprüfungen, die es sich lohnt hinzuzufügen, gehören die Überprüfung, ob URLs korrekt formatiert sind, ob numerische Felder innerhalb der erwarteten Bereiche liegen (ein Produktpreis von 0,00 $ könnte ein Parsing-Fehler sein) und ob erforderliche Textfelder keine leeren Zeichenfolgen sind, die als vorhandene Werte getarnt sind. Das Erstellen einer kleinen Validierungsfunktion, die nach jeder Scraping-Sitzung ausgeführt wird, macht Ihre Pipeline im Laufe der Zeit robuster.

Verwendung von Proxys mit Selenium zur Vermeidung von Sperren

Wenn Sie zu viele Anfragen von einer einzigen IP-Adresse senden, wird die Zielwebsite Sie irgendwann blockieren. Proxys verteilen Ihren Datenverkehr auf verschiedene IP-Adressen, wodurch das Risiko von Sperren verringert und der Zugriff auf geografisch eingeschränkte Inhalte ermöglicht wird.

Manuelle Proxy-Konfiguration

from selenium import webdriver

options = webdriver.ChromeOptions()
options.add_argument("--proxy-server=http://123.45.67.89:8080")

driver = webdriver.Chrome(options=options)
driver.get("https://httpbin.org/ip")
print(driver.find_element(By.TAG_NAME, "body").text)
driver.quit()

Dieser Ansatz funktioniert für einen einzelnen Proxy, aber das manuelle Durchlaufen eines Pools (Starten eines neuen Treibers pro IP) ist mühsam und langsam. Datencenter-Proxys sind günstiger, lassen sich von Websites jedoch leichter erkennen, während Residential-Proxys den Datenverkehr über echte Verbraucher-IPs leiten und viel schwerer von echten Besuchern zu unterscheiden sind.

Authentifizierte Proxys

Viele Proxy-Anbieter verlangen eine Authentifizierung per Benutzername und Passwort. Chrome unterstützt die Proxy-Authentifizierung über Befehlszeilenoptionen nicht nativ, sodass du eine Umgehungslösung benötigst. Eine gängige Methode ist eine schlanke Browser-Erweiterung, die die Anmeldedaten automatisch in den Proxy-Handshake einfügt. Eine weitere Option ist der Einsatz eines lokalen Proxys (wie z. B. mitmproxy), der den Auth-Header hinzufügt und den Datenverkehr an den Remote-Proxy weiterleitet.

Wann sollte man einen verwalteten Proxy-Dienst nutzen?

Die Pflege eines eigenen Proxy-Pools, die Verwaltung der Rotationslogik und der Umgang mit gesperrten IPs sind Betriebsaufwände, die mit zunehmender Größe steigen. Verwaltete Proxy-Dienste bieten Ihnen einen einzigen Endpunkt, der Rotation, Authentifizierung und IP-Zustand im Hintergrund verwaltet. Dies ist besonders nützlich für Selenium-Proxy-Webscraping im Produktionsmaßstab, wo Sie Hunderte von rotierenden IPs in verschiedenen Regionen benötigen.

Tipps, um nicht gesperrt zu werden: Die Strategien aus den Anleitungen zur Vermeidung von IP-Sperren beim Web-Scraping gelten auch direkt für Selenium-basierte Setups. Zu den wichtigsten Vorgehensweisen gehören die Rotation von User-Agents neben den IPs, das Einfügen zufälliger Verzögerungen zwischen Anfragen und die Beachtung von robots.txt Richtlinien.

Honeypot-Fallen erkennen und vermeiden

Ein Honeypot ist ein verstecktes HTML-Element, das für menschliche Besucher unsichtbar ist, aber Bots sichtbar ist. Wenn Ihr Scraper damit interagiert (auf einen versteckten Link klickt, ein verstecktes Formularfeld ausfüllt), markiert die Website Ihre Sitzung als automatisiert und blockiert Sie möglicherweise sofort.

Das häufigste Muster ist ein Formularfeld, das mit display:none oder visibility:hidden:

hidden_inputs = driver.find_elements(
    By.CSS_SELECTOR, "input[style*='display:none'], input[style*='visibility:hidden']"
)

for inp in hidden_inputs:
    print(f"Honeypot detected: name={inp.get_attribute('name')}")

Regeln für defensives Programmieren in Bezug auf Honeypots:

  • Durchlaufen Sie niemals blind alle Formularfelder und füllen Sie diese aus. Prüfen Sie zuerst die Sichtbarkeit.
  • Verwenden Sie element.is_displayed() , bevor Sie mit einem Element interagieren, das Sie nicht explizit anvisiert haben.
  • Wenn ein Link opacity: 0 oder mit negativen Koordinaten außerhalb des Bildschirms positioniert ist, überspringe ihn.
  • Manche Honeypots verwenden CSS-Klassen anstelle von Inline-Stilen. Überprüfe den berechneten Stil mit execute_script , wenn du vermutest, dass die Website externes CSS verwendet, um Fallenelemente zu verbergen.

Das Bewusstsein für Honeypots wird besonders wichtig, wenn Ihr Scraper Formulare ausfüllt (Suchfelder, Anmeldefelder, Kontaktformulare). Eine einfache Sichtbarkeitsprüfung vor jeder Interaktion verursacht nur minimalen Mehraufwand und vermeidet eine Art von Erkennung, die sonst schwer zu debuggen ist.

Tipps zur Leistungsoptimierung

Selenium steuert einen vollständigen Browser an, daher führt jede Optimierung, die den Arbeitsaufwand des Browsers reduziert, direkt zu schnelleren und kostengünstigeren Scraping-Läufen.

Führen Sie den Prozess im Headless-Modus aus. Das Entfernen der GUI-Ebene reduziert die Startzeit und den Speicherverbrauch erheblich. Aktivieren Sie --headless=new in Ihren Chrome-Einstellungen. Ein Leitfaden zum Headless-Browser behandelt die Feinheiten ausführlich.

Blockieren Sie unnötige Ressourcen. Bilder, Schriftarten und CSS-Dateien verlängern die Ladezeit, ohne Daten beizusteuern. Sie können Chrome DevTools-Protokollbefehle verwenden, um sie zu blockieren:

driver.execute_cdp_cmd("Network.setBlockedURLs", {
    "urls": ["*.jpg", "*.png", "*.gif", "*.svg", "*.woff2", "*.css"]
})
driver.execute_cdp_cmd("Network.enable", {})

Bevorzuge schnelle Locators. ID-basierte Suchvorgänge sind am schnellsten. Komplexe XPath-Ausdrücke, die große Teilbäume durchlaufen, sind aufwändiger. Wenn du die Wahl hast, verwende By.ID oder einen kurzen CSS-Selektor.

Passen Sie Ihre Wartezeiten an. Zu großzügige implizite Wartezeiten verlangsamen jede einzelne Element-Suche. Verwenden Sie gezielte explizite Wartezeiten mit WebDriverWait und legen Sie Timeouts basierend auf dem spezifischen Verhalten der Seite fest, statt pauschal auf 30 Sekunden zurückzugreifen.

Minimieren Sie Browser-Neustarts. Verwenden Sie nach Möglichkeit eine einzige Treiberinstanz seitenübergreifend. Jeder webdriver.Chrome() Aufruf startet einen eigenen Browserprozess.

Deaktivieren Sie Funktionen, die Sie nicht benötigen. Flags wie --disable-extensions, --disable-infobarsund --blink-settings=imagesEnabled=false reduzieren den Browser-Overhead weiter.

Strategie zum Laden der Seite. Stellen Sie options.page_load_strategy = "eager" , um das Warten auf das vollständige Laden von Bildern und Stylesheets zu beenden. Das DOM ist früher interaktiv, und Sie können früher mit der Extraktion beginnen.

Diese Optimierungen verstärken sich gegenseitig. Wenn Sie sie alle zusammen anwenden, können Sie die Laufzeit eines Selenium-Headless-Scraping-Jobs im Vergleich zur Standardkonfiguration um 50 % oder mehr reduzieren.

Häufige Herausforderungen und wie man sie löst

Selbst mit einem gut konfigurierten Scraper werden Sie auf Probleme stoßen. Hier sind die häufigsten Probleme und ihre Lösungen.

Herausforderung

Ursache

Lösung

NoSuchElementException

Element wurde noch nicht geladen

Verwenden Sie WebDriverWait mit einer geeigneten erwarteten Bedingung

Veraltete Elementreferenz

DOM wurde geändert, nachdem Sie das Element gefunden haben

Suchen Sie das Element nach jeder Seitennavigation oder jedem AJAX-Neuladen erneut

CAPTCHA-Blöcke

Die Website erkennt automatisierten Datenverkehr

Verlangsamen Sie Anfragen, wechseln Sie User-Agents, verwenden Sie Residential-Proxys

Inkonsistente Daten

Das Seitenlayout variiert je nach Produkt/Kategorie

Fügen Sie defensive Prüfungen mit find_elements und testen Sie die Listenlänge vor dem Zugriff

Speicherlecks bei langen Läufen

Der Browser sammelt den Status über Hunderte von Seiten hinweg

Starten Sie den Treiber regelmäßig neu (alle N Seiten) oder löschen Sie Cookies/Cache

Langsame Ausführung

Voller Overhead beim Browser-Rendering

Wenden Sie die Optimierungstipps aus dem vorherigen Abschnitt an

Muster zur Fehlerbehandlung: Umschließen Sie Ihre Scraping-Schleife mit einem try/except, das WebDriverException als umfassende Ausweichlösung fungiert. Protokollieren Sie die URL, speichern Sie einen Screenshot und fahren Sie mit dem nächsten Element fort, anstatt den gesamten Job zum Absturz zu bringen.

from selenium.common.exceptions import WebDriverException

for url in urls:
    try:
        driver.get(url)
        # ... extraction logic ...
    except WebDriverException as e:
        driver.save_screenshot(f"error_{url.split('/')[-1]}.png")
        print(f"Failed on {url}: {e}")
        continue

Wenn Sie auf einer Website, die normalerweise funktioniert, zeitweise Fehler feststellen, prüfen Sie, ob die Website je nach Standort, User-Agent oder Tageszeit unterschiedliche Inhalte bereitstellt. Das Protokollieren des vollständigen Seitenquellcodes bei einem Fehler (zusätzlich zum Screenshot) hilft bei der Diagnose solcher Umgebungsprobleme. Das Umgehen von Cloudflare und ähnlichen Schutzdiensten mit Selenium erfordert zusätzliche Techniken, die über die Grundkonfiguration hinausgehen.

Skalierung mit Selenium Grid

Eine einzelne Selenium-Instanz ist auf jeweils einen Browser beschränkt. Wenn Sie Tausende von Seiten parallel scrapen müssen, verteilt Selenium Grid die Browsersitzungen auf mehrere Rechner.

So funktioniert Grid

Selenium Grid nutzt eine Hub-and-Node-Architektur. Der Hub ist ein zentraler Server, der WebDriver-Anfragen empfängt und an verfügbare Nodes weiterleitet, von denen jeder eine oder mehrere Browserinstanzen ausführt. Sie verweisen Ihr Skript auf die Hub-URL statt auf einen lokalen Driver:

from selenium import webdriver

options = webdriver.ChromeOptions()
driver = webdriver.Remote(
    command_executor="http://grid-hub:4444/wd/hub",
    options=options
)

Sie können den Hub und die Nodes mit Docker ausführen, was die Skalierung vereinfacht:

docker run -d -p 4444:4444 selenium/hub
docker run -d --link selenium-hub:hub selenium/node-chrome

Praktische Überlegungen

Das Ausführen von Dutzenden paralleler Browsersitzungen beansprucht erhebliche CPU- und RAM-Ressourcen. Überwachen Sie Ihre Knoten und legen Sie eine Obergrenze für die Anzahl der Sitzungen fest, um eine Überlastung zu vermeiden. Das Debugging ist in einer verteilten Umgebung zudem schwieriger, da Screenshots und Protokolle auf Remote-Rechnern gespeichert werden. Nutzen Sie die integrierte Videoaufzeichnungsfunktion von Grid oder die zentralisierte Protokollierung, um den Überblick zu behalten.

Selenium Grid ist eine solide Wahl für Web-Scraping-Workloads, die eine moderate Parallelität erfordern (5 bis 20 gleichzeitige Sitzungen). Darüber hinaus steigt der Betriebsaufwand für die Verwaltung von Knoten, die Behandlung von Ausfällen und die Überwachung der Ressourcennutzung schnell an. Für Teams, die keine Grid-Infrastruktur verwalten möchten, bieten cloudgehostete Browser-Dienste oder API-basierte Scraping-Tools dasselbe parallele Ausführungsmodell wie ein Managed Service.

Selenium vs. Playwright vs. Puppeteer: Schneller Vergleich

Selenium ist nicht das einzige Tool zur Browser-Automatisierung. Playwright und Puppeteer sind beliebte moderne Alternativen, die jeweils unterschiedliche Stärken aufweisen.

Funktion

Selenium

Playwright

Puppeteer

Sprachunterstützung

Python, Java, C#, JS, Ruby

Python, Java, .NET, JS/TS

Nur JavaScript/TypeScript

Browser-Engines

Chrome, Firefox, Edge, Safari

Chromium, Firefox, WebKit

Nur Chromium

Automatisches Warten

Manuell (explizite/implizite Wartezeiten)

Integrierte automatische Wartezeit bei Aktionen

Manuell (waitForSelector)

Geschwindigkeit

Langsamer (WebDriver-Protokoll)

Schneller (CDP / Browserkanäle)

Schneller (CDP)

Parallele Ausführung

Über Selenium Grid

Native Browserkontexte

Über Seiten-/Browser-Kontexte

Community

Größte, ausgereifteste

Wächst schnell

Umfangreich (Chromium-fokussiert)

Entscheiden Sie sich für Selenium, wenn Sie Unterstützung für mehrere Sprachen, Kompatibilität bei browserübergreifenden Tests benötigen oder wenn Ihr Team bereits mit der Selenium-API vertraut ist. Wählen Sie Playwright, wenn Sie integrierte Auto-Wait-Funktionen, moderne asynchrone Muster und Unterstützung für mehrere Browser aus einer einzigen Bibliothek wünschen. Entscheiden Sie sich für Puppeteer, wenn Ihr Stack ausschließlich auf JavaScript basiert und Sie nur Chromium benötigen.

Alle drei Tools können browserbasiertes Scraping bewältigen, aber Playwrights integrierte Auto-Wait-Funktion und native Browserkontexte für Parallelität verschaffen ihm einen Vorteil bei neuen Projekten, die Seleniums breiteres Sprachökosystem nicht benötigen. Für einen tieferen Einblick in die Scraping-Fähigkeiten von Playwright bieten Ressourcen zum Thema Playwright-Web-Scraping einen umfassenden Vergleich.

Rendering von JavaScript ohne Browser: API-basierte Alternativen

Das Ausführen eines vollständigen Browsers funktioniert zwar, ist aber in Bezug auf CPU, RAM und technische Wartung aufwendig. Wenn Ihr Ziel lediglich darin besteht, das gerenderte HTML einer JavaScript-lastigen Seite zu erhalten, kann ein API-basierter Ansatz weitaus effizienter sein.

Verwaltete Scraping-APIs akzeptieren eine URL und geben den vollständig gerenderten HTML-Code (oder sogar vorab geparstes JSON) zurück. Im Hintergrund kümmern sie sich um das Browser-Rendering, die Proxy-Rotation, das Lösen von CAPTCHAs und die Wiederholungslogik. Sie senden eine einzige HTTP-Anfrage und erhalten saubere Daten zurück, was bedeutet, dass Ihr Scraping-Code so einfach bleibt wie ein requests.get() Aufruf.

Dieses Modell ist besonders geeignet, wenn:

  • Sie in großem Umfang scrapen und keine Selenium-Grid-Infrastruktur verwalten möchten.
  • Anti-Bot-Systeme aggressiv sind und eine Rotation von Residential-Proxys sowie Browser-Fingerprint-Management erfordern.
  • Sie Ihre Parsing-Logik vollständig von der Rendering-Ebene entkoppeln möchten.
  • Ihr Team nicht über die DevOps-Kapazitäten verfügt, um Browser- und Treiberversionen über verschiedene Umgebungen hinweg zu verwalten.

Der Nachteil ist die Kontrolle. Mit Selenium können Sie auf Schaltflächen klicken, Formulare ausfüllen und mehrstufige Workflows durchlaufen. API-basierte Renderer bewältigen in der Regel das Abrufen einzelner Seiten. Für komplexes interaktives Scraping (Anmeldeabläufe, mehrstufige Assistenten) ist Selenium oder ein ähnliches Browser-Automatisierungstool nach wie vor die bessere Wahl.

Ein hybrider Ansatz bewährt sich in der Praxis: Verwenden Sie Selenium lokal während der Entwicklung, um die Seitenstruktur zu verstehen und Ihre Selektoren zu erstellen, und wechseln Sie dann in der Produktion zu einem API-basierten Dienst, um den betrieblichen Aufwand für den groß angelegten Betrieb von Browsern zu vermeiden. Dies bietet Ihnen die Flexibilität des Web-Scrapings mit Selenium während der Prototypenentwicklung und die Zuverlässigkeit eines Managed Services in der Produktion. Sie behalten in beiden Fällen denselben Parsing-Code bei; nur die Ebene der Datenabfrage ändert sich.

Vollständiges Beispiel: End-to-End-Web-Scraping mit Selenium-Projekt

Lassen Sie uns alles zu einem einzigen, ausführbaren Skript zusammenfassen. Dieses Beispiel scrapt Buchtitel und Preise von einer paginierten Demo-Website, bereinigt die Daten und exportiert sie in CSV.

import csv
import time
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

# --- Setup ---
options = webdriver.ChromeOptions()
options.add_argument("--headless=new")
options.add_argument("--window-size=1920,1080")
options.add_argument("--disable-gpu")
driver = webdriver.Chrome(options=options)

all_books = []
base_url = "https://books.toscrape.com/catalogue/page-{}.html"

# --- Scrape with Pagination ---
for page in range(1, 51):
    driver.get(base_url.format(page))
    try:
        WebDriverWait(driver, 10).until(
            EC.presence_of_element_located((By.CSS_SELECTOR, ".product_pod"))
        )
    except Exception:
        break  # No more pages

    books = driver.find_elements(By.CSS_SELECTOR, ".product_pod")
    if not books:
        break

    for book in books:
        title = book.find_element(By.CSS_SELECTOR, "h3 a").get_attribute("title")
        price = book.find_element(By.CSS_SELECTOR, ".price_color").text
        all_books.append({"title": title, "price": price})

driver.quit()

# --- Clean ---
seen = set()
cleaned = []
for book in all_books:
    key = (book["title"], book["price"])
    if key not in seen:
        seen.add(key)
        price_str = book["price"].replace("\xa3", "").strip()
        try:
            book["price"] = float(price_str)
        except ValueError:
            continue
        cleaned.append(book)

# --- Export ---
with open("books.csv", "w", newline="", encoding="utf-8") as f:
    writer = csv.DictWriter(f, fieldnames=["title", "price"])
    writer.writeheader()
    writer.writerows(cleaned)

print(f"Scraped {len(cleaned)} books to books.csv")

Dieses Skript demonstriert den gesamten in diesem Tutorial behandelten Lebenszyklus: Headless-Chrome-Konfiguration, explizite Wartezeiten, Elementlokalisierung mit CSS-Selektoren, Paginierung mit Erkennung der letzten Seite, Deduplizierung, Normalisierung von Datentypen und CSV-Export. Sie können es an jedes beliebige Ziel anpassen, indem Sie das URL-Muster und die CSS-Selektoren austauschen.

Die wichtigsten Designentscheidungen in diesem Beispiel sind erwähnenswert. Wir nutzen --headless=new aus Geschwindigkeitsgründen. Wir verpacken die Wartezeit in ein try/except, um den Fall „Seite nicht gefunden“ elegant zu behandeln. Wir deduplizieren, indem wir gesehene Titel-/Preis-Paare in einem Set verfolgen. Und wir normalisieren Preise vor dem Export auf Floats, damit nachgelagerte Tools numerisch sortieren und filtern können.

Von hier aus könntest du dieses Skript erweitern, indem du Proxy-Rotation hinzufügst, Ergebnisse in eine Datenbank statt in CSV schreibst oder es nach einem Zeitplan mit einem Task-Runner wie cron oder Airflow einsetzt. Die Muster, die du in diesem Tutorial gelernt hast, fügen sich wie Bausteine zusammen.

Wichtige Erkenntnisse

  • Verwenden Sie Selenium, wenn JavaScript-Rendering erforderlich ist. Für statische HTML-Seiten sind leichtere Tools wie requests BeautifulSoup oder Scrapy sind schneller und kostengünstiger in der Ausführung.
  • Verwenden Sie immer explizite Wartezeiten anstelle von time.sleep(). WebDriverWait mit expected_conditions macht Ihren Scraper auf dynamischen Seiten sowohl schneller als auch zuverlässiger.
  • Blockieren Sie unnötige Ressourcen und führen Sie den Scraper im Headless-Modus aus. Das Deaktivieren von Bildern, Schriftarten und CSS im Headless-Modus kann die Ausführungszeit halbieren, ohne dass gescrapte Daten verloren gehen.
  • Bereinigen Sie Ihre Daten vor dem Export. Deduplizierung, Null-Behandlung und Typnormalisierung erkennen Probleme frühzeitig und sparen später Zeit beim Debuggen.
  • Planen Sie von Anfang an für Skalierbarkeit. Proxy-Rotation, Selenium Grid und API-basierte Rendering-Alternativen sorgen dafür, dass Ihr Scraper weiterläuft, auch wenn Zielseiten ihre Abwehrmaßnahmen verstärken.

FAQ

Eignet sich Selenium für Web-Scraping in großem Maßstab?

Es kann funktionieren, ist aber ressourcenintensiv. Jede Browserinstanz verbraucht erhebliche CPU- und RAM-Ressourcen, sodass die Ausführung von Hunderten paralleler Sitzungen eine leistungsfähige Infrastruktur erfordert. Selenium Grid hilft dabei, die Last auf mehrere Rechner zu verteilen, und der Headless-Modus reduziert den Overhead pro Sitzung. Für wirklich groß angelegte Aufgaben (Millionen von Seiten) sind API-basierte Rendering-Dienste oder für das Crawling entwickelte Frameworks oft kostengünstiger.

Kann Selenium Websites scrapen, die eine Login-Authentifizierung erfordern?

Ja. Sie können den gesamten Anmeldeprozess automatisieren: Navigieren Sie zur Anmeldeseite, suchen Sie die Felder für Benutzername und Passwort mit find_element, die Anmeldedaten über send_keysund auf die Schaltfläche „Absenden“ klicken. Nach der Authentifizierung behält Selenium die Sitzungscookies bei, sodass nachfolgende Seitenladevorgänge während der gesamten Sitzung authentifiziert bleiben.

Wie gehe ich mit CAPTCHAs um, wenn ich mit Selenium scrape?

CAPTCHAs sind darauf ausgelegt, Automatisierung zu verhindern, daher gibt es keine saubere Umgehungslösung innerhalb des Browsers selbst. Gängige Strategien umfassen die Verlangsamung der Anfragerate, um das Auslösen von CAPTCHAs von vornherein zu vermeiden, die Verwendung von Residential-Proxys zur Verringerung der Erkennung sowie die Integration von CAPTCHA-Lösungsdiensten von Drittanbietern, die Tokens zurückgeben, die Sie über JavaScript in die Seite einfügen.

Die Rechtmäßigkeit hängt von der Rechtsordnung, den Nutzungsbedingungen der Website und der Art der gesammelten Daten ab. In den Vereinigten Staaten hat das Urteil des Obersten Gerichtshofs im Fall hiQ gegen LinkedIn klargestellt, dass das Scraping öffentlich zugänglicher Daten nicht zwangsläufig einen Verstoß gegen den CFAA darstellt. Das Scraping personenbezogener Daten kann jedoch in der EU unter die DSGVO fallen. Überprüfen Sie stets die robots.txt und die Nutzungsbedingungen der Zielseite und konsultieren Sie bei kommerziellen Scraping-Projekten einen Rechtsbeistand.

Was ist der Unterschied zwischen Selenium und BeautifulSoup?

Sie lösen unterschiedliche Probleme. Selenium steuert einen echten Browser und kann JavaScript ausführen, auf Schaltflächen klicken und durch interaktive Seiten navigieren. BeautifulSoup ist ein Parser, der eine HTML-Zeichenkette verarbeitet und Ihnen Methoden zur Abfrage und Extraktion von Daten daraus bereitstellt, aber keine Seiten laden oder JavaScript ausführen kann. Viele Scraper kombinieren beides: Selenium rendert die Seite, dann analysiert BeautifulSoup den HTML-Snapshot für eine schnellere Extraktion.

Fazit

Web-Scraping mit Selenium ermöglicht es Ihnen, Daten aus praktisch jeder Website zu extrahieren, einschließlich JavaScript-intensiver Anwendungen, die statische HTTP-Bibliotheken nicht bewältigen können. In diesem Tutorial haben Sie gesehen, wie Sie die Umgebung einrichten, den Browser konfigurieren, Elemente lokalisieren und mit ihnen interagieren, dynamische Inhalte mit expliziten Wartezeiten handhaben, durch Ergebnismengen blättern und saubere Daten exportieren.

Der entscheidende Nachteil von Selenium sind die Ressourcenkosten. Ein echter Browser verbraucht mehr CPU-Leistung und Arbeitsspeicher als eine einfache HTTP-Anfrage, und diese Kosten vervielfachen sich bei Skalierung. Für viele Scraping-Szenarien ist der praktische Weg, Selenium für Entwicklung und Prototyping zu nutzen und dann das Rendering sowie die Komplexität der Anti-Bot-Maßnahmen auf einen dedizierten Dienst auszulagern, wenn Sie in die Produktion gehen.

Wenn Sie feststellen, dass Sie mehr Zeit mit der Verwaltung von Proxys, dem Kampf gegen CAPTCHAs und der Wartung der Browser-Infrastruktur verbringen als mit dem Schreiben der eigentlichen Parsing-Logik, kann WebScrapingAPI die Rendering- und Auslieferungsschicht für Sie übernehmen, sodass Sie sich auf die Daten selbst konzentrieren können. Diese Trennung der Aufgabenbereiche hält Ihren Code übersichtlich und Ihre Scraping-Pipeline zuverlässig.

Egal, für welches Tool Sie sich entscheiden: Beginnen Sie mit der einfachsten Lösung, die für Ihren Anwendungsfall funktioniert, fügen Sie Komplexität nur bei Bedarf hinzu und beachten Sie stets die Nutzungsbedingungen der Zielwebsite.

Über den Autor
Robert Sfichi, Full-Stack-Entwickler @ WebScrapingAPI
Robert SfichiFull-Stack-Entwickler

Robert Sfichi ist Teammitglied bei WebScrapingAPI, wo er an der Produktentwicklung mitwirkt und dabei hilft, zuverlässige Lösungen zu entwickeln, die die Plattform und ihre Nutzer unterstützen.

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.