🏠 Finding underpriced London properties

I was flat-hunting in London and, like anyone who’s done this, found the process painful. Rightmove gives you thousands of listings but no way to tell which ones are actually good value. So I built a tool that scrapes Rightmove and uses statistical analysis to surface underpriced properties.

How it works

The core idea is simple: compare each listing’s price-per-square-foot against similar properties in the same area. Properties that are cheap relative to their peers are worth investigating.

The app groups properties by postcode sector, bedroom count, and bathroom count. For each group, it computes the mean and standard deviation of price-per-square-foot. Each property then gets a deviation percentage (how far it is from its group average) and a z-score (how many standard deviations from the mean).

The results are shown on an interactive Leaflet map with a heatmap overlay — “hot” areas literally mean good deals (negative price deviation). Markers are colour-coded on a diverging scale: blues for underpriced, oranges/reds for overpriced. There’s also a sortable table alongside the map.

The scraper

The Rightmove parser is probably the most robust part. It tries three data sources per listing page in priority order:

  1. JSON-LD structured data
  2. Embedded page JSON (Rightmove stores a lot in JavaScript objects on the page)
  3. HTML fallback with BeautifulSoup

It handles multiple URL formats (modern /properties/{id} and legacy patterns), deals with 403/404/410 responses gracefully, and automatically converts square metres to square feet.

Practical features

Beyond the analysis, the app has a few features that made it genuinely useful during my flat hunt:

  • Bulk URL ingestion — paste multiple Rightmove URLs and the app scrapes them all, deduplicating by canonical URL
  • Offer price tracking — it prefers an offer_price (what you actually offered) over the listed_price, which is useful for tracking negotiated prices
  • CRUD management page for manually adding off-market or privately sourced listings
  • Minimum group size filter (default 3) to prevent unreliable stats from tiny samples

Stack

Flask, SQLite, BeautifulSoup for scraping, Leaflet.js with the leaflet.heat plugin for mapping. No npm, no bundler, no frontend framework — just vanilla JS. The whole backend is a single 440-line Python file.

Deployed on Render with optional HTTP Basic Auth for sharing with friends who were also flat-hunting.