Most scraping jobs are wasteful in the same way. Every night they fetch a few million pages, and on a typical day only a small fraction of those pages differ from yesterday’s copy. You pay for the proxies, the browsers and the bandwidth on all of them, and you take on the block risk that comes with all that traffic.
Incremental crawling is the obvious fix, and it’s also easy to get subtly wrong. This post walks through the three pieces that make it work: deciding what changed, keeping duplicates out, and resolving records that describe the same real-world thing. It also covers where each piece tends to fail.
What incremental crawling buys you, and what it costs
Incremental scraping means collecting only data that is new or changed since the last run, instead of re-collecting everything. Browse AI’s glossary lists the benefits you’d expect: less bandwidth and processing, faster refreshes, lower proxy and cloud spend, and fewer requests to a target site, which makes you less likely to be flagged by anti-bot systems.
The same page is honest about the costs, and they’re worth reading as a checklist:
- You need reliable state between runs (identifiers, timestamps, hashes). Lose it and you’re back to a full re-scrape.
- The first run is a full crawl, so there are no savings yet.
- Some sites have no trustworthy modification date, and others bump it for trivial edits.
- Deletions are hard. A missing item might be gone, or it might just be hidden for an hour.
- A redesign that changes how items are identified can make every existing item look new, which creates duplicates.
That last point is the one that bites in production. Incremental crawling moves your risk from "too expensive" to "quietly wrong", so the rest of the design should assume the change signal will sometimes lie.
Change detection: don’t trust the site’s timestamps
If a site gives you a reliable Last-Modified header, an ETag or a sitemap lastmod, use them first. They’re free. But treat them as a hint, not a fact, for the reason above: some sites update them for trivial edits, and others don’t have them at all.
The dependable fallback is to hash the part of the record you care about. Hash the extracted fields, not the raw HTML. Raw HTML changes every time a session token, a rotating ad slot or a "viewed 14 times" counter changes, and you’ll re-process pages that haven’t meaningfully changed.
import hashlib, json TRACKED = ("title", "price", "currency", "availability", "seller") def content_hash(record: dict) -> str: # Hash only the fields that matter, in a stable order. payload = {k: record.get(k) for k in TRACKED} blob = json.dumps(payload, sort_keys=True, ensure_ascii=False) return hashlib.sha256(blob.encode("utf-8")).hexdigest() def classify(record: dict, state: dict) -> str: key = record["canonical_id"] h = content_hash(record) if key not in state: return "new" return "unchanged" if state[key]["hash"] == h else "changed" Browse AI points out a gap in the simple version of this: scraping only listing pages efficiently catches additions and removals, but not changes inside existing items. Their suggested fix is to re-scrape only records whose listing-level fields differ from what you’ve stored. In practice that means a two-tier crawl. A cheap listing crawl runs often, and a deeper detail crawl runs only for records the listing flagged, plus a slow background sample to catch drift the listing never shows.
For deletions, don’t delete on the first miss. Mark a record missing and only retire it after it’s been absent for N consecutive crawls. The right N depends on how flaky the source is, so tune it per site.
Deduplication: exact matching isn’t enough
Duplicates arrive from several directions. ScrapeHero’s guide to cleaning scraped data lists the usual ones: URLs that change between crawls, products listed in multiple categories, pagination repeats and session parameters. It says duplicate rates in retail monitoring "frequently exceed 25%" when marketplaces syndicate identical listings. That figure comes from a vendor with no primary source given, so read it as "can be large" rather than as a benchmark.
The article also argues that exact-match deduplication is no longer sufficient, and suggests hash fingerprints, similarity scoring, SKU matching, fuzzy title comparison, image similarity and canonical URL mapping. I’d order those by cost:
- Canonicalize URLs first. Strip session and tracking parameters, normalize case and trailing slashes, and follow
rel=canonicalwhere it’s trustworthy. This alone removes a lot of noise and costs almost nothing. - Match on strong identifiers. SKU, GTIN, a listing ID from the source. If you have one, use it.
- Fall back to fuzzy matching on normalized title, brand and key attributes, but only for records that survive steps 1 and 2.
The same article makes a point that’s easy to skip: standardize before you load. Currencies, date formats, units, encodings and null representations all need one convention, or you’ll compare "USD 129" with "129 dollars" and conclude they’re different prices. Do this at ingestion, before the data reaches the warehouse, using whatever you already run (dbt and Spark are the examples they name).
Entity resolution: when two records are the same thing
Deduplication removes copies of the same record. Entity resolution answers a harder question: are these two different records about the same product, company or listing?
Take two marketplaces selling what looks like the same item. One calls it "Acme TrailRunner 2 Men’s 10", the other "TrailRunner II – Acme – US10". No shared SKU, different titles, different images. A reasonable approach:
- Block first, so you only compare plausible pairs (same brand, same category), not every record against every other.
- Score each pair on several signals: normalized title similarity, matching attributes, price within a tolerance, image similarity.
- Decide with thresholds: auto-merge above one, send to review between two, keep separate below.
- Keep the link, not just the merge. Store a stable entity ID that points to the source records. When your matching logic improves, you can re-resolve without re-crawling.
I’d push hard on that last item. Teams that overwrite source records with the merged result can’t undo a bad merge. Teams that store links can.
Putting it together, and testing it
A workable pipeline looks like this: a cheap listing crawl feeds a change classifier; only new and changed records go to the detail crawl; extracted fields get standardized; canonicalization and dedup run next; then entity resolution assigns or updates the entity ID; and only validated records are published. ScrapeHero recommends validating schema, ranges and freshness and quarantining bad records instead of publishing them. Incremental ingestion into the warehouse also reduces compute cost and helps keep pipelines stable.
Because the failure modes here are silent, test for them directly:
- Redesign drill. Replay a crawl where the ID scheme changed. Do existing items come out as
new? If so, your canonical ID depends too much on the URL. - Periodic full crawl. Run a full re-scrape on a small slice every week or so and diff it against the incremental state. The gap is your drift.
- Churn alarm. If the share of
newrecords jumps suddenly, suspect an identity break before you suspect a real catalog change. - State backup. Treat the state store like production data. Browse AI notes that lost or corrupted state forces a full re-scrape, which is the expensive outcome you were trying to avoid.
What to take away
Incremental crawling saves real money, but only if the change signal, the identity scheme and the matching logic are treated as first-class parts of the pipeline. Hash extracted fields instead of HTML, canonicalize before you fuzzy-match, retire deletions slowly, and store links between source records and entities so you can fix mistakes later.
One limit on all of this: the numbers in the public sources are mostly qualitative or vendor-supplied. Measure duplicate rate and change rate on your own targets before you size a project around anyone’s claimed percentages.
Sources
- Incremental scraping, Browse AI glossary
- 6 Steps to Clean & Ingest Scraped Data for Data Warehouses, ScrapeHero
- Web Scraping Techniques 2026, Import.io (scheduled refresh frequency should match business needs)
