Blog

Convertir DOCX en Markdown avec Python

12 août 2026 · 5 min de lecture


Si vous travaillez avec des données d'entraînement pour l'IA, des pipelines documentaires ou des systèmes RAG, vous avez presque certainement déjà eu affaire à des fichiers DOCX. Les documents Word sont partout — brouillons de recherche, rapports d'entreprise, contrats juridiques, manuscrits — mais ils sont pénibles à traiter par programmation.

Cet article montre comment convertir des DOCX en Markdown propre avec l'API REST d'AnyMD depuis Python, avec traitement asynchrone par lots, gestion des erreurs et pagination pour des workflows à l'échelle de la production.

Pourquoi convertir DOCX en Markdown ?

DOCX est un format binaire (un ZIP de fichiers XML). Des bibliothèques comme python-docx peuvent extraire le texte, mais elles perdent la structure : les titres deviennent de simples paragraphes, les listes se réduisent à du texte indenté et les tableaux deviennent du bruit séparé par des tabulations. Markdown préserve tout cela dans un format que chaque LLM, base de données vectorielle et générateur de site statique peut consommer nativement.

Conversion ponctuelle

Le cas le plus simple — un seul fichier 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 détecte automatiquement DOCX à partir de l'extension du fichier et renvoie du Markdown GitHub Flavored. Titres, listes numérotées, listes à puces, gras/italique, tableaux, images et hyperliens sont tous conservés — exactement comme si vous ouvriez le document dans Word.

Conversion asynchrone par lots avec nouvelles tentatives

Avec 50 ou 500 fichiers DOCX, une conversion séquentielle est trop lente. Voici un processeur asynchrone par lots avec une gestion correcte des erreurs :

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

Le script utilise un pool de sessions avec nombre de connexions limité (10 requêtes simultanées par défaut), relance les requêtes en cas de limitation 429 et d'erreurs transitoires avec un backoff exponentiel, puis écrit chaque résultat dans un .md fichier.

Pagination pour les grandes bibliothèques de documents

Si vous récupérez des documents depuis une API ou un bucket S3, vous pouvez avoir besoin d'un traitement paginé. Voici un modèle basé sur un générateur :

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

Gestion des gros fichiers DOCX

AnyMD prend en charge les fichiers DOCX jusqu'à 50 Mo avec l'offre payante.

Ce qui est préservé

Fonctionnalité DOCXSortie Markdown
Titres 1–6# à ######
Gras / Italique**bold** / *italic*
Listes numérotées1. item — imbrication correcte
Listes à puces- item — imbrication correcte
TableauxSyntaxe de tableau GFM avec alignement
Hyperliens[text](url)
Images![alt](path)
Blocs de codeBlocs délimités par ```
Citations en bloc> quote

Comparaison avec python-docx

La bibliothèque Python standard python-docx vous donne un accès bas niveau aux éléments internes de DOCX — paragraphes, segments et styles. C'est puissant pour générer des documents par programmation, mais pour l'extraction cela signifie que vous devez réimplémenter chaque règle structurelle appliquée par le moteur de rendu de Word :

  • Titres — il faut associer manuellement les styles de paragraphe aux titres Markdown
  • Listes — nécessite d'analyser les éléments numPr et de suivre la profondeur des listes
  • Tableaux — extraction cellule par cellule, ligne par ligne, avec mise en forme GFM manuelle
  • Images — extraction des références rId puis décompression des fichiers multimédias
  • Suivi des modifications — aucune prise en charge intégrée

AnyMD fait tout cela en un seul appel d'API. Un format à apprendre, une intégration à maintenir, un service à surveiller.

# 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 à un pipeline RAG

Une fois vos fichiers DOCX convertis en Markdown, l'étape suivante consiste à les découper et à créer des embeddings pour le RAG. La sortie d'AnyMD est une entrée idéale pour les stratégies de découpage sémantique — les limites des titres, la structure des listes et les en-têtes de tableaux sont conservés, de sorte que vos segments respectent la sémantique du document au lieu de couper des paragraphes en pleine phrase.

Pour une comparaison plus approfondie des méthodes de découpage appliquées au Markdown, consultez notre guide sur le découpage récursif par caractères.

Tarifs

OffrePages/moisTaille maximale du fichierPrix
Gratuit10010 Mo$0
Starter50025 Mo$19
Pro5,00050 Mo$99
EntrepriseSur mesureSur mesureSur mesure

Une page DOCX standard est convertie en moins de 0,3 seconde.

Obtenez votre clé API gratuite — 100 pages/mois, sans carte bancaire.


← Lire la suite →