Selenium steuert von Python aus einen echten Browser — und ist damit das Werkzeug, zu dem du greifst, wenn requests nur eine leere Hülle zurückbekommt und die benötigten Daten erst erscheinen, nachdem JavaScript gelaufen ist. Dieser Guide behandelt Selenium Web Scraping so, wie es 2026 tatsächlich funktioniert: Selenium 4 mit eingebautem Driver-Management (keine chromedriver-Downloads), der neue Headless-Modus, By.-Locators und explizite Waits, die das time.sleep-Raten ersetzen. Jedes Beispiel ist unverändert lauffähig.
Wenn du ganz neu im Scraping bist, starte mit unserem Pillar-Guide zu web scraping with Python — dieser Beitrag setzt voraus, dass du weißt, was ein CSS-Selektor ist, und setzt dort an, wo statisches HTML endet und ein headless browser beginnt.
Wann du Selenium wirklich brauchst
Seleniums Heimat ist Browser-Automatisierung und Testing, und darin ist es richtig gut: Es spricht den W3C-WebDriver-Standard, steuert echtes Chrome/Firefox/Edge und trägt die Kampfspuren von zwei Jahrzehnten. Beim Scraping lohnt sich diese Power nur in bestimmten Situationen:
| Situation | Richtiges Werkzeug |
|---|---|
Daten stehen im initialen HTML (view-source zeigt sie) | requests + BeautifulSoup — siehe unser BeautifulSoup tutorial |
| Daten kommen aus einer JSON-API, die die Seite aufruft | requests direkt gegen diese API (erst den Network-Tab prüfen) |
| Daten rendern erst nach JavaScript-Ausführung | Selenium oder Playwright |
| Daten erscheinen erst nach Klicken, Scrollen oder Tippen | Selenium oder Playwright |
| Du brauchst Screenshots oder PDF-Rendering | Selenium oder Playwright |
Die ersten beiden Zeilen sind wichtiger, als sie aussehen. Ein echter Browser verbraucht Sekunden und Hunderte Megabyte pro Seite; ein einfacher HTTP-Request Millisekunden. Bevor du eine Zeile Selenium schreibst, öffne die DevTools, beobachte den Network-Tab und prüfe, ob die vermeintlich „JavaScript-only"-Daten nicht doch in einem sauberen JSON-Endpoint liegen, den du direkt aufrufen kannst. In rund der Hälfte der Fälle ist das so — und genau dann leistet das web scraping with Python-Toolkit seine Arbeit. Greif zu Selenium, wenn die Antwort wirklich Nein lautet.
Setup 2026: ein pip install, keine Driver-Downloads
Dieser Teil ist dramatisch besser geworden. Seit Selenium 4.6 steckt der Selenium Manager im Paket: Er erkennt deinen installierten Browser, lädt das passende Driver-Binary herunter und cached es. Das alte Ritual — chromedriver herunterladen, Version abgleichen, PATH reparieren oder webdriver-manager dazuschrauben — ist Geschichte. Wenn ein Tutorial dir sagt, du sollst webdriver-manager installieren, ist es veraltet.
python -m venv venv && source venv/bin/activate
pip install selenium
from selenium import webdriver
options = webdriver.ChromeOptions()
options.add_argument("--headless=new") # the current headless mode
options.add_argument("--window-size=1920,1080") # default is 800x600 — breaks responsive layouts
driver = webdriver.Chrome(options=options) # Selenium Manager fetches the driver on first run
driver.get("https://www.selenium.dev")
print(driver.title)
driver.quit()
Zwei Flags, die eine Erklärung verdienen:
--headless=newist Chromes moderner Headless-Modus — er teilt deutlich mehr Code mit dem normalen Chrome, als es das alte--headless-Flag tat, sodass Seiten genauso rendern wie auf deinem Bildschirm. Nutze ihn für Server und CI; lass ihn beim Debuggen weg, damit du dem Browser bei der Arbeit zusehen kannst.--window-size=1920,1080ist wichtig, weil Headless standardmäßig mit 800x600 läuft und responsive Sites dir dann stillschweigend das Mobile-Layout ausliefern — und deine Desktop-Selektoren treffen plötzlich nichts mehr.
Der zentrale Scraping-Workflow
Die Schleife ist immer dieselbe: navigieren, auf den Inhalt warten, Elemente mit By.-Locators finden, Text und Attribute in einfache Dicts extrahieren. Hier läuft sie gegen quotes.toscrape.com/js, eine Übungssite, die alles per JavaScript rendert (ein einfaches requests.get liefert null Zitate):
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
options = webdriver.ChromeOptions()
options.add_argument("--headless=new")
driver = webdriver.Chrome(options=options)
try:
driver.get("https://quotes.toscrape.com/js/")
# Block until at least one quote card exists in the DOM (max 10s)
WebDriverWait(driver, 10).until(
EC.presence_of_element_located((By.CSS_SELECTOR, ".quote"))
)
quotes = []
for card in driver.find_elements(By.CSS_SELECTOR, ".quote"):
quotes.append({
"text": card.find_element(By.CSS_SELECTOR, ".text").text,
"author": card.find_element(By.CSS_SELECTOR, ".author").text,
"tags": [t.text for t in card.find_elements(By.CSS_SELECTOR, ".tag")],
})
print(f"scraped {len(quotes)} quotes")
print(quotes[0])
finally:
driver.quit()
Anmerkungen zur modernen API, weil noch viele veraltete Tutorials im Umlauf sind:
- Locators heißen
find_element(By.CSS_SELECTOR, "..."). Die altenfind_element_by_css_selector-Helfer wurden in Selenium 4.3 entfernt — Code, der sie nutzt, wirft heute einenAttributeError.By.CSS_SELECTORdeckt fast alles ab;By.IDundBy.XPATHfüllen die Lücken (XPath ist die Variante, die nach Textinhalt selektieren kann). find_elementvs.find_elements: Die Singular-Form wirftNoSuchElementException, wenn nichts passt; die Plural-Form gibt eine leere Liste zurück. Nutze den Plural für Karten-Grids und dann den Singular begrenzt auf jede Karte —card.find_element(...)sucht nur innerhalb dieser Karte.- Attribute liest du mit
element.get_attribute("href")(oder"src","data-id", ...); sichtbaren Text mitelement.text. try/finallymitdriver.quit()— sonst hinterlässt jeder abgestürzte Lauf einen Chrome-Prozess, und nach zehn abgestürzten Läufen geht es deiner Maschine schlecht.
Echte Interaktionen: klicken, scrollen, tippen
Der Grund, aus dem du Seleniums Overhead akzeptiert hast, ist Interaktion. Drei Muster decken die meisten Sites ab.
„Load more" klicken, bis der Button verschwindet. Auf Klickbarkeit warten, klicken, wiederholen; ist der Button weg, endet die Schleife:
from selenium.common.exceptions import TimeoutException
while True:
try:
button = WebDriverWait(driver, 5).until(
EC.element_to_be_clickable((By.CSS_SELECTOR, "button.load-more"))
)
button.click()
except TimeoutException:
break # no more button — everything is loaded
Einen endlosen Feed scrollen. Ans Ende scrollen, neuen Inhalt landen lassen und stoppen, wenn die Seitenhöhe nicht mehr wächst:
last_height = driver.execute_script("return document.body.scrollHeight")
while True:
driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")
try:
WebDriverWait(driver, 5).until(
lambda d: d.execute_script("return document.body.scrollHeight") > last_height
)
last_height = driver.execute_script("return document.body.scrollHeight")
except TimeoutException:
break # height stopped growing — feed exhausted
Ein Suchfeld ausfüllen. send_keys tippt wie ein User, Keys.RETURN schickt ab:
from selenium.webdriver.common.keys import Keys
box = WebDriverWait(driver, 10).until(
EC.element_to_be_clickable((By.CSS_SELECTOR, "input[name='q']"))
)
box.clear()
box.send_keys("mechanical keyboard", Keys.RETURN)
WebDriverWait(driver, 10).until(
EC.presence_of_element_located((By.CSS_SELECTOR, ".result"))
)
Warte nach jeder Interaktion auf ihre Wirkung (neue Karten, geänderte Höhe, ein Ergebnis-Container), bevor du extrahierst — dass der Klick zurückkehrt, heißt nicht, dass die Seite fertig reagiert hat.
Häufige Fehler und wie du sie behebst
Jeder Selenium-Scraper begegnet diesen Kandidaten früher oder später. Die Lösung ist fast nie „ein Sleep dazu":
| Exception | Was sie bedeutet | Lösung |
|---|---|---|
StaleElementReferenceException | Du hast eine Referenz auf ein Element gehalten, dann hat die Seite es neu gerendert (häufig nach Klicks und AJAX-Updates). Die alte Referenz zeigt auf einen DOM-Knoten, den es nicht mehr gibt. | Finde das Element nach jeder Aktion neu, die die Seite verändert. In Schleifen: find_elements in jeder Iteration neu abfragen, statt über eine gespeicherte Liste zu iterieren. |
ElementNotInteractableException / ElementClickInterceptedException | Das Element existiert, ist aber versteckt, hat Größe null oder wird von etwas verdeckt (Cookie-Banner, Sticky-Header, Modal). | Warte auf element_to_be_clickable, schließe zuerst das Overlay oder führe vor dem Klick driver.execute_script("arguments[0].scrollIntoView();", el) aus. |
TimeoutException | Deine WebDriverWait-Bedingung wurde nie wahr. Entweder ist der Selektor falsch, der Inhalt lädt in einem iframe, oder die Site hat dir eine Blockseite serviert. | Gib beim Auslösen driver.title aus und speichere driver.page_source — oft findest du dort ein CAPTCHA oder eine „verify you are human"-Seite statt des Inhalts. Prüfe iframes mit driver.switch_to.frame(...). |
NoSuchElementException | find_element (Singular) hat nichts gefunden — oft dieselben Ursachen wie oben, nur ohne das Warten. | Erst warten, dann suchen; oder find_elements nutzen und die leere Liste behandeln. |
Die dritte Zeile verdient Nachdruck: Eine TimeoutException ist häufig ein Erkennungsproblem im Kostüm eines Selektor-Problems. Womit wir beim ehrlichen Teil wären.
Der ehrliche Teil: Selenium ist langsam und erkennbar
Jeder echte Browser bringt zwei Kosten mit.
Geschwindigkeit und Gewicht. Eine Browser-Session kostet Sekunden pro Seite und rund 300-500 MB RAM, während ein HTTP-Request Millisekunden und fast nichts kostet. Bei zehn Seiten ist das egal; bei hunderttausend Seiten ist es eine Server-Flotte plus ein Queueing-System, das du ab jetzt betreibst.
Erkennung. Out of the box gibt sich Selenium-gesteuertes Chrome selbst zu erkennen: navigator.webdriver ist true, und Dutzende subtilere Signale — der TLS-Handshake, fehlende Plugins, Canvas- und Font-Eigenheiten, deine Datacenter-IP — füttern browser fingerprinting-Systeme, die dich als Bot einstufen, bevor dein erster Selektor läuft. Die Symptome sind rate limiting, endlose CAPTCHA-Wände oder stiller Datenmüll.
Tools wie undetected-chromedriver und der UC-Modus von SeleniumBase patchen die lautesten Signale, und sie funktionieren — bis der Anbieter einen neuen Check ausliefert, was er laufend tut, denn Automatisierung zu erkennen ist sein gesamtes Geschäft. Es ist ein Wettrüsten, in dem du der Hobbyist bist und die Gegenseite der Rüstungskonzern. Die komplette Landschaft haben wir in scraping sites that block bots aufgeschrieben; die Kurzfassung: Den eigenen Browser zu patchen ist ein Laufband, kein Ziel.
Und Playwright? Playwright ist die jüngere Alternative: Auto-Waiting-Locators eingebaut, schneller und stabiler im Headless-Betrieb, Async-Support und eine angenehmere API speziell fürs Scraping. Selenium gewinnt weiterhin, wenn du die breiteste Matrix echter Browser brauchst, den WebDriver-Standard, ein Jahrzehnt an Stack-Overflow-Antworten oder Bindings jenseits der vier großen Sprachen — und in QA-Teams ist es aus gutem Grund der Default. Für ein neues, reines Scraping-Projekt würden wir meist mit Playwright starten; für Automatisierung, die echte User-Abdeckung über Browser hinweg abbilden muss, mit Selenium. Weniger erkennbar ist allerdings keines von beiden — einem Fingerprinting-System ist egal, welche Library die Maus bewegt.
Skalieren: wann eine strukturierte API die Browser-Flotte schlägt
Hier fällt die Entscheidung, die im großen Maßstab wirklich zählt. Wenn dein Ziel eine große, gut verteidigte Plattform ist — Amazon, Google, soziale Netzwerke — wird der Selenium-Pfad zu: Proxys, Fingerprint-Patches, CAPTCHA-Handling, Retry-Logik, HTML-Parser, die bei jedem Redesign brechen, und Server, auf denen die Browser laufen. Jedes Teil ist machbar; zusammen sind sie ein Infrastruktur-Nebenjob.
Die Alternative ist eine web scraping API, die diese Infrastruktur hinter einem Endpoint betreibt und strukturiertes JSON zurückgibt. Dieselbe Amazon-Suche, die ~40 Zeilen Selenium, Waits und Parsing kostet, ist ein einziger HTTP-Request:
import requests
resp = requests.get(
"https://api.crawlora.net/api/v1/amazon/search",
params={"k": "mechanical keyboard"},
headers={"x-api-key": "YOUR_API_KEY"},
)
products = resp.json()["data"] # structured fields — no parser to maintain
Kein Browser, kein Driver, keine Selektoren zum Babysitten — und Crawlora ist pay-on-success: Berechnet werden nur erfolgreiche 2xx-Antworten, ein geblockter oder fehlgeschlagener Request kostet dich nichts. Die vernünftige Aufteilung, bei der die meisten Teams landen: Selenium für die Sites, auf denen du maßgeschneiderte Interaktion brauchst oder es schlicht das richtige Browser-Automatisierungswerkzeug ist; eine strukturierte API für die hochvolumigen, stark verteidigten Plattformen. Die Kosten kannst du selbst vergleichen — Browser-Flotten-Mathematik gegen per-request pricing — und Endpoints im playground testen, bevor du eine Zeile Code schreibst.
- Zuerst den Network-Tab geprüft — liegen die Daten schon in einem JSON-Endpoint, den requests erreichen kann?
- pip install selenium (4.6+) und die Driver dem Selenium Manager überlassen — keine chromedriver-Downloads
- --headless=new läuft mit explizitem --window-size
- Jedes Warten ist WebDriverWait + expected_conditions — null time.sleep-Aufrufe
- driver.quit() im finally-Block, damit abgestürzte Läufe keine Browser hinterlassen
- Bei TimeoutException page_source gedumpt und auf Blockseiten geprüft, bevor der Selektor die Schuld bekommt
- Ehrlich beim Skalieren: maßgeschneiderte Interaktionen in Selenium, hochvolumige geschützte Plattformen über eine strukturierte API
Spar dir die Browser-Flotte für die harten Ziele
Strukturiertes JSON von Amazon, Google und 700+ Endpoints — Proxys, Rendering und Retries inklusive, berechnet nur bei erfolgreichen 2xx-Antworten. 2,000 Credits pro Monat, ohne Karte.
Weiterführende Artikel
- Web scraping with Python: the complete guide — der Pillar: requests, Parsing, Speicherung und wann du auf einen Browser eskalierst
- BeautifulSoup tutorial — der schnellere Weg, wenn das HTML statisch ist
- Web scraping with Playwright — die moderne Browser-Automatisierungsalternative aus dem Vergleich oben
- Scraping sites that block bots — das Erkennungs-Wettrüsten in voller Länge
Häufig gestellte Fragen
Ist Selenium gut für Web Scraping geeignet?
Ja — wenn die Daten erst nach JavaScript-Ausführung oder nach einer Interaktion wie Klicken oder Scrollen erscheinen. Selenium steuert einen echten Browser und sieht damit genau das, was ein User sieht. Für statisches HTML ist es Overkill: Ein einfacher HTTP-Request mit requests und BeautifulSoup ist 10-50x schneller und deutlich leichter.
Selenium vs. BeautifulSoup — was sollte ich nutzen?
Sie lösen unterschiedliche Probleme. BeautifulSoup parst HTML, das du bereits geholt hast; Selenium betreibt einen Browser, der dieses HTML überhaupt erst erzeugt. Stehen die Daten im Seitenquelltext oder in einem JSON-Endpoint, nimm requests plus BeautifulSoup. Rendern sie clientseitig oder brauchen Klicks und Scrolls, nimm Selenium — und du kannst driver.page_source weiterhin an BeautifulSoup zum Parsen übergeben.
Muss ich für Selenium noch chromedriver herunterladen?
Nein. Seit Selenium 4.6 erkennt der eingebaute Selenium Manager deinen installierten Browser und lädt den passenden Driver automatisch herunter. pip install selenium ist das gesamte Setup — separate chromedriver-Downloads und das Paket webdriver-manager sind obsolet.
Können Websites Selenium erkennen?
Ja. Selenium-gesteuertes Chrome setzt navigator.webdriver auf true, und Browser-Fingerprinting-Systeme lesen viele subtilere Signale — TLS-Handshake, fehlende Plugins, Canvas-Eigenheiten, Datacenter-IPs. Patches wie undetected-chromedriver verstecken die lautesten Merkmale, aber Anti-Bot-Anbieter aktualisieren ihre Checks laufend; die Umgehung ist ein fortlaufendes Wettrüsten und keine einmalige Lösung.
Wie warte ich in Selenium, bis eine Seite geladen ist?
Nutze WebDriverWait mit expected_conditions statt time.sleep. WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.CSS_SELECTOR, ".quote"))) pollt, bis das Element existiert, und kehrt sofort zurück, sobald es da ist — schneller auf schnellen Seiten, tolerant auf langsamen. Nutze element_to_be_clickable vor Klicks und visibility_of_element_located, wenn du gerenderten Inhalt brauchst.
Ist Selenium oder Playwright besser für Web Scraping?
Für ein neues, reines Scraping-Projekt gewinnt meist Playwright: automatisch wartende Locators, schnellere Headless-Läufe und Async-Support. Selenium gewinnt bei der breitesten Abdeckung echter Browser, dem W3C-WebDriver-Standard und seinem Ökosystem — weshalb QA-Teams weiterhin standardmäßig darauf setzen. Weniger erkennbar ist keines von beiden: Anti-Bot-Systeme fingerprinten den Browser, nicht die Library, die ihn steuert.
Warum bekommt mein Selenium-Scraper eine TimeoutException?
Eine TimeoutException bedeutet, dass deine Wartebedingung nie wahr wurde. Häufige Ursachen: ein falscher Selektor, Inhalt in einem iframe, in das du nicht gewechselt hast, oder — sehr oft — eine Blockseite oder ein CAPTCHA statt des Inhalts. Dumpe driver.page_source, wenn sie auslöst; siehst du eine Anti-Bot-Seite, liegt das Problem bei der Erkennung, nicht bei deinem Selektor.
