JavaScript-websites scrapen: statisch versus gerenderd
Veel moderne sites sturen een bijna leeg HTML-document en bouwen de inhoud in de browser met JavaScript. Als je scraper de pagina ophaalt en geen producten, prijzen of artikeltekst vindt, is de inhoud er wel, maar komt die later. Deze gids laat zien hoe je ziet met welk soort pagina je te maken hebt, hoe je een snellere route vindt dan een browser waar die bestaat, en hoe je gerenderde pagina's scrapet met Playwright in Python. Hij eindigt met een notitie over wat je doet wanneer een gerenderde pagina een captcha toont.
Stap 1: controleer of de inhoud in de HTML staat
Open de pagina met view-source: in je browser, of haal hem op met een gewoon HTTP-verzoek, en zoek in de bron naar een stuk tekst dat je op het scherm ziet. Drie uitkomsten komen vaak voor:
- De tekst staat erin. De pagina is server-side gerenderd. Een gewone HTTP-client en een HTML-parser zijn genoeg, en die moet je gebruiken, want ze zijn sneller en eenvoudiger.
- De tekst ontbreekt, maar er staat een JSON-blob in. Veel sites slaan hun staat op in een script-tag, zoals
__NEXT_DATA__. Parse die JSON direct. - De tekst ontbreekt en er is geen blob. De pagina laadt zijn inhoud met extra requests. Kijk in het Network-tabblad van DevTools, gefilterd op Fetch/XHR, terwijl de pagina laadt.
Stap 2: roep het JSON-eindpunt aan als dat bestaat
Wanneer een pagina zijn gegevens van een eindpunt ophaalt, is het rechtstreeks aanroepen van dat eindpunt meestal de beste optie. Het is kleiner, sneller en stabieler dan de gerenderde pagina. Kopieer de request-URL, controleer welke headers de site vereist, en probeer het vanuit een script. Doe dit alleen waar de voorwaarden van de site het toestaan, en respecteer eventuele snelheidslimieten of authenticatie die de site verwacht. Een niet-gedocumenteerd eindpunt kan zonder waarschuwing veranderen, dus schrijf je parser zo dat hij hard faalt wanneer de vorm van het antwoord verandert.
Stap 3: render de pagina wanneer het moet
Als de inhoud echt de browser nodig heeft, stuur dan een headless browser aan. Playwright is een goede keuze voor Python. Het onderstaande voorbeeld opent een productoverzicht, wacht tot de inhoud verschijnt, en leest de tekst van elke kaart:
from playwright.sync_api import sync_playwright
URL = "https://example.com/products" # een pagina die je mag scrapen
with sync_playwright() as p:
browser = p.chromium.launch(headless=True)
page = browser.new_page()
page.goto(URL, wait_until="domcontentloaded", timeout=60_000)
# Wacht op het element dat bewijst dat de gegevens zijn aangekomen.
page.wait_for_selector(".product-card", timeout=30_000)
titles = page.locator(".product-card h2").all_inner_texts()
browser.close()
for title in titles:
print(title)
Wachten op een specifieke selector is veel betrouwbaarder dan een vaste sleep. Een vaste pauze is op een trage dag te kort, en op een snelle dag verspilt die tijd. Kies het element dat pas verschijnt wanneer de gegevens zijn geladen.
Stap 4: behandel oneindig scrollen en paginering
Sommige pagina's laden meer items wanneer je scrolt. Scroll naar beneden, wacht tot het aantal items groeit, en stop wanneer het niet meer groeit. Pagina's met genummerde paginering zijn eenvoudiger: zoek de link naar de volgende pagina, volg hem, en stop bij de laatste pagina. Stel altijd een maximum aantal pagina's of scrolls in, zodat een bug je crawler nooit eindeloos laat draaien.
def scroll_until_done(page, selector: str, max_rounds: int = 20) -> int:
previous = -1
for _ in range(max_rounds):
count = page.locator(selector).count()
if count == previous:
break
previous = count
page.mouse.wheel(0, 4000)
page.wait_for_timeout(1500) # geef de volgende batch tijd om te laden
return previous
Stap 5: houd de browser zuinig
Rendering is duur. Blokkeer afbeeldingen, lettertypen en media die je niet nodig hebt, en hergebruik één browser voor veel pagina's in plaats van elke keer een nieuwe te starten. Sluit pagina's en contexten wanneer je klaar bent, zodat het geheugen niet oploopt tijdens een lange run.
def block_heavy_assets(route):
if route.request.resource_type in ("image", "font", "media"):
route.abort()
else:
route.continue_()
page.route("**/*", block_heavy_assets)
Stap 6: wanneer een gerenderde pagina een captcha toont
Soms toont de gerenderde pagina een captcha voordat hij inhoud laat zien. Als dat captcha PopularCaptcha is, het live captchatype bij Scarnox, lees je de sitekey van de pagina, vraag je een token aan en plaats je het token in het formulierveld dat de site verwacht. De PopularCaptcha-gids voor Python loopt die stap door. Andere widgets zijn nog niet live, dus bekijk captchatypen uitgelegd voordat je ergens op vooruit plant.
Wees voorzichtig met de browser zelf. Een headless browser die duidelijk geautomatiseerd oogt, kan meer controles uitlokken, niet minder. Lees headless browserdetectie voor de signalen om op te letten, en geef de voorkeur aan langzamere en minder paginaladingen.
Scrape verantwoord
- Controleer
robots.txten de voorwaarden van de site voordat je pagina's rendert of hun endpoints aanroept. - Houd de snelheidslimieten laag, en cache resultaten zodat je elke pagina één keer ophaalt.
- Scrape geen persoonsgegevens zonder rechtmatige grondslag.
- Als een site de toegang beperkt, stop dan. Probeer de beperking niet te omzeilen.
Volgende stappen
- Scrapen zonder geblokkeerd te worden
- Headless browserdetectie
- Scrapen van door Cloudflare beschermde sites
- Playwright- en Puppeteer-captchaafhandeling
Start met gratis proefkrediet, zonder creditcard: maak een account aan en test de PopularCaptcha-flow op een pagina die van jou is. Voor de actuele tarieven zie de prijspagina.