There are two fundamentally different ways to get structured data off a web page. You can point at where the data lives with a CSS selector, or you can describe what the data is with a schema and let extraction find it. Neither is universally better. They fail in different ways and shine in different situations, so it is worth understanding the trade.
The selector approach
You inspect the page, find the element, and write a path to it:
const price = document.querySelector(".product__price-now").textContent;
This is fast, free, and completely deterministic. It runs in milliseconds and there is no model in the loop to second-guess. As long as the page keeps that exact class on that exact element, it works forever.
The catch is in that “as long as.” Selectors are coupled to the page’s structure, and structure is the thing site owners change constantly. A redesign, an A/B test, a framework upgrade, or a single renamed class quietly breaks your scraper, and you find out from the gap in your data rather than from an error.
The schema approach
You describe the data and let extraction map the page onto it:
const { price } = await client.extract({
url,
schema: { price: z.number() },
});
This couples you to the meaning of the data, not its position. When the site moves the price into a new container, “the price” is still the price, so the extraction survives the redesign. You also get type guarantees for free: price comes back as a number, validated, or you get a clean null if it genuinely is not there.
The cost is that there is more machinery involved. A request renders the page and runs an extraction step, so it is slower and not free per call, and it is the right tool for data that matters, not for scraping a million trivial pages where pennies add up.
Side by side
| CSS selectors | JSON schema | |
|---|---|---|
| Couples to | Page layout | Data meaning |
| Survives redesigns | No | Yes |
| Speed | Milliseconds | Seconds |
| Cost per page | Free | Per request |
| Type safety | You parse it yourself | Validated at the boundary |
| Best for | One stable page, fast | Many pages, changing sites |
How to choose
Reach for selectors when the page is stable, you control it or trust it not to change, and you need raw speed on high volume. Reach for a schema when you are extracting the same shape across many pages, the sites redesign on their own schedule, or you need typed, validated output you can drop straight into a database.
Most scraping jobs that cause ongoing pain are the second kind. That is the maintenance cost selectors hide until Wednesday.
Try it
See what describing the data instead of the DOM feels like. Run the live demo, or start free.