# Flat Scraper Automatischer Web Scraper für Wohnungangebote auf NHG.at mit Benachrichtigungen bei neuen Ergebnissen. ## Features - 🏠 **Automatisches Scraping** von NHG.at Wohnungsangeboten - 📍 **PLZ-basierte Suche** für 1120, 1140, 1150, 1160 - 📊 **CSV Storage** für Ergebnisverfolgung - 🔔 **Benachrichtigungen** bei neuen Wohnungen (Console + Email) - 🐳 **Docker Support** für Raspberry Pi (ARM64) - ⏰ **Automatisierte Ausführung** alle 6 Stunden - 🔐 **Environment Variables** für sensitive Daten (.env) - 📧 **Email Security** (SSL/TLS/STARTTLS Support) ## Projektstruktur ``` flat_scraper/ ├── src/ │ ├── scrapers/ # Scraper Module │ │ ├── base_scraper.py # Basis-Klasse │ │ └── nhg_scraper.py # NHG.at spezifisch │ ├── storage/ # Daten-Speicher │ │ └── csv_storage.py # CSV-basiert │ ├── notifier/ # Benachrichtigungen │ │ └── email_notifier.py │ ├── config/ # Konfiguration │ │ └── sites.yaml │ ├── config_loader.py # Konfigurations-Loader mit .env Support │ └── main.py # Hauptanwendung ├── data/ # CSV Ergebnisse ├── .env.example # Environment Vorlage ├── .env # Deine sensitiven Daten (nicht in VCS) ├── .gitignore # Git ignore für .env und data/ ├── requirements.txt # Python Dependencies ├── Dockerfile # ARM64 optimiert ├── deploy.sh # Automatisches Deployment Script ├── docker-compose.yml # Automatisierung └── README.md ``` ## Quick Start ### 1. Environment einrichten ```bash # Environment Vorlage kopieren cp .env.example .env # Deine Daten eintragen vim .env ``` **Wichtige .env Variablen:** ```bash SMTP_SERVER=smtp.gmail.com SMTP_PORT=587 EMAIL_USERNAME=deine-email@gmail.com EMAIL_PASSWORD=dein-app-password EMAIL_FROM=deine-email@gmail.com EMAIL_TO=empfänger@example.com EMAIL_SECURITY=starttls # Options: none, ssl, tls, starttls ``` ### 2. Docker auf Raspberry Pi ```bash # Build und Start docker-compose up -d # Logs ansehen docker-compose logs -f flat-scraper # Scheduler starten (automatisch alle 6 Stunden) docker-compose up -d scheduler ``` ### 3. Manuelles Testen ```bash # Einmaliger Lauf docker-compose run --rm flat-scraper # Mit Environment File docker run --rm -v $(pwd):/app --env-file .env flat-scraper-test python src/main.py ``` ## Konfiguration ### Sites konfigurieren (`src/config/sites.yaml`) ```yaml sites: nhg: name: "Neue Heimat Gewog" url: "https://nhg.at/immobilienangebot/wohnungsangebot/" scraper_class: "nhg_scraper.NHGScraper" enabled: true search_params: plz_list: - "1120 Wien" - "1140 Wien" - "1150 Wien" - "1160 Wien" schedule: cron: "0 */6 * * *" # Alle 6 Stunden timezone: "Europe/Vienna" ``` ### Email-Benachrichtigungen ```yaml notification: email: enabled: true smtp_server: "${SMTP_SERVER}" smtp_port: "${SMTP_PORT}" username: "${EMAIL_USERNAME}" password: "${EMAIL_PASSWORD}" from_email: "${EMAIL_FROM}" to_emails: - "${EMAIL_TO}" security: "${EMAIL_SECURITY:starttls}" # Options: none, ssl, tls, starttls console: enabled: true # Immer für Debugging ``` ## Datenformat Ergebnisse werden als CSV gespeichert: ```csv scrape_time,plz,address,link,hash,scraper 2024-01-15T10:30:00,1120,"1120 Wien, Flurschützstraße 5 / 2 / 10",https://...,abc123,nhg ``` **Hash-basierter Vergleich** vermeidet Duplikate zwischen Läufen. ## Erweiterbarkeit ### Neue Webseite hinzufügen 1. **Neue Scraper-Klasse** in `src/scrapers/`: ```python from .base_scraper import BaseScraper class NewSiteScraper(BaseScraper): async def scrape(self, search_params): # Implementierung pass ``` 2. **Konfiguration erweitern**: ```yaml sites: new_site: name: "New Site" url: "https://example.com" scraper_class: "new_site_scraper.NewSiteScraper" enabled: true search_params: # Site-spezifische Parameter ``` ### Environment Variables Der ConfigLoader unterstützt **automatische Substitution**: ```yaml # In YAML smtp_server: "${SMTP_SERVER}" username: "${EMAIL_USERNAME:default@example.com}" # Mit Default ``` ## Deployment auf Raspberry Pi ### Schritt-für-Schritt Anleitung #### Voraussetzungen - Raspberry Pi 4+ (empfohlen) - Docker & Docker Compose installiert - Git für das Klonen des Repositories #### 1. Repository auf Raspberry Pi laden ```bash # Projekt klonen git clone flat_scraper cd flat_scraper # Oder manuell kopieren scp -r ./flat_scraper pi@raspberry-pi:~/ ``` #### 2. Environment konfigurieren ```bash # Environment Vorlage kopieren cp .env.example .env # Mit Editor öffnen nano .env # Oder vim .env ``` **Deine SMTP-Daten eintragen:** ```bash SMTP_SERVER=dein-smtp-server.com SMTP_PORT=587 EMAIL_USERNAME=deine-email@domain.com EMAIL_PASSWORD=dein-app-password EMAIL_FROM=deine-email@domain.com EMAIL_TO=empfänger@domain.com EMAIL_SECURITY=starttls ``` #### 3. Docker Image bauen ```bash # Image für ARM64 bauen (kann einige Minuten dauern) docker build -t flat-scraper . # Build-Status prüfen docker images | grep flat-scraper ``` **Fehlerbehandlung:** ```bash # Falls Docker Daemon nicht läuft: sudo systemctl start docker sudo systemctl enable docker # Falls Podman-Socket Fehler: # Prüfen ob Docker oder Podman aktiv ist: which docker which podman # Docker Socket prüfen: ls -la /var/run/docker.sock sudo chmod 666 /var/run/docker.sock # Falls nötig, Docker neu starten: sudo systemctl restart docker # Environment Variablen Fehler: # Prüfen ob .env Datei existiert und korrekt ist: ls -la .env cat .env # Falls SMTP_PORT nicht gesetzt ist: echo "SMTP_PORT=587" >> .env # Permission Fehler mit data/ Verzeichnis: sudo chown -R $USER:$USER data/ chmod 755 data/ ``` #### 4. Erster Testlauf ```bash # Einmaligen Scraper-Lauf testen docker run --rm \ --env-file $(pwd)/.env \ -v $(pwd)/data:/app/data \ -v $(pwd)/src/config:/app/src/config \ flat-scraper python src/main.py ``` **Erwartete Ausgabe:** ``` 2026-02-15 10:30:00 - INFO - Starting flat scraper run 2026-02-15 10:30:00 - INFO - Start scraping nhg Scraping PLZ 1120 Wien... Scraping PLZ 1140 Wien... Scraping PLZ 1150 Wien... Scraping PLZ 1160 Wien... 2026-02-15 10:30:15 - INFO - Found X results for nhg Email-Benachrichtigung gesendet an 1 Empfänger 2026-02-15 10:30:15 - INFO - Scraping completed: 1/1 sites successful ``` #### 5. Docker Compose konfigurieren ```bash # ARM64 Support aktivieren (wichtig für Raspberry Pi) sed -i 's/# platform: linux/arm64/platform: linux/arm64/' docker-compose.yml # Konfiguration prüfen cat docker-compose.yml ``` #### 6. Production Deployment ```bash # Manuelles Deployment docker-compose up -d # Status prüfen docker-compose ps # ODER: Automatisches Deployment Script verwenden ./deploy.sh ``` **Deployment Script Features:** - ✅ **Automatisches Build** mit Fehlerprüfung - ✅ **Image Cleanup** für Speicherplatz - ✅ **Status Überwachung** vor/nach Deployment - ✅ **Flexible Optionen** für verschiedene Szenarien ```bash # Vollständiges Deployment mit Cleanup ./deploy.sh # Deployment ohne Cleanup (für schnelle Tests) ./deploy.sh --no-cleanup # Nur Services neu starten (kein Build) ./deploy.sh --no-build # Verbose Output für Debugging ./deploy.sh --verbose # Hilfe anzeigen ./deploy.sh --help ``` **Erwartete Ausgabe:** ``` NAME COMMAND SERVICE STATUS PORTS flat_scraper-flat-scraper-1 "python src/main.py" flat-scraper running flat_scraper-scheduler-1 "python -c 'import..." scheduler running ``` #### 7. Logs überwachen ```bash # Live Logs ansehen docker-compose logs -f # Nur Scraper Logs docker-compose logs -f flat-scraper # Nur Scheduler Logs docker-compose logs -f scheduler ``` #### 8. Automatisierung verifizieren ```bash # Prüfen ob Scheduler läuft docker-compose logs scheduler | grep "Scheduler started" # Nächsten Lauf prüfen (alle 6 Stunden) docker-compose logs scheduler | tail -10 ``` #### 9. Daten persistenz prüfen ```bash # CSV Dateien prüfen ls -la data/ # Inhalt ansehen cat data/nhg_results.csv # Letzte Ergebnisse tail -5 data/nhg_results.csv ``` #### 10. Wartung und Updates ```bash # Services stoppen docker-compose down # Code aktualisieren git pull origin main # Automatisches Deployment mit Script ./deploy.sh # ODER manueller Weg: # Neues Image bauen docker build -t flat-scraper . # Services neu starten docker-compose up -d # Alte Images aufräumen docker image prune -f ``` **Deployment Script für Updates:** ```bash # Vollständiges Update mit Cleanup ./deploy.sh # Schneller Restart ohne Build ./deploy.sh --no-build --no-cleanup ``` ### ARM64 Support Der Dockerfile ist für ARM64 optimiert: ```dockerfile FROM python:3.11-slim-bullseye # ARM64 optimierte Browser Installation RUN apt-get update && apt-get install -y chromium ``` ### Performance-Tipps - `--no-sandbox` für Chromium (im Dockerfile berücksichtigt) - Shared Browser Path: `PLAYWRIGHT_BROWSERS_PATH=/ms-playwright` - Memory-optimierte Settings - Environment Variables statt Hardcoding ### Docker Compose Features - **Volume Mounting**: `./data:/app/data` für persistente CSVs - **Environment Support**: `--env-file .env` für sensitive Daten - **Scheduler Service**: Automatische Ausführung alle 6 Stunden - **Restart Policy**: `unless-stopped` für Zuverlässigkeit ## Troubleshooting ### Häufige Probleme 1. **Browser startet nicht**: `playwright install-deps chromium` 2. **Keine Ergebnisse**: PLZ nicht verfügbar oder Website geändert 3. **Email funktioniert nicht**: SMTP-Einstellungen und Security prüfen 4. **Environment nicht geladen**: `.env` Datei prüfen und Rechte 5. **Docker Socket Fehler**: Podman vs Docker Konflikt 6. **TypeError: int() argument must be a string**: Environment Variable fehlt oder hat Default-Wert nicht 7. **Permission denied: data/nhg_results.csv**: data/ Verzeichnis gehört falschem User ### Docker/Podman Konflikt lösen ```bash # 1. Prüfen welcher Container-Manager aktiv: echo $CONTAINER_MANAGER which docker which podman # 2. Docker als Standard setzen (falls nötig): export DOCKER_HOST=unix:///var/run/docker.sock # 3. Podman deaktivieren (falls gewünscht): sudo systemctl disable podman sudo systemctl stop podman # 4. Docker Socket Rechte prüfen: sudo ls -la /var/run/docker.sock sudo usermod -aG docker $USER # User zur Docker Gruppe hinzufügen newgrp docker # Gruppe neu laden # 5. System neu starten nach Änderungen: sudo systemctl restart docker ``` ### Raspberry Pi spezifische Probleme ```bash # ARM64 Architektur prüfen: uname -m # Sollte: aarch64 oder armv7l # Docker Architektur prüfen: docker version --format '{{.Server.Arch}}' # Falls x86_64 Images auf ARM64 laufen sollen: # docker run --platform linux/arm64 flat-scraper # Memory prüfen (mindestens 1GB empfohlen): free -h # Speicherplatz prüfen (mindestens 2GB frei): df -h ``` ### Debugging ```bash # Logs ansehen docker-compose logs -f flat-scraper # Manuell testen docker-compose run --rm flat-scraper python src/main.py # Email Test docker run --rm -v $(pwd):/app --env-file .env flat-scraper-test python -c " from src.notifier.email_notifier import EmailNotifier from src.config_loader import ConfigLoader config = ConfigLoader() notifier = EmailNotifier(config.get_notification_config()['email']) test_results = [{'plz': '1120', 'address': 'Test', 'link': '#', 'hash': 'test'}] notifier.send_notification('test', test_results) " ``` ## Entwicklung ### Testing ```bash # Einzelnen Scraper testen python -c " import asyncio from src.scrapers.nhg_scraper import NHGScraper scraper = NHGScraper({'url': 'https://nhg.at/immobilienangebot/wohnungsangebot/', 'search_params': {'plz_list': ['1120 Wien']}}) results = asyncio.run(scraper.scrape()) print(results) " ``` ### Logging Logs werden automatisch geschrieben: - Level: `INFO` (kann in `sites.yaml` angepasst werden) - Format: `Zeitstempel - Modul - Level - Nachricht` - Output: Console + Docker Logs ## Sicherheit ### Environment Variables - **`.env`** wird nicht in Git eingecheckt (siehe `.gitignore`) - **`.env.example`** als Vorlage für das Team - **Keine Passwörter** im Code oder in YAML - **Docker Secrets** optional für Production ### Email Security Unterstützte Security Modi: - **`none`** - Keine Verschlüsselung - **`ssl`** - SMTP_SSL (Port 465) - **`tls`** - Explicit TLS (Port 587 + STARTTLS) - **`starttls`** - STARTTLS (Standard für Gmail) - **`ssl/tls`** - Kompatibilitätsmodus ## Architektur ### Hybrid-Ansatz - **BaseScraper**: Gemeinsame Funktionalität (Hashing, Metadata) - **Site-spezifische Scraper**: Individuelle Implementierungen - **Config-Driven**: YAML Konfiguration mit Environment Support - **Modular**: Storage und Notifier austauschbar ### Datenfluss ``` Config → Scraper → Results → Storage → Comparison → Notifier ↓ ↓ ↓ ↓ ↓ Environment Playwright CSV Hash-Vergleich Email/Console ``` ## Lizenz MIT License