diff --git a/dealfront_enrichment.py b/dealfront_enrichment.py index a40ef60e..95c1a650 100644 --- a/dealfront_enrichment.py +++ b/dealfront_enrichment.py @@ -4,71 +4,54 @@ import time import logging from selenium import webdriver from selenium.webdriver.common.by import By -from selenium.webdriver.chrome.service import Service as ChromeService from selenium.webdriver.chrome.options import Options as ChromeOptions -from webdriver_manager.chrome import ChromeDriverManager from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC from selenium.common.exceptions import TimeoutException, NoSuchElementException +# Wichtig: Die zentralen Imports zuerst from config import Config, DEALFRONT_LOGIN_URL, DEALFRONT_CREDENTIALS_FILE -from helpers import setup_logging -# Logging aus dem helpers-Modul initialisieren -setup_logging(log_level=logging.DEBUG if Config.DEBUG else logging.INFO) -# Spezifischen Logger für dieses Modul erstellen +# Logging-Konfiguration, eigenständig für dieses Skript +LOG_LEVEL = logging.DEBUG if Config.DEBUG else logging.INFO +LOG_FORMAT = '%(asctime)s - %(levelname)-8s - %(name)-25s - %(message)s' +logging.basicConfig(level=LOG_LEVEL, format=LOG_FORMAT, force=True) logger = logging.getLogger(__name__) +# Definiere einen festen Ausgabeordner, der im Dockerfile erstellt wird +OUTPUT_DIR = "/app/output" -# ... (Kontext: Zeile davor) class DealfrontScraper: - """ - Kapselt alle Interaktionen mit der Dealfront-Plattform mittels Selenium. - """ - def __init__(self): - """ - Initialisiert den WebDriver und den WebDriverWait. - Verwendet den system-installierten chromedriver im Docker-Container. - """ logger.info("Initialisiere den DealfrontScraper und den Chrome WebDriver.") chrome_options = ChromeOptions() - - # Docker-optimierte und Headless-Argumente chrome_options.add_argument("--headless") chrome_options.add_argument("--no-sandbox") chrome_options.add_argument("--disable-dev-shm-usage") chrome_options.add_argument("--window-size=1920,1080") - - # Anti-Detection-Maßnahmen chrome_options.add_argument("--disable-blink-features=AutomationControlled") chrome_options.add_experimental_option("excludeSwitches", ["enable-automation"]) chrome_options.add_experimental_option('useAutomationExtension', False) - # Da der chromedriver im Dockerfile systemweit installiert wird, - # ist der webdriver-manager nicht mehr nötig. Selenium findet den Treiber von selbst. try: self.driver = webdriver.Chrome(options=chrome_options) - # Wichtig, um als "echter" Browser zu erscheinen self.driver.execute_script("Object.defineProperty(navigator, 'webdriver', {get: () => undefined})") except Exception as e: logger.critical(f"WebDriver konnte nicht initialisiert werden. Fehler: {e}", exc_info=True) self.driver = None raise - # Zentraler WebDriverWait mit einem Timeout von 20 Sekunden self.wait = WebDriverWait(self.driver, 20) logger.info("WebDriver erfolgreich initialisiert.") def _load_credentials(self): - """Lädt Dealfront-Zugangsdaten sicher aus der JSON-Datei.""" try: with open(DEALFRONT_CREDENTIALS_FILE, 'r') as f: creds = json.load(f) username = creds.get("username") password = creds.get("password") if not username or "DEIN_DEALFRONT_BENUTZERNAME" in username or not password or "DEIN_DEALFRONT_PASSWORT" in password: - logger.error(f"Zugangsdaten in '{DEALFRONT_CREDENTIALS_FILE}' sind ungültig oder Platzhalter.") + logger.error(f"Zugangsdaten in '{DEALFRONT_CREDENTIALS_FILE}' sind ungültig.") return None, None return username, password except FileNotFoundError: @@ -78,12 +61,17 @@ class DealfrontScraper: logger.error(f"Fehler beim Parsen der Credentials-Datei: '{DEALFRONT_CREDENTIALS_FILE}'") return None, None + def _save_error_screenshot(self): + try: + os.makedirs(OUTPUT_DIR, exist_ok=True) + filepath = os.path.join(OUTPUT_DIR, "login_error_screenshot.png") + self.driver.save_screenshot(filepath) + logger.error(f"Screenshot '{filepath}' wurde für die Analyse gespeichert.") + except Exception as e_ss: + logger.error(f"Konnte Screenshot nicht speichern: {e_ss}") + def login(self): - """ - Führt den Login-Prozess auf der Dealfront-Plattform durch. - """ if not self.driver: - logger.error("Login nicht möglich, da der WebDriver nicht initialisiert wurde.") return False username, password = self._load_credentials() @@ -93,85 +81,65 @@ class DealfrontScraper: try: logger.info(f"Navigiere zur Login-Seite: {DEALFRONT_LOGIN_URL}") self.driver.get(DEALFRONT_LOGIN_URL) + + # Warten, bis die Seite grundsätzlich geladen ist (z.B. auf das Username-Feld warten) + self.wait.until(EC.visibility_of_element_located((By.ID, "username"))) - # 1. Cookie-Banner behandeln (falls vorhanden) + # Cookie-Banner behandeln, wenn es existiert try: - logger.debug("Suche nach Cookie-Banner...") - # NEU: Robusterer XPath-Selektor, der nach einem Button mit spezifischem Text sucht. - # Dies ist weniger anfällig für ID-Änderungen. - cookie_button_xpath = "//button[contains(text(), 'Alle zulassen') or contains(text(), 'Alle akzeptieren')]" - cookie_button = self.wait.until(EC.element_to_be_clickable((By.XPATH, cookie_button_xpath))) - cookie_button.click() - logger.info("Cookie-Banner erfolgreich via XPath geklickt.") - # Kurze Pause nach dem Klick, damit sich die Seite anpassen kann - time.sleep(1) - except TimeoutException: - logger.warning("Cookie-Banner konnte nicht via XPath gefunden werden. Das kann OK sein, wenn kein Banner da war.") + # Wir suchen nach einem allgemeineren Cookie-Banner-Container + cookie_banner = self.driver.find_element(By.ID, "CybotCookiebotDialog") + if cookie_banner.is_displayed(): + logger.debug("Cookie-Banner-Container gefunden. Suche nach 'Alle zulassen'-Button.") + # Suchen des Buttons innerhalb des Banners + allow_button = cookie_banner.find_element(By.ID, "CybotCookiebotDialogBodyLevelButtonLevelOptinAllowAll") + allow_button.click() + logger.info("Cookie-Banner akzeptiert.") + time.sleep(1) # Kurze Pause + except NoSuchElementException: + logger.warning("Kein Cookie-Banner gefunden. Fahre mit Login fort.") - # 2. Anmeldedaten ausfüllen - logger.info("Fülle Anmeldeformular aus...") - username_field = self.wait.until(EC.visibility_of_element_located((By.ID, "username"))) + username_field = self.driver.find_element(By.ID, "username") password_field = self.driver.find_element(By.ID, "password") - + username_field.send_keys(username) password_field.send_keys(password) logger.info("Benutzername und Passwort eingetragen.") - # 3. Login-Button klicken login_button = self.driver.find_element(By.CSS_SELECTOR, "button[type='submit']") login_button.click() logger.info("Login-Button geklickt. Warte auf die Verifizierung...") - # 4. Login-Erfolg verifizieren - # Wir warten auf ein Element, das nur nach erfolgreichem Login existiert, - # z.B. das Haupt-Suchfeld auf dem Dashboard. verification_element_xpath = "//input[@data-cy='header-search-input']" self.wait.until(EC.visibility_of_element_located((By.XPATH, verification_element_xpath))) logger.info("Login erfolgreich! Dashboard-Element gefunden.") return True - except TimeoutException as e: - logger.error(f"Login fehlgeschlagen. Timeout beim Warten auf ein Element.") - self.driver.save_screenshot("login_error_screenshot.png") - logger.error("Screenshot 'login_error_screenshot.png' wurde für die Analyse gespeichert.") - return False - except NoSuchElementException as e: - logger.error(f"Login fehlgeschlagen. Ein Element konnte nicht gefunden werden.") - self.driver.save_screenshot("login_error_screenshot.png") - return False except Exception as e: - logger.critical(f"Ein unerwarteter Fehler ist während des Logins aufgetreten: {e}", exc_info=True) - self.driver.save_screenshot("login_error_screenshot.png") + logger.critical(f"Ein Fehler ist während des Logins aufgetreten: {type(e).__name__}", exc_info=True) + self._save_error_screenshot() return False def close(self): - """ - Schließt den WebDriver und beendet die Browser-Session. - """ if self.driver: logger.info("Schließe den WebDriver.") self.driver.quit() if __name__ == "__main__": logger.info("Starte Dealfront Automatisierung - Phase 1: Login-Test") - scraper = None try: scraper = DealfrontScraper() - if scraper.driver: # Nur wenn der Driver erfolgreich gestartet wurde + if scraper.driver: if scraper.login(): - logger.info("Login-Prozess erfolgreich abgeschlossen. Der Bot ist nun eingeloggt.") - # Hier würde in Zukunft die weitere Logik (Suche, Extraktion) folgen. - # Für den Test warten wir 5 Sekunden, damit man das Ergebnis sehen kann. + logger.info("Login-Prozess erfolgreich abgeschlossen.") time.sleep(5) else: - logger.error("Login-Prozess ist fehlgeschlagen. Bitte Log-Meldungen und Screenshot prüfen.") + logger.error("Login-Prozess ist fehlgeschlagen.") except Exception as e: - logger.critical(f"Ein kritischer Fehler ist im Hauptprozess aufgetreten: {e}", exc_info=True) + logger.critical(f"Ein kritischer Fehler ist im Hauptprozess aufgetreten.", exc_info=True) finally: - # Stellt sicher, dass der Browser immer geschlossen wird, auch bei einem Fehler. if scraper: scraper.close() - logger.info("Login-Test beendet.") \ No newline at end of file