From caf76d2383c05c6fdb2318937ff79055ea74d633 Mon Sep 17 00:00:00 2001 From: Floke Date: Wed, 16 Jul 2025 18:18:35 +0000 Subject: [PATCH] scrape_fotograf.py aktualisiert --- scrape_fotograf.py | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/scrape_fotograf.py b/scrape_fotograf.py index 5a061ac7..1d0b6f16 100644 --- a/scrape_fotograf.py +++ b/scrape_fotograf.py @@ -8,7 +8,7 @@ from selenium.webdriver.chrome.options import Options from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC -from selenium.common.exceptions import TimeoutException, NoSuchElementException, StaleElementReferenceException +from selenium.common.exceptions import TimeoutException, NoSuchElementException, StaleElementReferenceException, InvalidArgumentException # --- Konfiguration & Konstanten --- CREDENTIALS_FILE = 'fotograf_credentials.json' @@ -16,7 +16,7 @@ OUTPUT_DIR = 'output' OUTPUT_FILE = os.path.join(OUTPUT_DIR, 'nutzer_ohne_logins.csv') LOGIN_URL = 'https://app.fotograf.de/login/login' -# --- Selektoren (unverändert) --- +# --- Selektoren (FINALE, VOLLSTÄNDIGE VERSION) --- SELECTORS = { "cookie_accept_button": "#CybotCookiebotDialogBodyLevelButtonLevelOptinAllowAll", "login_user": "#login-email", @@ -97,7 +97,12 @@ def process_full_job(driver, job_url): wait = WebDriverWait(driver, 15) print(f"\nVerarbeite Job-URL: {job_url}") - driver.get(job_url) + try: + driver.get(job_url) + except InvalidArgumentException: + print(f"!!! FEHLER: Die URL '{job_url}' wurde von Selenium als ungültig angesehen. Bitte prüfen Sie die Eingabe.") + return [] + try: job_name = wait.until(EC.visibility_of_element_located((By.CSS_SELECTOR, SELECTORS["job_name"]))).text print(f"Auftragsname: '{job_name}'") @@ -148,12 +153,9 @@ def process_full_job(driver, job_url): driver.get(access_code_page_url) print(f" Navigiere zur Kommunikations-Seite für '{vorname}'...") - # GEÄNDERTER, ROBUSTER BLOCK ZUR DATENEXTRAKTION for attempt in range(3): try: - # Wir warten explizit, bis die Seite die Details anzeigt (z.B. den Schnell-Login) wait.until(EC.visibility_of_element_located((By.XPATH, SELECTORS["quick_login_url"]))) - schnell_login_url = driver.find_element(By.XPATH, SELECTORS["quick_login_url"]).get_attribute('href') potential_buyer_element = driver.find_element(By.XPATH, SELECTORS["potential_buyer_link"]) kaeufer_name = potential_buyer_element.text @@ -171,11 +173,11 @@ def process_full_job(driver, job_url): "E-Mail-Adresse Käufer": email, "Schnell Login URL": schnell_login_url }) - break # Erfolg, also die Retry-Schleife verlassen + break except StaleElementReferenceException: print(f" Timing-Fehler (StaleElement), Versuch {attempt + 1}/3. Warte kurz...") - time.sleep(1) # Längere Pause, um der Seite Zeit zu geben + time.sleep(1) if attempt == 2: print(" Fehler war persistent, überspringe diese Person.") take_error_screenshot(driver, f"stale_error_{vorname}") @@ -183,9 +185,8 @@ def process_full_job(driver, job_url): except TimeoutException: print(f" Timeout beim Warten auf Details für '{vorname}'. Überspringe.") take_error_screenshot(driver, f"timeout_error_{vorname}") - break # Breche die Retry-Schleife ab, da Warten keinen Sinn macht + break - # Zurückkehren zur Album-Übersicht für die nächste Person print(f" Kehre zurück zur Album-Übersicht '{album['name']}'...") driver.get(album['url']) wait.until(EC.presence_of_element_located((By.XPATH, SELECTORS["person_rows"]))) @@ -229,7 +230,11 @@ def main(): print("--- Fotograf.de Scraper für Nutzer ohne Logins (FINALE VERSION) ---") credentials = get_profile_choice() if not credentials: return - job_url = input("Bitte gib die URL des zu bearbeitenden Fotoauftrags ein (Einstellungs-Seite): ") + + # GEÄNDERT: URL-Eingabe wird bereinigt + job_url_raw = input("Bitte gib die URL des zu bearbeitenden Fotoauftrags ein (Einstellungs-Seite): ") + job_url = job_url_raw.strip() + if "fotograf.de/config_jobs_settings/index/" not in job_url: print("Dies scheint keine gültige URL für die Auftragseinstellungen zu sein.") return