DrissionPage und Proxys: Ein Werkzeug für HTTP und Browser
Wer in Python scrapet, kennt das Dilemma: requests ist schnell und günstig, scheitert aber an JavaScript-gerenderten Seiten; Playwright oder Selenium lösen das Rendering-Problem, kosten jedoch 5–10× mehr Ressourcen pro Request. DrissionPage schließt diese Lücke, indem es beide Welten in einer API vereint — und mit ProxyHat-Residential-Proxys skaliert das Ganze auf harte Ziele wie SERPs, E-Commerce-Plattformen und Social-Media-Daten.
Bevor wir in die Implementierung gehen, ein wichtiger Hinweis: Dieses Tutorial behandelt ausschließlich das Scraping öffentlich zugänglicher Daten. Die Nutzung muss den robots.txt-Regeln und den Nutzungsbedingungen der Zielseite entsprechen. In den USA kann unbefugter automatisierter Zugriff unter den Computer Fraud and Abuse Act (CFAA, 18 U.S.C. § 1030) fallen; in der EU greift die DSGVO (GDPR) bei personenbezogenen Daten. Wenn eine offizielle API existiert, sollte diese Vorrang haben.
Warum dieses Problem existiert: Die Kosten-Dichotomie
Ein Headless-Chromium-Prozess verbraucht typischerweise 150–400 MB RAM und benötigt 800–2000 ms für das Laden einer moderaten Seite. Ein HTTP-Request mit requests braucht dagegen 5–20 MB und 100–500 ms. Wenn Sie 10.000 Seiten scrapen, ist der Unterschied zwischen 15 GB und 2 GB RAM-Stunden — und zwischen 30 Minuten und 3 Stunden Laufzeit.
DrissionPage (offizielle Dokumentation) adressiert dies durch drei Seitenklassen:
- SessionPage — HTTP-Modus, basierend auf
requests.Session, mit Cookie-Persistenz und Keep-Alive. - ChromiumPage — Browser-Modus, gesteuert über das Chrome DevTools Protocol (CDP), ohne WebDriver-Schnittstelle, was es schneller und weniger erkennbar macht als Selenium.
- WebPage — kombiniert beide Modi und teilt Cookies, Header und Session-State zwischen ihnen. Sie können mitten in einem Workflow von HTTP auf Browser umschalten.
Das bedeutet: Sie starten günstig im HTTP-Modus und eskalieren nur dann zum Browser, wenn JavaScript-Rendering oder XHR-Interception erforderlich sind. Das senkt die Infrastrukturkosten um 60–80 % in typischen Scraping-Pipelines.
DrissionPage Chromium vs. SessionPage: Wann was?
| Kriterium | SessionPage (HTTP) | ChromiumPage (CDP) |
|---|---|---|
| RAM-Verbrauch | 5–20 MB | 150–400 MB |
| Latenz pro Request | 100–500 ms | 800–2000 ms |
| JavaScript-Rendering | Nein | Ja |
| XHR/Fetch-Interception | Nein | Ja (über listen) |
| Anti-Bot-Erkennung | Höher (kein Browser-Fingerprint) | Niedriger (echter Browser) |
| Proxy-Konfiguration | set_proxies() | ChromiumOptions.set_proxy() |
| Kosten pro 10k Requests | ~$0,50 Compute | ~$5,00 Compute |
Die idiomatic API: ele(), eles() und Locators
DrissionPage verwendet eine einheitliche Locator-Syntax, die in beiden Modi funktioniert. Das ist einer der größten Vorteile gegenüber der Kombination aus requests + BeautifulSoup + Selenium.
Locator-Syntax im Überblick
from DrissionPage import WebPage
page = WebPage()
# Tag-basierte Suche
input_elem = page.ele('tag:input')
# Attribut-basierte Suche
button = page.ele('@class=submit-btn')
# XPath
title = page.ele('xpath://h1[@id="product-title"]')
# Mehrere Elemente
links = page.eles('tag:a')
# Kombiniert: Tag + Attribut
login_form = page.ele('tag:form@class=login')
Die ele()-Methode gibt das erste passende Element zurück, eles() eine Liste. Beide Methoden funktionieren identisch im HTTP- und im Browser-Modus — ein Element, das Sie im HTTP-Modus gefunden haben, bleibt gültig, wenn Sie in den Browser-Modus wechseln (solange die Seite neu geladen wird).
ChromiumOptions konfigurieren
from DrissionPage import ChromiumOptions
co = ChromiumOptions()
co.headless(True)
co.set_argument('--no-sandbox')
co.set_argument('--disable-gpu')
co.set_argument('--window-size=1920,1080')
co.set_user_agent('Mozilla/5.0 (Windows NT 10.0; Win64; x64) ...')
# Proxy setzen — ProxyHat Residential
co.set_proxy('http://user-country-US-session-abc123:pass@gate.proxyhat.com:8080')
page = ChromiumPage(co)
Hintergrund-XHR mit listen.start() erfassen
Eines der mächtigsten Features von ChromiumPage ist die Fähigkeit, Hintergrund-Requests abzufangen — JSON-APIs, die die Seite asynchron lädt. Das ist oft der Schlüssel, um versteckte APIs zu finden und Browser-Eskalation ganz zu vermeiden.
from DrissionPage import ChromiumPage, ChromiumOptions
co = ChromiumOptions()
co.set_proxy('http://user-country-DE-session-sess01:pass@gate.proxyhat.com:8080')
page = ChromiumPage(co)
# Auf XHR/Fetch-Requests lauschen, die JSON zurückgeben
page.listen.start('api/products')
page.get('https://example-shop.de/catalog')
# Warte auf das erste passende Paket
packet = page.listen.wait(count=1, timeout=10)
if packet:
print(packet.response.body) # JSON-Daten der Hintergrund-API
print(packet.url) # Die echte API-URL
Mit dieser Technik finden Sie oft die ungeschützte JSON-API hinter einer JavaScript-gerenderten Seite — und können dann im nächsten Lauf direkt über SessionPage (HTTP) auf diese API zugreifen, ohne den Browser starten zu müssen.
Proxys in DrissionPage konfigurieren
SessionPage: set_proxies()
from DrissionPage import SessionPage
page = SessionPage()
page.set_proxies({
'http': 'http://user-country-US-session-abc123:pass@gate.proxyhat.com:8080',
'https': 'http://user-country-US-session-abc123:pass@gate.proxyhat.com:8080',
})
page.get('https://httpbin.org/ip')
print(page.html)
ChromiumPage: ChromiumOptions.set_proxy()
from DrissionPage import ChromiumOptions, ChromiumPage
co = ChromiumOptions()
co.set_proxy('http://user-country-DE-city-berlin-session-sess42:pass@gate.proxyhat.com:8080')
page = ChromiumPage(co)
page.get('https://httpbin.org/ip')
print(page.ele('tag:pre').text)
Warum sind Residential-Proxys für harte Ziele notwendig? Datacenter-IPs werden von Cloud-Anbietern (AWS, Google Cloud, Azure) zugewiesen und sind auf Anti-Bot-Systemen oft blockiert oder mit CAPTCHAs belegt. Residential-IPs stammen von echten ISPs und haben eine viel niedrigere Erkennungsrate. ProxyHat bietet Standorte in über 90 Ländern mit City-Level-Geo-Targeting.
Komplettes Beispiel: WebPage mit Moduswechsel und ProxyHat
Das folgende Beispiel zeigt den idiomatischen DrissionPage-Workflow: Start im HTTP-Modus über einen US-Residential-Proxy, dann Eskalation zum Browser für eine JS-gerenderte Seite — mit durchgehendem Cookie- und Session-Sharing.
from DrissionPage import WebPage
import json
def build_proxyhat_user(country='US', session_id='abc123', city=None):
"""Erstellt einen ProxyHat-Benutzernamen mit Geo-Targeting und Sticky Session."""
user = f'user-country-{country}'
if city:
user += f'-city-{city}'
user += f'-session-{session_id}'
return user
# ProxyHat-Konfiguration
PROXYHAT_HOST = 'gate.proxyhat.com'
PROXYHAT_PORT = 8080
PROXYHAT_PASS = 'your_password'
session_user = build_proxyhat_user(country='US', session_id='order-001')
proxy_url = f'http://{session_user}:{PROXYHAT_PASS}@{PROXYHAT_HOST}:{PROXYHAT_PORT}'
# WebPage initialisieren — startet im HTTP-Modus
page = WebPage()
# --- Phase 1: HTTP-Modus für die Übersichtsseite ---
page.set_proxies({'http': proxy_url, 'https': proxy_url})
page.get('https://example-shop.com/api/categories')
categories = json.loads(page.response.content)
print(f"Gefunden: {len(categories)} Kategorien")
# --- Phase 2: Browser-Eskalation für JS-gerenderte Produktdetails ---
# Cookies und Session-State werden automatisch übernommen
page.change_mode() # Wechselt zu ChromiumPage
# Browser-Proxy muss separat gesetzt werden (vor Browser-Start)
from DrissionPage import ChromiumOptions
co = ChromiumOptions()
co.set_proxy(proxy_url)
co.headless(True)
page = WebPage(chromium_options=co)
page.get('https://example-shop.com/product/12345')
# Auf XHR lauschen, um die echte Produkt-API zu finden
page.listen.start('api/product')
page.get('https://example-shop.com/product/12345')
packet = page.listen.wait(count=1, timeout=15)
if packet:
product_data = json.loads(packet.response.body)
print(f"Produkt: {product_data['name']}, Preis: {product_data['price']}")
else:
# Fallback: DOM-Scraping
name = page.ele('@class=product-title').text
price = page.ele('@class=price').text
print(f"Produkt (DOM): {name}, Preis: {price}")
Dieses Pattern — HTTP zuerst, Browser nur bei Bedarf — reduziert die Browser-Starts um typischerweise 70–90 %, wenn die meisten Seiten eine zugängliche API im Hintergrund haben.
Produktionsmuster: Skalierung, Retries und Concurrency
1. Per-Session Proxy-Pinning
Für Login-Flows und mehrstufige Workflows müssen alle Requests von derselben IP kommen. ProxyHat unterstützt Sticky Sessions über den -session-{id}-Flag im Benutzernamen. Die Session bleibt für bis zu 30 Minuten an dieselbe IP gebunden.
import uuid
def make_page_for_session(country='US'):
sid = uuid.uuid4().hex[:12]
user = f'user-country-{country}-session-{sid}'
proxy = f'http://{user}:pass@gate.proxyhat.com:8080'
page = WebPage()
page.set_proxies({'http': proxy, 'https': proxy})
return page, sid
2. Retries mit exponentiellem Backoff
import time
def fetch_with_retry(page, url, max_retries=3):
for attempt in range(max_retries):
try:
resp = page.get(url, timeout=15)
if resp.status_code == 200:
return resp
elif resp.status_code == 429:
wait = 2 ** attempt * 5 # 5s, 10s, 20s
print(f"Rate-limited, warte {wait}s")
time.sleep(wait)
else:
print(f"Status {resp.status_code}, Retry {attempt+1}")
time.sleep(2 ** attempt)
except Exception as e:
print(f"Fehler: {e}, Retry {attempt+1}")
time.sleep(2 ** attempt)
return None
3. Concurrency-Limits und Parallelität
DrissionPage ist nicht async-nativ. Für Parallelität verwenden Sie concurrent.futures.ThreadPoolExecutor für SessionPage oder mehrere ChromiumPage-Instanzen in separaten Prozessen. Eine Faustregel: maximal 5–10 gleichzeitige Browser-Instanzen pro CPU-Kern, 50–100 gleichzeitige HTTP-Sessions pro Kern.
from concurrent.futures import ThreadPoolExecutor, as_completed
def scrape_url(url):
page = SessionPage()
sid = url.split('/')[-1] # Eindeutige Session pro URL
proxy = f'http://user-country-US-session-{sid}:pass@gate.proxyhat.com:8080'
page.set_proxies({'http': proxy, 'https': proxy})
page.get(url)
return page.ele('tag:title').text
urls = ['https://example.com/page/1', 'https://example.com/page/2']
with ThreadPoolExecutor(max_workers=20) as executor:
futures = {executor.submit(scrape_url, u): u for u in urls}
for f in as_completed(futures):
try:
result = f.result(timeout=30)
print(result)
except Exception as e:
print(f"Fehler: {e}")
4. Versteckte APIs über listen finden
Der listen-Mechanismus ist Ihr wichtigstes Werkzeug, um Browser-Eskalation zu vermeiden. Starten Sie den Browser einmalig, erfassen Sie alle XHR/Fetch-Requests, identifizieren Sie die JSON-Endpunkte — und scrapen Sie danach ausschließlich über SessionPage. Ein typischer E-Commerce-Shop lädt 3–8 Hintergrund-APIs pro Seitenaufruf; oft sind Preis-, Bewertung- und Verfügbarkeitsdaten über direkte JSON-Endpunkte zugänglich.
Wann Sie NICHT zum Browser eskalieren sollten
- Statische HTML-Seiten — wenn der Inhalt im Quelltext steht, ist SessionPage ausreichend und 10× schneller.
- Bekannte JSON-APIs — wenn Sie die API-URL kennen (z. B. über
listengefunden), rufen Sie sie direkt auf. - Massen-Downloads — für Tausende von Produktseiten mit bekannter Struktur ist der Browser-Overhead nicht vertretbar.
- Sitemaps und RSS-Feeds — diese sind per Definition maschinenlesbar und benötigen kein Rendering.
Eskalieren Sie zum Browser nur, wenn: (a) die Seite JavaScript benötigt, um den Inhalt zu rendern, (b) Sie mit interaktiven Elementen arbeiten müssen (Klicks, Scrollen, Formulare), oder (c) Sie XHR-Requests abfangen müssen, um eine versteckte API zu finden.
ProxyHat-spezifische Einrichtung
ProxyHat bietet drei Proxy-Typen, die alle über denselben Gateway gate.proxyhat.com erreichbar sind:
| Typ | Use Case | Empfehlung für DrissionPage |
|---|---|---|
| Residential | SERPs, E-Commerce, Social Media | Standardwahl für harte Ziele |
| Mobile | App-APIs, mobil-spezifische Inhalte | Wenn die Seite Mobile-IPs erfordert |
| Datacenter | Schnelle, ungeschützte APIs | Kostenoptimiert für Phase 1 (HTTP) |
Die ProxyHat-Preise sind nutzungsbasiert. Für DrissionPage-Workflows empfiehlt sich eine Kombination: Datacenter-Proxys für die HTTP-Phase (niedrige Kosten, hohe Geschwindigkeit) und Residential-Proxys für die Browser-Phase (höhere Erfolgsquote bei Anti-Bot-Checks).
Weitere Details zur Konfiguration finden Sie in der ProxyHat-Dokumentation. Für spezifische Use Cases siehe Web Scraping und SERP Tracking.
Häufige Fehler und Edge Cases
- Proxy wird im Browser-Modus nicht angewendet —
set_proxy()muss aufChromiumOptionsvor der Browser-Erstellung gesetzt werden, nicht nachträglich auf der Page. - Session-Verlust beim Moduswechsel —
WebPage.change_mode()überträgt Cookies automatisch, aber benutzerdefinierte Header müssen manuell neu gesetzt werden. - listen.wait() blockiert endlos — setzen Sie immer einen
timeout-Wert. Wenn die Seite asynchron lädt, warten Sie zusätzlich auf ein DOM-Element, bevor Sielisten.wait()aufrufen. - Zu viele Browser-Instanzen — jeder ChromiumPage-Prozess hält einen echten Browser offen. Schließen Sie Pages mit
page.close(), um Zombie-Prozesse zu vermeiden. - Proxy-Rotation ignoriert Sessions — wenn Sie Sticky Sessions verwenden, dürfen Sie nicht auch
-rotate-1setzen. Wählen Sie einen Modus pro Request.
Key Takeaways
- DrissionPage vereint HTTP- und Browser-Scraping in einer API — nutzen Sie SessionPage für 80–90 % der Requests und eskalieren Sie nur bei Bedarf zu ChromiumPage.
- Residential-Proxys sind für harte Ziele unerlässlich; Datacenter-IPs reichen für ungeschützte APIs.
- listen.start() ist Ihr wichtigstes Werkzeug, um versteckte JSON-APIs zu finden und Browser-Overhead zu eliminieren.
- ProxyHat-Sticky-Sessions (
-session-{id}) garantieren IP-Konsistenz für mehrstufige Workflows.- Concurrency: 50–100 HTTP-Sessions pro Kern, 5–10 Browser-Instanzen pro Kern — nicht mehr.
- Ethik: Nur öffentliche Daten,
robots.txtrespektieren, offizielle APIs bevorzugen, DSGVO/CFAA beachten.
DrissionPage mit ProxyHat-Residential-Proxys gibt Ihnen die Werkzeuge, um praktisch jede öffentliche Webseite effizient und zuverlässig zu scrapen — mit der Geschwindigkeit von HTTP und der Macht eines echten Browsers, genau dann, wenn Sie ihn brauchen.






