In diesem Modul werden wir uns mit der Funktionsweise von „tornet_scraper“ befassen. Sie haben bereits gelernt, wie Sie Daten aus den Foren „tornet“ und „clearnet“ extrahieren können. Um diesen Prozess zu optimieren, benötigen wir jedoch einen fortschrittlichen Web-Scraper, der über Funktionen für das Scraping großer Datenmengen verfügt.

Dieses Thema mag für manche herausfordernd erscheinen, aber im Zeitalter der KI, in dem ein Großteil der Programmierung automatisiert werden kann, und da dieser Kurs KI für die Cyberabwehr nutzt, ist die Erstellung einer Web-App für das Scraping von Daten nun einfacher zugänglich.

Jede Webanwendung basiert auf einem Tech-Stack, einer Sammlung von Software-Tools, die zur Entwicklung verschiedener Komponenten der Anwendung verwendet werden. Websites wie Facebook verfügen beispielsweise über eine Benutzeroberfläche (Frontend) und eine serverseitige Logik (Backend), die den Zugriff auf die Seiten ermöglicht. In einigen Fällen können Frontend und Backend dieselbe Programmiersprache verwenden. Beispielsweise können JavaScript-basierte Web-Apps Vue für das Frontend und Nuxt für das Backend verwenden, was die Entwicklung vereinfacht, da beide in JavaScript geschrieben sind.

Für unsere Zwecke benötigen wir einen API-freundlichen und skalierbaren Tech-Stack, den Sie unabhängig anpassen können. In diesem Modul werden Programmierkonzepte nicht vertieft, da Sie den Code kopieren und in Ihr bevorzugtes KI-Tool einfügen können, um eine auf Ihren Lernstil zugeschnittene Erklärung zu erhalten.

Wenn Sie dieses Modul überspringen und mit einem funktionierenden Projekt beginnen möchten, können Sie dies tun, aber Sie verpassen dann die Erklärung der Funktionsweise des Projekts und es bleibt lediglich ein weiteres Tool. Das Abschlussprojekt finden Sie hier:

https://github.com/CyberMounties/tornet_scraper

Die Themen dieses Abschnitts umfassen Folgendes:

  1. Wie Vorlagen funktionieren
  2. Wie main.py funktioniert
  3. Wie Datenbanken funktionieren
  4. Wie Routen funktionieren
  5. Wie Scraper funktionieren
  6. Wie Dienste funktionieren

Wie Vorlagen funktionieren

Moderne Webanwendungen verwenden Vorlagen, wie z. B. eine Standard- oder Basisvorlage, um Elemente zu definieren, die auf allen Seiten erscheinen, wie z. B. ein Navigationsmenü oder eine Navigationsleiste. Sie könnten die Navigationsleiste zwar in jede Seite kopieren und einfügen, aber jede Änderung würde eine Aktualisierung jeder einzelnen Seite erfordern.

Um dies zu optimieren, verwenden wir eine Vorlagen-Engine. In Python-Webanwendungen können wir mit Jinja2 eine Basisvorlage mit Inhaltsblöcken für ein konsistentes und effizientes Design erstellen.

Öffnen Sie app/templates/base.html:

<!-- app/templates/base.html -->
<!DOCTYPE html>
<html lang="en" data-theme="nord">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>{% block title %}{% endblock %} - Tornet Scraper</title>
    <link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/full.min.css" rel="stylesheet" type="text/css" />
    <script src="https://cdn.tailwindcss.com"></script>
    <script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
</head>
<body class="min-h-screen bg-base-200">
    <!-- Navbar -->
    <div class="navbar bg-base-100 shadow-xl">
        <div class="navbar-start">
            <a class="btn btn-ghost text-xl">Tornet Scraper</a>
        </div>
        <div class="navbar-center hidden lg:flex">
            <ul class="menu menu-horizontal px-1">
                <li><a href="/" class="btn btn-ghost">Dashboard</a></li>
                <li><a href="/proxy-gen" class="btn btn-ghost">Proxy Gen</a></li>
                <li><a href="/manage-api" class="btn btn-ghost">API Management</a></li>
                <li><a href="/bot-profile" class="btn btn-ghost">Bot Profile</a></li>
                <li><a href="/marketplace-scan" class="btn btn-ghost">Marketplace Scan</a></li>
                <li><a href="/posts-scans" class="btn btn-ghost">Posts Scans</a></li>
                <li><a href="/watchlist" class="btn btn-ghost">Watchlist</a></li>
            </ul>
        </div>
        <div class="navbar-end">
            <div class="dropdown dropdown-end lg:hidden">
                <label tabindex="0" class="btn btn-ghost">
                    <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
                        <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16" />
                    </svg>
                </label>
                <ul tabindex="0" class="menu menu-sm dropdown-content mt-3 p-2 shadow bg-base-100 rounded-box w-52">
                    <li><a href="/">Dashboard</a></li>
                    <li><a href="/proxy-gen">Proxy Gen</a></li>
                    <li><a href="/manage-api">API Management</a></li>
                    <li><a href="/bot-profile">Bot Profile</a></li>
                    <li><a href="/marketplace-scan">Marketplace Scan</a></li>
                    <li><a href="/posts-scans" class="btn btn-ghost">Posts Scans</a></li>
                    <li><a href="/watchlist" class="btn btn-ghost">Watchlist</a></li>
                </ul>
            </div>
        </div>
    </div>

    <!-- Main content -->
    <main class="container mx-auto p-4 mt-6 bg-base-300 rounded-box">
        <!-- Flash Messages Container -->
        <div class="mb-4" id="flash-messages">
            {% if messages %}
                {% for message in messages %}
                    <div class="alert alert-{{ message.category | default('info') }} shadow-lg mb-4 flex justify-between items-center flash-message">
                        <div>
                            <span>{{ message.text }}</span>
                        </div>
                        <button class="btn btn-sm btn-circle btn-ghost" onclick="this.parentElement.remove()">✕</button>
                    </div>
                {% endfor %}
            {% endif %}
        </div>

        {% block content %}
        {% endblock %}
    </main>

    <script>
        // Automatically remove flash messages after 5 seconds
        document.addEventListener('DOMContentLoaded', function() {
            function removeFlashMessages() {
                const flashMessages = document.querySelectorAll('.flash-message');
                flashMessages.forEach(function(message) {
                    // Skip if already fading
                    if (message.style.opacity === '0') return;
                    setTimeout(function() {
                        message.style.transition = 'opacity 0.5s ease';
                        message.style.opacity = '0';
                        setTimeout(function() {
                            message.remove();
                        }, 500);
                    }, 5000);
                });
            }
            removeFlashMessages();
            const flashContainer = document.querySelector('#flash-messages');
            if (flashContainer) {
                const observer = new MutationObserver(function() {
                    removeFlashMessages();
                });
                observer.observe(flashContainer, { childList: true });
            }
        });
    </script>
</body>
</html>

Die Vorlage „base.html” ist eine Jinja2-Basisvorlage, die ein einheitliches Layout für alle Seiten bereitstellt. Sie enthält eine Navigationsleiste, Flash-Meldungen und Platzhalter für Titel und Inhalte, die von untergeordneten Vorlagen überschrieben werden können. Nachfolgend finden Sie eine technische Erklärung der Funktionsweise und des Erweiterungsmechanismus.

  1. HTML-Struktur:

    • Definiert ein Standard-HTML5-Dokument mit dem Attribut data-theme=„nord“ für das DaisyUI-Styling.
    • Enthält externe Ressourcen: DaisyUI CSS, Tailwind CSS und jQuery für Styling und Interaktivität.
  2. Titelblock:

    • Der Tag <title> enthält einen Jinja2-Block {% block title %}{% endblock %} - Tornet Scraper.
    • Untergeordnete Vorlagen überschreiben den title-Block, um einen seitenbezogenen Titel festzulegen, an den „ - Tornet Scraper” angehängt wird.
    • Beispiel: Eine untergeordnete Vorlage mit {% block title %}Dashboard{% endblock %} ergibt <title>Dashboard - Tornet Scraper</title>.
  3. Navigationsleiste:

    • Eine responsive Navigationsleiste mit einem Logo („Tornet Scraper”) und Navigationslinks (Dashboard, Proxy Gen usw.).
    • Auf großen Bildschirmen (lg:flex) werden die Links horizontal angezeigt, auf kleineren Bildschirmen zeigt ein Dropdown-Menü (ausgelöst durch ein Hamburger-Symbol).
  4. Flash-Meldungen:

    • Ein <div id="flash-messages"> zeigt Meldungen an, die vom Backend gesendet werden (z. B. Erfolgsmeldungen oder Fehlermeldungen).
    • Verwendet Jinja2: {% if messages %} durchläuft messages (eine Liste von Objekten mit text und category).
    • Jede Meldung wird als DaisyUI alert mit einer dynamischen Klasse (alert-{{ message.category }}, standardmäßig info) formatiert.
    • Eine Schaltfläche zum Schließen () entfernt die Meldung beim Klicken.
    • JavaScript blendet Flash-Meldungen nach 5 Sekunden mithilfe eines CSS-Übergangs (opacity) automatisch aus und entfernt sie.
    • Ein MutationObserver stellt sicher, dass neue Flash-Meldungen (die dynamisch hinzugefügt werden) ebenfalls automatisch entfernt werden.
  5. Inhaltsblock:

    • Der Abschnitt <main> enthält einen Platzhalter {% block content %}{% endblock %}.
    • Untergeordnete Vorlagen überschreiben diesen Block, um seitenbezogene Inhalte in den Hauptcontainer einzufügen, die mit Tailwind/DaisyUI-Klassen (container, mx-auto usw.) formatiert sind.
  6. Erweiterung in untergeordneten Vorlagen:

    • Untergeordnete Vorlagen verwenden {% extends ‚base.html‘ %}, um die Basisstruktur zu übernehmen.
    • Sie überschreiben „{% block title %}“ für den Seitentitel und „{% block content %}“ für den Hauptinhalt.
    • Dadurch wird das vollständige Layout „base.html“ mit „Dashboard“ im Titel und dem angegebenen Inhalt im Abschnitt „
      “ gerendert.

Diese Konfiguration gewährleistet eine konsistente Benutzeroberfläche, dynamische Titel, wiederverwendbare Navigation und eine benutzerfreundliche Verarbeitung von Flash-Meldungen auf allen Seiten.

dashboard.html

Die Datei unter app/templates/dashboard.html bietet ein anschauliches Beispiel dafür, wie base.html erweitert wird. Diese Datei ist zwar nicht unbedingt erforderlich, ich habe mir jedoch angewöhnt, sie einzufügen. Nachfolgend finden Sie den darin enthaltenen Code:

<!-- app/templates/dashboard.html -->
{% extends "base.html" %}
{% block title %}Dashboard{% endblock %}

{% block content %}
<h1 class="text-3xl font-bold text-base-content mb-4">Dashboard</h1>
{% endblock %}

Beachten Sie, wie es base.html erweitert? Der Inhalt innerhalb von {% block content %} wird in den in base.html definierten Hauptinhaltscontainer eingefügt:

    <!-- Main content -->
    <main class="container mx-auto p-4 mt-6 bg-base-300 rounded-box">
        {% block content %}
        {% endblock %}
    </main>

Der Titel wird ebenfalls definiert:

{% block title %}Dashboard{% endblock %}

In base.html lautet die Titelstruktur:

<title>{% block title %}{% endblock %} - Tornet Scraper</title>

Wenn Sie den Quellcode der Dashboard-Seite anzeigen, wird der Titel wie folgt angezeigt:

<title>Dashboard - Tornet Scraper</title>

Anstatt den Titel für jede Seite neu zu schreiben, können Sie ihn in base.html definieren und dann nur den Seitennamen für jede andere Seite definieren.


Wie main.py funktioniert

Sie finden main.py unter app/main.py.

Die Datei main.py ist das Herzstück einer FastAPI-Webanwendung und definiert Routen, Vorlagen und Datenbankinteraktionen für eine Web-Scraper-Schnittstelle. Im Folgenden finden Sie eine technische Erläuterung der globalen Variablen, der enthaltenen Router und der Routen-Handler-Funktionen.

Globale Variablen

  1. app:

    • Zweck: Die Hauptinstanz der FastAPI-Anwendung.
    • Details: Initialisiert das FastAPI-Framework zur Verarbeitung von HTTP-Anfragen und -Antworten.
  2. BASE_DIR:

    • Zweck: Speichert den absoluten Pfad des Stammverzeichnisses des Projekts.
    • Details: Wird mit os.path abgeleitet, um das Verzeichnis app/templates für Jinja2-Vorlagen zu finden.
  3. TEMPLATES_DIR:

    • Zweck: Gibt das Verzeichnis für Jinja2-Vorlagen an.
    • Details: Wird auf app/templates im Projektstammverzeichnis gesetzt, um HTML-Vorlagen zu rendern.
  4. templates:

    • Zweck: Jinja2-Vorlagen-Engine-Instanz.
    • Details: Konfiguriert, um Vorlagen aus TEMPLATES_DIR zum Rendern von dynamischem HTML zu laden.

Enthaltene Router

  1. proxy_gen_router:

    • Zweck: Registriert Routen für die Proxy-Generierungsfunktion.
    • Details: Importiert aus app.routes.proxy_gen, verarbeitet Proxy-bezogene Endpunkte.
  2. manage_api_router:

    • Zweck: Registriert Routen für die API-Verwaltung.
    • Details: Importiert aus app.routes.manage_api, verwaltet API-Schlüssel und Einstellungen.
  3. bot_profile_router:

    • Zweck: Registriert Routen für die Bot-Profilverwaltung.
    • Details: Importiert aus app.routes.bot_profile, verarbeitet die Bot-Konfiguration.
  4. marketplace_api_router:

    • Zweck: Registriert Routen für das Scannen des Marktplatzes.
    • Details: Importiert aus app.routes.marketplace, verwaltet den Abruf von Marktplatzdaten.
  5. posts_api_router:

    • Zweck: Registriert Routen für das Scannen von Beiträgen.
    • Details: Importiert aus app.routes.posts, verarbeitet Datenoperationen im Zusammenhang mit Beiträgen.
  6. watchlist_api_router:

    • Zweck: Registriert Routen für die Verwaltung der Beobachtungsliste.
    • Details: Importiert aus „app.routes.watchlist“, verwaltet Watchlist-Elemente und Scans.

Route-Handler-Funktionen

  1. „dashboard“:

    • Zweck: Rendert die Dashboard-Seite.
    • Wichtige Parameter:
      • request: FastAPI Request-Objekt für den Template-Kontext.
      • db: SQLAlchemy Session für den Datenbankzugriff (über Depends(get_db)).
    • Rückgabewert: TemplateResponse, die dashboard.html mit geflashten Meldungen aus der Sitzung rendert.
  2. proxy_gen:

    • Zweck: Zeigt die Proxy-Generierungsseite mit einer Liste von Proxys an.
    • Wichtige Parameter:
      • request: FastAPI Request-Objekt.
      • db: SQLAlchemy Session für die Abfrage von Proxys.
    • Rückgabewert: TemplateResponse, die proxy_gen.html mit Proxy-Daten (Containername, IP, Tor-Ausgangsknoten, Zeitstempel, Ausführungsstatus) und geflashten Meldungen rendert.
  3. manage_api:

    • Zweck: Zeigt die API-Verwaltungsseite mit API-Details an.
    • Wichtige Parameter:
      • request: FastAPI Request-Objekt.
      • db: SQLAlchemy Session zum Abfragen von APIs.
    • Rückgabewert: TemplateResponse, das manage_api.html mit API-Daten (ID, Name, Anbieter, Schlüssel, Modell, maximale Tokens, Eingabeaufforderung, Zeitstempel, Aktivstatus) und angezeigten Meldungen rendert.
  4. bot_profile:

    • Zweck: Zeigt die Seite zur Verwaltung von Bot-Profilen an.
    • Wichtige Parameter:
      • request: FastAPI-Objekt Request.
      • db: SQLAlchemy-Sitzung Session zum Abfragen von Bot-Profilen und der aktuellen Onion-URL.
    • Rückgabewerte: TemplateResponse, das bot_profile.html mit Bot-Profildaten (ID, Benutzername, maskiertes Passwort, Zweck, Tor-Proxy, Zeitstempel), der neuesten Onion-URL und angezeigten Meldungen rendert.
  5. marketplace:

    • Zweck: Rendert die Marktplatz-Scan-Seite mit Paginierung und Post-Scan-Daten.
    • Wichtige Parameter:
      • request: FastAPI Request-Objekt.
      • db: SQLAlchemy Session zum Abfragen von Scans.
    • Rückgabewert: TemplateResponse, die marketplace.html mit Paginierungsscans, Post-Scans und angezeigten Meldungen rendert.
  6. posts_scans:

    • Zweck: Zeigt die Übersichtsseite der Post-Scans an.
    • Wichtige Parameter:
      • request: FastAPI Request-Objekt.
      • db: SQLAlchemy Session für den Datenbankzugriff.
    • Rückgabewert: TemplateResponse, das posts_scans.html mit Flash-Meldungen rendert.
  7. posts_scan_result:

    • Zweck: Zeigt die Ergebnisse für einen bestimmten Post-Scan an.
    • Wichtige Parameter:
      • scan_id: Ganzzahlige ID des Scans.
      • request: FastAPI Request-Objekt.
      • db: SQLAlchemy Session für den Datenbankzugriff.
      • name: Optionaler Name des Scans (Zeichenkette, standardmäßig leer).
    • Rückgabewert: TemplateResponse, das posts_scan_result.html mit Scan-ID, Name und geflashten Meldungen rendert.
  8. watchlist:

    • Zweck: Rendert die Übersichtsseite der Beobachtungsliste.
    • Wichtige Parameter:
      • request: FastAPI-Objekt Request.
      • db: SQLAlchemy Session für den Datenbankzugriff.
    • Rückgabewert: TemplateResponse, die watchlist.html mit angezeigten Meldungen rendert.
  9. watchlist_profile:

    • Zweck: Zeigt das Profil eines bestimmten Watchlist-Elements und die zugehörigen Scans an.
    • Wichtige Parameter:
      • target_id: Ganzzahlige ID des Watchlist-Eintrags.
      • request: FastAPI-Objekt „Request“.
      • db: SQLAlchemy Session zum Abfragen von Watchlist-Elementen und Scans.
    • Rückgabewert: TemplateResponse, die watchlist_profile.html mit Watchlist-Element, Scandaten und Flash-Meldungen rendert; löst HTTPException aus (404 für fehlende Elemente, 500 für Serverfehler).

Funktionsweise von Datenbanken

In unserer FastAPI-Anwendung verwaltet db.py die Konfigurationen der Datenbank-Engine, während models.py das Datenbankschema und die Tabellen definiert. Die Verwendung einer separaten db.py-Datei ermöglicht ein einfaches Wechseln zwischen verschiedenen Datenbanktypen. Obwohl tornet_scraper SQLite3 für Prototyping und Tests verwendet, ist es für das Scraping großer Datenmengen nicht ideal. Für Produktionsumgebungen empfehlen wir den Wechsel zu einer robusteren Datenbank wie PostgreSQL.

Sie können auch Logik in db.py implementieren, um je nach Bedingungen oder Umgebung (z. B. Entwicklung, Test oder Produktion) dynamisch zwischen SQLite3 und einem Datenbankserver zu wechseln.

db.py

Die Datei db.py konfiguriert die Datenbankverbindung und die Sitzungsverwaltung für eine FastAPI-Anwendung mit SQLAlchemy. Nachfolgend finden Sie eine kurze Erläuterung der Komponenten und Funktionen.

Sie finden db.py in app/database/db.py.

  1. Zweck:
  • Richtet eine SQLite-Datenbankverbindung ein, erstellt Tabellen und stellt eine Abhängigkeit für Datenbanksitzungen in FastAPI-Routen bereit.
  1. Globale Variablen:

    • BASE_DIR:
      • Zweck: Definiert das Stammverzeichnis des Projekts.
      • Details: Verwendet Path(__file__).resolve().parent.parent.parent, um den absoluten Pfad zum Projektstammverzeichnis zu berechnen und sicherzustellen, dass die Datenbankdatei korrekt gespeichert ist.
    • DATABASE_URL:
      • Zweck: Gibt die Datenbankverbindungszeichenfolge an.
      • Details: Für SQLite als sqlite:///{BASE_DIR}/tornet_scraper.db konfiguriert, wobei auf eine Datei tornet_scraper.db im Projektstammverzeichnis verwiesen wird.
    • engine:
      • Zweck: SQLAlchemy-Engine für Datenbankinteraktionen.
      • Details: Erstellt mit create_engine(DATABASE_URL, connect_args={„check_same_thread“: False}), um SQLite-Verbindungen zu verarbeiten. Das Argument check_same_thread ermöglicht die Verwendung von SQLite in einer multithreaded FastAPI-Umgebung.
    • SessionLocal:
      • Zweck: Factory zum Erstellen von Datenbanksitzungen.
      • Details: Konfiguriert mit sessionmaker(autocommit=False, autoflush=False, bind=engine), um Sitzungen zu erstellen, die an die Engine gebunden sind, mit manueller Commit- und Flush-Steuerung.
  2. Funktionen:

    • get_db:
      • Zweck: Stellt eine Datenbanksitzung für FastAPI-Routen bereit.
      • Wichtige Parameter: Keine (Abhängigkeitsfunktion).
      • Rückgabewert: Gibt eine SQLAlchemy-Sitzung (db) zurück und stellt sicher, dass sie nach der Verwendung geschlossen wird.
      • Details: Wird als FastAPI-Abhängigkeit (über Depends(get_db)) verwendet, um eine Sitzung in Routen-Handler einzufügen. Der try/finally-Block garantiert, dass die Sitzung geschlossen wird, wodurch Ressourcenlecks verhindert werden.
    • init_db:
      • Zweck: Initialisiert die Datenbank durch Erstellen von Tabellen.
      • Wichtige Parameter: Keine.
      • Rückgabewert: Keine.
      • Details: Importiert Base aus models.py und ruft Base.metadata.create_all(bind=engine) auf, um alle definierten Tabellen (z. B. proxies, apis) in der SQLite-Datenbank zu erstellen, sofern diese noch nicht vorhanden sind.
  3. Verwendung:

    • Zweck: Aktiviert Datenbankoperationen in der Anwendung.
    • Details: init_db wird in main.py aufgerufen, um das Datenbankschema beim Start einzurichten. get_db wird in Routen-Handlern (z. B. /proxy-gen, /manage-api) verwendet, um Daten in den in models.py definierten Tabellen abzufragen oder zu ändern. Die Sitzung gewährleistet die Transaktionsintegrität und die ordnungsgemäße Bereinigung der Ressourcen.

models.py

Die Datei models.py definiert das Datenbankschema für eine FastAPI-Anwendung unter Verwendung von SQLAlchemy und legt Tabellen, Spalten und Beziehungen fest. Nachfolgend finden Sie eine kurze Erläuterung der Komponenten und Funktionen.

Sie finden models.py unter app/database/models.py.

  1. SQLAlchemy-Übersicht:

    • Zweck: SQLAlchemy ist eine ORM-Bibliothek (Object-Relational Mapping) für Python, die die Interaktion mit einer relationalen Datenbank mithilfe von Python-Objekten ermöglicht.
    • Details: Ordnet Python-Klassen Datenbanktabellen zu und ermöglicht CRUD-Operationen über eine objektorientierte Syntax. Verwendet declarative_base, um eine Basisklasse (Base) für Modelldefinitionen zu erstellen.
  2. Spalten:

    • Zweck: Stellen Felder in einer Datenbanktabelle dar.
    • Details: Werden mit der SQLAlchemy-Klasse „Column“ definiert, wobei Attribute wie Datentyp (z. B. „Integer“, „String“, „DateTime“), Einschränkungen (z. B. „primary_key“, „unique“, „nullable“) und Standardwerte (z. B. „datetime.utcnow“). Spalten speichern Daten für jeden Datensatz in einer Tabelle.
  3. Tabellen:

    • Zweck: Stellen Datenbanktabellen dar, in denen strukturierte Daten gespeichert sind.
    • Details: Jede Klasse (z. B. „Proxy“, „APIs“) erbt von „Base“ und definiert eine Tabelle über „tablename“. Tabellen enthalten Spalten und optionale Einschränkungen (z. B. „UniqueConstraint“ für eindeutige Kombinationen von Feldern). Beispiele hierfür sind „proxies“, „apis“, „bot_profiles“ usw.
  4. Beziehungen:

    • Zweck: Definieren von Assoziationen zwischen Tabellen für relationale Abfragen.
    • Details: Werden mithilfe von „ForeignKey“-Spalten festgelegt (z. B. verweist „MarketplacePost.scan_id“ auf „marketplace_post_scans.id“). Beziehungen ermöglichen das Verknüpfen von Tabellen, z. B. das Verknüpfen von „MarketplacePost“ mit „MarketplacePostScan“ über „scan_id“. SQLAlchemy behandelt diese in Abfragen als Objektverweise.
  5. Enums:

    • Zweck: Definieren Sie kontrollierte Vokabulare für bestimmte Felder.
    • Details: Verwendet Python enum.Enum (z. B. BotPurpose, ScanStatus), um Spaltenwerte auf vordefinierte Optionen (z. B. SCRAPE_MARKETPLACE, RUNNING) zu beschränken. Wird über Enum als Zeichenfolgen in der Datenbank gespeichert.
  6. Schlüsselkomponenten:

    • Base: Die SQLAlchemy-Basisklasse für alle Modelle, die zur Generierung von Tabellenschemata verwendet wird.
    • Tabellendefinitionen: Klassen wie Proxy, APIs, BotProfile usw. definieren Tabellen zum Speichern von Proxy-Details, API-Konfigurationen, Bot-Profilen, Onion-URLs, Marktplatz-Scans, Beiträgen und Beobachtungslisten.
    • Einschränkungen: Eindeutige Einschränkungen (z. B. UniqueConstraint in MarketplacePost) gewährleisten die Datenintegrität, indem sie doppelte Datensätze basierend auf bestimmten Spaltenkombinationen verhindern.
    • Zeitstempel: Viele Tabellen enthalten eine Spalte timestamp (standardmäßig datetime.utcnow), um die Erstellungs-/Aktualisierungszeiten von Datensätzen zu verfolgen.
  7. Verwendung:

    • Zweck: Modelle werden von der FastAPI-Anwendung verwendet, um mit der Datenbank zu interagieren.
    • Details: Die Routen in main.py fragen diese Modelle (über SQLAlchemy-Sitzungen) ab, um Daten abzurufen oder zu speichern, die dann zur Darstellung an Jinja2-Vorlagen übergeben oder in der API-Logik verarbeitet werden. Beispielsweise werden Proxy-Daten in der Route /proxy-gen abgefragt, um Proxy-Details anzuzeigen.

So funktionieren Routen

Bei der API-Entwicklung ist eine effektive Organisation der API-Endpunkte von entscheidender Bedeutung, da es unpraktisch ist, alle Anfragen über einen einzigen Endpunkt zu leiten.

Das Format /api/posts/get-post gibt beispielsweise an, dass /api/posts/* für postbezogene Vorgänge wie das Anzeigen, Löschen oder Ändern von Posts vorgesehen ist. Alle zugehörigen API-Aufrufe werden mit /api/posts/ vorangestellt.

Da unsere Anwendung Funktionen wie Proxy-Generierung, Bot-Verwaltung, Marktplatz-Scraping, Post-Scraping, Bedrohungsüberwachung und mehr umfasst, ist eine ordentliche Organisation der Endpunkte unerlässlich.

Sie finden alle Routen in „app/routes/*.py“, das sechs Python-Dateien enthält. Nachfolgend finden Sie ein Beispiel für „bot_profile.py“:

# app/routes/bot_profile.py
import logging
from fastapi import APIRouter, Depends, HTTPException, Request
from sqlalchemy.orm import Session
from pydantic import BaseModel
from app.database.models import BotProfile, OnionUrl, BotPurpose, APIs
from app.database.db import get_db
from typing import Optional
from app.services.tornet_forum_login import login_to_tor_website
from app.services.gen_random_ua import gen_desktop_ua


logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)


bot_profile_router = APIRouter(prefix="/api/bot-profile", tags=["API", "Bot Profile Management"])


# Pydantic models for validation
class BotProfileCreate(BaseModel):
    username: str
    password: str
    purpose: str
    tor_proxy: Optional[str] = None
    session: Optional[str] = None


class BotProfileUpdate(BaseModel):
    username: Optional[str] = None
    password: Optional[str] = None
    purpose: Optional[str] = None
    tor_proxy: Optional[str] = None
    user_agent: Optional[str] = None
    session: Optional[str] = None


class OnionUrlCreate(BaseModel):
    url: str


# Get all bot profiles
@bot_profile_router.get("/list")
async def get_bot_profiles(db: Session = Depends(get_db)):
    try:
        profiles = db.query(BotProfile).all()
        return [
            {
                "id": p.id,
                "username": p.username,
                "password": "********",
                "actual_password": p.password,
                "purpose": p.purpose.value,
                "tor_proxy": p.tor_proxy,
                "has_session": bool(p.session and len(p.session) > 0),
                "session": p.session,
                "user_agent": p.user_agent,
                "timestamp": p.timestamp.isoformat()
            } for p in profiles
        ]
    except Exception as e:
        logger.error(f"Error fetching bot profiles: {str(e)}")
        raise HTTPException(status_code=500, detail="Internal server error")

Wir verwenden Protokollierung, um Ereignisse innerhalb der Anwendung zu verfolgen. Die Protokollierung ist entscheidend für das Verständnis der Vorgänge in Ihrer Webanwendung. Ohne sie arbeiten Sie blind. Protokolle können zwar in einer Datei gespeichert werden, ich vermeide dies jedoch in der Regel während der lokalen Entwicklung, empfehle es aber für Produktionsumgebungen.

Der wichtigste Aspekt ist jedoch die Erstellung von Routern:

bot_profile_router = APIRouter(prefix="/api/bot-profile", tags=["API", "Bot Profile Management"])

Dieser Router legt ein Endpunktpräfix /api/bot-profile fest, das alle zugehörigen Endpunkte verwenden.

Die folgende Funktion ruft beispielsweise alle Bot-Profile aus der Tabelle BotProfile ab:

@bot_profile_router.get("/list")
async def get_bot_profiles(db: Session = Depends(get_db)):

Wenn Sie @bot_profile_router.get(„/list“) verwenden, wird dem Endpunkt das Präfix /api/bot-profile vorangestellt, was zu /api/bot-profile/list führt.

Diese Struktur ermöglicht eine effiziente Organisation von Hunderten oder Tausenden von API-Routen. Der bot_profile_router wird in main.py importiert und registriert.


How scrapers work

Alle Scraper werden in app/scrapers/*.py gespeichert.

Diese Dateien werden in der Regel als Module in andere Teile der Codebasis importiert. In marketplace_scraper.py nimmt beispielsweise die Funktion create_pagination_batches eine Webseiten-URL als Eingabe, z. B.:

http://site.url/pagination?page=1

Sie erstellt einen Stapel von Paginierungen wie folgt:

http://site.url/pagination?page=1
http://site.url/pagination?page=2
http://site.url/pagination?page=3
--- snip ---
http://site.url/pagination?page=10

Dieser Ansatz eignet sich gut für die Aufgabenverteilung, beispielsweise um jedem Bot einen Stapel von 10 Paginierungsseiten zum Scrapen zuzuweisen.


Funktionsweise der Dienste

Alle Dienste befinden sich in app/services/*.py.

Dienste werden ebenfalls als Module verwendet, erfüllen jedoch einen anderen Zweck und übernehmen Aufgaben wie:

  1. Verwalten von Docker für Proxys
  2. Proxys generieren
  3. Anmeldungen durchführen
  4. CAPTCHAs umgehen

Wie Sie später erfahren werden, verwenden wir Docker-Container, um Tor-Proxys lokal zu erstellen. Wenn ein Proxy nicht mehr benötigt wird, muss er gelöscht werden. Um zu überprüfen, ob Proxys ausgeführt werden, können Sie eine Funktion wie container_running aus container_status.py verwenden, um den Status des Docker-Containers zu überprüfen.