In this Article
Wer dynamische Webseiten scrapen möchte, muss sich mit Inhalten befassen, die im HTML der ersten Anfrage nicht vorhanden sind. Moderne Websites erstellen Produktübersichten, Preise, Kommentare und Feeds im Browser mit JavaScript. Ein einfacher HTTP-Abruf liefert daher nur eine leere Hülle statt der eigentlichen Daten.
Dieser Leitfaden führt durch den kompletten Workflow in Python. Zunächst erkennen Sie dynamische Inhalte, indem Sie die Rohantwort mit dem gerenderten DOM vergleichen. Anschließend ordnet er die Extraktionsmethoden von günstig bis aufwendig: die versteckte JSON API finden, Playwright oder Selenium steuern, Infinite Scroll und Mehr-laden-Schaltflächen behandeln, Proxy-Bandbreite reduzieren, Wiederholungsversuche ergänzen, parallel scrapen sowie sauberes JSON und CSV exportieren.
DataImpulse ist ein ethischer Proxy-Anbieter mit mehr als 90 Millionen Residential-, Mobile- und Datacenter-IP-Adressen in 195 Ländern. Das Unternehmen nutzt ein Pay-as-you-go-Modell ab 1 Dollar pro GB mit nicht ablaufendem Traffic und wird für Web Scraping, Anzeigenverifizierung, Preisüberwachung, Marktforschung und Multi-Account-Management eingesetzt.
Die wichtigsten Fakten
- Dynamisches Scraping: Von JavaScript erzeugte Inhalte fehlen im Roh-HTML. Daher rufen Sie entweder die zugrunde liegende JSON API auf, die die Seite abfragt, oder rendern die Seite in einem Headless-Browser.
- Bester Proxy-Typ: rotierende Residential-Proxies, die echte Verbraucher-IP-Adressen verwenden und Erkennungen passieren.
- Preis: ab 1 Dollar pro GB, Pay-as-you-go, mit nicht ablaufendem Traffic und ohne Abonnement.
- Abdeckung: Über 90 Mio. ethisch beschaffte IP-Adressen in 195 Ländern.
- Zuverlässigkeit: 99,51 % Erfolgsquote, mit 4,8 von 5 Punkten auf G2 bewertet.
- Protokolle und Targeting: HTTP, HTTPS, and SOCKS5, inklusive Länder-Targeting.

Was macht eine Webseite dynamisch?
Eine Seite ist dynamisch, wenn die relevanten Inhalte im Browser durch JavaScript erzeugt werden, statt bereits in der ersten HTML-Antwort geliefert zu werden. Der Server sendet ein schlankes Grundgerüst; clientseitiger Code lädt danach Daten und fügt sie nach dem Öffnen der Seite in das DOM ein.
Am schnellsten prüfen Sie dies manuell, indem Sie zwei Ansichten derselben Seite vergleichen. Klicken Sie mit der rechten Maustaste und wählen Sie Seitenquelltext anzeigen, um das vom Server zurückgegebene Roh-HTML zu sehen. Öffnen Sie dann die Entwicklertools und prüfen Sie den Bereich Elements, der das aktuelle DOM nach der Ausführung der Skripte zeigt. Erscheint der gewünschte Preis oder Eintrag in Elements, aber nicht im Seitenquelltext, ist der Inhalt dynamisch und wird von einer einzelnen Anfrage nicht erfasst.
- Statische Inhalte sind im Seitenquelltext vorhanden und lassen sich direkt aus einer HTTP-Antwort parsen.
- Dynamische Inhalte erscheinen nur im gerenderten DOM und werden über Hintergrundanfragen geladen.
Diese Diagnose lässt sich im Code automatisieren. Zählen Sie zunächst die Zielelemente im Roh-HTML, das ein normaler HTTP-Client herunterlädt:
import requests
from bs4 import BeautifulSoup
url = "https://example.com/products"
headers = {"User-Agent": "Mozilla/5.0"}
resp = requests.get(url, headers=headers, timeout=30)
soup = BeautifulSoup(resp.text, "html.parser")
raw_cards = soup.select("div.product-card")
print("Cards in raw HTML:", len(raw_cards))
# If this prints 0 but the page clearly shows products in a browser,
# the content is injected by JavaScript and the page is dynamic.
Rendern Sie anschließend dieselbe URL in einem Headless-Browser und zählen Sie erneut. Die Differenz zwischen den beiden Zahlen ist der Beweis:
from playwright.sync_api import sync_playwright
with sync_playwright() as p:
browser = p.chromium.launch(headless=True)
page = browser.new_page()
page.goto("https://example.com/products", wait_until="networkidle")
rendered_cards = page.query_selector_all("div.product-card")
print("Cards after JavaScript runs:", len(rendered_cards))
browser.close()
# Raw HTML 0, rendered DOM 48 means the page builds its content dynamically.
Bevor Sie einen vollständigen Scraper schreiben, sollten Sie außerdem prüfen, ob das Ziel automatisierten Zugriff erlaubt. Unser Leitfaden dazu, wie Sie prüfen, ob eine Website Scraping erlaubt erläutert das Lesen von robots-Regeln und Nutzungsbedingungen.
Welche Methodee eignet sich zum Scraping dynamischer Seiten?
Wählen Sie die schlankste Methodee, die die Daten zuverlässig liefert: Rufen Sie eine versteckte JSON API auf, wenn es eine gibt, und greifen Sie nur dann auf einen Headless-Browser zurück, wenn keine vorhanden ist. Jede Option wägt Geschwindigkeit und Kosten gegen den Umfang ab, in dem sie die Seite nachbilden kann.
Die folgende Tabelle vergleicht die vier üblichen Ansätze, damit Sie vor dem Schreiben von Code wählen können. In der Praxis kombinieren die meisten Projekte zwei davon: eine versteckte API für die Masse der Daten und einen Browser für Seiten, die sich dagegen sperren.
| Methode | Geschwindigkeit | Kosten | Am besten geeignet für | Wichtigste Einschränkung |
|---|---|---|---|---|
| Hidden JSON API | Am schnellsten | Am niedrigsten | Daten hinter einem sauberen XHR-Aufruf | Endpoint kann sich ändern oder Tokens benötigen |
| Playwright | Mittel | Höher | Vollständiges Rendering, moderne async-Steuerung | Nutzt CPU, Speicher und Bandbreite |
| Selenium | Mittel | Höher | Legacy-Stacks, breite Sprachunterstützung | Langsamere Einrichtung, ausführlichere Waits |
| requests-html | Langsam | Niedrig | Einfaches JavaScript auf simplen Seiten | Weitgehend ungepflegt, fragiles Rendering |
Kosten spielen eine Rolle, weil ein über einen getakteten Proxy geleiteter Headless-Browser deutlich mehr Bytes herunterlädt als ein einzelner API-Aufruf. Wenn Sie im großen Maßstab scrapen, beeinflusst die gewählte Methodee die Rechnung ebenso stark wie der Code. Weitere Hinweise zu Taktung und Headers finden Sie in unseren Best Practices für Web Scraping.
Wie finden Sie die versteckte API hinter einer dynamischen Seite?
Suchen Sie nach der Hintergrundanfrage, mit der die Seite ihre Daten abruft, denn dieser Endpoint liefert normalerweise sauberes JSON, das Sie direkt aufrufen können. Dies ist der schnellste und zuverlässigste Ansatz und erspart den Browser komplett.
Öffnen Sie die Entwicklertools, wechseln Sie zum Tab Network, filtern Sie nach Fetch/XHR und laden Sie die Seite neu. Achten Sie beim Erscheinen der Inhalte auf Anfragen, die JSON mit den gewünschten Werten zurückgeben. Prüfen Sie Anfrage-URL, Methodee, Query-Parameter sowie alle gesendeten Headers oder Tokens und kopieren Sie die Anfrage per Rechtsklick als cURL, um ihre vollständige Form zu sehen. Sobald Sie diesen Aufruf mit einem HTTP-Client nachbilden können, überspringen Sie das Rendering vollständig und erhalten strukturierte Daten zu einem Bruchteil der Kosten.
Hier ist ein minimales Beispiel, das einen JSON-Endpoint über einen Proxy aufruft und die Felder direkt ausliest:
import requests
proxy = "http://USERNAME:[email protected]:823"
proxies = {"http": proxy, "https": proxy}
headers = {
"User-Agent": "Mozilla/5.0",
"Accept": "application/json",
"Referer": "https://example.com/products",
}
params = {"category": "laptops", "page": 1}
r = requests.get(
"https://example.com/api/v2/products",
headers=headers,
params=params,
proxies=proxies,
timeout=30,
)
r.raise_for_status()
data = r.json()
for item in data["items"]:
print(item["name"], item["price"])
Ist der Endpoint paginiert, erhöhen Sie den page-Parameter oder folgen Sie dem nächsten Cursor aus der Antwort. Benötigt eine Anfrage ein Token, ermitteln Sie, wo die Seite es erstmals ausstellt, und verwenden Sie es innerhalb derselben Sitzung erneut. Wenn Sie den Aufruf über DataImpulse leiten, sieht der Endpoint eine Residential-IP statt Ihrer Serveradresse. Das ist wichtig, wenn die API nach Herkunft rate-limitiert. Stoßen Sie bei der Authentifizierung am Proxy selbst auf eine Hürde, erklärt unser Hinweis zur Proxy-Authentifizierung explains das Format der Zugangsdaten.
Wie scrapen Sie dynamische Seiten mit Playwright?
Wenn keine saubere API aufrufbar ist, steuern Sie einen Headless-Browser wie Playwright, damit JavaScript genau wie für einen Nutzer ausgeführt wird, und lesen anschließend das fertige DOM aus. Playwright ist der moderne Standard, weil es zuverlässiges Auto-Waiting, erstklassige async-Unterstützung und eine einfache Proxy-Option bietet.
Installieren Sie es und laden Sie zunächst eine Browser-Binärdatei herunter:
pip install playwright
playwright install chromium
Entscheidend ist, auf das konkrete benötigte Element zu warten, statt mit festen Pausen zu raten. Das folgende Beispiel startet Chromium, leitet ihn mithilfe der Dictionary-Form über einen Proxy, wartet auf die Produktübersicht und extrahiert die gerenderten Werte:
from playwright.sync_api import sync_playwright
PROXY = {
"server": "http://gw.dataimpulse.com:823",
"username": "USERNAME",
"password": "PASSWORD",
}
with sync_playwright() as p:
browser = p.chromium.launch(headless=True, proxy=PROXY)
context = browser.new_context(user_agent="Mozilla/5.0")
page = context.new_page()
page.goto("https://example.com/products", wait_until="domcontentloaded")
page.wait_for_selector("div.product-card", timeout=15000)
cards = page.query_selector_all("div.product-card")
for card in cards:
name = card.query_selector(".title").inner_text()
price = card.query_selector(".price").inner_text()
print(name, price)
browser.close()
Beim Warten entscheidet sich bei den meisten dynamischen Scrapern Erfolg oder Misserfolg. Deshalb sollten Sie die vier Strategien und ihre jeweiligen Einsatzfälle kennen. Warten Sie bevorzugt auf einen konkreten Selector oder Zustand statt auf networkidle, das bei Seiten mit langlebigen offenen Verbindungen hängen kann:
# Wait for a specific element (most reliable)
page.wait_for_selector("div.product-card", timeout=15000)
# Wait for the initial DOM to be built, before every script finishes
page.goto(url, wait_until="domcontentloaded")
# Wait until there are no network connections for 500 ms
page.goto(url, wait_until="networkidle")
# Wait for a custom condition, such as a minimum item count
page.wait_for_function(
"document.querySelectorAll('div.product-card').length > 20"
)
Wenn Ihr Ziel stark auf im Browser ausgeführte Skripte setzt, behandelt unser begleitender Artikel über Web Scraping mit JavaScript ausführlicher das clientseitige Rendering, das diese Seiten schwierig macht.
Wie behandeln Sie Infinite Scroll und Mehr-laden-Schaltflächen?
Lösen Sie dieselben Ereignisse aus, die die Seite zum Laden weiterer Inhalte verwendet, und warten Sie, bis jede neue Charge gerendert ist, bevor Sie sie auslesen. Infinite Scroll und Mehr-laden-Schaltflächen rufen Daten nur bei Nutzeraktionen ab. Ein einzelnes Laden der Seite erfasst daher nur den ersten Bildschirm.
Bevor Sie zum Browser greifen, prüfen Sie erneut den Tab Network. Infinite Scroll wird fast immer von einem paginierten XHR-Aufruf gespeist, und das Durchlaufen dieses Endpoints mit dem obigen requests-Ansatz ist wesentlich günstiger als Scrollen. Müssen Sie scrollen, nutzen Sie eine kontrollierte Schleife, die stoppt, sobald keine neuen Elemente mehr erscheinen:
def scroll_until_stable(page, item_selector, max_rounds=30):
seen = 0
for _ in range(max_rounds):
page.evaluate("window.scrollTo(0, document.body.scrollHeight)")
page.wait_for_timeout(1500)
count = len(page.query_selector_all(item_selector))
if count == seen:
break # no new items loaded, stop
seen = count
return seen
Seiten, die statt Scrollen eine explizite Schaltfläche verwenden, benötigen eine Klickschleife. Lesen Sie die Anzahl der Elemente vor jedem Klick, klicken Sie die Schaltfläche und warten Sie auf eine höhere Anzahl, damit Sie dem Rendering nicht vorauslaufen:
def click_load_more(page, button_selector, item_selector, max_clicks=50):
for _ in range(max_clicks):
button = page.query_selector(button_selector)
if button is None or not button.is_visible():
break
before = len(page.query_selector_all(item_selector))
button.click()
page.wait_for_function(
"([sel, n]) => document.querySelectorAll(sel).length > n",
arg=[item_selector, before],
)
return len(page.query_selector_all(item_selector))
Entfernen Sie Duplikate während des Sammelns, denn dieselben Nodes bei jedem Durchlauf erneut zu lesen, ist häufig. Führen Sie eine Menge eindeutiger IDs oder URLs und fügen Sie nur noch nicht bekannte Datensätze hinzu.
Can you use Selenium instead of Playwright?
Yes, Selenium drives a real browser the same way and is a solid choice when your team already uses it or needs its wide language support. The pattern mirrors Playwright: load the page, wait for a condition with WebDriverWait, then read elements from the rendered DOM.
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
options = Options()
options.add_argument("--headless=new")
options.add_argument("--proxy-server=http://gw.dataimpulse.com:823")
driver = webdriver.Chrome(options=options)
driver.get("https://example.com/products")
WebDriverWait(driver, 15).until(
EC.presence_of_element_located((By.CSS_SELECTOR, "div.product-card"))
)
for card in driver.find_elements(By.CSS_SELECTOR, "div.product-card"):
name = card.find_element(By.CSS_SELECTOR, ".title").text
price = card.find_element(By.CSS_SELECTOR, ".price").text
print(name, price)
driver.quit()
One caveat: the plain –proxy-server flag does not accept a username and password, so an authenticated proxy needs a helper. The Selenium Wire library injects credentials cleanly, and our walkthrough on the Selenium Wire proxy setup shows the exact configuration. Selenium is heavier to configure than Playwright and its waits are more verbose, so new projects often start with Playwright unless there is a reason to stay on Selenium.
Wie reduzieren Sie Proxy-Bandbreite beim Rendern von Seiten?
Block the resources you do not need so the browser stops downloading images, fonts, and media you never parse. A headless browser fetches every asset by default, and each of those bytes costs money when routed through a proxy priced per gigabyte.
Playwright lets you intercept requests and abort ones that are irrelevant to your extraction. Blocking images, media, and fonts can cut proxy traffic sharply while leaving the JSON and HTML you actually read untouched:
BLOCK = {"image", "media", "font"}
def block_heavy(route):
if route.request.resource_type in BLOCK:
route.abort()
else:
route.continue_()
context = browser.new_context()
context.route("**/*", block_heavy)
page = context.new_page()
page.goto("https://example.com/products", wait_until="domcontentloaded")
You can extend the same handler to skip analytics and ad domains that add load without adding data. With DataImpulse traffic billed pay-as-you-go from 1 dollar per GB and non-expiring, trimming wasted bytes lowers what each scrape costs directly. Reusing one browser context across related pages also avoids repeated startup cost, and the cheapest request remains the one you avoid by falling back to a hidden API.
Wie fügen Sie Fehlerbehandlung und Wiederholungsversuche hinzu?
Wrap each navigation in a retry loop with exponential backoff so a single slow load or transient block does not kill the whole run. Dynamic pages fail intermittently, and a scraper that gives up on the first timeout will lose a large share of records at scale.
Catch the timeout that Playwright raises, wait a growing interval, and retry a bounded number of times before recording the failure and moving on:
import time
from playwright.sync_api import TimeoutError as PlaywrightTimeout
def fetch_with_retries(page, url, selector, retries=3, backoff=2):
for attempt in range(1, retries + 1):
try:
page.goto(url, wait_until="domcontentloaded", timeout=30000)
page.wait_for_selector(selector, timeout=15000)
return page.query_selector_all(selector)
except PlaywrightTimeout:
wait = backoff ** attempt
print(f"Attempt {attempt} timed out, retrying in {wait}s")
time.sleep(wait)
raise RuntimeError(f"Failed to load {url} after {retries} attempts")
Pair retries with proxy rotation so each attempt goes out through a fresh IP. If one exit is rate-limited, the next request lands on a different address and often succeeds. A rotating pool of residential proxies or mobile proxies assigns a new IP per request automatically, which turns many hard blocks into a simple retry. For handling proxy-level failures specifically, our note on HTTP error 407 covers the most common authentication response.
Wie scrapen Sie viele dynamische Seiten gleichzeitig?
Use Playwright’s async API with an asyncio semaphore so several pages render at once without overwhelming your machine or the target. Concurrency is where dynamic scraping gets its throughput back, since each browser page spends most of its time waiting on the network.
The semaphore caps how many pages run in parallel. Share one browser and context across tasks, open a page per URL, and gather the results:
import asyncio
from playwright.async_api import async_playwright
PROXY = {
"server": "http://gw.dataimpulse.com:823",
"username": "USERNAME",
"password": "PASSWORD",
}
async def scrape_one(context, url, sem):
async with sem:
page = await context.new_page()
try:
await page.goto(url, wait_until="domcontentloaded")
await page.wait_for_selector("div.product-card", timeout=15000)
cards = await page.query_selector_all("div.product-card")
return [await c.inner_text() for c in cards]
finally:
await page.close()
async def main(urls):
sem = asyncio.Semaphore(5) # at most 5 pages at once
async with async_playwright() as p:
browser = await p.chromium.launch(headless=True, proxy=PROXY)
context = await browser.new_context()
tasks = [scrape_one(context, u, sem) for u in urls]
results = await asyncio.gather(*tasks)
await browser.close()
return results
urls = ["https://example.com/products?page=%d" % i for i in range(1, 21)]
data = asyncio.run(main(urls))
Keep the concurrency limit modest. Too many parallel pages exhaust memory and trip rate limits, which costs you more retries than the parallelism saves. Start around five and raise it only while success rates stay high.
Wie exportieren Sie gescrapte Daten in JSON und CSV?
Collect each page’s results into a list of dictionaries, then write that list to JSON for nested data or CSV for flat, spreadsheet-friendly tables. Keeping extraction and export separate makes the scraper easier to test and rerun.
import json
import csv
records = [
{"name": "Laptop A", "price": "999"},
{"name": "Laptop B", "price": "1299"},
]
# Export to JSON
with open("products.json", "w", encoding="utf-8") as f:
json.dump(records, f, ensure_ascii=False, indent=2)
# Export to CSV
with open("products.csv", "w", newline="", encoding="utf-8") as f:
writer = csv.DictWriter(f, fieldnames=["name", "price"])
writer.writeheader()
writer.writerows(records)
Choose JSON when records contain nested lists or objects, and CSV when every record shares the same flat fields and analysts will open it in a spreadsheet. Normalize your fields before writing so every record has the same keys, and the export step stays trivial no matter how the dynamic page was rendered. From here, the same JSON feeds a database load or an analytics job without further parsing.

Häufig gestellte Fragen
Woran erkenne ich vor dem Schreiben eines Scrapers, ob eine Seite dynamisch ist?
Vergleichen Sie den Seitenquelltext mit dem Bereich Elements in den Entwicklertools oder zählen Sie ein Zielelement im Roh-HTML und im gerenderten DOM. Erscheinen die Daten erst nach der Ausführung von JavaScript, ist die Seite dynamisch und benötigt einen API-Aufruf oder einen Headless-Browser.
Ist es besser, die versteckte API aufzurufen oder einen Headless-Browser zu verwenden?
Rufen Sie die versteckte API auf, wenn Sie sie finden können, denn sie liefert sauberes JSON zu einem Bruchteil der Kosten und Zeit des Renderings. Nutzen Sie einen Headless-Browser nur, wenn keine reproduzierbare API existiert oder die Daten von komplexen clientseitigen Skripten abhängen.
Sollte ich für dynamisches Scraping Playwright oder Selenium verwenden?
Playwright ist dank zuverlässigem Auto-Waiting und integrierter async-Unterstützung der moderne Standard, daher beginnen neue Projekte meist damit. Selenium eignet sich gut, wenn Ihr Stack es bereits nutzt oder Sie seine breitere Sprachunterstützung benötigen.
Wie reduziere ich Proxy-Bandbreite bei der Verwendung eines Headless-Browsers?
Fangen Sie Anfragen ab und brechen Sie Bilder, Fonts, Medien und Analytics ab, die Sie nicht parsen. Das kann den Traffic deutlich reduzieren, da Proxy-Kosten pro Gigabyte gemessen werden, und beschleunigt zugleich jeden Seitenaufruf.
Wie scrape ich Inhalte, die per Infinite Scroll geladen werden?
Prüfen Sie zuerst im Tab Network den paginierten XHR-Aufruf hinter dem Scrollen und durchlaufen Sie diesen Endpoint direkt. Müssen Sie scrollen, tun Sie dies in einer kontrollierten Schleife und warten Sie vor dem Auslesen auf das Rendering jeder neuen Charge.
Wann ist DataImpulse nicht die passende Wahl?
Wenn Sie statische ISP-Proxies, eine vollständig verwaltete Scraping API oder Zugriff auf Banking- und Regierungsseiten benötigen, ist DataImpulse nicht das passende Tool. Der Fokus liegt auf rotierenden Residential-, Mobile- und Datacenter-Proxies zum Sammeln öffentlicher Daten und zum Zugriff auf Inhalte.
Weiterführende Leitfäden
Scrapen Sie dynamische Seiten mit zuverlässigen Proxies
If your scraper needs residential, mobile, or datacenter IPs that behave like real visitors, you can start on pay-as-you-go traffic from 1 dollar per GB. Create a DataImpulse account and route your dynamic scraping through rotating or sticky sessions.
