Se você trabalha com dados de treinamento de IA, pipelines de documentos ou sistemas RAG, quase certamente já teve de lidar com arquivos DOCX. Documentos do Word estão em toda parte — rascunhos de pesquisa, relatórios empresariais, contratos jurídicos, manuscritos — mas são trabalhosos de processar programaticamente.
Este artigo mostra como converter DOCX em Markdown limpo usando a API REST da AnyMD a partir de Python, com processamento assíncrono em lote, tratamento de erros e paginação para fluxos de trabalho em escala de produção.
Por que converter DOCX em Markdown?
DOCX é um formato binário (um ZIP de arquivos XML). Bibliotecas como python-docx conseguem extrair texto, mas perdem a estrutura: títulos viram parágrafos simples, listas viram texto indentado e tabelas viram ruído separado por tabulações. Markdown preserva tudo isso em um formato que qualquer LLM, banco de dados vetorial e gerador de sites estáticos consegue consumir nativamente.
Conversão única
O caso mais simples — um único arquivo 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}")
A AnyMD detecta DOCX automaticamente pela extensão do arquivo e retorna GitHub-Flavored Markdown. Títulos, listas numeradas, listas com marcadores, negrito/itálico, tabelas, imagens e links são todos preservados — exatamente como você veria ao abrir o documento no Word.
Conversão assíncrona em lote com novas tentativas
Quando você tem 50 ou 500 arquivos DOCX, a conversão sequencial é lenta demais. Veja um processador assíncrono em lote com tratamento adequado de erros:
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"))
O script usa um pool de sessões com limite de conexões (10 solicitações simultâneas por padrão), tenta novamente em limites de taxa 429 e erros transitórios com backoff exponencial e grava cada resultado como um .md arquivo.
Paginação para grandes bibliotecas de documentos
Se você busca documentos em uma API ou bucket S3, talvez precise de processamento paginado. Veja um padrão com gerador:
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")
Tratamento de arquivos DOCX grandes
A AnyMD aceita arquivos DOCX de até 50 MB no plano pago.
O que é preservado
| Recurso do DOCX | Saída Markdown |
|---|---|
| Títulos 1–6 | # até ###### |
| Negrito / Itálico | **bold** / *italic* |
| Listas numeradas | 1. item — aninhamento correto |
| Listas com marcadores | - item — aninhamento correto |
| Tabelas | Sintaxe de tabela GFM com alinhamento |
| Links | [text](url) |
| Imagens |  |
| Blocos de código | Blocos delimitados por ``` |
| Citações em bloco | > quote |
Comparação com python-docx
A biblioteca padrão de Python python-docx dá acesso de baixo nível aos elementos internos de DOCX — parágrafos, runs e estilos. Isso é poderoso para gerar documentos programaticamente, mas, para extração, significa reimplementar cada regra estrutural aplicada pelo renderizador do Word:
- Títulos — é preciso mapear manualmente estilos de parágrafo para títulos Markdown
- Listas — requer analisar elementos
numPre acompanhar a profundidade das listas - Tabelas — extração célula por célula e linha por linha, com formatação GFM manual
- Imagens — extração de referências
rIde depois descompactação dos arquivos de mídia - Controle de alterações — sem suporte integrado
A AnyMD faz tudo isso em uma única chamada de API. Um formato para aprender, uma integração para manter e um serviço para monitorar.
# 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 DOCX para um pipeline RAG
Depois que seus arquivos DOCX estiverem em Markdown, o próximo passo é fazer chunking e embeddings para RAG. A saída da AnyMD é a entrada ideal para estratégias de chunking semântico — limites de títulos, estrutura das listas e cabeçalhos de tabelas são preservados, para que seus chunks respeitem a semântica do documento em vez de cortar parágrafos no meio de uma frase.
Para uma comparação mais aprofundada das abordagens de chunking em saída Markdown, consulte nosso guia de chunking recursivo por caracteres.
Preços
| Plano | Páginas/mês | Tamanho máximo do arquivo | Preço |
|---|---|---|---|
| Grátis | 100 | 10 MB | $0 |
| Starter | 500 | 25 MB | $19 |
| Pro | 5,000 | 50 MB | $99 |
| Empresarial | Personalizado | Personalizado | Personalizado |
Uma página DOCX típica é convertida em menos de 0,3 segundo.
Obtenha sua chave de API gratuita — 100 páginas/mês, sem cartão de crédito.