diff --git a/scrape_fotograf.py b/scrape_fotograf.py index d976ab4a..988096ef 100644 --- a/scrape_fotograf.py +++ b/scrape_fotograf.py @@ -34,13 +34,13 @@ SELECTORS = { # Selektoren für die Statistik-Zählung "person_all_photos": ".//div[@data-key]", "person_purchased_photos": ".//div[@data-key and .//img[@alt='Bestellungen mit diesem Foto']]", + "person_access_card_photo": ".//div[@data-key and contains(@class, 'opacity-50')]", # NEU: Identifiziert die Zugangskarte "potential_buyer_link": "//a[contains(@href, '/config_customers/view_customer')]", "quick_login_url": "//a[@id='quick-login-url']", "buyer_email": "//span[contains(., '@')]" } def take_error_screenshot(driver, error_name): - """Speichert einen Screenshot des aktuellen Browserfensters in den output-Ordner.""" os.makedirs(OUTPUT_DIR, exist_ok=True) timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") filename = f"error_{error_name}_{timestamp}.png" @@ -52,7 +52,6 @@ def take_error_screenshot(driver, error_name): print(f"!!! Konnte keinen Screenshot speichern: {e}") def setup_driver(): - """Initialisiert und konfiguriert den Chrome WebDriver.""" print("Initialisiere Chrome WebDriver...") options = Options() options.add_argument('--headless') @@ -68,7 +67,6 @@ def setup_driver(): return None def load_all_credentials(): - """Lädt alle Anmeldedaten aus der JSON-Datei.""" try: with open(CREDENTIALS_FILE, 'r') as f: return json.load(f) @@ -76,7 +74,6 @@ def load_all_credentials(): return None def login(driver, username, password): - """Führt den Login-Vorgang auf fotograf.de durch.""" print("Starte Login-Vorgang...") try: driver.get(LOGIN_URL) @@ -102,9 +99,7 @@ def login(driver, username, password): take_error_screenshot(driver, "login_error") return False -# --- Modus 1: E-Mail-Listen-Erstellung --- def process_reminder_mode(driver, job_url): - """Sammelt Daten für die E-Mail-Erinnerungskampagne.""" wait = WebDriverWait(driver, 15) try: @@ -228,7 +223,6 @@ def process_reminder_mode(driver, job_url): return final_results def aggregate_results_by_email(results): - """Fasst Ergebnisse pro E-Mail-Adresse zusammen.""" print("\nBeginne mit der Aggregation der Ergebnisse pro E-Mail-Adresse...") aggregated_data = {} for result in results: @@ -263,7 +257,6 @@ def aggregate_results_by_email(results): return final_list def save_aggregated_results_to_csv(results): - """Speichert die aggregierten Daten für Supermailer.""" if not results: print("\nKeine Daten zum Speichern vorhanden.") return @@ -280,7 +273,6 @@ def save_aggregated_results_to_csv(results): # --- Modus 2: Statistik-Auswertung --- def process_statistics_mode(driver, job_url): - """Sammelt und druckt Statistiken pro Album.""" wait = WebDriverWait(driver, 15) try: @@ -328,13 +320,17 @@ def process_statistics_mode(driver, job_url): try: photo_container = person_row.find_element(By.XPATH, "./following-sibling::div[1]") + # GEÄNDERTE ZÄHLLOGIK num_total_photos = len(photo_container.find_elements(By.XPATH, SELECTORS["person_all_photos"])) num_purchased_photos = len(photo_container.find_elements(By.XPATH, SELECTORS["person_purchased_photos"])) + num_access_cards = len(photo_container.find_elements(By.XPATH, SELECTORS["person_access_card_photo"])) + + buyable_photos = num_total_photos - num_access_cards if num_purchased_photos > 0: children_with_purchase += 1 - if num_total_photos > 0 and num_total_photos == num_purchased_photos: + if buyable_photos > 0 and buyable_photos == num_purchased_photos: children_with_all_purchased += 1 except NoSuchElementException: continue @@ -356,7 +352,6 @@ def process_statistics_mode(driver, job_url): return statistics def save_statistics_to_csv(results): - """Speichert die Statistik-Daten in einer CSV-Datei.""" if not results: print("\nKeine Statistikdaten zum Speichern vorhanden.") return @@ -371,9 +366,7 @@ def save_statistics_to_csv(results): writer.writerows(results) print("Speichern erfolgreich!") -# --- Haupt-Logik --- def get_profile_choice(): - """Zeigt ein Menü zur Profilauswahl.""" all_credentials = load_all_credentials() if not all_credentials: return None profiles = list(all_credentials.keys()) @@ -390,8 +383,7 @@ def get_profile_choice(): except ValueError: print("Ungültige Eingabe.") def main(): - """Hauptfunktion des Skripts.""" - print("--- Fotograf.de Scraper (v3.1 - The Analyst) ---") + print("--- Fotograf.de Scraper (v3.2 - The Master Analyst) ---") while True: mode = input("Bitte Modus wählen:\n 1) E-Mail-Liste erstellen\n 2) Statistik auswerten\nWahl: ")