In diesem Abschnitt werde ich erklären, wie wir ein groß angelegtes Post-Scraping der gesammelten Beiträge durchführen können. Die Vorarbeit ist bereits erledigt, die Beitragsdetails wurden gescrapt, aber ihr Inhalt noch nicht. In der letzten Phase scrapen wir den Inhalt und führen bei Bedarf eine Übersetzung durch.

Dies ist die interessanteste Phase, da wir uns dem Ende des Kurses nähern. Die Identifizierung der IAB-Verkäufe war unser Ausgangspunkt, und dafür sind Sie ja schließlich hier, aber dieser Abschnitt ist zumindest auf den ersten Blick der komplizierteste von allen.

Sie ist komplex, weil wir eine Lösung für das Scannen großer Datenmengen entwickeln, was natürlich mit vielen beweglichen Teilen kompliziert ist – das ist einfach die Realität der groß angelegten Bedrohungsüberwachung.

Die Themen dieses Abschnitts umfassen Folgendes:

  1. Komponenten des Datenscrapers
  2. Datenbankmodelle
  3. Vorlagen für die Verwaltung und Anzeige von Scans
  4. Vorlage für die Anzeige der Ergebnisse jedes Scans
  5. Backend-Routen
  6. Testen

Komponenten des Datenscrapers

Unser Datenscrapers besteht aus mehreren Komponenten, darunter Module für Aufgaben wie das Scrapen von Post-Details, das Übersetzen von Inhalten bei Bedarf und das Klassifizieren von Daten.

Die Hauptkomponenten befinden sich unter app/scrapers/post_scraper.py.

  1. scrape_post_details:

    • Zweck: Extrahiert Details (Titel, Zeitstempel, Autor, Inhalt) aus einer angegebenen Post-URL mithilfe von Web-Scraping-Techniken.
    • Wichtige Parameter:
      • post_link: URL des zu scrapend Post.
      • session_cookie: Authentifizierungs-Cookie für den Zugriff auf den Post.
      • tor_proxy: Optionale Proxy-Adresse für Tor-Routing.
      • user_agent: User-Agent-Zeichenfolge für Request-Header.
      • timeout: Zeitlimit für die Anfrage (Standard: 30 Sekunden).
    • Rückgabewert: JSON-Zeichenfolge mit den extrahierten Beitragsdetails oder Fehlerinformationen, wenn die Anfrage fehlschlägt.
  2. translate_string:

    • Zweck: Erkennt die Sprache einer Eingabezeichenfolge und übersetzt sie mithilfe der DeepL-API ins Englische (oder in die angegebene Zielsprache), sofern sie nicht bereits auf Englisch ist.
    • Wichtige Parameter:
      • input_string: Zu analysierender und möglicherweise zu übersetzender Text.
      • auth_key: DeepL-API-Authentifizierungsschlüssel.
      • target_lang: Zielsprache für die Übersetzung (Standard: EN-US).
    • Rückgabewerte: JSON-Zeichenkette mit Originaltext, erkannter Sprache und übersetztem Text (falls zutreffend) oder Fehlerdetails.
  3. iab_classify:

    • Zweck: Klassifiziert einen Beitrag mithilfe des Claude-Modells von Anthropic, um festzustellen, ob er den Verkauf von Erstzugängen, nicht verwandte Artikel oder Warnungen/Beschwerden behandelt.
    • Wichtige Parameter:
      • api_key: Anthropic-API-Schlüssel für die Authentifizierung.
      • model_name: Name des zu verwendenden Claude-Modells (z. B. „claude-3-5-sonnet-20241022“).
      • prompt: Textprompt mit dem zu klassifizierenden Beitrag.
      • max_tokens: Maximale Anzahl der Ausgabetoken (Standard: 100).
    • Rückgabewerte: JSON-Zeichenkette mit Klassifizierungsergebnis, Bewertungen oder Fehlerinformationen, wenn die Klassifizierung fehlschlägt.

Funktion „scrape_post_details”

Das Tornet-Forum erfordert eine angemeldete Sitzung, um Beiträge lesen zu können, was für die meisten Foren typisch ist. Um dies zu umgehen, habe ich eine Funktion entwickelt, die einen Beitragslink übernimmt und dessen Daten abruft.

Dieser Ansatz ist logisch, da die Tabelle marketplace_posts alle Beitragdetails und Links speichert. Durch das Laden dieser Daten können wir jeden Beitragslink an eine Funktion wie scrape_post_details übergeben, um die erforderlichen Informationen zu extrahieren.

Funktion „translate_string“

In der Funktion „translate_string“ verwenden wir DeepL für die Datenübersetzung. In „app/routes/posts.py“ verwenden wir jedoch zunächst die Bibliothek „langdetect“, um die Sprache eines Beitrags zu identifizieren. Wenn die Spracherkennung fehlschlägt oder die erkannte Sprache nicht Englisch ist, übergeben wir den Beitrag an die Funktion „translate_string“.

Ein wesentlicher Vorteil ist, dass, wenn Sie eine Zeichenfolge mit Zeilenumbrüchen angeben:

Venta de acceso a Horizon Logistics\nIngresos: 1200 millones de dólares\nAcceso: RDP con DA\nPrecio: 0,8 BTC\nDM para más detalles

Die Funktion diese in der übersetzten Ausgabe beibehält:

Sale of access to Horizon Logistics\nRevenue: $1.2 billion\nAccess: RDP with DA\nPrice: 0.8 BTC\nDM for more details

Funktion „iab_classify”

In „iab_classify” ist unsere Temperatur standardmäßig auf 0,1 eingestellt. Sie können diesen Wert bei Bedarf ändern.

In KI- oder LLM-Interaktionen ist die Temperatur ein Hyperparameter, der die Zufälligkeit oder Kreativität der Ausgabe des Modells steuert:

  • Zweck: Passt die Wahrscheinlichkeitsverteilung über die möglichen Ausgaben des Modells (z. B. Wörter oder Token) während der Generierung an.
  • Funktionsweise:
  • Niedrige Temperatur (z. B. 0,1): Macht das Modell deterministischer und begünstigt Ausgaben mit hoher Wahrscheinlichkeit. Führt zu fokussierteren, vorhersehbareren und konservativeren Antworten.
  • Hohe Temperatur (z. B. 1,0 oder höher): Erhöht die Zufälligkeit und gibt Ausgaben mit geringerer Wahrscheinlichkeit eine höhere Chance. Führt zu kreativeren, vielfältigeren oder unerwarteten Antworten.
  • Beispiel im Code: In der bereitgestellten Funktion „iab_classify“ wird „temperature=0.1“ verwendet, um die Klassifizierungsausgabe des Claude-Modells konsistenter und weniger zufällig zu machen.
  • Bereich: In der Regel zwischen 0 und 1, obwohl einige Modelle höhere Werte für extreme Zufälligkeit zulassen.

Datenbankmodelle

Für das Scraping von Beiträgen, die Batchgröße der Beiträge und die Speicherung der Daten benötigen wir zwei Tabellen. So sehen Ihre Modelle aus:

class PostDetailScan(Base):
    __tablename__ = "post_detail_scans"

    id = Column(Integer, primary_key=True, index=True)
    scan_name = Column(String, nullable=False, unique=True)
    source_scan_name = Column(String, ForeignKey("marketplace_post_scans.scan_name"), nullable=False)
    start_date = Column(DateTime(timezone=True), default=datetime.utcnow)
    completion_date = Column(DateTime(timezone=True), nullable=True)
    status = Column(Enum(ScanStatus), default=ScanStatus.STOPPED, nullable=False)
    batch_size = Column(Integer, nullable=False)
    site_url = Column(String, nullable=False)
    timestamp = Column(DateTime(timezone=True), default=datetime.utcnow)


class MarketplacePostDetails(Base):
    __tablename__ = "marketplace_post_details"

    id = Column(Integer, primary_key=True, index=True)
    scan_id = Column(Integer, ForeignKey("post_detail_scans.id"), nullable=False)
    batch_name = Column(String, nullable=False)
    title = Column(String, nullable=False)
    content = Column(Text, nullable=False)
    timestamp = Column(String, nullable=False)
    author = Column(String, nullable=False)
    link = Column(String, nullable=False)
    original_language = Column(String, nullable=True)
    original_text = Column(Text, nullable=True)
    translated_language = Column(String, nullable=True)
    translated_text = Column(Text, nullable=True)
    is_translated = Column(Boolean, default=False)
    sentiment = Column(String, nullable=True)
    positive_score = Column(Float, nullable=True)
    negative_score = Column(Float, nullable=True)
    neutral_score = Column(Float, nullable=True)
    timestamp_added = Column(DateTime(timezone=True), default=datetime.utcnow)
    __table_args__ = (UniqueConstraint('scan_id', 'timestamp', 'batch_name', name='uix_scan_timestamp_batch'),)

post_detail_scans

Die Tabelle post_detail_scans wird verwendet, um Scans zu erstellen, die Daten aus der Tabelle marketplace_post_scans abrufen. Sie speichert auch die Batchgröße, da viele Websites Ratenbeschränkungen auferlegen, z. B. Beschränkungen für die Anzahl der Beiträge, die Sie innerhalb von 24 Stunden lesen können. Um dies zu verwalten, teilen wir Beiträge in Batches von 10 oder 20 auf und weisen diese Batches Bots zu, die mit dem Zweck scrape_post konfiguriert sind.

Die Tabelle marketplace_post_scans speichert Metadaten zu Beiträgen, darunter Titel, Link, Zeitstempel und Autor, jedoch keine detaillierten Inhalte.

marketplace_post_details

Diese Tabelle speichert die Ergebnisse jedes Scans. Wir starten Scans in „post_detail_scans“, laden sie in den Scraper und beginnen mit der Datenerfassung. Nach der Erfassung werden die Daten in der Tabelle „marketplace_post_details“ gespeichert.

Wir erfassen umfassende Details, darunter Originaltext, Originalsprache, übersetzter Text, Stimmung, Konfidenzwerte, Autor, Zeitstempel und mehr.


Vorlagen für die Verwaltung und Anzeige von Scans

Wir benötigen zwei Vorlagen: eine für die Verwaltung von Scans und eine für die Anzeige der Ergebnisse jedes Scans. Die Vorlage für die Verwaltung von Scans ist posts_scans.html. Hier ist eine Vorschau der Benutzeroberfläche:

Vorlage für Posts Scans

Der entsprechende Code befindet sich in „app/templates/posts_scans.html“.

  1. Erstellung und Start eines Post-Detail-Scans:

    • Zweck: Erstellt und startet einen neuen Post-Detail-Scan.
    • Backend-Interaktion:
      • Die Schaltfläche „New Post Detail Scan“ öffnet ein Modal (newScanModal) mit Feldern für den Scan-Namen, den Quell-Post-Scan (Dropdown-Liste der abgeschlossenen Scans), die Batch-Größe und die Website-URL.
      • Das Absenden des Formulars löst eine AJAX-POST-Anfrage an /api/posts-scanner/create (verarbeitet von posts_api_router) mit den Formulardaten aus, gefolgt von einer POST-Anfrage an /api/posts-scanner/{id}/start, um den Scan zu starten.
      • Das Backend erstellt einen PostDetailScan-Datensatz, verknüpft ihn mit einem MarketplacePostScan und beginnt mit dem Scraping. Die Formulardaten werden in sessionStorage gespeichert, um in startScan() wiederverwendet zu werden. Bei Erfolg wird eine Erfolgsmeldung angezeigt, das Modal wird geschlossen und refreshScans() aktualisiert die Tabelle. Fehler lösen eine Warnmeldung mit der Fehlermeldung aus.
  2. Auflistung und Aktualisierung der Post-Detail-Scans:

    • Zweck: Zeigt eine Tabelle mit Post-Detail-Scans an und aktualisiert sie.
    • Backend-Interaktion:
      • Die Funktion „refreshScans()“, die beim Laden der Seite und über die Schaltfläche „Scans aktualisieren“ aufgerufen wird, sendet eine AJAX-GET-Anfrage an „/api/posts-scanner/list“ (verarbeitet von „posts_api_router“).
      • Das Backend gibt eine Liste mit „PostDetailScan“-Datensätzen zurück (ID, Scan-Name, Name der Quelldatei, Start-/Enddatum, gescrapte Beiträge, Status). Die Tabelle wird mit Status-Badges (z. B. „badge-success“ für abgeschlossen) gefüllt. Wenn keine Scans vorhanden sind, wird die Meldung „Keine Scans verfügbar“ angezeigt. Fehler lösen eine Warnmeldung aus.
  3. Starten eines Post-Detail-Scans:

    • Zweck: Startet einen bestehenden Post-Detail-Scan.
    • Backend-Interaktion:
      • Die Schaltfläche „Start“ in jeder Tabellenzeile (deaktiviert für laufende Scans) ruft startScan(scanId) auf und sendet eine AJAX-POST-Anfrage an /api/posts-scanner/{scanId}/start (verarbeitet von posts_api_router) mit batch_size und site_url aus sessionStorage.
      • Das Backend startet den Scan und aktualisiert den Status „PostDetailScan“. Bei Erfolg wird eine Erfolgsmeldung angezeigt und „refreshScans()“ aktualisiert die Tabelle. Fehler lösen eine Warnmeldung aus.
  4. Anzeigen der Scan-Ergebnisse:

    • Zweck: Leitet zu einer Ergebnisseite für einen bestimmten Scan weiter.
    • Backend-Interaktion:
      • Die Schaltfläche „Anzeigen“ in jeder Tabellenzeile ruft „viewResults(scanId, scanName)“ auf und leitet zu „/posts-scan-result/{scanId}?name={scanName}“ weiter (wird von „main.py::posts_scan_result“ verarbeitet).
      • Das Backend rendert eine Vorlage mit den Scan-Details und ruft die zugehörigen MarketplacePostDetails-Datensätze ab. Hier erfolgt kein direkter AJAX-Aufruf, aber die Weiterleitung basiert auf Backend-Daten.
  5. Löschen eines Post-Detail-Scans:

    • Zweck: Löscht einen Post-Detail-Scan.
    • Backend-Interaktion:
      • Die Schaltfläche „Löschen“ in jeder Tabellenzeile ruft nach Bestätigung durch den Benutzer deleteScan(scanId) auf und sendet eine AJAX-DELETE-Anfrage an /api/posts-scanner/{scanId} (wird von posts_api_router verarbeitet).
      • Das Backend entfernt den Datensatz „PostDetailScan“. Bei Erfolg wird eine Erfolgsmeldung angezeigt und „refreshScans()“ aktualisiert die Tabelle. Fehler lösen eine Warnmeldung aus.
  6. Ausfüllen der Dropdown-Liste „Quell-Beitragsscan“:

    • Zweck: Füllt die Dropdown-Liste „Quell-Beitragsscan“ mit abgeschlossenen Beitragsscans.
    • Backend-Interaktion:
      • Beim Laden der Seite ruft eine AJAX-GET-Anfrage an /api/posts-scanner/completed-post-scans (verarbeitet von posts_api_router) eine Liste der abgeschlossenen MarketplacePostScan-Namen ab.
      • Das Backend gibt die Scan-Namen zurück, die als Optionen zum Dropdown-Menü im neuen Scan-Modal hinzugefügt werden. Fehler werden in der Konsole protokolliert.

Vorlage für die Anzeige der Ergebnisse jedes Scans

Bei der Anzeige der Ergebnisse für jeden Marktplatz-Scan haben wir Modals verwendet. Da hier jedoch viele Informationen vorhanden sind und Such- und Filterfunktionen erforderlich sind, benötigen wir eine andere Vorlage, um die Ergebnisse anzuzeigen.

So verwende ich die Suche und die Sentiment-Klassifizierung, um positive Ergebnisse zu filtern, die sich auf IAB beziehen und das Schlüsselwort „shell” im Titel enthalten:

Vorlage für Posts-Scan-Ergebnisse

Als Analyst ist so etwas äußerst nützlich, da Sie die Ergebnisse filtern, nur die für Ihre Untersuchung relevanten Informationen anzeigen und diese als JSON-Datei herunterladen können.

Die Vorlage für die Anzeige der Ergebnisse jedes Scans befindet sich unter: app/templates/posts_scan_result.html.

  1. Laden der Scan-Ergebnisse:

    • Zweck: Zeigt die Ergebnisse für einen bestimmten Post-Detail-Scan in einer Tabelle an.
    • Backend-Interaktion:
      • Beim Laden der Seite extrahiert die Funktion loadResults() die scanId aus der URL und sendet eine AJAX-GET-Anfrage an /api/posts-scanner/{scanId}/results (verarbeitet durch posts_api_router).
      • Das Backend fragt die Tabelle MarketplacePostDetails nach den Scan-Ergebnissen (ID, Titel, Zeitstempel, Autor, Batch-Name, Stimmungswerte, Sprache, übersetzter Text) und gibt sie als JSON zurück.
      • Die Tabelle wird mit Zeilen gefüllt, die jeweils ein Kontrollkästchen, den Batch-Namen, den gekürzten Titel, den Zeitstempel, den Autor, die Stimmungswerte (positiv, negativ, neutral), die vorherrschende Stimmung (klientseitig als höchste Wertung berechnet), die Sprache und den Übersetzungsstatus enthalten. Fehler lösen eine Warnmeldung aus.
  2. Suche und Stimmungsfilterung:

    • Zweck: Filtert die Ergebnistabelle nach Beitragstitel und Sentiment.
    • Backend-Interaktion:
      • Die Funktion filterTable(), die durch Keyup auf #searchInput und Änderung auf #sentimentFilter ausgelöst wird, filtert die Tabellenzeilen clientseitig anhand des Suchbegriffs (Titel) und des ausgewählten Sentiments (all, positive, negative, neutral).
      • Es werden keine direkten Backend-Aufrufe getätigt; die Filterung erfolgt über das Attribut „data-sentiment“, das während „loadResults()“ gesetzt wird. Zeilen werden basierend auf Übereinstimmungen ein- oder ausgeblendet, wodurch dynamische Aktualisierungen ohne zusätzliche Anfragen gewährleistet sind.
  3. Anzeigen von Beitragsdetails:

    • Zweck: Zeigt detaillierte Informationen zu einem ausgewählten Beitrag in einem Modal an.
    • Backend-Interaktion:
      • Die Schaltfläche „Anzeigen“ in jeder Tabellenzeile füllt das viewModal mit Daten, die in den data-*-Attributen der Schaltfläche (Titel, Zeitstempel, Autor, Batch, Sentiment-Werte, Sentiment, Sprache, Übersetzungsstatus, Link, Original-/Übersetzungsinhalt) aus der ersten loadResults()-Antwort gespeichert sind.
      • Es ist kein zusätzlicher Backend-Aufruf erforderlich; das Modal zeigt schreibgeschützte Felder und Textbereiche an. Die Schaltfläche „Schließen” blendet das Modal ohne Backend-Interaktion aus.
  4. Ausgewählte Ergebnisse herunterladen:

    • Zweck: Exportiert ausgewählte Beitragsergebnisse als JSON-Datei.
    • Backend-Interaktion:
      • Die Schaltfläche „Ausgewählte herunterladen“ (#downloadSelected) sammelt die IDs der markierten Zeilen (gefiltert nach Sichtbarkeit) und sendet eine AJAX-POST-Anfrage an /api/posts-scanner/{scanId}/download (verarbeitet von posts_api_router) mit dem Array post_ids.
      • Das Backend ruft die entsprechenden MarketplacePostDetails-Datensätze ab und gibt sie als JSON zurück. Der Client erstellt mithilfe eines Blob eine herunterladbare JSON-Datei (scan_{scanId}_results.json). Wenn keine Zeilen ausgewählt sind oder ein Fehler auftritt, wird eine Warnmeldung angezeigt.

Backend-Routen

Der Backend-Code befindet sich in app/routes/posts.py. Im Folgenden werden die wichtigsten Funktionen erläutert.

get_post_scans:

  • Zweck: Ruft alle Post-Detail-Scans aus der Datenbank ab, einschließlich Details wie Scan-ID, Name, Name der Quelldatei, Start-/Fertigstellungsdatum, Status und Anzahl der gescrapten Posts.
  • Wichtigste Funktionen:
    • Fragt PostDetailScan ab und verknüpft die Ergebnisse mit MarketplacePostDetails, um die Anzahl der gescrapten Posts zu zählen.
    • Gibt eine JSON-Antwort mit den Scan-Details zurück.
    • Behandelt Fehler mit einem 500-Statuscode, wenn die Abfrage fehlschlägt.

get_completed_post_scans:

  • Zweck: Ruft die Namen abgeschlossener MarketplacePostScan-Scans zur Verwendung in einem Dropdown-Menü ab.
  • Wichtigste Funktionen:
    • Filtert nach Scans mit dem Status „COMPLETED“ und einem nicht null-wertigen Abschlussdatum.
    • Gibt eine JSON-Liste mit Scannamen zurück.
    • Löst einen 500-Fehler aus, wenn die Abfrage fehlschlägt.

create_post_scan:

  • Zweck: Erstellt einen neuen Post-Detail-Scan basierend auf einer bereitgestellten Konfiguration.
  • Wichtigste Funktionen:
    • Überprüft, ob der Scan-Name eindeutig ist und der Quell-Scan abgeschlossen ist.
    • Erstellt einen PostDetailScan-Datensatz mit dem Status STOPPED und speichert die Batchgröße und die Website-URL.
    • Gibt eine JSON-Antwort mit der Scan-ID und einer Erfolgsmeldung zurück.
    • Behandelt doppelte Scan-Namen (400) oder fehlende Quell-Scans (404).

start_post_scan:

  • Zweck: Startet einen Post-Detail-Scan, indem Posts aus einem Quell-Scan in Batches mit mehreren Bots verarbeitet werden.
  • Wichtigste Funktionen:
    • Überprüft, ob der Scan vorhanden ist, nicht ausgeführt wird und über die erforderlichen APIs (Übersetzung und IAB) sowie aktive Bots verfügt.
    • Teilt Beiträge in Stapel auf und weist sie Bots zu, um sie gleichzeitig zu scrapen, zu übersetzen und zu klassifizieren.
    • Verwendet scrape_post_details, translate_string und iab_classify, um Beiträge zu verarbeiten.
    • Speichert die Ergebnisse in MarketplacePostDetails und aktualisiert den Scan-Status je nach Erfolg auf RUNNING oder COMPLETED/STOPPED.
    • Behandelt Fehler mit entsprechenden HTTP-Statuscodes (404, 400, 500).

delete_post_scan:

  • Zweck: Löscht einen bestimmten Post-Detail-Scan aus der Datenbank.
  • Wichtigste Funktionen:
    • Überprüft vor dem Löschen, ob der Scan vorhanden ist.
    • Entfernt den PostDetailScan-Datensatz und übernimmt die Änderung.
    • Gibt eine JSON-Erfolgsmeldung oder einen 404-Fehler zurück, wenn der Scan nicht gefunden wird.
    • Behandelt unerwartete Fehler mit einem 500-Statuscode.

get_scan_results:

  • Zweck: Ruft detaillierte Ergebnisse eines bestimmten Post-Detail-Scans ab.
  • Wichtigste Funktionen:
    • Fragt „MarketplacePostDetails“ nach einer bestimmten Scan-ID ab.
    • Gibt eine JSON-Antwort mit Details wie Titel, Inhalt, Autor, Zeitstempel, Übersetzungsdaten und Klassifizierungsbewertungen zurück.
    • Löst einen 500-Fehler aus, wenn die Abfrage fehlschlägt.

„download_post_results“:

  • Zweck: Lädt bestimmte Post-Details für eine bestimmte Scan-ID basierend auf den angegebenen Post-IDs herunter.
  • Wichtigste Funktionen:
    • Überprüft, ob der Scan vorhanden ist und die angeforderten Post-IDs gültig sind.
    • Gibt eine JSON-Antwort mit ausgewählten Post-Details zurück, darunter Titel, Zeitstempel, Autor, Stimmungsbewertungen und Übersetzungsdaten.
    • Löst einen 404-Fehler aus, wenn der Scan oder die Posts nicht gefunden werden, oder einen 500-Fehler bei anderen Problemen.

Wir verwenden die Funktion „detect” der Bibliothek „langdetect” innerhalb von „scrape_post_batches”. Bei Bedarf können Sie die Funktion „translate_string” in „post_scraper.py” ändern, um „langdetect” ebenfalls zu integrieren. Dies ist zwar eine Option, ich bevorzuge jedoch den aktuellen Ansatz aufgrund seiner Effizienz.


Testen

Um diese Funktionalität zu testen, konfigurieren Sie die folgenden Komponenten:

  1. Scrape die Details der Marktplatzbeiträge mithilfe der Seite „/marketplace-scan“.
  2. Konfigurieren Sie eine KI-API zum Umgehen von CAPTCHAs.
  3. Erstellen Sie mindestens zwei Bot-Profile mit dem Zweck „scrape_post“ und melden Sie sich an, um Sitzungscookies zu erhalten.
  4. Richten Sie die DeepL-API für die Übersetzung ein.
  5. Richten Sie die IAB-API zur Identifizierung von Initial Access Brokern ein.

Für die IAB-API benötigen Sie die folgende Eingabeaufforderung:

Does this post discuss selling initial access to a company (e.g., RDP, VPN, admin access), selling unrelated items (e.g., accounts, tools), or warnings/complaints? Classify it as:
- Positive Posts: direct sale of unauthorized access to a company, this usually include the target's name.
- Neutral Posts: general offers for tools, exploits or malware without naming a specific target.
- Negative Posts: off-topic or unrelated services such as hosting, spam tools or generic VPS sales.

The content must be specifically about selling access to a company or business whose name is mentioned in the post. 

Return **only** a JSON object with:
- `classification`: "Positive", "Neutral", or "Negative".
- `scores`: Probabilities for `positive`, `neutral`, `negative` (summing to 1).

Wrap the JSON in ```json
{
  ...
}
``` to ensure proper formatting. Do not include any reasoning or extra text.

Post:
```markdown
TARGET-POST-PLACEHOLDER
``` 

Sie können die Eingabeaufforderung ändern und selbst damit experimentieren.