Als je werkt met AI-trainingsdata, documentpipelines of RAG-systemen, heb je vrijwel zeker met DOCX-bestanden te maken gehad. Word-documenten zijn overal — onderzoeksconcepten, bedrijfsrapporten, juridische contracten, manuscripten — maar programmatisch verwerken is omslachtig.
In dit artikel zie je hoe je DOCX vanuit Python met de REST API van AnyMD omzet naar schone Markdown, met asynchrone batchverwerking, foutafhandeling en paginering voor workflows op productieschaal.
Waarom Markdown uit DOCX?
DOCX is een binair formaat (een ZIP met XML-bestanden). Bibliotheken zoals python-docx kunnen tekst extraheren, maar verliezen de structuur: koppen worden gewone alinea's, lijsten veranderen in ingesprongen tekst en tabellen worden door tabs gescheiden ruis. Markdown behoudt dit allemaal in een formaat dat elk LLM, elke vectordatabase en elke static-sitegenerator direct kan gebruiken.
Eenmalige conversie
Het eenvoudigste geval — één DOCX-bestand:
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 herkent DOCX automatisch aan de bestandsextensie en retourneert GitHub-Flavored Markdown. Koppen, genummerde lijsten, opsommingen, vet/cursief, tabellen, afbeeldingen en hyperlinks blijven allemaal behouden — precies zoals je ze zou zien wanneer je het document in Word opent.
Asynchrone batchconversie met retries
Met 50 of 500 DOCX-bestanden is sequentiële conversie te traag. Hier is een asynchrone batchprocessor met goede foutafhandeling:
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"))
Het script gebruikt een sessiepool met verbindingslimiet (standaard 10 gelijktijdige requests), probeert opnieuw bij 429-rate-limits en tijdelijke fouten met exponentiële backoff en schrijft elk resultaat als een .md bestand.
Paginering voor grote documentbibliotheken
Als je documenten uit een API of S3-bucket haalt, heb je mogelijk gepagineerde verwerking nodig. Hier is een generatorpatroon:
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")
Grote DOCX-bestanden verwerken
AnyMD verwerkt in het betaalde abonnement DOCX-bestanden tot 50 MB.
Wat behouden blijft
| DOCX-functie | Markdown-uitvoer |
|---|---|
| Kop 1–6 | # tot en met ###### |
| Vet / Cursief | **bold** / *italic* |
| Genummerde lijsten | 1. item — correcte nesting |
| Opsommingslijsten | - item — correcte nesting |
| Tabellen | GFM-tabelsyntaxis met uitlijning |
| Hyperlinks | [text](url) |
| Afbeeldingen |  |
| Codeblokken | Met ``` afgebakende blokken |
| Blokcitaten | > quote |
Vergelijking met python-docx
De standaard Python-bibliotheek python-docx geeft je low-level toegang tot de interne structuur van DOCX — alinea's, runs en stijlen. Dat is krachtig voor programmatische documentgeneratie, maar voor extractie betekent het dat je elke structurele regel van de Word-renderer opnieuw moet implementeren:
- Koppen — alineastijlen moeten handmatig aan Markdown-koppen worden gekoppeld
- Lijsten — vereist het parsen van
numPr-elementen en het bijhouden van de lijstniveau's - Tabellen — cel-voor-cel- en rij-voor-rij-extractie met handmatige GFM-opmaak
- Afbeeldingen — het extraheren van
rId-referenties en daarna uitpakken van mediabestanden - Wijzigingen bijhouden — geen ingebouwde ondersteuning
AnyMD doet dit allemaal met één API-call. Eén formaat om te leren, één integratie om te onderhouden, één service om te bewaken.
# 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"
Van DOCX naar een RAG-pipeline
Zodra je DOCX-bestanden Markdown zijn, is de volgende stap chunking en embedding voor RAG. AnyMD-uitvoer is de ideale invoer voor semantische chunkingstrategieën — kopgrenzen, lijststructuur en tabelkoppen blijven behouden, zodat je chunks de documentsemantiek respecteren in plaats van alinea's midden in een zin af te snijden.
Voor een uitgebreidere vergelijking van chunkingmethoden voor Markdown-uitvoer, bekijk onze gids over recursieve character chunking.
Prijzen
| Abonnement | Pagina's/maand | Maximale bestandsgrootte | Prijs |
|---|---|---|---|
| Gratis | 100 | 10 MB | $0 |
| Starter | 500 | 25 MB | $19 |
| Pro | 5,000 | 50 MB | $99 |
| Enterprise | Maatwerk | Maatwerk | Maatwerk |
Een typische DOCX-pagina wordt in minder dan 0,3 seconde geconverteerd.
Ontvang je gratis API-sleutel — 100 pagina's/maand, geen creditcard vereist.