176 lines
8.1 KiB
Python
176 lines
8.1 KiB
Python
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__)
|
|
|
|
|
|
class DealfrontScraper:
|
|
"""
|
|
Kapselt alle Interaktionen mit der Dealfront-Plattform mittels Selenium.
|
|
"""
|
|
|
|
def __init__(self):
|
|
"""
|
|
Initialisiert den WebDriver und den WebDriverWait.
|
|
"""
|
|
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")
|
|
|
|
chrome_options.add_argument("--disable-blink-features=AutomationControlled")
|
|
chrome_options.add_experimental_option("excludeSwitches", ["enable-automation"])
|
|
chrome_options.add_experimental_option('useAutomationExtension', False)
|
|
chrome_options.add_argument("--disable-blink-features=AutomationControlled")
|
|
chrome_options.add_experimental_option("excludeSwitches", ["enable-automation"])
|
|
chrome_options.add_experimental_option('useAutomationExtension', False)
|
|
|
|
# Stellt sicher, dass der korrekte ChromeDriver automatisch heruntergeladen und verwendet wird
|
|
try:
|
|
service = ChromeService(ChromeDriverManager().install())
|
|
self.driver = webdriver.Chrome(service=service, 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. Stellen Sie sicher, dass Chrome installiert ist. Fehler: {e}")
|
|
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...")
|
|
# Expliziter Wait für den Button, der durch die ID identifiziert wird
|
|
cookie_button_id = "CybotCookiebotDialogBodyLevelButtonLevelOptinAllowAll"
|
|
cookie_button = self.wait.until(EC.element_to_be_clickable((By.ID, cookie_button_id)))
|
|
cookie_button.click()
|
|
logger.info("Cookie-Banner akzeptiert.")
|
|
# Kurze Pause nach dem Klick, damit sich die Seite anpassen kann
|
|
time.sleep(1)
|
|
except TimeoutException:
|
|
logger.warning("Cookie-Banner wurde nicht gefunden oder war nicht klickbar. Fahre fort.")
|
|
|
|
# 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.") |