Data scraping
Browserbase, Stagehand of Playwright: carrier scraping
Een Rotterdamse expediteur wilde 4.800 wekelijkse tariefchecks bij Maersk, MSC en Hapag-Lloyd. We testten Browserbase, Stagehand en een eigen Playwright-loop. Dit brak.

Het is 23:14 op een zondagavond. Maersk heeft stilletjes een Next.js-rewrite van hun instant-quote pagina uitgerold. Elke CSS-selector waar onze agent op leunde, is weg. De expediteur uit Rotterdam met 18 man, die ons inschakelde om 200 trade lanes te monitoren, wordt straks wakker met een dashboard vol nullen — tenzij iemand binnen het uur achter een laptop zit.
Deze post is wat we leerden bij het beoordelen van drie scraping-stacks — Browserbase, Stagehand en een zelfgebouwde Playwright-loop met Claude als brein — op de enige drie vragen waar een expediteur echt om geeft: wat kost elke tariefcheck, wat gebeurt er als Cloudflare Turnstile strenger wordt, en wie heeft dienst als de carrier zijn site herschrijft op een zondagavond.
De opdracht, in cijfers
De klant boekt containers vanuit Azië en de Middellandse Zee naar Rotterdam, Hamburg en Felixstowe. Hun offerteproces begon altijd bij een junior die spot-tarieven uit de portals van Maersk, MSC en Hapag-Lloyd haalde. Drie carriers, 200 actieve routes, ongeveer acht refreshes per route per week om GRI-aankondigingen te pakken voordat de concurrent van de klant ze ziet. Die som geeft 4.800 quote-checks per week, en de agent die we bouwden moet dat doen onder een Cloudflare Turnstile-schild, achter een portal-login, met het resultaat in een Postgres-tabel die een Slack-melding afvuurt zodra een spot-rate meer dan 7% beweegt op een gevolgde route.
We hadden drie serieuze opties voor de scraping-laag.
Browserbase als ondergrond
Browserbase is een managed headless-browser dienst. Je verbindt er via CDP mee, krijgt een Chromium-instance met stealth-defaults, residential proxies en een session recorder die je maandagochtend om 09:00 aan een customer-success engineer kunt overhandigen. Je schrijft nog steeds je eigen Playwright-code. Browserbase lost het probleem op van waar de browser draait. Het lost niet op wat de browser doet op de pagina.
Voor deze workload waren de aantrekkelijke kanten de proxy-pool en de ingebouwde Turnstile-afhandeling. Het minder aantrekkelijke deel was de afrekening per browser-minuut. 4.800 checks van zo'n 35 seconden per stuk is ongeveer 47 browser-uren per week, voor retries. Die rekening is prima als de data nergens anders te ontsluiten valt; ze wordt minder prima zodra je je herinnert dat je voor €40 per maand een VPS huurt die Chromium tot in het oneindige draait.
Stagehand als intentielaag
Stagehand is het open-source framework van het Browserbase-team dat Playwright in drie LLM-gedreven primitieven verpakt: act(), extract() en observe(). In plaats van de browser opdracht te geven om op [data-testid="rate-row-0"] te klikken, vertel je 'm de spot-rate voor een 40HC van Yantian naar Rotterdam op te halen, met vertrek binnen 14 dagen. Een model lost de intentie op tegen de actuele DOM tijdens runtime.
Dat is precies het juiste verhaal voor deze workload. Selectors breken elk kwartaal; marketingteams van carriers rollen redesigns uit zonder hun eigen engineers in te lichten, laat staan hun scrapers. Een script op intentieniveau overleeft een Next.js-rewrite zolang de woorden op de pagina nog hetzelfde betekenen.
import { Stagehand } from "@browserbasehq/stagehand";
import { z } from "zod";
const stagehand = new Stagehand({ env: "BROWSERBASE" });
await stagehand.init();
await stagehand.page.goto("https://www.maersk.com/instant-quote");
await stagehand.page.act("dismiss the cookie banner");
await stagehand.page.act(
"search for a 40HC from Yantian to Rotterdam, sailing within 14 days"
);
const { rate } = await stagehand.page.extract({
instruction:
"extract the cheapest spot rate in USD for a 40HC, including the carrier surcharges line",
schema: z.object({
rate: z.object({
amount: z.number(),
currency: z.string(),
transitDays: z.number(),
sailingDate: z.string(),
}),
}),
});
De kosten zijn eerlijk: elke check kost je Browserbase-minuten en een paar duizend LLM-tokens voor de act/extract-calls. Over 4.800 wekelijkse checks tikt dat aan. De onderhoudskosten lopen de omgekeerde kant op — bijna niets in een normale week, en nog steeds bijna niets in de nacht dat Maersk hun rewrite uitrolt, omdat het model gewoon "de goedkoopste 40HC rate" opnieuw oplost tegen de nieuwe DOM.
De zelfgebouwde Playwright-loop met Claude tool use
De derde optie was er een die we eerder voor twee klanten hadden gebouwd: een Playwright-instance op onze eigen infrastructuur, aangestuurd door een Claude tool-use loop. De agent krijgt vier tools — screenshot, click_at(x, y), type(text), extract_region(box, schema) — en een system prompt die de taak uitlegt. Het model redeneert over de screenshot, kiest de volgende actie, en de loop stopt zodra het gestructureerde rate-object terugkomt.
Dit is dezelfde vorm als Anthropic uitbrengt in computer use, alleen smaller. Je geeft de act/extract-ergonomie van Stagehand op en schrijft de loop zelf; je wint de mogelijkheid om visie-gedreven stappen te combineren met goedkope, deterministische Playwright-stappen binnen dezelfde session. Voor de 30% van de pagina's per carrier waar de layout nooit verandert (het loginformulier, de routekiezer) roep je page.fill() rechtstreeks aan en verstook je geen tokens. Voor de 70% die elk kwartaal verandert, neemt het model over.
De echte kosten per route zijn geen LLM-tokens of browser-minuten. Het zijn de mediane minuten die een engineer kwijt is om de scraper te redden nadat een carrier om 23:00 op een zondag een layout-wijziging uitrolt.
Kosten per check, met het rekenwerk erbij
We hebben alle drie een testweek lang gebenchmarkt. De eenheid was één Maersk-, MSC- of Hapag-Lloyd-quote-check, van koude session-start tot gestructureerde regel weggeschreven. Alleen orde van grootte — jouw routes, retries en proxy-mix verschuiven deze getallen:
- Zelfgebouwde Playwright op een VPS van €40, geen LLM in de inner loop. Effectief gratis per check. Engineer-uren: heel weinig, totdat de layout verandert, dan tientallen in één weekend.
- Stagehand op Browserbase. Browser-minuten plus ruwweg 3-6k LLM-tokens per check voor act en extract. Bij 4.800 checks per week is de LLM-rekening alleen al niet triviaal, maar wel voorspelbaar. Engineer-uren per redesign: ongeveer nul.
- Claude tool-use loop op onze eigen Chromium. Vision-tokens domineren. We maten gemiddeld 12-18k input-tokens per check op volatiele pagina's, omdat elke loop-stap een screenshot meestuurt. Goedkoop op stabiele pagina's, duur op de volatiele, en geen browser-service-rekening.
Zodra je de kostprijs per check vermenigvuldigt met 4.800 wekelijkse checks, is de volgorde duidelijk: zelfgebouwd is het goedkoopst wanneer het werkt, Stagehand is het goedkoopst over het hele jaar zodra je redesigns meerekent, en de Claude tool-use loop is het duurst per check maar het meest flexibel als je werk half scrapen en half formulieren invullen is.
Captcha-weerbaarheid onder Turnstile
Alle drie de carriers zetten hun instant-quote portals achter Cloudflare Turnstile. Turnstile is lastiger te brute-forcen dan reCAPTCHA v3, omdat het browser-fingerprint, TLS-handshake en gedragssignalen correleert voordat er überhaupt een challenge verschijnt. Het juiste antwoord is zelden "los de challenge op" en bijna altijd "trigger 'm niet".
Het stealth-profiel van Browserbase haalde Maersk en Hapag-Lloyd in onze testweek op zo'n 94 van de 100 koude sessies door. MSC was lastiger; hun portal zit op een strengere zone en de koude-sessie-doorlaat lag rond 71 op 100. Stagehand erft welke browser het ook gebruikt, dus het antwoord is hetzelfde wanneer je 'm op Browserbase richt. De zelfgebouwde stack, met een headed Chromium op onze VPS en residential proxies via een derde partij, zat rond 82 op 100 over alle drie de carriers — slechter dan Browserbase, maar bij te stellen.
Wanneer de challenge wél afgaat doe je een retry vanuit een verse session met een ander exit-IP, of besteed je het uit aan een solver. Elke minuut die je in dat pijplijntje stopt, steek je niet in het werkelijke probleem van je klant. Het eerlijke oordeel: het anti-bot werk van Browserbase is z'n geld waard op de routes waar de carrier vijandig is.
De vraag of selectors zichzelf op zondag patchen
Tariefagents zijn een onderhoudsproduct, geen bouwproduct. De scraping-laag die je uitrolt gaat minstens twee carrier-redesigns overleven, en de mediane tijd tussen de ene Maersk front-end rewrite en de volgende is in onze ervaring zo'n zeven maanden. De enige vraag die op de lange termijn telt: wanneer de rewrite live gaat, hoe ziet maandagochtend er dan uit?
Voor de zelfgebouwde Playwright-agent met hardcoded selectors ziet maandagochtend eruit als een senior engineer met de carrier-site open in DevTools, die CSS-paden herschrijft en hoopt dat verder niets verschoven is. We zagen dit alleen in 2025 al vier keer gebeuren bij een eerdere klant. De gemiddelde reparatietijd was 90 minuten; de ergste was een paniek van drie uur op een zondagavond toen de nieuwe trackingpagina van Maersk tarieven uit een ander React-eiland begon te serveren.
Voor Stagehand bestaat maandagochtend uit het dashboard checken, nullen zien voor de eerste 40 minuten van zondagverkeer terwijl het LLM intenties tegen de nieuwe DOM oplost onder retry, en daarna komen de tarieven terug. Twee van de vier rewrites in 2025 die we meemaakten, hadden geen enkele codewijziging nodig. De andere twee vereisten één regel update in de natuurlijke-taalinstructie.
Voor de Claude tool-use loop lijkt het antwoord op dat van Stagehand, met één voorbehoud: visie-gedreven loops slagen soms in de verkeerde cel, vooral bij tabelopmaak waar twee tarieven bijna identiek lijken. We losten dat op met een sanity-check op schemaniveau — elk tarief onder $400 per 40HC ex-Azië wordt gemarkeerd voor menselijke review — maar het kostte ons twee weken vertrouwen in de autonome modus. Kies je voor een LLM-gedreven scraper, bouw die sanity-check dan vanaf dag één in. De faalmodus is geen crash; het is een zelfverzekerd, fout getal dat je prijsmodel binnenrolt.
Wat we uiteindelijk uitrolden
De uiteindelijke architectuur is saai, en dat is precies de juiste vorm voor een onderhoudsproduct. Browserbase draait de browsers. Stagehand handelt de twee carriers af (Maersk en Hapag-Lloyd) wiens portals elk kwartaal veranderen. Een plain Playwright-script — geen LLM in de loop — handelt de derde carrier, MSC, af, omdat we achter hun portal een JSON-endpoint vonden dat het rate-object rechtstreeks teruggeeft, en het zinloos is tokens te verstoken op een pagina die we feitelijk nooit renderen. Een nachtelijke cron diff't de gestructureerde tarieven in Postgres; Slack krijgt een ping bij de 7%-drempel.
De 4.800 wekelijkse checks kosten nu ongeveer evenveel als één middag van één junior-FTE vroeger, zonder mensen in de loop in een normale week en met één Slack-ping per kwartaal in de abnormale weken. De expediteur heeft die ruimte gebruikt om te bieden op routes die ze eerder niet konden monitoren, en dat is de enige ROI-berekening die in deze branche telt.
Toen we de tariefmonitorings-agent bouwden voor de Rotterdamse expediteur, kwamen we steeds weer uit op het inzicht dat de goedkoopste stack die is die niemand wakker maakt. Uiteindelijk losten we het op door per check iets meer aan Stagehand te betalen op de volatiele carriers en het te behandelen als een hybride probleem in plaats van een one-tool-fits-all antwoord. Hetzelfde patroon zien we onder de meeste AI-agents die we uitrollen: een managed substraat onderaan, een intentielaag waar de DOM volatiel is, deterministische code waar het oppervlak stabiel is. Wil je dezelfde audit van vijf minuten op je eigen scraper doen, open dan je incident-log en tel de zondagavond-entries van de afgelopen twaalf maanden. Dat getal zijn je echte kosten per route.
Kern
Behandel de scraping-laag als een onderhoudsproduct. De goedkoopste stack is die welke een engineer niet om 23:00 op een zondag wakker maakt.
FAQ
Welke stack hebben jullie uiteindelijk uitgerold voor de Rotterdamse expediteur?
Stagehand op Browserbase voor Maersk en Hapag-Lloyd, een plain Playwright-script tegen een reverse-engineerde JSON-endpoint voor MSC, met een Postgres-diff en een Slack-alert bij een spot-rate beweging van 7%.
Is Stagehand de extra tokens per check waard?
Op portals die meer dan één keer per jaar opnieuw ontworpen worden, ja. De tokenrekening is een vaste kostenpost; engineer-tijd op zondagavond niet, en dat is de regel die budgetten verwoest.
Komt Browserbase betrouwbaar langs Cloudflare Turnstile?
In onze testweek haalde het Maersk en Hapag-Lloyd vanaf een koude sessie ongeveer 94 keer op 100. MSC was strakker, rond 71 op 100. Je wilt nog steeds een retry-met-verse-sessie-policy.
Wat was de ergste faalmodus die jullie tegenkwamen?
Een LLM-gedreven loop die een zelfverzekerd, fout getal teruggaf uit de naastliggende tabelcel. We vingen het op met een ondergrens op schemaniveau voor de goedkoopste plausibele rate per route, niet met retries.