All side projects

Car market

I wanted to know what a car really costs in Tunisia, and how that compares with Europe. I couldn’t find those numbers side by side anywhere, so I scrape them every day.

Sources
3 sites, 9annas.tn, AutoScout24, automobile.tn
Runs
GitHub Actions every day, plus a Raspberry Pi twice a month
Started
January 2026

Latest scrape

Snapshot from

  • 9annas.tn Used cars in Tunisia 12,503 listings Every day ·
  • AutoScout24 Cars up to 8 years old in Germany, France, Italy, and Belgium 2,287 listings Every day ·
  • automobile.tn New-car list prices in Tunisia 565 trims Twice a month ·
See every chart and listing

What it shows

Four views, in English or French.

Open the dashboard
  • Used cars in Tunisia

    Asking prices, mileage, fuel, gearbox, and governorate, with the most listed models next to their European price.

  • New cars in Tunisia

    Official list prices for every trim, by brand, budget, fuel, and body type.

  • Europe

    Median prices in four countries over time, and every listing linked to AutoScout24.

  • Catalog history

    Which new-car prices went up or down between two scrapes, and which trims were added or removed.

How it works

Scrapers, one build step, and static files. No server.

  1. Scrape

    GitHub Actions runs at 02:00 UTC every day for 9annas.tn and AutoScout24. A Raspberry Pi at home scrapes automobile.tn on the 1st and 15th of the month.

  2. Clean

    Listings with an impossible price, year, or mileage are dropped, duplicates are removed, and free-text fuel types are mapped to a few known values.

  3. Build

    A Python step writes a small summary file, a daily history with one point per day, and column-packed listing tables.

  4. Publish

    The workflow commits the files, and GitHub Pages serves them as static JSON.

  5. Show

    This site reads that JSON straight from GitHub Pages. There is no server and no database.

What was hard

The parts that took more than one try.

  • automobile.tn blocks GitHub

    Since May 2026 the site returns 403 to GitHub runner IPs. The scraper moved to a Raspberry Pi with a polite rate limit, and the daily workflow fails when the new-car data is more than 18 days old, so a broken cron can’t hide.

  • 9annas.tn has no page numbers

    Its API pages with a cursor that encodes the last listing’s id and timestamp as letters. I decoded the scheme, so the scraper walks every page and stops at the first listing it already has.

  • Listings are written by people

    Some ask 100,000,000 DT, some are from the year 8 or 9999. The dashboard keeps prices between 3,000 and 400,000 DT and plausible years, and it shows how many listings it left out.

  • AutoScout24 fights bots

    Each listing is read from JSON-LD first, then from the spec table in the country’s language, then with a regex. Requests run five at a time with jittered delays and back off on 429 and 403.

  • The raw data is 29 MB

    The dashboard first loads an 80 KB summary. The full listing tables load only when you open them.

Built with

Python for the scrapers, plain JavaScript and SVG for the charts.

Read the code on GitHub
  • Python
  • GitHub Actions
  • Raspberry Pi
  • GitHub Pages
  • JavaScript

The scrapers use httpx and BeautifulSoup, with Playwright as a fallback for AutoScout24. The charts are a small SVG kit with no chart library.