ITSkillsCenter
Intelligence Artificielle

Résumer 50 PDF juridiques avec Claude et exporter en Notion

4 min de lecture
Miniature - Résumer 50 PDF juridiques avec Claude et exporter en Notion

Ce que vous saurez faire

  1. Analyser 50 PDF juridiques en batch
  2. Extraire structure JSON
  3. Créer pages Notion avec métadonnées
  4. Lier documents entre eux

Étape 1 — Setup

pip install anthropic pymupdf notion-client tenacity

Étape 2 — Lire PDF + OCR si besoin

import pymupdf
from pathlib import Path

def lire_pdf_ocr(path):
    doc = pymupdf.open(path)
    texte = "\n".join(p.get_text() for p in doc)
    # Si peu de texte, probablement scanné
    if len(texte.strip()) < 200 * len(doc):
        import pytesseract
        from PIL import Image
        import io
        ocr = []
        for p in doc:
            pix = p.get_pixmap(dpi=200)
            img = Image.open(io.BytesIO(pix.tobytes("png")))
            ocr.append(pytesseract.image_to_string(img, lang="fra"))
        texte = "\n".join(ocr)
    return texte

Étape 3 — Prompt d’extraction

import json
from anthropic import Anthropic
from tenacity import retry, stop_after_attempt, wait_exponential

claude = Anthropic()

PROMPT = """À partir du document, produis un JSON strict:
{
  "titre": "",
  "type": "arret|contrat|avis|loi|autre",
  "juridiction": "",
  "date": "YYYY-MM-DD",
  "parties": [""],
  "resume": "150 mots max",
  "points_cles": ["3-5 puces"],
  "clauses_notables": ["références articles"],
  "tags": ["4-6 tags fr"],
  "documents_relies": ["titres exacts"]
}"""

@retry(stop=stop_after_attempt(3), wait=wait_exponential(multiplier=2))
def analyser(pdf_path):
    texte = lire_pdf_ocr(pdf_path)[:60000]
    r = claude.messages.create(
        model="claude-sonnet-4-6", max_tokens=2000,
        system=[{"type":"text","text":PROMPT,"cache_control":{"type":"ephemeral"}}],
        messages=[{"role":"user","content":
            f"<document>\n{texte}\n</document>\n\nJSON:"}])
    return json.loads(r.content[0].text)

Étape 4 — Créer pages Notion

from notion_client import Client as Notion
import os

notion = Notion(auth=os.environ["NOTION_TOKEN"])
DB = os.environ["NOTION_DATABASE_ID"]

def creer_notion(analyse, pdf_name):
    page = notion.pages.create(
        parent={"database_id": DB},
        properties={
            "Titre":       {"title":[{"text":{"content":analyse["titre"][:200]}}]},
            "Type":        {"select":{"name":analyse["type"]}},
            "Juridiction": {"rich_text":[{"text":{"content":analyse["juridiction"]}}]},
            "Date":        {"date":{"start":analyse["date"]}},
            "Fichier":     {"rich_text":[{"text":{"content":pdf_name}}]},
            "Tags":        {"multi_select":[{"name":t[:100]} for t in analyse["tags"]]},
            "Resume":      {"rich_text":[{"text":{"content":analyse["resume"][:2000]}}]},
        },
        children=[
            {"object":"block","heading_2":{"rich_text":[{"text":{"content":"Points clés"}}]}},
            *[{"object":"block","bulleted_list_item":{"rich_text":[{"text":{"content":p}}]}}
              for p in analyse["points_cles"]],
            {"object":"block","heading_2":{"rich_text":[{"text":{"content":"Clauses notables"}}]}},
            *[{"object":"block","bulleted_list_item":{"rich_text":[{"text":{"content":c}}]}}
              for c in analyse["clauses_notables"]],
        ])
    return page["id"]

Étape 5 — Pipeline parallèle

from concurrent.futures import ThreadPoolExecutor, as_completed

pdfs = list(Path("./docs_juridiques").glob("*.pdf"))
resultats = []

def traiter(pdf):
    try:
        analyse = analyser(str(pdf))
        page_id = creer_notion(analyse, pdf.name)
        return {"pdf":pdf.name, "page_id":page_id, "status":"ok"}
    except Exception as e:
        return {"pdf":pdf.name, "error":str(e), "status":"error"}

with ThreadPoolExecutor(max_workers=5) as pool:
    futures = [pool.submit(traiter, p) for p in pdfs]
    for fut in as_completed(futures):
        r = fut.result()
        print(r["pdf"], r["status"])
        resultats.append(r)

with open("traitement_log.json","w") as f:
    json.dump(resultats, f, indent=2, ensure_ascii=False)

Étape 6 — Relations Notion

# Phase 2: lier les pages selon "documents_relies"
titres_vers_page = {}
for p in notion.databases.query(database_id=DB)["results"]:
    titre = p["properties"]["Titre"]["title"][0]["plain_text"]
    titres_vers_page[titre] = p["id"]

for p in notion.databases.query(database_id=DB)["results"]:
    # Extract relations des "documents_relies" block
    blocks = notion.blocks.children.list(p["id"])["results"]
    # ... lier via notion.pages.update avec property "Documents reliés"

Étape 7 — Déduplication des tags

from collections import Counter

pages = notion.databases.query(database_id=DB)["results"]
compteur = Counter()
for p in pages:
    for t in p["properties"]["Tags"]["multi_select"]:
        compteur[t["name"]] += 1

# Fusionner variantes
fusions = {"contrat": ["contrats","contractuel"],
           "OHADA": ["Ohada","ohada"]}

Étape 8 — Recherche sémantique sur Notion

import voyageai, chromadb
vo = voyageai.Client()
chroma = chromadb.PersistentClient("./notion_chroma")
coll = chroma.get_or_create_collection("juridique")

for p in pages:
    titre = p["properties"]["Titre"]["title"][0]["plain_text"]
    resume = p["properties"]["Resume"]["rich_text"][0]["plain_text"] if p["properties"]["Resume"]["rich_text"] else ""
    emb = vo.embed([titre + " " + resume], model="voyage-3",
                    input_type="document").embeddings[0]
    coll.add(documents=[resume], embeddings=[emb],
             metadatas=[{"notion_id":p["id"],"titre":titre}], ids=[p["id"]])

Coûts 50 PDF 20 pages

Lecture + OCR: gratuit (local)
Claude Sonnet avec cache: ~4,5 USD
Notion API: gratuit
Embeddings: 0,06 USD
Total: ~5 USD (3k FCFA) pour 50 documents
Besoin d'un site web ?

Confiez-nous la Création de Votre Site Web

Site vitrine, e-commerce ou application web — nous transformons votre vision en réalité digitale. Accompagnement personnalisé de A à Z.

À partir de 250.000 FCFA
Parlons de Votre Projet
Publicité