kw-scrape: Engine, CLI und MCP
Diese Seite beschreibt die kw-scrape-Komponente im Detail. Die Einbettung ins Gesamtsystem (Beziehung zu ki-web, gemeinsames Architekturmuster, Schreib- und DRY-Regeln) steht auf der Seite /web-scraping.
Was kw-scrape macht
kw-scrape holt eine oder viele externe URLs, extrahiert den Hauptinhalt als Markdown plus strukturierte Metadaten und gibt das Ergebnis als JSON-serialisierbare Datenstruktur zurück. Das Paket ist in drei Schichten aufgeteilt: eine Engine-Library, ein klassisches CLI und ein MCP-Server für AI-Tools.
Stack
- httpx 0.28: synchroner Fetch mit Connection-Pooling und Thread-Pool für parallele Batches.
- trafilatura 2.0: Erkennung des Hauptinhalts, Boilerplate-Entfernung, Metadaten-Extraktion, Markdown-Rendering.
- Python 3.13: dataclasses, frozen Slots, Protocol-Ports.
Bewusst nicht enthalten: JavaScript-Rendering. Dynamisch nachgeladener Inhalt wird in dieser Ausbaustufe nicht gesehen.
Drei Schnittstellen, eine Engine
| Schicht | Pfad | Zugriff |
|---|---|---|
| Engine (Library) | /var/www/scrape/ | direkter Python-Import: from kw_scrape.composition import Composition |
| CLI (scrape-cli) | /var/www/cli/scrape/ | Wrapper /usr/local/bin/scrape-cli |
| MCP-Server | /var/www/mcp/scrape/ | Wrapper /usr/local/bin/scrape (stdio JSON-RPC) |
Die CLI- und MCP-Schichten sind dünne Adapter um dieselbe Engine. Geschäftslogik liegt einmalig in der Engine.
Tool-Surface
Die drei Tools sind in CLI und MCP semantisch identisch, nur die Aufrufform unterscheidet sich.
| Tool | Zweck | Hauptargumente |
|---|---|---|
fetch_url (CLI: fetch) | Eine URL holen, Hauptinhalt plus Metadaten | url, want_markdown, want_text, include_raw_html |
extract_html (CLI: extract) | Markdown aus rohem HTML, ohne Netzwerk-Aufruf | html, base_url, want_markdown, want_text |
scrape_urls (CLI: batch) | Viele URLs parallel, getrennte Erfolg- und Fehlerlisten | urls, concurrency (1 bis 50), want_markdown, want_text |
Rückgabe-Struktur (ScrapeResult)
Bei fetch_url und jedem Eintrag in scrape_urls.items:
- url: finale URL nach Redirects
- status: HTTP-Status, 200 für Erfolg
- byte_size: ungekürzte HTML-Länge
- fetched_at: ISO 8601 mit Zeitzone
- title, author, date, description, sitename, language: Metadaten von trafilatura, jeweils optional
- markdown: Markdown-Rendering des Hauptinhalts (None falls want_markdown=False)
- text: Plain-Text-Variante (None falls want_text=False)
- html_raw: rohes HTML (None falls include_raw_html=False)
CLI-Beispiele
Eine Seite holen
scrape-cli fetch https://lisa-sundermeyer.de
scrape-cli --format table --columns title,sitename,date,language fetch https://lisa-sundermeyer.de
scrape-cli fetch https://lisa-sundermeyer.de | jq -r '.markdown'HTML aus stdin oder Datei extrahieren
cat seite.html | scrape-cli extract --base-url https://example.com/
scrape-cli extract --file seite.html --base-url https://example.com/Batch mit Fehler-Trennung
printf '%s\n' \
https://ki.karlkratz.com/hallo-welt \
https://ki.karlkratz.com/mcp-demo \
https://ki.karlkratz.com/missing \
| scrape-cli batch -c 3 \
| jq '{total, ok: (.items | length), fail: (.errors | length)}'Pipeline: Root holen, interne Links folgen
scrape-cli fetch https://lisa-sundermeyer.de \
| jq -r '.markdown' \
| grep -oE '\(https://lisa-sundermeyer\.de/[^)#]+\)' \
| tr -d '()' \
| sort -u | head -5 \
| scrape-cli --format plain --columns url,title batch -c 5MCP-Integration in drei AI-CLIs
Der MCP-Server ist als kw-scrape in allen drei lokalen AI-CLIs registriert: Claude Code, Codex und qwen-code. Tool-Aufrufe laufen über stdio JSON-RPC durch /usr/local/bin/scrape.
Claude Code
// ~/.claude.json, je Projekt unter mcpServers
"kw-scrape": {
"type": "stdio",
"command": "/usr/local/bin/scrape",
"args": [],
"env": {}
}Codex
# ~/.codex/config.toml
[mcp_servers.kw-scrape]
command = "/usr/local/bin/scrape"qwen-code
// ~/.qwen/settings.json, top-level mcpServers
"kw-scrape": {
"command": "/usr/local/bin/scrape",
"args": []
}Performance
Eine Seite kostet ungefähr 200 ms Fetch und 30 bis 50 ms Extraktion. Mit Concurrency 10 erreicht das CLI 30 bis 50 Seiten pro Sekunde, bis die Höflichkeit gegen den Zielserver greift. Die typische Decke ist nicht das Tool, sondern die Rate-Limits der Gegenseite.
Env-Variablen
| Variable | Default | Wirkung |
|---|---|---|
| KW_SCRAPE_TIMEOUT | 15.0 | HTTP-Timeout in Sekunden |
| KW_SCRAPE_RETRIES | 1 | Transport-Retries bei netzseitigen Fehlern |
| KW_SCRAPE_USER_AGENT | kw-scrape/0.1 (+https://ki.karlkratz.com) | User-Agent-Header |
| KW_SCRAPE_MAX_BYTES | 5000000 | Hartes Limit pro Response (5 MB) |
| KW_SCRAPE_CONCURRENCY | 10 | Default-Parallelität für Batch |
Exit-Codes (CLI)
| Code | Bedeutung |
|---|---|
| 0 | OK |
| 1 | Generischer Fehler |
| 2 | Usage-Fehler (argparse) |
| 3 | HTTP 404 |
| 4 | Validierung oder Extract-Fehler |
| 5 | Backend 5xx |
| 6 | Transport-Fehler (DNS, Timeout, non-JSON) |
Grenzen
- Kein JavaScript-Rendering. Statische HTML-Seiten werden vollständig unterstützt, JS-gesteuerter Inhalt ist nicht sichtbar.
- Kein Sitemap-Crawling, keine Job-Queue, kein Resume. Für mehrere tausend URLs in einem Lauf wäre ein Vollframework (Scrapy oder Crawlee) geeigneter.
- Keine HTTP-API. Die Engine wird direkt als Python-Library importiert. Ein zusätzlicher
/api/scrape-Endpoint folgt erst, wenn ein nicht-Python-Konsument hinzukommt. - Keine eingebaute Authentifizierung. URLs hinter Login sind nicht erreichbar.
Erweiterungspunkt: PlaywrightFetcher
Die FetcherPort-Schnittstelle ist genau eine Methode: fetch(url) plus fetch_many(urls, concurrency). Eine zukünftige Playwright-Implementierung lässt sich an dieser Stelle einhängen, ohne UseCases oder die anderen Adapter zu ändern. Der Composition-Root entscheidet, welcher Fetcher genutzt wird.
Wichtigste Pfade
| Pfad | Inhalt |
|---|---|
| /var/www/scrape/ | Engine kw-scrape, Library |
| /var/www/cli/scrape/ | CLI scrape-cli |
| /var/www/mcp/scrape/ | MCP-Server kw-scrape |
| /usr/local/bin/scrape-cli | CLI-Wrapper |
| /usr/local/bin/scrape | MCP-Wrapper (stdio) |
| /etc/bash_completion.d/scrape-cli | Bash-Completion für das CLI |