Dacă lucrezi cu date de antrenare pentru AI, pipeline-uri de documente sau sisteme RAG, aproape sigur ai avut de-a face cu fișiere DOCX. Documentele Word sunt peste tot — ciorne de cercetare, rapoarte de business, contracte juridice, manuscrise — dar sunt dificil de procesat programatic.
Acest articol îți arată cum să convertești DOCX în Markdown curat folosind API-ul REST AnyMD din Python, cu procesare asincronă în lot, gestionarea erorilor și paginare pentru fluxuri de lucru la scară de producție.
De ce Markdown din DOCX?
DOCX este un format binar (un ZIP cu fișiere XML). Biblioteci precum python-docx pot extrage text, dar pierd structura: titlurile devin paragrafe simple, listele se reduc la text indentat, iar tabelele devin zgomot separat prin taburi. Markdown păstrează toate acestea într-un format pe care orice LLM, bază de date vectorială și generator de site-uri statice îl poate consuma nativ.
Conversie dintr-un singur pas
Cel mai simplu caz — un singur fișier DOCX:
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 detectează automat DOCX din extensia fișierului și returnează GitHub-Flavored Markdown. Titlurile, listele numerotate, listele cu marcatori, bold/italic, tabelele, imaginile și hyperlinkurile sunt toate păstrate — exact cum le-ai vedea dacă ai deschide documentul în Word.
Conversie asincronă în lot cu reîncercări
Când ai 50 sau 500 de fișiere DOCX, conversia secvențială este prea lentă. Iată un procesor asincron în lot cu gestionarea corectă a erorilor:
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"))
Scriptul folosește un pool de sesiuni cu număr limitat de conexiuni (implicit 10 cereri simultane), reîncearcă la limitări de rată 429 și erori tranzitorii cu backoff exponențial și scrie fiecare rezultat ca un .md fișier.
Paginare pentru biblioteci mari de documente
Dacă preiei documente dintr-un API sau dintr-un bucket S3, este posibil să ai nevoie de procesare paginată. Iată un model cu generator:
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")
Gestionarea fișierelor DOCX mari
AnyMD gestionează fișiere DOCX de până la 50 MB în planul plătit.
Ce se păstrează
| Caracteristică DOCX | Output Markdown |
|---|---|
| Titluri 1–6 | # până la ###### |
| Bold / Italic | **bold** / *italic* |
| Liste numerotate | 1. item — imbricare corectă |
| Liste cu marcatori | - item — imbricare corectă |
| Tabele | Sintaxă de tabel GFM cu aliniere |
| Hyperlinkuri | [text](url) |
| Imagini |  |
| Blocuri de cod | Blocuri delimitate cu ``` |
| Citate bloc | > quote |
Comparație cu python-docx
Biblioteca Python standard python-docx îți oferă acces de nivel jos la elementele interne DOCX — paragrafe, run-uri, stiluri. Este puternic pentru generarea programatică de documente, dar pentru extracție înseamnă că trebuie să reimplementezi fiecare regulă structurală aplicată de rendererul Word:
- Titluri — stilurile de paragraf trebuie mapate manual la titluri Markdown
- Liste — necesită parsarea elementelor
numPrși urmărirea adâncimii listelor - Tabele — extragere celulă cu celulă, rând cu rând, cu formatare GFM manuală
- Imagini — extragerea referințelor
rIdapoi dezarhivarea fișierelor media - Urmărirea modificărilor — fără suport integrat
AnyMD face toate acestea printr-un singur apel API. Un format de învățat, o integrare de întreținut, un serviciu de monitorizat.
# 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"
De la DOCX la un pipeline RAG
După ce fișierele DOCX sunt în Markdown, următorul pas este chunking-ul și embedding-ul pentru RAG. Outputul AnyMD este intrarea ideală pentru strategii de chunking semantic — limitele titlurilor, structura listelor și anteturile tabelelor sunt păstrate, astfel încât chunk-urile respectă semantica documentului în loc să taie paragrafele la mijlocul propoziției.
Pentru o comparație mai aprofundată a metodelor de chunking pe output Markdown, consultă ghidul nostru despre chunking recursiv pe caractere.
Prețuri
| Plan | Pagini/lună | Dimensiune maximă fișier | Preț |
|---|---|---|---|
| Gratuit | 100 | 10 MB | $0 |
| Starter | 500 | 25 MB | $19 |
| Pro | 5,000 | 50 MB | $99 |
| Enterprise | Personalizat | Personalizat | Personalizat |
O pagină DOCX obișnuită se convertește în mai puțin de 0,3 secunde.
Obține cheia API gratuită — 100 de pagini/lună, fără card de credit.