import os import json 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 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 logger = logging.getLogger(__name__) # ... (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.") return None, None return username, password except FileNotFoundError: logger.error(f"Credentials-Datei nicht gefunden: '{DEALFRONT_CREDENTIALS_FILE}'") return None, None except json.JSONDecodeError: logger.error(f"Fehler beim Parsen der Credentials-Datei: '{DEALFRONT_CREDENTIALS_FILE}'") return None, None 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() if not username or not password: return False try: logger.info(f"Navigiere zur Login-Seite: {DEALFRONT_LOGIN_URL}") self.driver.get(DEALFRONT_LOGIN_URL) # 1. Cookie-Banner behandeln (falls vorhanden) 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.") # 2. Anmeldedaten ausfüllen logger.info("Fülle Anmeldeformular aus...") username_field = self.wait.until(EC.visibility_of_element_located((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") 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.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. time.sleep(5) else: logger.error("Login-Prozess ist fehlgeschlagen. Bitte Log-Meldungen und Screenshot prüfen.") except Exception as e: logger.critical(f"Ein kritischer Fehler ist im Hauptprozess aufgetreten: {e}", 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.")