css_extractor or outputs goes through a model that converts types ("$69.00" becomes 69.0, "Yes" becomes true), checks required fields, and sends any row that doesn’t fit to a flagged list for review. Your pipeline stores clean, typed data every time.
Uses: Extract (extract=auto), css_extractor and outputs, with Pydantic or Zod
Ask your AI agent
With the Zenrows MCP server connected to Claude, Cursor or any MCP client, paste this prompt. Your agent picks the right Zenrows tools and settings on its own.Validate css_extractor rows
css_extractor returns each selector as a list of strings. Zip the lists into rows, then validate each row. The model trims names, turns "$1,299.00" into 1299.0, requires a positive price and an absolute URL.
Output (Python)
What a flagged row looks like
A row with an empty name, a price that isn’t a number and a relative URL fails all three checks. The error names each field, so a reviewer, or your agent, knows what to fix:Python (Pydantic)
Output (trimmed)
z.flattenError(result.error).fieldErrors gives the same answer as an object:
Output (Node.js)
Validate Extract output
extract=auto already returns numbers for prices and null for fields a page leaves empty. A schema pins the fields your pipeline relies on, fills defaults (a null or missing currency becomes "USD"), and drops every other field, so new fields from Extract never break your storage. This one requires a name, a positive price, a SKU and a product URL, and saves valid and flagged rows to separate files.
Output (Python)
Normalize table rows
Rows fromoutputs=tables are keyed by the column headings on the page, and every value is a string. Field aliases map headings such as "Product ID" to clean names, and Pydantic turns "Yes"/"No" into booleans on its own:
Python (Pydantic)
Output
Example output
The first rows ofproducts.json from the Extract run above. Every row is typed, carries the USD default, and keeps only the schema’s fields:
flagged.json holds [] for this page. Any row that fails the schema lands there with its errors, as shown in What a flagged row looks like.
Customize it
Troubleshooting
- Every row is flagged on
price: the price arrives in a format your parser doesn’t expect, such as"69,00 €". Print one raw value and adjust theparse_pricestep for that currency format. - A required field is
nullon many rows: the page leaves it empty, or loads it late. Make the fieldOptionalwith a default, or fetch withjs_render=trueandwait_forso it renders first. - URLs fail as relative: the selector returned paths such as
/product/blue-shirt. Join them with the page URL (urljoinin Python,new URL(path, page)in Node.js) before validation. - Names and prices don’t line up: one card has no price, so the lists differ in length. Use
extract=auto, which returns one object per item, or parse one card at a time as in Extract data with CSS selectors.
Related recipes
Get structured JSON from any page
Get product, listing and article fields as JSON.
Turn search and listing pages into JSON
Collect every item across every page into one dataset.
Pull tables and links from any page
Save every table on a page as CSV.
Extract data with CSS selectors
Pick exact elements with css_extractor.