dealfront_enrichment.py aktualisiert
This commit is contained in:
@@ -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.")
|
||||
Reference in New Issue
Block a user