QUESTZ

stopping a scraper before it writes a CSV from a page that changed shape.

A scraper that writes a CSV from a page that quietly changed shape has not failed, it has lied. This one checks the shape first and fails closed when it cannot.

Tests
272
Python
3.11 to 3.14
Release
v0.1.0
Licence
MIT

The demo, as it really ran

$ questz canary check --contract examples/contracts/items.json --html questz/testsite/v2/items.html; echo "exit $?"

questz canary: DRIFT
contract: items v1
target:   questz/testsite/v2/items.html

findings (4):
  CRITICAL  missing_selector   tr[data-testid="item-row"] td[data-testid="price"]
                               expected 5 to 50, found 0
  CRITICAL  missing_selector   tr[data-testid="item-row"] td[data-testid="stock"]
                               expected 5 to 50, found 0
  MAJOR     structure_removed
                               expected every baseline node still present, found 4 gone
                               th[data-testid=col-price], th[data-testid=col-stock], td[data-testid=price], td[data-testid=stock]
  WARNING   structure_added
                               expected no nodes outside the baseline, found 7 new
                               th[data-testid=col-sku], th[data-testid=col-cost], td[data-testid=sku], td[data-testid=cost], and 3 more

signature: baseline 6c9c2add1e0b observed defb7f9573c6
counts:    tr[data-testid=item-row]=12, li[data-testid=stock]=12

structure diff:
  --- baseline
  +++ observed
  @@ -7,4 +7,4 @@
   4|th|data-testid=col-name
  -4|th|data-testid=col-price
  -4|th|data-testid=col-stock
  +4|th|data-testid=col-sku
  +4|th|data-testid=col-cost
   2|tbody|
  @@ -12,3 +12,6 @@
   4|td|data-testid=name
  -4|td|data-testid=price
  -4|td|data-testid=stock
  +4|td|data-testid=sku
  +4|td|data-testid=cost
  +1|div|data-testid=stock-panel
  +2|ul|
  +3|li|data-testid=stock|x*
exit 1

Output captured on 2026-08-24. It is committed to the repository and a test fails when it stops matching a live run, so this page cannot quietly drift from the code it describes.

Read it

The rest of the toolset