ReviewProof
Dieses Projekt ist ein Python-basiertes Web-Scraping-System zur Erfassung umfassender Daten ueber lokale Geschaefte in Bielefeld, Deutschland - einschliesslich Restaurants, Aerzten, Cafes, Geschaeften und mehr. Der Scraper nutzt Playwright (eine moderne Browser-Automatisierungsbibliothek), um mit Google Search zu interagieren und Geschaeftsinformationen zu extrahieren, darunter Bewertungen, Rezensionsanzahlen, Adressen, Telefonnummern und DSGVO-bezogene Loeschhinweise.

Hauptziele:
- Erfassung von Daten von 500+ Geschaeften aus mehreren Kategorien
- Erfassung von Bewertungen, Rezensionsanzahlen, Adressen und Loeschhinweisen
- Datenqualitaet durch strenge Validierungsregeln gewaehrleisten
- Eine wiederverwendbare, wartbare Scraper-Architektur aufbauen
- Mehrere Geschaeftstypen unterstuetzen: Restaurants, Aerzte, Geschaefte, Cafes
Architektur
Systemuebersicht
Komponentenarchitektur
Daten-Pipeline
Ende-zu-Ende-Flow
Phase 1: Suche und Sammlung
Der Scraper verwendet eine Multi-Term-Suchstrategie fuer maximale Abdeckung verschiedener Geschaeftstypen:
Jeder Suchbegriff durchsucht 3 Seiten mal 10 Ergebnisse equals 30 potenzielle Eintraege pro Begriff. Mit 6 Suchbegriffen sind das bis zu 180 potenzielle Eintraege.

Phase 2: Namensextraktion
Phase 3: Individuelles Scraping
Scraping-Methodik
Browser-Setup
Wichtige Konfigurationen:
- headless=False: fuer visuelle Fehlersuche
- AutomationControlled Flag: Bot-Erkennung umgehen
- no-sandbox: fuer einige Linux-Umgebungen erforderlich
Such-URL-Muster
| Parameter | Wert | Zweck | |-----------|------|--------| | q | Suchbegriff | Google-Abfrage | | tbm=lcl | local | Lokale Geschaeftsergebnisse | | hl=de-DE | Deutsch | Deutsche Sprachergebnisse | | start | 0, 10, 20 | Seitennummerierung |
Consent-Handling
Datenextraktionsmuster
Bewertungsmuster
Adressmuster
Geloeschte Rezensionen (DSGVO)
Datenvalidierung und Bereinigung
Validierungsregeln
Filter-Mathematik
Der Namensfilter-Algorithmus verwendet praezise mathematische Beschraenkungen. Er validiert Namen zwischen 8-70 Zeichen und verwendet Schluesselwort-Mengenoperationen um sicherzustellen, dass nur gueltige Geschaeftsnamen enthalten sind. Fuzzy-Abgleich mit Levenshtein-Distanz hilft doppelte Eintraege zu erkennen und zu entfernen.
Geschaefts-Schluesselwoerter
Das Filtersystem unterstuetzt mehrere Geschaeftskategorien:
Ablehnungsmuster
Saubere Kategorien
Das System kategorisiert Geschaefte automatisch in:
Medizinisch:
- Aerzte (Dr., Dr. med., Prof.)
- Praxen (Praxis, Gemeinschaftspraxis)
- Kliniken (Klinik, Krankenhaus)
- Zahnarzte (Zahnarzt, Zahnärzte)
- Fachärzte (Facharzt, Centrum)
- Therapiezentren
Restaurants und Essen:
- Restaurants (Restaurant, Gastronomie)
- Cafes (Cafe, Konditorei)
- Fast Food (Pizza, Doener, Grill)
- Baeckereien (Baeckerei)
Einzelhandel und Dienstleistungen:
- Geschaefte und Laeden
- Elektronik (Elektro, Elektronik)
- Mode (Mode, Boutique)
- Maerkte (Markt)

Statistische Formeln
Datenqualitaet wird mit statistischen Metriken gemessen:
-
Durchschnittsbewertung: mean = SUM(x) / n
-
Standardabweichung: std = sqrt(SUM((x-mean)^2)/n)
-
Daten-Vollstaendigkeitsraten:
- Bewertungs-Vollstaendigkeit: R = count(rating) / total * 100%
- Adress-Vollstaendigkeit: A = count(address) / total * 100%
- Geloeschte Rezensionen: D = count(deleted) / total * 100%
-
Bewertungs-Histogramm-Bins: | Bin | Bereich | Anzahl | |-----|---------|--------| | 1 | 4.5-5.0 | 89 | | 2 | 4.0-4.4 | 45 | | 3 | 3.5-3.9 | 18 | | 4 | unter 3.5 | 5 |
-
Kategorieverteilungs-Prozentsaetze:
- Arzt: 62/157 = 39.5%
- Zahnarzt: 35/157 = 22.3%
- Klinik: 28/157 = 17.8%
- Arztpraxis: 20/157 = 12.7%
- Facharzt: 12/157 = 7.6%
Zeitberechnungen
Leistungszeit wird wie folgt berechnet:
-
Seitenladezeit-Schaetzungen:
- Durchschnittliche Ladezeit: t_load = 1.8s +/- 0.5s
- Timeout-Schwelle: t_max = 10s
-
Zufaellige Verzoegerungsverteilung: U(1.0, 2.0) Sekunden zwischen Anfragen
-
Gesamtlaufzeit-Formel: T_total = n * (t_load + t_parse + t_save) + d * (n-1) wobei: n = Anzahl der Eintraege t_load = 1.8s durchschnittliche Seitenladung t_parse = 0.3s Parsing-Zeit t_save = 0.1s Datenbank-Schreibzeit d = Zufallsverzoegerung ~ U(1.0, 2.0)
Fuer 157 Eintraege: T = 157 * (1.8 + 0.3 + 0.1) + 156 * 1.5 = 680s = 11 Minuten
Leistungsanalyse
| Operation | Zeitkomplexitaet | Beschreibung | |-----------|----------------|-------------| | Namensfilterung | O(n) | Lineare Scan der Token-Menge | | Fuzzy-Abgleich | O(n * m) | Levenshtein auf alle Paare | | Bewertungsextraktion | O(1) | Ein einzelner Regex-Abgleich | | DB-Insert/Update | O(1) | Hash-Tabellen-Lookup | | CSV-Export | O(n) | Vollstaendiger Tabellenscan |
-
Speicherschaetzung:
- Roher HTML-Puffer: ~5MB pro Seite
- Namen-Menge: ~50KB fuer 1000 Namen
- SQLite-DB: ~500KB fuer 157 Eintraege
- Gesamt-Spitze: ~10MB
-
Durchsatz-Berechnung: throughput = entries / time = 157 / 680s = 0.23 Eintraege/Sekunde
Erweiterte Berechnungen
-
Haversine-Distanz (fuer zukuenftige Kartenvisualisierung):
-
Google Maps Koordinaten-Extraktion:
-
Qualitaetsbewertungs-Algorithmus (0-100):
Datenbankschema
SQLite-Schema
Entity-Beziehung
Feldbeschreibungen
| Feld | Typ | Beschreibung | Beispiel | |-------|------|-------------|---------| | id | INTEGER | Primaerschluessel | 1 | | name | TEXT | Geschaeftsname | "Dr. med. Hans Mueller" | | rating | TEXT | Bewertung (1.0-5.0) | "4.5" | | total_reviews | TEXT | Anzahl der Rezensionen | "103" | | deleted_reviews | TEXT | DSGVO-Loeschhinweis | "Einige Ergebnisse..." | | address | TEXT | Vollstaendige Adresse | "Adresse: Hauptstr. 1, 33602 Bielefeld" | | url | TEXT | Google Maps URL | "https://www.google.com/maps/..." | | category | TEXT | Auto-kategorisierte Kategorie | "Doctor", "Dentist", "Clinic" | | scrape_date | TEXT | Erstes Scraping-Datum | "2026-05-11" | | last_updated | TEXT | Letztes Aktualisierungsdatum | "2026-05-11" | | status | TEXT | Aktiv/Inaktiv | "active" |
Kategorie-Auto-Tagging
Datenfluss-Diagramme
Vollstaendiger Pipeline
Fehlerbehandlungsflow
Herausforderungen und Loesungen
Herausforderung 1: Dynamische Google-UI
Problem: Google aendert haeufig die HTML-Struktur und CSS-Klassennamen.
Loesung: Textbasierte Extraktion statt CSS-Selektoren verwenden:
Vorher (Selektor-basiert):
Nachher (Text-basiert):
Herausforderung 2: Consent-Cookie-Banner
Problem: Google zeigt Cookie-Consent-Overlay, das Inhalte blockiert.
Loesung: Automatisches Button-Klicken mit mehreren Versuchen:
Herausforderung 3: Nicht-medizinische Eintraege
Problem: Suchergebnisse enthalten Fitnessstudios, Apotheken, Optiker.
Loesung: Strenge Filterung mit Schluesselwortvalidierung:
Herausforderung 4: Doppelte Eintraege
Problem: Gleiche Klinik erscheint unter verschiedenen Namen.
Loesung: Datenbank-Level-Deduplizierung mit Namen-Abgleich:
Herausforderung 5: Fehlende Bewertungsdaten
Problem: Einige Eintraege zeigen keine Bewertungen.
Loesung: Strenge Validierung - nur Eintraege mit vollstaendigen Daten speichern:

Ergebnisse und Statistiken
Aktueller Datensatz
| Metrik | Wert | |--------|-------| | Gesamteintraege | 157 | | Mit Bewertungen | 157 (100%) | | Mit Rezensionen | 157 (100%) | | Mit geloeschten Rezensionen | 58 (37%) | | Mit Adressen | 16 (10%) |
Bewertungsverteilung
Kategorieaufschluesselung
| Kategorie | Anzahl | Prozentsatz | |----------|----|-------------| | Arzt | 62 | 39% | | Zahnarzt | 35 | 22% | | Klinik | 28 | 18% | | Arztpraxis | 20 | 13% | | Facharzt | 12 | 8% |
Geloeschte Rezensionen Analyse

Technischer Stack
Abhaengigkeiten
Dateistruktur

Zukuenftige Verbesserungen
Geplante Verbesserungen
-
Parallele Verarbeitung
- Mehrere Browser-Kontexte gleichzeitig verwenden
- Gesamt-Scraping-Zeit um 50% reduzieren
-
Erweiterte Validierung
- Fuzzy-Namensabgleich fuer Duplikaterkennung
- URL-basierte Deduplizierung als Backup
-
Adressextraktion
- Regex-Muster fuer deutsche Adressen verbessern
- Mehrere Adressformate parsen
-
Rezensionsinhalt
- Tatsaechlichen Rezensionstext erfassen (mit Consent)
- Stimmungsanalyse auf Rezensionen
-
Ueberwachung
- Aenderungen ueber Zeit verfolgen (Re-Scraping-Erkennung)
- Warnung bei Bewertungsaenderungen
Potenzielle Erweiterungen
Anhang: Code-Referenz
Hauptschleifen-Struktur
CSV-Export-Funktion
Fazit
Dieses Projekt demonstriert einen praktischen Ansatz zur automatisierten Datensammlung aus Web-Suchergebnissen:
- Textbasierte Extraktion ist robuster als CSS-Selektoren fuer dynamische Webseiten
- Strenge Validierung gewaehrleistet hohe Datenqualitaet, auch wenn es weniger Eintraege bedeutet
- Modulares Design ermöglicht einfache Wartung und Erweiterung
- Automatisierte Bereinigung erkennt schlechte Eintraege, die durch die anfängliche Filterung rutschen
- Mathematische Validierung liefert quantifizierbare Datenqualitaetsmetriken
Der Scraper erfasst erfolgreich validierte Geschaeftsdaten aus Bielefeld, wobei 100% der Eintraege Bewertungen und Rezensionsanzahlen enthalten. Die Architektur ist fuer Erweiterbarkeit auf andere Staedte und Datenquellen ausgelegt.
Dokumentversion: 1.1
Zuletzt aktualisiert: 11. Mai 2026
Lizenz: MIT