Web Scraping + Full Stack
Deal Round
A Copenhagen deal aggregator that scrapes 8+ Danish deal sites, filters out junk, deduplicates listings, and presents them in a polished web app with search, map view, geolocation, and Telegram alerts.


Status
Live
Deal Sources
8+ sites
Frontend
Vercel
Scraper
Hetzner VPS
Why I built this
Living in Copenhagen, I kept missing good deals on activities, restaurants, and entertainment because they were scattered across different Danish deal sites. I wanted one place to see everything — filtered, deduplicated, and mapped — with Telegram alerts so I never miss a deal that matches my interests.
How it works
Scrape deals from 8+ Danish sites
A Python scraper runs twice daily (08:00 + 18:00 CET) on a Hetzner VPS, pulling deals from Meyou.dk, Sweetdeal, All2day, Bownty, Madbillet, and more. Handles Danish compound words and bilingual keyword matching.
Filter, deduplicate, and classify
Aggressive quality filtering removes ~60% of raw scrapes — products, fake discounts, non-Copenhagen locations, and junk providers. Duplicate venues across sources keep only the lowest price. Food deals with drink keywords get auto-reclassified.
Browse, search, and filter in the web app
A Next.js frontend on Vercel shows deals in grid, list, or map view. Filter by category, price range, time added, and expiry. Sort by newest, cheapest, or highest discount. Use "Near Me" to find deals closest to your location.
Get notified via Telegram
New deals matching your interests are sent to a Telegram channel twice daily. SQLite deduplication ensures you only see new deals.
Key features
3 view modes
Grid cards, compact list, and interactive Leaflet map with marker clustering
4 categories
Activities, Food, Drinks, Entertainment — color-coded with smart filtering
Geolocation
"Near Me" button calculates distance to 27 Copenhagen neighborhoods via Haversine
Advanced search
Full-text search, price ranges, time filters, sort by discount/price/date
Dark mode
Auto-detects system preference, instant toggle with no flash on load
Export as PNG
Download any deal card as a shareable image via html2canvas


Tech stack
Frontend
Maps
CartoDB tiles (free, no API key), react-leaflet-cluster for marker grouping
Backend and Scraping
BeautifulSoup for HTML parsing, APScheduler for cron jobs
Infrastructure
Architecture
Dual deploy: Next.js frontend on Vercel, Python scraper + REST API on Hetzner VPS (systemd service). Frontend proxies API calls through Next.js route handlers with 60s ISR revalidation.
SQLite on the VPS stores all deals with deduplication. Quality filtering happens both server-side (scraper) and client-side (dealFilter.ts blocks 47 product keywords, 11 junk providers, and non-Copenhagen locations).
Geolocation uses a static lookup table of 27 Copenhagen neighborhoods with Nominatim API fallback. Markers are jittered deterministically by deal_id hash to prevent overlap.
Mobile-first with pull-to-refresh (damped touch gesture), collapsible filters, and list view as the default on small screens. React Query handles caching and background refetching.
Data sources
Scraped twice daily at 08:00 and 18:00 CET — typically yields 150-200 quality deals after filtering
What I learned
- -Danish compound words like "healingsmassage" break standard word boundary regex — use substring matching for longer keywords and word boundaries only for short ambiguous ones like "spa".
- -The hardest part of a deal aggregator is data quality. ~60% of raw scrapes are junk — products, fake discounts, or wrong locations. Aggressive filtering is essential.
- -Downtown.dk redirects to meyou.dk but still SSR-renders 60+ deal cards — scraping Angular apps works fine when they use server-side rendering.
- -Free map tiles from CartoDB + Leaflet clustering make a polished map experience without any API key costs.