← Blog

Renting a house with a local LLM as the filter

DEENESRUUK

Looking for a house to rent is a part-time job: refresh the listing sites, skim the same dead-end ads, miss the good one because it appeared at 2 a.m. So I built a watcher that does the skimming for me. It lives at home.khanoff.com.

Two sources, one pipeline

It pulls fresh listings near Kyiv from two sources and normalises them into a single stream:

  • OLX.ua — the search results page is parsed with BeautifulSoup, and the full description is fetched per new listing. The hard filters (price, minimum area, radius) are baked into the search URL, so the site does that filtering for me.
  • otodim.com.ua — instead of fighting fragile CSS selectors, the connector reads the listings straight out of the Next.js __NEXT_DATA__ JSON blob the page ships with. That gives clean, structured data — including the exact settlement and district from the site's own reverse-geocoding — with no scraping guesswork.

Both feed the same dedup + scoring + report pipeline; each ad remembers its source.

The LLM does the fuzzy filtering

Hard filters are easy — a search URL handles price and area. The hard part is the soft criteria you can't put in a query string: is it actually quiet? year-round heating? a standalone house with a plot? any red flags in the description? Those get scored by a local Ollama model reading the listing text, returning a structured verdict per criterion. A strict geo filter (tuned on Ukrainian village names) keeps out the "technically nearby but wrong direction" listings.

When something clears the bar, I get a Telegram message with the score and a link — usually long before I'd have refreshed the page myself.

The report is a PWA

Beyond the alerts, there's an installable, mobile-friendly HTML report with every scored listing, source chips, and a price range I can edit from the page itself (stored in a small JSON file, no rebuild). It's a Progressive Web App, so it sits on my phone's home screen like a native app.

The recurring theme: keep the model off the critical path

Like the rest of my infrastructure, the LLM here is an enhancement, not a dependency. Ingestion, dedup, and the report always work; if the model is unavailable a listing simply isn't scored yet, not lost. The scraper keeps running, and scoring catches up when the model is back.

It's a small tool, but it turned an anxious daily ritual into a Telegram ping when something genuinely worth looking at shows up.

Transparency: most posts here are drafted with AI assistance, and the non-English versions are machine-translated by a local LLM and then reviewed. Spotted an error? Please let me know.