← back to live progress · see real examples →

How this scrape works

Pulling every product (name, price, stock, description, image, category) from all 7 country sites of Smyths Toys — 58,650 products across UK, IE, DE, AT, CH, FR, NL — for free, from one laptop running a small fleet of browser lanes.

The obstacle

Smyths sits behind Imperva / Distil Advanced Bot Protection — a "bouncer" that makes every visitor's browser solve a hidden JavaScript puzzle before it serves any page. Ordinary scraping tools have no JavaScript engine, so they get a "Request unsuccessful" wall instead of data. Even the sitemap (the list of products) is behind it.

What we tried — and what happened

ApproachResultWhy
plain curl / Python requestsblockedNo JS engine — can't solve the challenge.
Firecrawl (cloud, stealth mode)challengedManaged scraper still flagged by Imperva.
Playwright — headless ChromeblockedDistil specifically detects headless browsers.
Playwright — headed real ChromeblockedImperva fingerprints the automation control layer itself.
Playwright + stealth pluginblockedStealth hides some tells, not the core CDP leak.
patchright (patched Chrome driver) + real Chromeworks ✓Removes the exact automation signal Distil hunts for.
The breakthrough: patchright — a patched browser driver that strips the leftover "I'm a robot" signals from Chrome. It's the only free thing that walks through Imperva's front door.

How it actually pulls the data

  1. Discovery. A real browser opens each country's sitemap (solving the challenge automatically) and collects every product URL — the pattern is /p/<id>. That's how we know there are 58,650.
  2. Extraction. Each product page already contains a clean JSON-LD block (structured data search engines read) with name, price, availability, brand, image, description. We fetch it inside the warmed browser session and parse it — no need to render the whole page.
  3. Storage. One line per product appended to a file, so the run is fully resumable — crash, reboot, or block, it picks up where it left off.

Staying under the radar

Imperva counts requests per IP, so every lane (see the fleet, below) deliberately goes slow and human-like:

The walls we hit live

EventFix
Tripped a per-IP rate limit (~450 requests in 25 min) → got challengedStop, cool down, resume at a gentler pace.
Tried NordVPN to spread loadAutomated access on the VPN IP is blocked (datacenter IPs are pre-flagged) — but once a human solves one CAPTCHA on it, that VPN session scrapes fine. The current run is on a NordVPN IP.
A CAPTCHA / picture puzzle appearedA free vision-AI solver now clears it automatically — reads the legend, clicks the doodles in order, submits; on repeated failure the lane just rotates to a fresh exit. No human needed (see below).
Single lane too slow (~days)Scaled to a parallel fleet — up to 9 browsers, each on a different NordVPN exit IP (see below).
Datacenter IPs degradeEach Nord exit worked in bursts (~100 products) then re-challenged; exits died outright. Datacenter IPs are pre-flagged by Imperva, so the whole VPN fleet kept burning out — throughput collapsed to ~10/min.
NordVPN fleet burned outSwitched the whole fleet to Tor. Tor's exit nodes look like ordinary clients, so most pass Imperva for free — and there are thousands of them. This is what the run uses now.
Key lesson: the real bottleneck is a cleared session — every IP needs to get past the wall once. patchright clears it automatically over a good Tor exit, so speed comes from running many cleared sessions in parallel (next section), rotating away from any exit that gets blocked.

Going parallel: the Tor browser fleet

One lane would take days, so we run several browsers at once — each routed through a different Tor exit node (a different IP). Because Imperva's rate limit is per-IP, each lane gets its own budget, multiplying throughput. Tor gives a near-unlimited, free supply of IPs.

Honest trade-off: Tor is slower per request than a direct connection, and the exit pool quality swings over the day — throughput oscillates (roughly 15–80 products/min) as good exits come and go. But it's free and unlimited. The genuinely fast, steady path is paid residential proxies (trusted home IPs) — hours instead of overnight, for a few dollars.

When the wall escalates: the picture puzzle

On flagged sessions Imperva escalates from the invisible JS challenge to a visible "select in this order" puzzle — childlike doodles drawn over a photo that you click in a given order. The run now solves these itself, for free, instead of waiting for a person:

This replaced the old "a human solves one in the window" step, so the run is now fully unattended. Roughly 75–85% per puzzle — a booster layered on top of IP rotation, not a replacement for it.

Timeline

Every block, fix, and milestone of this run — with timestamps — lives on its own page: see the full timeline →

Coverage: why it says 58,650 / 58,650 *

The headline counter reads 58,650 / 58,650 with an asterisk. Here is exactly what that means and why it is honest.

What was captured: 58,605 individual product pages — name, price, stock, brand, image, description — across all 7 country stores. That is the real, deliverable dataset.

How coverage was measured: the list of every scraped product URL was diffed against the full list of 58,650 target URLs discovered from the sitemaps. That left 51 URLs uncaptured. Each of the 51 was then opened by hand in a normal browser to see what it actually was.

Of the 51 uncaptured URLsWhat they are
44not real productsClicking them redirects to a category or home page: the product was pulled from sale, so the old /p/<id> URL only lingers in the sitemap. Nothing to scrape.
7real, reachable productsGenuine pages that had simply hit a bot-wall on their final attempt. A focused fresh-IP retry recovered 6 of the 7.
1 — the 7thAn “assortment” / multi-pack listing (Mini Brands, IE) that loads fine but contains no machine-readable product data (no JSON-LD), so there is nothing to extract automatically.

So after recovery, 45 URLs remain uncaptured (44 redirecting + 1 unreadable assortment), and all 45 are non-products. Coverage of real, in-stock, buyable items is therefore effectively 100% — which is what the asterisked counter reflects.

Why those 44 disappeared — the verifiable part

Most of the 44 are roughly 20 unique products repeated across the 7 country sites. Two groups have a concrete, public explanation; the rest are ordinary retail churn.

The deepest version of this — every one of the 45 URLs, categorised, with all sources — is in the coverage report (PDF) →. It’s shareable on its own.

A note on privacy

This dashboard publishes only progress numbers — counts, rate, ETA, and the event timeline. The actual scraped product data (names, prices, descriptions) is never put on this site; it stays in a local file. What you see here is the run's metadata, not the catalogue itself.

← back to live progress