564 lines
13 KiB
Markdown
564 lines
13 KiB
Markdown
# 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 <dein-repo-url> 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
|