diff --git a/scrape_fotograf.py b/scrape_fotograf.py index 8cf58ab7..e3eba23c 100644 --- a/scrape_fotograf.py +++ b/scrape_fotograf.py @@ -17,10 +17,12 @@ OUTPUT_FILE = os.path.join(OUTPUT_DIR, 'nutzer_ohne_logins.csv') LOGIN_URL = 'https://app.fotograf.de/login/login' # --- Selektoren (zentral verwaltet für einfache Anpassung) --- +# GEÄNDERT: Bessere, spezifischere Selektoren SELECTORS = { + "cookie_accept_button": "//button[normalize-space()='Allow all']", # NEU: Robuster XPath für den Cookie-Button "login_user": "#FotografEmail", "login_pass": "#FotografPassword", - "login_button": "button[type='submit']", + "login_button": "#login-submit", # GEÄNDERT: Stabiler Selector über die ID "job_name": "h1", "album_rows": "div.table-row-group > div.table-row", "album_link": "a.text-legacy-azure-200", @@ -47,6 +49,8 @@ def setup_driver(): """Initialisiert und konfiguriert den Chrome WebDriver.""" print("Initialisiere Chrome WebDriver...") options = Options() + # Aktivieren Sie die nächste Zeile für die Fehlersuche, um zu sehen, was passiert + # options.add_argument('--headless=new') options.add_argument('--headless') options.add_argument('--no-sandbox') options.add_argument('--disable-dev-shm-usage') @@ -60,7 +64,6 @@ def setup_driver(): print(f"Fehler bei der Initialisierung des WebDrivers: {e}") return None -# GEÄNDERT: Lädt alle Credentials, damit wir ein Menü anzeigen können def load_all_credentials(): """Lädt alle Anmeldedaten aus der JSON-Datei.""" try: @@ -73,29 +76,46 @@ def load_all_credentials(): print(f"Fehler: {CREDENTIALS_FILE} ist keine gültige JSON-Datei.") return None +# GEÄNDERT: Die Login-Funktion klickt jetzt den Cookie-Banner weg def login(driver, username, password): """Führt den Login-Vorgang auf fotograf.de durch.""" print("Starte Login-Vorgang...") try: driver.get(LOGIN_URL) - wait = WebDriverWait(driver, 15) - + wait = WebDriverWait(driver, 10) # 10 Sekunden sollten reichen + + # NEU: Auf den Cookie-Banner warten und ihn akzeptieren + try: + print("Suche nach Cookie-Banner...") + # Wir verwenden hier XPath, da es für Textsuche sehr gut geeignet ist. + cookie_button = wait.until(EC.element_to_be_clickable((By.XPATH, SELECTORS["cookie_accept_button"]))) + cookie_button.click() + print("Cookie-Banner akzeptiert.") + time.sleep(1) # Kurze Pause, damit der Banner verschwindet + except TimeoutException: + # Wenn der Banner nicht erscheint (z.B. bei späteren Besuchen), ist das auch OK. + print("Kein Cookie-Banner gefunden, fahre fort.") + + # Warten und Anmeldedaten eingeben wait.until(EC.visibility_of_element_located((By.CSS_SELECTOR, SELECTORS["login_user"]))).send_keys(username) driver.find_element(By.CSS_SELECTOR, SELECTORS["login_pass"]).send_keys(password) driver.find_element(By.CSS_SELECTOR, SELECTORS["login_button"]).click() + # Warten auf erfolgreichen Login wait.until(EC.url_contains('/config_dashboard/index')) print("Login erfolgreich!") return True except TimeoutException: print("Login fehlgeschlagen. Timeout beim Warten auf Elemente oder die nächste Seite.") - take_error_screenshot(driver, "login_timeout") # NEU: Screenshot bei Fehler + take_error_screenshot(driver, "login_timeout") return False except Exception as e: print(f"Ein unerwarteter Fehler beim Login ist aufgetreten: {e}") - take_error_screenshot(driver, "login_unexpected") # NEU: Screenshot bei Fehler + take_error_screenshot(driver, "login_unexpected") return False +# ... (der Rest des Skripts bleibt unverändert) ... + def process_job(driver, job_url): """Verarbeitet einen einzelnen Fotoauftrag.""" print(f"\nVerarbeite Job-URL: {job_url}") @@ -111,7 +131,7 @@ def process_job(driver, job_url): print(f"Auftragsname: '{job_name}'") except TimeoutException: print("Konnte den Auftragsnamen nicht finden. Überprüfe den Selector oder die Seite.") - take_error_screenshot(driver, "job_name_not_found") # NEU: Screenshot bei Fehler + take_error_screenshot(driver, "job_name_not_found") return [] print(f"Navigiere zur Alben-Übersicht: {albums_url}") @@ -134,7 +154,7 @@ def process_job(driver, job_url): except TimeoutException: print("Keine Alben auf der Seite gefunden oder Timeout beim Warten.") - take_error_screenshot(driver, "album_list_timeout") # NEU: Screenshot bei Fehler + take_error_screenshot(driver, "album_list_timeout") return [] results = [] @@ -164,10 +184,10 @@ def process_job(driver, job_url): except TimeoutException: print(f" Fehler: Timeout beim Verarbeiten der Detailseite für '{album['child_name']}'.") - take_error_screenshot(driver, f"detail_page_timeout_{album['child_name']}") # NEU: Screenshot bei Fehler + take_error_screenshot(driver, f"detail_page_timeout_{album['child_name']}") except Exception as e: print(f" Ein unerwarteter Fehler bei '{album['child_name']}': {e}") - take_error_screenshot(driver, f"detail_page_unexpected_{album['child_name']}") # NEU: Screenshot bei Fehler + take_error_screenshot(driver, f"detail_page_unexpected_{album['child_name']}") return results @@ -184,7 +204,6 @@ def save_results_to_csv(results): writer.writerows(results) print("Speichern erfolgreich!") -# NEU: Funktion zur Profilauswahl def get_profile_choice(): """Zeigt ein Menü der verfügbaren Profile und gibt die Auswahl des Benutzers zurück.""" all_credentials = load_all_credentials() @@ -208,12 +227,10 @@ def get_profile_choice(): except ValueError: print("Ungültige Eingabe. Bitte gib eine Zahl ein.") - def main(): """Hauptfunktion des Skripts.""" print("--- Fotograf.de Scraper für Nutzer ohne Logins ---") - # GEÄNDERT: Profilauswahl über Menü credentials = get_profile_choice() if not credentials: return