← all builds

Headless Delivery Check

playwrightscrapinglogistics
Download .md

A sanitised reference implementation. Drop it into Claude Code, point it at your own tracking page, adapt the selectors. Generic on purpose. No carrier specifics, no real tracking numbers.

The problem

Plenty of the status you need has no API. It exists only on a page that renders in the browser with JavaScript, sometimes behind a bot check. You cannot fetch it with a plain HTTP request; the HTML that arrives is an empty shell and the real content paints later.

This layer drives a real browser headlessly, lets the page render, reads the status, and writes it back to wherever you track things. No API required.

tracking refs ──> [headless browser: load, wait for render] ──> read status ──> write back
                         one fresh session per ref                       to your sheet/DB

Minimal reference implementation

# delivery_check.py, reference only. Swap in your own URL pattern + selectors + store.
from playwright.sync_api import sync_playwright

def check_one(ref, base_url, status_selector):
    """One ref, one fresh context. Return the rendered status text or None."""
    with sync_playwright() as p:
        browser = p.chromium.launch(headless=True)
        ctx = browser.new_context()        # fresh context: no cached state bleeds across refs
        page = ctx.new_page()
        try:
            page.goto(base_url.format(ref=ref), wait_until="domcontentloaded")
            page.wait_for_selector(status_selector, timeout=15000)   # wait for the JS paint
            return page.inner_text(status_selector).strip()
        except Exception:
            return None                     # treat unreadable as unknown, never as delivered
        finally:
            browser.close()                 # close fully so the next ref starts clean

def run(refs, base_url, status_selector, write_back):
    for ref in refs:
        status = check_one(ref, base_url, status_selector)
        write_back(ref, status)             # update your sheet / DB row for this ref

Why it works

Adapt it

Built for my own logistics flow, runs every couple of days; this is the generalised version. Take it, point it at your own tracking page, tell me where it breaks.