Blogi

Muunna DOCX Markdowniksi Pythonilla

12. elokuuta 2026 · 5 min lukuaika


Jos työskentelet tekoälyn koulutusdatan, asiakirjaputkien tai RAG-järjestelmien parissa, olet lähes varmasti joutunut käsittelemään DOCX-tiedostoja. Word-asiakirjoja on kaikkialla — tutkimusluonnoksia, liiketoimintaraportteja, oikeudellisia sopimuksia ja käsikirjoituksia — mutta niiden ohjelmallinen käsittely on työlästä.

Tässä artikkelissa näytetään, miten DOCX muunnetaan puhtaaksi Markdowniksi AnyMD:n REST API:lla Pythonista käsin, mukaan lukien asynkroninen eräkäsittely, virheenkäsittely ja sivutus tuotantotason työnkulkuihin.

Miksi Markdownia DOCX:stä?

DOCX on binäärimuoto (ZIP-paketti XML-tiedostoja). Kirjastot kuten python-docx voivat poimia tekstin, mutta rakenne katoaa: otsikot muuttuvat tavallisiksi kappaleiksi, listat sisennetyksi tekstiksi ja taulukot sarkainerotelluksi sotkuksi. Markdown säilyttää kaiken tämän muodossa, jota jokainen LLM, vektoritietokanta ja staattisten sivustojen generaattori voi käyttää suoraan.

Kertamuunnos

Yksinkertaisin tapaus — yksi DOCX-tiedosto:

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 tunnistaa DOCX-muodon automaattisesti tiedostopäätteestä ja palauttaa GitHub-Flavored Markdownia. Otsikot, numeroidut listat, luettelomerkkilistat, lihavointi/kursivointi, taulukot, kuvat ja hyperlinkit säilyvät — juuri sellaisina kuin ne näkyisivät Wordissa.

Asynkroninen erämuunnos uudelleenyrityksillä

Kun DOCX-tiedostoja on 50 tai 500, peräkkäinen muunnos on liian hidasta. Tässä on asynkroninen eräkäsittelijä kunnollisella virheenkäsittelyllä:

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

Skripti käyttää yhteysrajoitettua istuntopoolia (oletuksena 10 samanaikaista pyyntöä), yrittää uudelleen 429-nopeusrajoituksissa ja tilapäisissä virheissä eksponentiaalisella backoffilla ja kirjoittaa jokaisen tuloksen .md tiedostoksi.

Sivutus suurille asiakirjakirjastoille

Jos haet asiakirjoja API:sta tai S3-bucketista, saatat tarvita sivutettua käsittelyä. Tässä on generaattorimalli:

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

Suurten DOCX-tiedostojen käsittely

AnyMD käsittelee maksullisessa paketissa enintään 50 Mt:n DOCX-tiedostoja.

Mitä säilytetään

DOCX-ominaisuusMarkdown-tulos
Otsikot 1–6# asti ######
Lihavointi / Kursivointi**bold** / *italic*
Numeroidut listat1. item — oikea sisäkkäisyys
Luettelomerkkilistat- item — oikea sisäkkäisyys
TaulukotGFM-taulukkosyntaksi tasauksella
Hyperlinkit[text](url)
Kuvat![alt](path)
KoodilohkotLohkot, jotka on rajattu merkeillä ```
Lohkolainaukset> quote

Vertailu python-docx-kirjastoon

Pythonin vakiokirjasto python-docx antaa matalan tason pääsyn DOCX:n sisäisiin rakenteisiin — kappaleisiin, runeihin ja tyyleihin. Se on tehokasta asiakirjojen ohjelmalliseen tuottamiseen, mutta poiminnassa se tarkoittaa, että jokainen Wordin renderöijän käyttämä rakennesääntö on toteutettava itse uudelleen:

  • Otsikot — kappaletyylit on yhdistettävä Markdown-otsikoihin käsin
  • Listat — edellyttää elementtien numPr jäsentämistä ja listasyvyyden seurantaa
  • Taulukot — poiminta solu ja rivi kerrallaan sekä manuaalinen GFM-muotoilu
  • Kuvat — viitteiden rId poiminta ja mediatiedostojen purkaminen
  • Muutosten seuranta — ei sisäänrakennettua tukea

AnyMD tekee kaiken tämän yhdellä API-kutsulla. Yksi opittava formaatti, yksi ylläpidettävä integraatio ja yksi valvottava palvelu.

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

DOCX:stä RAG-putkeen

Kun DOCX-tiedostosi ovat Markdownia, seuraava vaihe on chunking ja embedding RAG-käyttöä varten. AnyMD:n tulos on ihanteellinen syöte semanttisiin chunking-strategioihin — otsikkorajat, listarakenne ja taulukoiden otsikot säilyvät, joten chunkit noudattavat asiakirjan semantiikkaa sen sijaan, että katkaisisivat kappaleita keskeltä lausetta.

Syvällisemmän vertailun Markdown-tuloksen chunking-menetelmistä löydät oppaastamme rekursiiviseen merkkipohjaiseen chunkingiin.

Hinnoittelu

PakettiSivua/kuukausiTiedoston enimmäiskokoHinta
Ilmainen10010 Mt$0
Starter50025 Mt$19
Pro5,00050 Mt$99
EnterpriseMukautettuMukautettuMukautettu

Tyypillinen DOCX-sivu muunnetaan alle 0,3 sekunnissa.

Hanki ilmainen API-avaimesi — 100 sivua/kuukausi, luottokorttia ei tarvita.


← Lue lisää →