All projects

Georgia Inmate Scraper

Adaptive partitioning conquers a 150-record server cap to deliver every offender across 323 Georgia institutions.

The goal

Deliver a complete, one-row-per-charge dataset of every offender in the Georgia DOC Offender Query — despite the site's hard 150-record cap that silently drops ~85% of large-prison data.

The solution

A Python/Playwright scraper walking all 323 GA institutions with adaptive partitioning (subdivide by last-name first-letter until under 150), outputting 18 columns/charge to offenders.csv, with progress.json for resume and rebuild_progress.py for recovery.

Highlights

  • Guaranteed 100% coverage despite a hard 150-record cap
  • 50K+ charge rows across 323 institutions
  • Resumable + recoverable via progress.json + rebuild utility

The challenge

The site caps results at 150 per query, silently dropping ~85% of a large institution's data.

Adaptive partitioning — start with a broad query, subdivide by last-name first-letter when results hit ~150, recurse deeper if a partition is still capped. Every completed partition recorded in progress.json.

GDC uses JSP sessions and returns 500 errors to plain HTTP clients.

Playwright runs a real Chromium — handles session cookies automatically. Visible-browser mode further avoids headless detection.

A network hiccup after hours of scraping could lose everything.

progress.json records completed partitions after every batch; rebuild_progress.py walks the existing CSV to regenerate progress if it desyncs.

Clients wanted one row per charge, but GDC shows charges as a nested table under the inmate.

Parser duplicates inmate attributes (AIS, Name, Race, etc.) across each charge row so downstream CSV consumers don't need a join.

What was delivered

  • scraper.py runner with --test flag
  • collector.py, parser.py, browser.py, fetch_requests.py
  • progress.py + rebuild_progress.py recovery utilities
  • analyze.py pre-delivery quality report
  • config.py, log_setup.py
  • requirements.txt + Playwright Chromium install
  • README documenting workflow, schema and partitioning rationale

Results

50K+

Records

323

Institutions

100%

Coverage

Resumable

Recovery

What it taught me

  • Adaptive partitioning is the canonical technique for APIs/sites with a hard result cap
  • For JSP/ASP.NET government sites, Playwright is almost always faster than reverse-engineering the session
  • Store progress after every partition, not every row — tiny overhead, clean recovery