Blog

Konverter DOCX til Markdown med Python

12. august 2026 · 5 min. læsning


Hvis du arbejder med AI-træningsdata, dokumentpipelines eller RAG-systemer, har du næsten helt sikkert haft med DOCX-filer at gøre. Word-dokumenter er overalt — forskningsudkast, forretningsrapporter, juridiske kontrakter, manuskripter — men de er besværlige at behandle programmatisk.

Dette indlæg viser, hvordan du konverterer DOCX til ren Markdown med AnyMD's REST API fra Python, med asynkron batchbehandling, fejlhåndtering og paginering til workflows i produktionsskala.

Hvorfor Markdown fra DOCX?

DOCX er et binært format (en ZIP med XML-filer). Biblioteker som python-docx kan udtrække tekst, men de mister strukturen: overskrifter bliver til almindelige afsnit, lister kollapser til indrykket tekst, og tabeller bliver til tabulatorsepareret støj. Markdown bevarer det hele i et format, som enhver LLM, vektordatabase og statisk sitegenerator kan bruge direkte.

Engangskonvertering

Det enkleste tilfælde — én DOCX-fil:

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 registrerer automatisk DOCX ud fra filtypen og returnerer GitHub-Flavored Markdown. Overskrifter, nummererede lister, punktlister, fed/kursiv, tabeller, billeder og hyperlinks bevares alle — præcis som du ville se dem, hvis du åbnede dokumentet i Word.

Asynkron batchkonvertering med genforsøg

Når du har 50 eller 500 DOCX-filer, er sekventiel konvertering for langsom. Her er en asynkron batchprocessor med korrekt fejlhåndtering:

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"))

Scriptet bruger en session-pool med forbindelsesbegrænsning (10 samtidige forespørgsler som standard), forsøger igen ved 429-rate limits og midlertidige fejl med eksponentiel backoff og skriver hvert resultat som en .md fil.

Paginering til store dokumentbiblioteker

Hvis du henter dokumenter fra et API eller en S3-bucket, kan du have brug for pagineret behandling. Her er et generator-mønster:

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")

Håndtering af store DOCX-filer

AnyMD håndterer DOCX-filer på op til 50 MB på den betalte plan.

Hvad der bevares

DOCX-funktionMarkdown-output
Overskrift 1–6# til ######
Fed / Kursiv**bold** / *italic*
Nummererede lister1. item — korrekt indlejring
Punktlister- item — korrekt indlejring
TabellerGFM-tabelsyntaks med justering
Hyperlinks[text](url)
Billeder![alt](path)
KodeblokkeBlokke afgrænset med ```
Blokcitater> quote

Sammenligning med python-docx

Standardbiblioteket i Python python-docx giver dig adgang på lavt niveau til DOCX' interne struktur — afsnit, runs og typografier. Det er effektivt til programmatisk dokumentgenerering, men ved udtrækning betyder det, at du selv skal genimplementere hver strukturel regel, som Words renderer anvender:

  • Overskrifter — afsnitstypografier skal manuelt mappes til Markdown-overskrifter
  • Lister — kræver parsing af numPr -elementer og sporing af listeniveau
  • Tabeller — udtrækning celle for celle og række for række med manuel GFM-formatering
  • Billeder — udtrækning af rId -referencer og derefter udpakning af mediefiler
  • Registrer ændringer — ingen indbygget understøttelse

AnyMD gør alt dette i ét API-kald. Ét format at lære, én integration at vedligeholde, én tjeneste at overvåge.

# 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"

Fra DOCX til en RAG-pipeline

Når dine DOCX-filer er Markdown, er næste trin chunking og embedding til RAG. AnyMD-output er det ideelle input til semantiske chunking-strategier — grænser ved overskrifter, listestruktur og tabeloverskrifter bevares, så dine chunks respekterer dokumentets semantik i stedet for at skære afsnit over midt i en sætning.

Se en dybere sammenligning af chunking-metoder til Markdown-output i vores guide til rekursiv character chunking.

Priser

PlanSider/månedMaksimal filstørrelsePris
Gratis10010 MB$0
Starter50025 MB$19
Pro5,00050 MB$99
EnterpriseTilpassetTilpassetTilpasset

En typisk DOCX-side konverteres på under 0,3 sekund.

Få din gratis API-nøgle — 100 sider/måned, intet kreditkort påkrævet.


← Læs mere →