Wenn du mit KI-Trainingsdaten, Dokument-Pipelines oder RAG-Systemen arbeitest, hattest du mit großer Wahrscheinlichkeit schon mit DOCX-Dateien zu tun. Word-Dokumente sind überall — Forschungsentwürfe, Geschäftsberichte, Rechtsverträge, Manuskripte — doch programmgesteuert lassen sie sich nur mühsam verarbeiten.
Dieser Beitrag zeigt, wie du DOCX mit der REST-API von AnyMD aus Python in sauberes Markdown konvertierst — einschließlich asynchroner Stapelverarbeitung, Fehlerbehandlung und Paginierung für Workflows im Produktionsmaßstab.
Warum Markdown aus DOCX?
DOCX ist ein Binärformat (ein ZIP-Archiv aus XML-Dateien). Bibliotheken wie python-docx können Text extrahieren, verlieren dabei aber die Struktur: Überschriften werden zu einfachen Absätzen, Listen zu eingerücktem Text und Tabellen zu tabulatorgetrenntem Durcheinander. Markdown bewahrt all das in einem Format, das jedes LLM, jede Vektordatenbank und jeder Static-Site-Generator nativ verarbeiten kann.
Einmalige Konvertierung
Der einfachste Fall — eine einzelne DOCX-Datei:
import requests
url = "https://anymd.net/api/convert"
headers = {"Authorization": "Bearer your-api-key"}
with open("report.docx", "rb") as f:
resp = requests.post(url, headers=headers, files={"file": ("report.docx", f)})
if resp.ok:
markdown = resp.text
print(f"Got {len(markdown)} chars of Markdown")
else:
print(f"Error {resp.status_code}: {resp.text}")
AnyMD erkennt DOCX automatisch anhand der Dateiendung und gibt GitHub-Flavored Markdown zurück. Überschriften, nummerierte Listen, Aufzählungen, Fett/Kursiv, Tabellen, Bilder und Hyperlinks bleiben vollständig erhalten — genau so, wie du sie beim Öffnen des Dokuments in Word sehen würdest.
Asynchrone Stapelkonvertierung mit Wiederholungsversuchen
Bei 50 oder 500 DOCX-Dateien ist eine sequenzielle Konvertierung zu langsam. Hier ist ein asynchroner Batch-Prozessor mit sauberer Fehlerbehandlung:
import asyncio
import aiohttp
from pathlib import Path
API_URL = "https://anymd.net/api/convert"
HEADERS = {"Authorization": "Bearer your-api-key"}
async def convert_one(session: aiohttp.ClientSession, path: Path, out_dir: Path) -> str:
for attempt in range(3):
try:
data = aiohttp.FormData()
data.add_field("file", path.read_bytes(), filename=path.name)
async with session.post(API_URL, headers=HEADERS, data=data) as resp:
if resp.status == 429:
wait = int(resp.headers.get("Retry-After", 5))
await asyncio.sleep(wait)
continue
if resp.ok:
md = await resp.text()
out_path = out_dir / f"{path.stem}.md"
out_path.write_text(md)
return f"✓ {path.name}"
return f"✗ {path.name} ({resp.status})"
except (aiohttp.ClientError, asyncio.TimeoutError) as e:
if attempt == 2:
return f"✗ {path.name} — {e}"
await asyncio.sleep(1.5 ** attempt)
async def batch_convert(docx_dir: str, out_dir: str, concurrency: int = 10):
docx_files = list(Path(docx_dir).glob("*.docx"))
out_path = Path(out_dir)
out_path.mkdir(exist_ok=True)
connector = aiohttp.TCPConnector(limit=concurrency)
async with aiohttp.ClientSession(connector=connector) as session:
tasks = [convert_one(session, p, out_path) for p in docx_files]
results = await asyncio.gather(*tasks)
ok = sum(1 for r in results if r.startswith("✓"))
print(f"{ok}/{len(results)} converted — see {out_dir}/")
asyncio.run(batch_convert("docx_input", "markdown_output"))
Das Skript verwendet einen Verbindungspool mit Begrenzung (standardmäßig 10 gleichzeitige Anfragen), wiederholt Anfragen bei 429-Rate-Limits und vorübergehenden Fehlern mit exponentiellem Backoff und schreibt jedes Ergebnis als .md Datei.
Paginierung für große Dokumentbibliotheken
Wenn du Dokumente aus einer API oder einem S3-Bucket abrufst, benötigst du möglicherweise paginierte Verarbeitung. Hier ist ein Generator-Muster:
import requests
from pathlib import Path
from typing import Generator
def document_batches(doc_dir: str, batch_size: int = 25) -> Generator[list[Path], None, None]:
files = sorted(Path(doc_dir).glob("*.docx"))
for i in range(0, len(files), batch_size):
yield files[i : i + batch_size]
def convert_batch(batch: list[Path], api_key: str) -> list[tuple[str, bool]]:
results = []
with requests.Session() as sess:
sess.headers.update({"Authorization": f"Bearer {api_key}"})
for path in batch:
with open(path, "rb") as f:
resp = sess.post("https://anymd.net/api/convert", files={"file": (path.name, f)})
if resp.ok:
out = path.with_suffix(".md")
out.write_text(resp.text)
results.append((path.name, True))
else:
results.append((path.name, False))
return results
# Process 10,000 documents in batches
total, ok = 0, 0
for batch in document_batches("archive", batch_size=25):
results = convert_batch(batch, "your-api-key")
for name, success in results:
total += 1
if success:
ok += 1
print(f"Progress: {ok}/{total}")
print(f"Done: {ok}/{total} documents converted")
Umgang mit großen DOCX-Dateien
AnyMD verarbeitet im kostenpflichtigen Tarif DOCX-Dateien bis 50 MB.
Was erhalten bleibt
| DOCX-Funktion | Markdown-Ausgabe |
|---|---|
| Überschrift 1–6 | # bis ###### |
| Fett / Kursiv | **bold** / *italic* |
| Nummerierte Listen | 1. item — korrekte Verschachtelung |
| Aufzählungslisten | - item — korrekte Verschachtelung |
| Tabellen | GFM-Tabellensyntax mit Ausrichtung |
| Hyperlinks | [text](url) |
| Bilder |  |
| Codeblöcke | Mit ``` umgrenzte Blöcke |
| Blockzitate | > quote |
Vergleich mit python-docx
Die Standard-Python-Bibliothek python-docx gibt dir Low-Level-Zugriff auf die internen DOCX-Strukturen — Absätze, Runs und Stile. Das ist leistungsfähig für die programmgesteuerte Dokumenterzeugung, bedeutet bei der Extraktion aber, dass du jede Strukturregel des Word-Renderers selbst neu implementieren musst:
- Überschriften — Absatzstile müssen manuell auf Markdown-Überschriften abgebildet werden
- Listen — erfordert das Parsen von
numPr-Elementen und das Nachverfolgen der Listentiefe - Tabellen — zellen- und zeilenweise Extraktion mit manueller GFM-Formatierung
- Bilder — Extraktion von
rId-Referenzen und anschließendes Entpacken der Mediendateien - Änderungsverfolgung — keine integrierte Unterstützung
AnyMD erledigt all das mit einem einzigen API-Aufruf. Ein Format zum Lernen, eine Integration zum Pflegen, ein Dienst zum Überwachen.
# python-docx approach — ~150 lines to get passable Markdown
from docx import Document
import re
doc = Document("report.docx")
output = []
for para in doc.paragraphs:
if para.style.name.startswith("Heading"):
level = para.style.name[-1]
output.append(f"{'#' * int(level)} {para.text}")
else:
output.append(para.text)
# Compare: AnyMD — 6 lines, no edge cases
# curl https://anymd.net/api/convert -H "Authorization: Bearer ***" -F "file=@report.docx"
Von DOCX zur RAG-Pipeline
Sobald deine DOCX-Dateien in Markdown vorliegen, folgen Chunking und Embedding für RAG. Die AnyMD-Ausgabe ist die ideale Eingabe für semantische Chunking-Strategien — Überschriftengrenzen, Listenstruktur und Tabellenköpfe bleiben erhalten, sodass deine Chunks die Dokumentsemantik respektieren, statt Absätze mitten im Satz zu zerschneiden.
Einen tieferen Vergleich von Chunking-Ansätzen für Markdown-Ausgabe findest du in unserem Leitfaden zum rekursiven Character-Chunking.
Preise
| Tarif | Seiten/Monat | Maximale Dateigröße | Preis |
|---|---|---|---|
| Kostenlos | 100 | 10 MB | $0 |
| Starter | 500 | 25 MB | $19 |
| Pro | 5,000 | 50 MB | $99 |
| Enterprise | Individuell | Individuell | Individuell |
Eine typische DOCX-Seite wird in weniger als 0,3 Sekunden konvertiert.
Hol dir deinen kostenlosen API-Schlüssel — 100 Seiten/Monat, keine Kreditkarte erforderlich.