The Data Types Web Scraping Actually Returns
Scrapers don't return clean tables. They return a grab bag of formats that each break in their own special way.
Nested JSON is the most common. API responses, JavaScript-rendered pages, and single-page apps all serve deeply nested objects where the field you need is 4 levels deep inside an array of dicts. The schema changes without warning because the site redesigned a component, and now your product.offers.price path returns None on 300 of 10,000 pages.
JSON-LD is the best-case scenario when it exists. It's structured data embedded in <script type="application/ld+json"> tags, and it often contains exactly what you need without CSS selectors. But JSON-LD on real websites is frequently malformed: unquoted keys, invalid syntax, missing critical fields. The Dolce & Gabbana website ships a "highly detailed" product schema that's missing the price field entirely.
Date strings are ambiguous by default. "01/02/2026" is January 2nd in the US and February 1st in Europe. Without ISO 8601 normalization, you get off-by-one errors that compound silently across millions of records.
Currency strings mix symbols, commas, periods, and spaces in ways that break naive parsing. "$1,000.50" and "€1 000,50" represent the same concept but require completely different handling.
HTML fragments sneak into text fields: encoded entities, stray tags, broken Unicode. And encoding issues (mojibake, mixed UTF-8 and Latin-1) must be caught at scrape time. Converting garbled text downstream wastes pipeline cycles and creates persistent problems that leak into storage.
The worst part isn't the failures. It's the silent correctness drift. A page loads, a CSS selector matches, fields populate, the row is stored. The system reports success. But only partial data was captured, and downstream analytics run on incomplete records.