read_transactions.webcrawler.base

author:

Tim Häberlein

version:

2.2

date:

21.10.2025

organisation:

TU Dresden, FZM

WebCrawler

Zentrale Basisklasse für alle Crawler im Projekt read_transactions_fm. - Einheitliches Logging über MainLogger - Nutzung einer externen WebDriverFactory - Robuste Typprüfung & flexible Datumsübergabe - Standardmethoden für Login, Download, Verarbeitung und Speicherung

Classes

WebCrawler

Abstrakte Basisklasse für alle Crawler im Paket read_transactions.

Module Contents

class read_transactions.webcrawler.base.WebCrawler(name: str = 'WebCrawler', output_path: str = 'out', start_date: str | pandas.Timestamp | datetime.date | None = None, end_date: str | pandas.Timestamp | datetime.date | None = None, details: bool = True, logging_level: str = 'INFO', logfile: str | None | None = None, *, browser: str = 'edge', headless: bool = False, user_agent: str | None = None)

Abstrakte Basisklasse für alle Crawler im Paket read_transactions.

Diese Klasse kapselt den gesamten gemeinsamen Funktionsumfang: - zentrale Logging-Initialisierung über MainLogger - automatisches Laden von Konfiguration und Zugangsdaten aus config.yaml - standardisierte Selenium-WebDriver-Erzeugung über WebDriverFactory - konsistente Handhabung von Downloads, Datenverarbeitung und Speicherung

Subklassen (z. B. ArivaCrawler, AmazonCrawler) müssen mindestens die Methoden login() und download_data() überschreiben.

Typischer Ablauf:

```python with MyCrawler(start_date=“01.01.2024“, end_date=“31.03.2024“) as crawler:

crawler.login() crawler.download_data() crawler.process_data() crawler.save_data()

```

Parameter

namestr, optional

Logisch eindeutiger Name der Crawler-Instanz (z. B. "ariva").

output_pathstr, optional

Verzeichnis, in dem Ausgabedateien gespeichert werden (Standard: out).

start_datestr | pandas.Timestamp | datetime.date, optional

Startdatum für den Datenabruf.

end_datestr | pandas.Timestamp | datetime.date, optional

Enddatum für den Datenabruf.

detailsbool, optional

Ob zusätzliche Details extrahiert werden sollen? Uum beispiel bei trade_republic zusätzliche Order-Details oder bei amazon_visa vgl. mit amazon käufen. (Standard: True).

logging_levelstr, optional

Log-Level der Instanz (z. B. „DEBUG“, „INFO“, „WARNING“). Standard: INFO.

global_log_levelstr, optional

Globales Log-Level für das gesamte Paket (Standard: INFO).

logfilestr, optional

Pfad zu einer zentralen Logdatei (Standard: logs/read_transactions.log).

browserstr, optional

Verwendeter Browser-Treiber (edge, chrome oder firefox). Standard: edge.

headlessbool, optional

Aktiviert Headless-Modus (sofern vom Browser unterstützt). Standard: False.

user_agentstr, optional

Optionaler benutzerdefinierter User-Agent.

Attribute

driverselenium.webdriver.Remote

Aktiver Selenium-WebDriver.

datapandas.DataFrame | dict[str, pandas.DataFrame]

Heruntergeladene bzw. verarbeitete Daten.

_credentialsdict

Login-Daten des Crawlers (aus config.yaml).

_urlsdict

URL-Mappings des Crawlers (aus config.yaml).

_loggerlogging.Logger

Instanzspezifischer Logger.

_download_directorystr

Temporäres Verzeichnis für heruntergeladene Dateien.

__name = 'WebCrawler'
__output_path = 'out'
_logging_lvl = 'INFO'
__logger
property start_date: pandas.Timestamp

Startdatum (immer als pandas.Timestamp gespeichert).

property end_date: pandas.Timestamp

Enddatum (immer als pandas.Timestamp gespeichert).

property with_details: bool

Gibt zurück, ob zusätzliche Details extrahiert werden:

  • Bei Trade Republic z. B. zusätzliche Order-Details

  • Bei Amazon Visa z. B. Verknüpfung mit Amazon-Käufen

_state = 'initialized'
_download_directory = b'.'
_initial_file_count = 0
__credentials: Dict[str, str]
__urls: Dict[str, str]
__data: pandas.DataFrame | Dict[str, pandas.DataFrame]
__account_balance = 0.0
driver
property name: str

Name der Crawler-Instanz.

property data: pandas.DataFrame | Dict[str, pandas.DataFrame]

Heruntergeladene und ggf. verarbeitete Daten.

property _credentials: Dict[str, str]

Login-Daten für den Crawler. Beinhaltet z. B. Benutzername und Passwort. - user: str, Benutzername - password: str, Passwort

property _urls: Dict[str, str]

URLs für den Crawler. Beinhaltet z. B. Login- und Download-Links. - login: str, Login-URL - transactions: str, Transaktions-URL - kurse: Dict[str, str]: Kurs-URLs

property _logger: logging.Logger

Interner Logger (für Subklassen).

property account_balance: str

Gibt den aktuellen Kontostand zurück.

login() None

Wird von Subklassen überschrieben – führt Login auf der Webseite aus.

download_data() None

Wird von Subklassen überschrieben – startet Download-Vorgang.

process_data(read_temp_files: bool = True, sep: str = ';') None

Optional von Subklassen überschreiben – verarbeitet geladene Daten. Standardmäßig werden alle Dateien im temporären Download-Verzeichnis eingelesen und in ein einziges DataFrame zusammengeführt. Dabei wird die Funktion preprocess_data() für jedes DataFrame aufgerufen.

Im Anschluss wird self.data normalisiert.

Parameter:
  • read_temp_files (bool, optional) – Ob Dateien im temporären Download-Verzeichnis eingelesen werden sollen.

  • sep (str, optional) – Trennzeichen für CSV-Dateien. Standard ist ‚;‘.

Rückgabe:

None

preprocess_data(key: str, df: pandas.DataFrame) pandas.DataFrame

Bereinigt ein einzelnes DataFrame. Für weitere Verarbeitung muss funktion in Unterklasse überschrieben werden.

Parameter:
  • key (str) – Schlüssel des DataFrames (bei dict-Daten).

  • df (pd.DataFrame) – Eingabedaten.

Rückgabe:

Bereinigte Daten.

Rückgabetyp:

pd.DataFrame

save_data() None

Speichert geladene Daten als CSV.

close() None

Schließt WebDriver und löscht temporäre Ordner.

_load_config() None

Lädt Crawler-spezifische Konfiguration aus der zentralen config.yaml.

Diese Methode liest:
  • Zugangsdaten (user, password, token, …)

  • URLs (für den jeweiligen Crawler)

Verursacht:
  • FileNotFoundError – Wenn keine gültige config.yaml gefunden wurde.

  • KeyError – Wenn keine passenden Einträge für diesen Crawler vorhanden sind.

__enter__() WebCrawler

Context-Manager-Einstiegspunkt.

Wird automatisch aufgerufen, wenn der Crawler in einem with-Block verwendet wird. Gibt die aktuelle Instanz zurück, sodass alle Methoden wie gewohnt verfügbar sind.

Beispiel:
>>> with WebCrawler(browser="edge", headless=True) as crawler:
...     crawler.login()
...     crawler.download_data()
...     crawler.process_data()
...     crawler.save_data()
# Nach Ende des Blocks wird automatisch close() ausgeführt.
__exit__(exc_type, exc_value, traceback) bool

Context-Manager-Ausstiegspunkt.

Wird automatisch aufgerufen, wenn der with-Block endet, unabhängig davon, ob ein Fehler aufgetreten ist. Führt close() aus, um den WebDriver zu schließen und temporäre Dateien zu löschen.

Parameter:
  • exc_type – Typ der Exception (falls eine aufgetreten ist)

  • exc_value – Exception-Instanz

  • traceback – Traceback-Objekt der Exception

Rückgabe:

False, damit Exceptions im with-Block nicht unterdrückt werden. (Python wirft sie weiter.)

Rückgabetyp:

bool

wait_for_element(by: str, selector: str, timeout: int = 15) selenium.webdriver.remote.webelement.WebElement

Wartet auf das Vorhandensein eines Elements und gibt es zurück.

Parameter:
  • by (str | selenium.webdriver.common.by.By) – Suchstrategie für Selenium. Akzeptiert entweder: - einen case‑insensitiven String-Key (siehe Accepted string keys) - oder direkt eine Selenium-By‑Konstante (z. B. By.CSS_SELECTOR). Bei Übergabe eines bereits aufgelösten By/Tuple wird dieses direkt verwendet.

  • selector (str) – Selektor-String passend zur gewählten Strategie (z. B. CSS-Selector oder XPath).

  • timeout (int, optional) – Maximale Wartezeit in Sekunden. Standard ist 15.

Rückgabe:

Das gefundene Webelement.

Rückgabetyp:

WebElement

Verursacht:

selenium.common.exceptions.TimeoutException – Wenn das Element innerhalb der timeout-Zeit nicht gefunden wird.

Accepted string keys (case-insensitive) and mapping:
  • „id“ -> By.ID

  • „name“ -> By.NAME

  • „css“, „css selector“-> By.CSS_SELECTOR

  • „xpath“ -> By.XPATH

  • „link text“ -> By.LINK_TEXT

  • „partial link text“ -> By.PARTIAL_LINK_TEXT

  • „tag“ -> By.TAG_NAME

  • „class“ -> By.CLASS_NAME

Default behavior:

Wenn ein unbekannter String-Key übergeben wird, wird By.CSS_SELECTOR als Fallback verwendet. Wenn bereits eine By-Konstante oder ein Tuple (By.SOMETHING, selector) übergeben wird, wird dieser Wert unverändert verwendet.

Examples

>>> # mit String-Key (case-insensitive)
>>> elem = self.wait_for_element("css", "div.my-class")
>>> # mit vollständigem Key
>>> elem = self.wait_for_element("css selector", "div.my-class")
>>> # mit Selenium By-Konstante
>>> from selenium.webdriver.common.by import By
>>> elem = self.wait_for_element(By.ID, "username")
>>> # direktes Tuple (By, selector) möglich, falls intern verwendet
>>> elem = self.wait_for_element((By.XPATH, "//button[text()=\"OK\"]"), None)
wait_clickable_and_click(by: str, selector: str, timeout: int = 15) None

Wartet auf ein Element und klickt es dann an.

Parameter:
  • by (str | By) – Suchstrategie oder By-Konstante.

  • selector (str) – Selektor-String.

  • timeout (int, optional) – Timeout in Sekunden. Standard 15.

Siehe auch

wait_for_element: Wartet auf das Element und gibt es zurück (verwendet von dieser Methode).

Sphinx cross-reference (für generierte Docs / IDE‑Plugins):

wait_for_element() or fully qualified: wait_for_element()

find_first_matching_element(selectors: list[tuple[str, str]], timeout_each: int = 10, debug_msg: bool = False) selenium.webdriver.remote.webelement.WebElement

Versucht mehrere Selektoren nacheinander und gibt das erste gefundene Element zurück. :param selectors: Liste von (by, selector)-Tupeln. :param timeout_each: Timeout pro Selektor in Sekunden.

Rückgabe:

Erstes gefundenes Element.

Rückgabetyp:

WebElement

Verursacht:

selenium.common.exceptions.TimeoutException – Wenn kein Element für die gegebenen Selektoren gefunden wird.

Example

>>> selectors = (("css", "div.class1"), ("xpath", "//div[@id='main']"))
>>> elem = self.find_first_matching_element(selectors, timeout_each=5)

See also: wait_for_element: Wartet auf das Element und gibt es zurück (verwendet von dieser Methode).

Sphinx cross-reference (für generierte Docs / IDE‑Plugins):

wait_for_element() or fully qualified: wait_for_element()

click_first_matching_element(selectors: list[tuple[str, str]], timeout_each: int = 10) None

Versucht mehrere Selektoren nacheinander und klickt das erste gefundene Element an. :param selectors: Liste von (by, selector)-Tupeln. :param timeout_each: Timeout pro Selektor in Sekunden.

Verursacht:

selenium.common.exceptions.TimeoutException – Wenn kein Element für die gegebenen Selektoren gefunden wird.

Example

>>> selectors = (("css", "button.accept"), ("xpath", "//button[text()='Accept']"))
>>> self.click_first_matching_element(selectors, timeout_each=5)
find_all_in(elem: selenium.webdriver.remote.webelement.WebElement, selectors: list[tuple[str, str]], debug_msg: bool = False) list[selenium.webdriver.remote.webelement.WebElement]

Findet alle passenden Unterelemente innerhalb eines Elements.

find_first_in(elem: selenium.webdriver.remote.webelement.WebElement, selectors: list[tuple[str, str]], debug_msg: bool = False) selenium.webdriver.remote.webelement.WebElement

Findet das erste passende Unterelement innerhalb eines Elements.

scroll_into_view(element) None

Scrollt ein Element ins Viewport.

click_js(element) None

Klickt ein Element via JavaScript (Fallback bei Overlay o.Ä.).

accept_cookies_if_present(selectors: tuple[str, Ellipsis] = ('button#onetrust-accept-btn-handler', "button[aria-label='Akzeptieren']", 'button.cookie-accept'), timeout_each: int = 3) bool

Versucht gängige Cookie-Banner wegzuklicken.

Parameter:
  • selectors – Liste möglicher CSS-Selektoren für Zustimmungs-Buttons.

  • timeout_each – Zeitfenster pro Selektor.

Rückgabe:

True, wenn ein Banner geschlossen wurde.

Rückgabetyp:

bool

_wait_for_new_file(timeout: float = 30, check_interval: float = 0.5, include_temp: bool = True) str | None

Wartet auf eine neue Datei im Download-Ordner und gibt deren Dateinamen zurück.

Parameter:
  • timeout – Maximale Wartezeit in Sekunden.

  • check_interval – Prüfintervall in Sekunden.

  • include_temp – Ob temporäre Dateien (.crdownload/.tmp) berücksichtigt werden.

Rückgabe:

Der Dateiname der neu erkannten Datei oder None bei Timeout.

_read_temp_files(sep: str = ';', max_retries: int = 10, retry_wait: float = 1.0, check_interval: float = 0.1, download_timeout: float = 10.0) bool

Liest Dateien aus dem Download-Ordner in self.data.

Unterstützt CSV, XLS, XLSX. Wartet optional, bis temporäre Download-Dateien (.crdownload/.tmp) verschwunden sind.

Rückgabe:

True bei Erfolg, sonst False.

_retry_func(func, max_retries: int = 3, wait_seconds: float = 1.0, args: tuple | None = None, kwargs: dict | None = None) bool

Versucht die Funktion mehrfach bei Fehlschlag.

Parameter:
  • func – Funktion, die ausgeführt werden soll.

  • max_retries – Maximale Anzahl an Versuchen.

  • wait_seconds – Wartezeit zwischen den Versuchen.

Rückgabe:

True bei erfolgreicher Ausfürhung, sonst False.

Rückgabetyp:

bool

_wait_for_manual_exit(msg: str = None)

Wartet auf manuelles Schließen des Browsers durch den Nutzer.

Parameter:

msg – Nachricht, die angezeigt werden soll. (Optional)

Rückgabe:

_wait_for_condition(condition_func, timeout: float = 30.0, check_interval: float = 0.5) bool

Wartet, bis eine Bedingungsfunktion True zurückgibt.

Parameter:
  • condition_func – Funktion, die eine boolesche Bedingung prüft.

  • timeout – Maximale Wartezeit in Sekunden.

  • check_interval – Prüfintervall in Sekunden.

Rückgabe:

True, wenn die Bedingung erfüllt wurde, sonst False.

Rückgabetyp:

bool

_delete_header(df: pandas.DataFrame, header_key: str = 'datum') pandas.DataFrame

Löscht die Header-Zeile bis zum header_key aus einem DataFrame und setzt die Spaltennamen.

Parameter:
  • df – Eingabe-DataFrame.

  • header_key – Key der Spalte, die im Header enthalten sein muss. (Standard: ‚datum‘)

Rückgabe:

DataFrame ohne Header-Zeile.

_normalize_dataframe(df: pandas.DataFrame, remove_nan: bool = False, date_as_str: bool = False) pandas.DataFrame

Normalisiert die Transaktionsdaten eines DataFrames. - Datumsspalten in einheitliches Format bringen - Betragsspalten bereinigen - Spaltennamen vereinheitlichen

Parameter:
  • df – Eingabe-DataFrame.

  • remove_nan – Ob Zeilen mit NaN-Werten entfernt werden sollen. (Standard: False)

Rückgabe:

DataFrame mit normalisierten Daten.

_normalize_date_in_dataframe(df: pandas.DataFrame, date_column: str, *, date_as_str: bool = False, dayfirst: bool = True) pandas.DataFrame

Normalisiert die Datumswerte in der angegebenen Spalte eines DataFrames.

Parameter:
  • df – Eingabe-DataFrame.

  • date_column – Name der Spalte mit den Datumswerten.

  • date_as_str – Ob das Datum als String zurückgegeben werden soll. (Standard: False)

  • dayfirst – Ob der Tag vor dem Monat steht. (Standard: True)

Rückgabe:

DataFrame mit normalisierten Datumswerten.

_normalize_amount_in_dataframe(df: pandas.DataFrame, amount_column: str, *, remove_nan: bool = False) pandas.DataFrame

Normalisiert die Beträge in der angegebenen Spalte eines DataFrames.

Parameter:
  • df – Eingabe-DataFrame.

  • amount_column – Name der Spalte mit den Beträgen.

  • remove_nan – Ob Zeilen mit NaN-Werten entfernt werden sollen. (Standard: False)

Rückgabe:

DataFrame mit normalisierten Beträgen.

_normalize_amount(value: Any) float

Bereinigt Währungs-Strings und konvertiert sie in float. Unterstützt pandas Series, DataFrames und einzelne Strings.

Parameter:

value – Eingabewert (Series, DataFrame oder String).

Rückgabe:

Bereinigter Wert als float, Series oder DataFrame.

_filter_out_rows_by_needles(df: pandas.DataFrame, column: str, needles: list[str], *, case_sensitive: bool = False, allow_regex: bool = False, whole_word: bool = False, keep_na: bool = True) pandas.DataFrame

Entfernt Zeilen, wenn column einen Begriff der needles enthält.

Parameter:
  • df – Eingabe-DataFrame.

  • column – Zu durchsuchende Spalte.

  • needles – Liste Suchbegriffe (oder Regex, wenn allow_regex=True).

  • case_sensitive – Groß-/Kleinschreibung beachten?

  • allow_regexneedles als echte Regex behandeln?

  • whole_word – Nur ganze Wörter matchen (setzt allow_regex=True intern).

  • keep_na – NaN in column behalten (True) oder als „kein Treffer“ behandeln (False).

Rückgabe:

Gefiltertes DataFrame (Treffer werden entfernt).

_filter_in_rows_by_needles(df: pandas.DataFrame, column: str, needles: list[str], *, case_sensitive: bool = False, allow_regex: bool = False, whole_word: bool = False, keep_na: bool = True) pandas.DataFrame

Behalte nur Zeilen, wenn column einen Begriff der needles enthält. :param df: Eingabe-DataFrame. :param column: Zu durchsuchende Spalte. :param needles: Liste Suchbegriffe (oder Regex, wenn allow_regex=True). :param case_sensitive: Groß-/Kleinschreibung beachten? :param allow_regex: needles als echte Regex behandeln? :param whole_word: Nur ganze Wörter matchen (setzt allow_regex=True intern). :param keep_na: NaN in column behalten (True) oder als „kein Treffer“ behandeln (False).

Rückgabe:

Gefiltertes DataFrame (nur Treffer werden behalten).

_filter_columns_by_names(df: pandas.DataFrame, column_names: list[str], *, add_missing: bool = False, fill_value=pd.NA, case_insensitive: bool = False) pandas.DataFrame

Behält nur die Spalten in column_names (in derselben Reihenfolge). Optional:

  • add_missing: fehlende Spalten erzeugen (mit fill_value)

  • case_insensitive: Spaltennamen case-insensitiv auflösen

_rename_columns_by_map(df: pandas.DataFrame, rename_map: dict[str, str], *, case_insensitive: bool = False)

Benennt Spalten gemäß rename_map um. :param df: Eingabe-DataFrame. :param rename_map: Dict mit {alter_spaltenname: neuer_spaltenname}. :param case_insensitive: Ob Spaltennamen case-insensitiv gesucht werden sollen. :type case_insensitive: bool, optional

Optional: case_insensitive: Sucht Spaltennamen case-insensitiv.

Rückgabe:

DataFrame mit umbenannten Spalten.

_abort_windows_passkey(tries: int = 10, timeout: int = 10) bool

Versucht, einen nativen Windows-Passkey/Hello/WebAuthn-Dialog zu schließen. Priorität: pywinauto -> ctypes SendInput -> pyautogui/keyboard -> ESC an Browser. Gibt True zurück, wenn mind. ein Abbruchversuch gesendet wurde.

_log_error_with_debug_msg(msg: str | None = None) None

Loggt eine Debug-Nachricht mit Funktionsname, Dateiname und Zeilennummer der aufrufenden Stelle (nicht der Logger-Funktion selbst).

Parameter:

msg – Zusätzliche Nachricht, die geloggt werden soll. (Optional)

Rückgabe:

None