Web-Scraping im Karl-Kratz-System

Diese Seite beschreibt, wie Web-Scraping unter ki.karlkratz.com eingebettet ist: die beiden fachlichen Bausteine ki-web und kw-scrape, ihre Schnittstellen und das gemeinsame Architekturmuster dahinter.

Die Bausteine

ki-web

Verwaltet die Seiten dieser Domain (Slug, Titel, Meta-Description, HTML-Content). Die Geschäftslogik (Slug-Regex, HTML-Sanitization, Längen-Limits) liegt einmalig in der PHP-Schicht unter /api/pages und schreibt nach Postgres. Alle weiteren Zugriffsformen sind dünne Adapter über diese eine API.

kw-scrape

Eine eigenständige Python-Engine. Sie holt externe URLs mit httpx ab, extrahiert mit trafilatura den Hauptinhalt plus Metadaten (Titel, Autor, Datum, Sprache, Sitename) und gibt eine ScrapeResult-Datenstruktur zurück. JavaScript-gerenderte Seiten werden in dieser Ausbaustufe bewusst nicht unterstützt, weil dafür Playwright nötig wäre und der zusätzliche Aufwand sich für die aktuellen Anwendungsfälle nicht rechnet.

Die Schnittstellen-Klassen

Jeder Baustein wird über dieselbe Logik aus mehreren Richtungen bedient. Die Klassen sind komplementär und adressieren unterschiedliche Aufrufer.

KlasseWer ruft aufki-webkw-scrape
HTTP-APIBrowser, andere Server/api/pages (PHP)noch nicht exponiert
CLIMensch am Terminal, Skripte, CIki-web-cliscrape-cli
MCP-ServerClaude Code, Codex, qwen-codeki-webin Planung

Architekturmuster

Beide Codebasen folgen dem gleichen DDD-Hexagonal-Schnitt mit drei Schichten.

  1. Domain: reine Datenstrukturen (Page, ScrapeResult, RawFetch, Extracted) und domänenspezifische Fehler. Kennt weder HTTP noch ein Framework.
  2. Application: Ports als Python-Protocols (PagesPort, FetcherPort, ExtractorPort) und Use Cases, die die Operationen orchestrieren.
  3. Infrastructure: die Adapter. PdoPageRepository für Postgres, HttpxFetcher für das Web, TrafilaturaExtractor für die HTML-Analyse, JsonHttpClient für die /api/pages-Aufrufe.

Die Application-Schicht spricht ausschließlich mit Ports. Konkrete Adapter werden in einem Composition-Root injiziert, sodass Tests den Fetcher gegen einen FakeFetcher tauschen können, ohne das Netzwerk zu berühren. Die gleiche Technik erlaubt, später eine PlaywrightFetcher-Implementierung der FetcherPort hinterzulegen, ohne die Use Cases zu ändern.

DRY-Schnitt: wo die Wahrheit lebt

Geschäftsregeln existieren genau einmal. Für ki-web liegen sie in der PHP-API. Für kw-scrape im Python-Paket unter /var/www/scrape/. CLI und MCP sind Adapter über diese Engines. Wenn eine Validierung ergänzt werden muss, geschieht das an einer Stelle, nicht parallel in mehreren Clients.

Output-Formate der CLIs

Sowohl ki-web-cli als auch scrape-cli kennen drei Output-Formate, gesteuert über --format.

Über --columns slug,title,updated_at lassen sich in table und plain einzelne Felder herausziehen. In json hat das Flag keine Wirkung, weil dort die volle Struktur ohnehin verfügbar ist.

Beispiele

ki-web

ki-web-cli list --search mcp --limit 5
ki-web-cli get hallo-welt
ki-web-cli --format plain --columns slug list
echo '<h1>Demo</h1>' | ki-web-cli create --slug demo \
  --title "Demo" --meta-description "Test" --content-file -

kw-scrape

scrape-cli fetch https://ki.karlkratz.com/mcp-vs-api-vs-cli
scrape-cli fetch URL | jq -r '.markdown'
scrape-cli --format table --columns title,sitename,status fetch URL
scrape-cli batch --file urls.txt -c 20

Was bewusst nicht im System ist

Wichtigste Pfade

PfadInhalt
/var/www/ki.karlkratz.com/src/PHP-Backend mit /api/pages
/var/www/mcp/MCP-Server ki-web (Python, stdio)
/var/www/cli/shared/Wiederverwendbare CLI-Infrastruktur (PresenterFactory, Streams, Exit-Codes, Help-Aliasse)
/var/www/cli/ki-web/CLI ki-web-cli
/var/www/scrape/Engine kw-scrape
/var/www/cli/scrape/CLI scrape-cli
/var/www/mcp.scrape/geplanter MCP-Server für kw-scrape