Web Scraping

  1. Home
  2. Services
  3. Web Scraping
Data extraction & anti-bot engineering

The selector breaks. The pipeline keeps going.

A scraper that returns a clean 200 on an empty page looks exactly like one that works. We make the request look like the browser it claims to be, check every row before it lands, and watch the row count instead of the uptime graph.

  • Runs on servers you control
  • Priced per scraper, never per row
scrape.run retailer.example
  1. startfetching 240 pages
  2. ok240 fetched, 0 errors
  3. okparse complete
  4. writerows written: 0
  5. exit 0run succeeded

A 200 response and an empty page. The price moved from a class to a data-price attribute. The selector matched nothing, the job wrote a null, and the dashboard stayed green.

The problem

A clean response, and an empty page

Most scraper problems are loud. A connection drops, a proxy dies, someone sees an error in Slack. Those are the cheap ones.

What the run reported

All 240 product pages came back fine. No timeouts. No proxy errors. Nothing retried.

Finished in the usual time. Exit code 0.

What it actually wrote

The shop had moved its price into a different bit of code. Our rule was still looking in the old place.

It didn't crash. It found nothing useful and wrote a blank into every row.

Pulling one page with a script? A day's work in Python. Getting from there to something your business can rely on is a different job. It isn't about writing smarter rules. It's about what happens to the data the day the site changes.

The question

It isn't did the job run. It's did it save what was really on the page today.

  • FetchFingerprint matched to the declared browser
  • ExtractSelectors with fallback anchors
  • ValidateSchema check before anything is written
  • DeliverWarehouse, or a dead-letter table

Where most scrapers skip a step

Fetch and extract are the fun bits, and that's what every tutorial covers. Check and deliver are where a quiet failure gets caught. They're also the two that get dropped when a project runs late.

What we pin down, in this order

  • Target Which sites, which pages, and how deep the paging goes.
  • Shape The fields you need, what type each one is, and which are allowed to be empty.
  • How often Daily, hourly, weekly. And what a stale record actually costs you when it slips.
  • Volume Records per run. This sets your proxy bill more than anything else does.
  • Delivery A table in your warehouse, a file in S3, or we post it to an endpoint of yours.
What we build

Built for your target, priced to match

Extractors written for your site

Written against the real markup, the real paging, and whatever checks the site runs today. Not a template pointed at a URL.

Getting past the blocks

We match the fingerprint and rotate proxies, but only as far as the site actually pushes back. No point paying for defences nobody is using.

Every row gets checked

Each record is checked before it reaches your warehouse. Anything that fails goes to a side table with the reason attached.

Alerts that mean something

Row counts per run. Blank rates per column. And an alert when a run writes nothing on a day that usually writes thousands.

Architecture

Where a record goes after it leaves the page

Sources on the left. Your warehouse on the right. In between sits the part that decides whether today's run can be trusted. Nothing reaches storage without passing the check.

  • Fetch Python, with the fingerprint matched to a real browser. Routed through home-user proxies when the site warrants it.
  • Render Playwright in a container. The browser version is pinned, so a Chrome update can't change how your scraper behaves overnight.
  • Check Every record gets checked against a schema. Failures land in a side table with the error attached, not in a log nobody reads.
  • Deliver Into your warehouse. Plus row counts and blank rates written per run, so an empty result raises an alarm.
Isometric diagram of web sources and APIs feeding a data ingestion and processing node, which writes to an enterprise data warehouse
Anti-bot

What actually blocks a scraper

Cheapest checks first, most expensive last. That's the order sites tend to add them in.

Mechanism What the target is checking What we do about it
TLS / JA3 fingerprint Whether the handshake matches the browser the User-Agent claims. Make the handshake match the browser we say we are. It's the cheapest check a site can run, and the one most scrapers fail first.
Canvas and WebGL Whether rendering output looks like real hardware. Vary the output a little, but stay inside the range a real device would give. Perfectly identical is as suspicious as obviously fake.
Rate limiting and IP reputation Request pacing, and whether the address belongs to a data centre. Stay under the limit and route through home-user addresses. This is where your monthly bill actually goes.
Interactive challenge Whether a human is present. We don't solve these. We go looking for a paid feed or an official API, and tell you straight if neither exists.
Validation

Bad data costs more than no data

No data is an outage. Everyone notices. Bad data is worse, because someone acts on it. It sets a price, triggers a restock, or ends up in a report that goes to a board. So every record gets checked before it goes anywhere.

A row that fails lands in a side table with the exact reason attached. Not a catch-all log nobody opens. Once the extractor is fixed, those rows run again through the same code. No special migration script.

strict_validation.py
from pydantic import BaseModel, HttpUrl, field_validator


class ExtractedProduct(BaseModel):
    sku:         str
    price:       float
    in_stock:    bool
    product_url: HttpUrl

    @field_validator("price")
    def sane_price(cls, v):
        # a scraped 0.0 is a parse failure,
        # not a free product
        if v <= 0:
            raise ValueError("non-positive price")
        return v

# A field that fails validation routes the whole
# row to a dead-letter table with the error
# attached, and never reaches the warehouse.
Lessons

Mistakes we keep running into

Across sites that have nothing else in common.

Same IP for logging in and scraping

Hit a hundred pages a minute from the address you logged in with, and it's the account that gets flagged, not just the request. Keep the two separate.

Rules tied to one class name

A class like .ProductCard__price is a convenience, not a promise. A build tool generated it and will change it without telling anyone. Anchor on labels and visible text where you can.

Passwords sitting in the code

A login password typed straight into a script is a problem with a delay on it. Secret managers exist and cost close to nothing.

Ignoring robots.txt with no reason written down

Going past it without a business reason on record is risk with no upside. We write down why, so someone can review the call later.

Where the law sits, honestly

Most of what you'll read online about scraping law is American. That isn't what binds a studio billing in rupees, or most of its clients. Indian law is the relevant one here, and this section is waiting on someone qualified to write it. Until then, here's our honest position. We work with information anyone can see. We keep our request rate low enough that we're not slowing the site down. We don't touch personal data without a lawful reason. Anything near the line, your lawyer should read a written description of what the pipeline does, and we'll write one.

Due diligence

Warning signs in any scraping vendor, us included

  • "100% success guarantee" Site defences change with no warning. Anyone promising success is either lying, or only scraping sites that don't fight back.
  • "Proprietary AI bypasses" Getting through is about fingerprints and proxy quality. There's no clever model doing it. If they can't explain what a fingerprint mismatch is, they're reselling someone else's tool.
  • Flat pay-per-row pricing A site that fights back costs far more in proxy fees than a plain one. One flat price across both means corners get cut on the expensive job.
  • No answer on what happens when the layout changes It will change. Ask what spots it, how quickly, and who pays for the fix.
Engagement

Four phases

  1. 1

    We look at the target first

    We check what the site does to block scrapers and give you a straight read, including the monthly proxy cost. If it's not worth doing, you find out here.

    Week 1
  2. 2

    A small proof on your real target

    We pull and check real records from the actual site, so you can look at the output before we price the full build.

    Week 2
  3. 3

    The full build

    Retries, speed limits, row checks, a side table for failures, and delivery into your warehouse.

    Weeks 3 to 4
  4. 4

    Support after that, if you want it

    We watch the row counts and patch the extractor when the layout moves. Optional. The handover exists so saying no is a real choice.

Pricing

We price per pipeline, never per row

Per-row pricing makes a vendor want your volume up and their proxy spend down. Both of those pull against what you want.

Maintenance

We watch the numbers and fix things when the site changes.

₹3k to ₹90k

Per month, by pipeline count and target volatility.

  • Alerts on row counts and blank fields
  • We patch it when the layout moves
  • Proxies and hosting billed at cost
Discuss support
FAQ

Frequently asked questions

Two of these have answers a vendor would rather not give. Those are the two worth reading.

Is web scraping legal?

Depends on the data, the country, and the terms attached to the source. We stick to information anyone can see, keep our request rate low enough that we aren't slowing the site down, and we don't touch personal data without a lawful reason.

We're engineers, not your lawyers. For anything near the line, get your counsel to look. We'll write down exactly what the pipeline does so they have something real to read.

Do you solve CAPTCHAs?

No. When a site puts up a challenge screen, that's a deliberate statement about automated access. We go looking for a paid feed or an official API instead.

If neither exists, we'll tell you the project isn't worth doing rather than sell you something that breaks in a month.

What happens when the site changes its layout?

The row-count and schema checks catch it, usually before you do. A run that writes far fewer rows than yesterday, or one whose records start failing the check, raises an alarm instead of passing quietly.

On a maintenance retainer we patch the extractor and re-run the missed window through the same code. The gap fills in instead of staying a hole in your data.

Why not use Octoparse, Apify or a similar platform?

For a pilot, or a site that doesn't fight back, use them. They'll get you a first result faster than we will, and we'll say so.

Two things change at scale. Their servers carry a fingerprint protected sites have already seen thousands of times. And the per-credit bill keeps growing while each record stays worth the same to you.

Can you scrape a site that needs a login?

Sometimes, and it deserves a longer chat than a yes. Logging in usually means someone accepted terms, and it's the account that carries the consequence, not the IP address.

Where it's legitimate, like your own data sitting inside a supplier portal, we use a limited account, keep the password in a secret manager, and pace requests well below anything that looks automated.

Who owns the pipeline and the data?

You do, both. The code lands in your repo, the pipeline runs on your infrastructure, and the data goes to your warehouse. There's no platform of ours sitting in the middle that stops working the day you stop paying us.

Send us the target

A URL, and a line about the records you need. We'll tell you what's guarding it, roughly what the proxies will cost each month, and whether it's worth building at all.

Related: workflow automation and Chrome extensions