dealfront_enrichment.py aktualisiert

This commit is contained in:
2025-07-03 04:55:33 +00:00
parent a8aa57bf29
commit 401be12bc0

View File

@@ -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.")