Pour extraire les données d'une facture à partir d'un PDF avec Python, vous lisez le texte du fichier, puis vous extrayez des champs nommés de ce texte. Deux étapes, peut-être quarante lignes de code, et cela fonctionne à merveille sur la première facture que vous essayez. Puis le fournisseur numéro sept déplace son numéro de facture de trois lignes vers le haut, et votre parseur commence à renvoyer None à deux heures du matin.
Cette deuxième partie est le véritable sujet de cet article. La lecture est facile. Rester correct est le vrai travail.
Points clés
- Python extrait le texte des factures en quelques lignes. Le maintenir correct sur des centaines de mises en page de fournisseurs est ce qui vous coûte réellement.
- Un PDF n'est pas un format de données. C'est une description typographique d'une page imprimée, c'est pourquoi une regex écrite pour la mise en page d'un fournisseur est une chose fragile.
- Les expressions régulières et les modèles par fournisseur évoluent de manière linéaire avec votre liste de fournisseurs. Ce n'est pas le cas des modèles de vision, car ils lisent la page au lieu de la chaîne de caractères.
- Quel que soit l'outil qui extrait les données, votre propre code doit vérifier l'arithmétique. Les articles dont la somme ne correspond pas au sous-total sont le détecteur de bugs le moins cher que vous n'écrirez jamais.
- L'extraction est rarement ce qui pousse les gens à arrêter de construire eux-mêmes. La file d'attente des exceptions, la correspondance des fournisseurs et l'intégration comptable le sont.
Le format PDF
Le format PDF est polyvalent, permettant une représentation fidèle des documents papier, tels que les factures, sans limiter leur conception. Il provient de l’univers de l’impression et a été conçu pour être une représentation numérique d’une page imprimée. Cette flexibilité offre une grande liberté, permettant aux créateurs de PDF de s’exprimer et de se conformer à diverses réglementations et normes.
Cependant, le défi apparaît lorsque les données sont verrouillées au sein d’un PDF. La nature libre et parfois complexe du format s’accorde mal avec l’approche structurée et cohérente nécessaire au traitement des grandes quantités de données dans une entreprise.

Un PDF stocke l'emplacement de chaque glyphe sur la page. Il ne stocke pas le fait que le nombre en bas à droite est le total. Cette relation vit dans votre tête, et chaque méthode d'extraction de cet article est une tentative de l'encoder.
Quelles sont les étapes pour extraire des données d'une facture ?
Une facture est un document qui se présente généralement au format PDF. Une facture formalise une transaction entre un fournisseur et un client, où un produit ou un service est échangé contre un montant d’argent précis. Voici les étapes nécessaires pour extraire les données de ce document :
- Définir un schéma de données pour les données à extraire de vos factures
- Convertir votre facture de l’image au texte
- Extraire le texte de votre facture selon votre schéma de données
- Collecter les données extraites

Définir un schéma pour vos données de facture
Les factures proviennent de différents fournisseurs et chaque fournisseur personnalise généralement la présentation de ses documents. Malgré cette diversité des formes, le fond de toutes les factures reste le même : il vous faut un fournisseur, un client, une référence de facture, une date et une liste d’articles avec une quantité, une description et un coût associés. Un bon point de départ pour définir votre schéma de facture est votre logiciel de comptabilité, car c’est très probablement là que vous stockerez ces données extraites à la fin, n'est-ce pas ? Si vous recherchez simplement un format couvrant tous les cas possibles, je vous recommande le site schema.org, qui définit de manière pratique une série de formats de données conformes aux normes de l'industrie pour de nombreux domaines, y compris les factures. Parseur définit un schéma de données par défaut pour vos factures, mais vous pouvez l’adapter à votre cas d’usage en renommant les champs dans votre boîte de réception de factures, comme expliqué ici. Une fois votre format de données défini, vous pouvez passer à la conversion de votre facture de l’image au texte.
Par exemple, vous pouvez définir les champs suivants pour votre facture, à l’aide du format JSON Swagger :
{
"InvoiceNumber": {
"type": "string",
"description": "The invoice number"
},
"InvoiceIssueDate": {
"type": "string",
"description": "The invoice date"
},
"Items": {
"type": "array",
"description": "The list of items in the invoice",
"items": {
"type": "object",
"properties": {
"quantity": {
"type": "number",
"description": "The quantity of the item"
},
"description": {
"type": "string",
"description": "The description of the item"
},
"unit_price": {
"type": "number",
"description": "The unit price of the item"
},
"price": {
"type": "number",
"description": "The total price of the item"
}
}
}
}
}
Notez ceci avant d'écrire le moindre code de parsing. C'est le contrat que chaque méthode ci-dessous doit respecter, et c'est ce que vous fournirez plus tard à un modèle de vision.
Convertir votre facture de l'image au texte

Un fichier PDF peut contenir une image. Par exemple, votre employé peut prendre rapidement une photo d'une facture avec l'appareil photo de son smartphone. Il la sauvegarde ensuite sous forme de PDF et l’envoie à votre service comptable. Votre équipe comptable est chargée d’extraire les données de cette facture et de les introduire d'une manière ou d'une autre dans votre système comptable sans erreur. L’étape suivante consiste à convertir cette image en texte, à l’aide d’un système de Reconnaissance Optique de Caractères (OCR). L’un des systèmes OCR les plus populaires est Tesseract. Tesseract est écrit en C et C++. Pour utiliser Tesseract depuis notre programme Python, nous devrons utiliser une interface (binding) comme PyTesseract. Une interface est un moyen d'appeler une bibliothèque logicielle (ici, Tesseract) à partir d'un langage dans lequel elle n'est pas écrite (ici, Python). Il existe de nombreux systèmes de ce type et leurs résultats varient fortement selon la technologie sous-jacente et la qualité du scan du document sur lequel ils travaillent. Parseur détecte de manière transparente si votre document est une image et le convertit automatiquement en texte en interne. Une fois les données du document sous forme textuelle, elles sont prêtes à être extraites.
Extraire le texte de votre facture selon votre schéma de données
Une fois votre PDF en format texte (ou interrogeable), vous pouvez utiliser la bibliothèque Python pdftotext pour extraire les données du fichier PDF, sous forme de texte. Voici un extrait de code pour extraire le texte d'un fichier PDF :
import pdftotext
# Load your invoice
with open("invoice.pdf", "rb") as file_handle:
pdf = pdftotext.PDF(file_handle)
# Iterate over all the pages
for page in pdf:
print(page)
Nommez ce script convert_pdf_to_text.py et exécutez-le, vous obtiendrez la facture au format texte dans la sortie standard.
Si vous souhaitez rediriger la sortie vers un fichier, vous pouvez exécuter :
$ python convert_pdf_to_text.py > invoice.txt
pdftotext vous donne une chaîne de caractères plate, ce qui est parfait pour les champs d'en-tête et sans espoir pour les tableaux. Lorsque vous avez besoin des articles, optez plutôt pour pdfplumber, car il conserve les coordonnées de chaque mot et peut tenter d'extraire le tableau lui-même :
import pdfplumber
with pdfplumber.open("invoice.pdf") as pdf:
page = pdf.pages[0]
# Words with their positions on the page
for word in page.extract_words():
print(word["text"], word["x0"], word["top"])
# And an attempt at the line-item table
table = page.extract_table()
if table:
for row in table:
print(row)
Exécutez cela sur une vraie facture de fournisseur et vous constaterez très souvent que table est None. Ce n'est pas un bug. C'est le premier signal honnête que ce problème est plus difficile qu'il n'y paraît, et nous y reviendrons ci-dessous.
Maintenant que vous avez la facture sous forme de texte, vous pouvez en extraire les données souhaitées en utilisant n'importe quelle combinaison des techniques suivantes :
- Vous pouvez utiliser une expression régulière pour extraire les données que vous souhaitez. Les expressions régulières sont un moyen puissant d'extraire des données à partir de texte, mais elles sont également très fragiles. Si le format de la facture change, vous devrez mettre à jour votre expression régulière. De plus, les expressions régulières ne sont pas très adaptées pour extraire des données à partir de tableaux.
- Vous pouvez utiliser un système de modèles visuels, idéalement en tirant parti de l'OCR Dynamique et de l'OCR Zonal. Il s'agit d'une méthode plus avancée pour extraire des données à partir de texte. Elle est plus robuste que les expressions régulières, mais aussi plus complexe à mettre en œuvre.
- Vous pouvez confier la page à un modèle de vision avec un schéma et le laisser lire le document comme le fait une personne. C'est l'approche qui a changé au cours des deux dernières années, et elle a sa propre section ci-dessous.
Extrayons les données de votre facture avec le module d'expressions régulières re de Python. Voici un extrait de code pour extraire le numéro de la facture de votre facture :
import re
# Load your invoice
with open("invoice.txt", "r") as file_handle:
invoice = file_handle.read()
# Extract the invoice number
invoice_number = re.search(r"Invoice number: (\w+)", invoice).group(1)
print(invoice_number)
Nommez ce script extract.py et exécutez-le, vous obtiendrez le numéro de facture dans la sortie standard :
$ python extract.py
Et vous obtiendrez quelque chose comme :
INV-1234
Pourquoi votre parseur de factures par regex se casse
Une regex correspond à une chaîne de caractères. Une facture est une image. Tout ce qui tourne mal découle de cette inadéquation, et cela tourne mal d'un petit nombre de façons prévisibles :
- L'étiquette a bougé. Votre modèle est ancré à
Invoice number:et le nouveau modèle ditInvoice #, ou place la valeur sur la ligne suivante au lieu de la même. extract_tablerenvoieNone. Les extracteurs de tableaux recherchent des lignes de séparation. La plupart des factures de fournisseurs alignent leurs colonnes avec des espaces blancs et ne tracent aucune bordure.- Le texte sort dans le mauvais ordre. Les mises en page à deux colonnes et les blocs d'adresses flottants s'entremêlent lorsque la page est aplatie en une chaîne, de sorte que les lignes de vos articles arrivent mélangées.
- Le tableau s'étend sur plusieurs pages. Les lignes deux à neuf sont sur la page un, dix à quatorze sur la page deux, avec les en-têtes de colonnes répétés entre les deux et une ligne de sous-total prétendant être un article.
- Trois nombres ressemblent tous au total. Sous-total, total, montant dû et solde reporté. Choisir le plus grand est une erreur sur toute facture comportant un avoir.
- Le scan est une photographie. L'OCR lit un
8taché comme un3et rien en aval ne le remarque, car3est un chiffre parfaitement valide. - Cellules fusionnées et descriptions multilignes. La description d'un produit s'étend sur trois lignes et votre logique de séparation des lignes la transforme en trois articles sans prix.
Aucun de ces problèmes n'est réparable avec une meilleure expression régulière. Ce sont tous des problèmes de mise en page déguisés en problèmes de chaînes de caractères. C'est le point où la plupart des gens commencent soit à écrire un modèle par fournisseur, qui croît indéfiniment, soit à changer d'approche.
L'approche 2026 - un modèle de vision et un schéma
Le changement utile est que vous n'avez plus besoin d'aplatir la page en texte avant d'en extraire les données. Un modèle de vision regarde la facture rendue, donc un fournisseur déplaçant son numéro de facture n'est plus un événement. Ce que vous fournissez n'est pas un modèle, c'est le schéma que vous avez écrit au début de cet article.
Contraignez la sortie pour obtenir les mêmes clés à chaque fois. Pydantic associé à un mode de sortie structurée, tel que les OpenAI's structured outputs, le fait pour vous :
from typing import List
from pydantic import BaseModel
class LineItem(BaseModel):
description: str
quantity: float
unit_price: float
amount: float
class Invoice(BaseModel):
vendor_name: str
invoice_number: str
invoice_date: str # ISO 8601
currency: str
subtotal: float
tax: float
total: float
line_items: List[LineItem]
# Render the PDF page to an image, send it to a vision model,
# and require the response to match the Invoice schema.
# The model fills the fields. It does not get to invent the shape.
C'est véritablement la majeure partie du problème d'extraction résolue, et c'est pourquoi cette approche s'est répandue si vite. Elle introduit également un nouveau mode d'échec que les regex n'ont jamais eu : une regex qui ne trouve pas le numéro de facture renvoie None, tandis qu'un modèle qui ne le trouve pas en écrira parfois un plausible. La règle qui vous protège est simple. Le modèle propose et votre code vérifie.
Si vous préférez ne pas exécuter le modèle vous-même, la même capacité est vendue en tant que service géré par les fournisseurs de cloud, dans Azure AI Document Intelligence et Amazon Textract's AnalyzeExpense, qui renvoient tous deux les champs d'en-tête et les articles séparément. Nous avons décrit en quoi l'approche sous-jacente diffère du parsing basé sur des règles dans AI versus rule-based PDF parsers, et à quoi elle ressemble appliquée aux factures spécifiquement dans vision AI invoice processing.
La couche de validation que vous devez écrire vous-même
Quel que soit l'outil qui a produit votre JSON, ces vérifications ont leur place dans votre code, pas dans le score de confiance de l'extracteur. Elles sont peu coûteuses, déterministes et détectent les erreurs qui coûtent de l'argent :
subtotal + tax + shipping - discounttombe surtotal, au centime près.- La somme des montants des articles correspond au sous-total. Si ce n'est pas le cas, vous avez oublié une ligne ou en avez inventé une.
- Chaque
quantity * unit_priceest égal à son propreamount. - La date est parsée correctement, et elle n'est pas dans le futur.
- Le numéro de facture n'a pas déjà été payé pour ce fournisseur. Les paiements en double sont le bug le plus coûteux de la comptabilité fournisseurs.
- Le nom du fournisseur correspond à un enregistrement dans votre fichier principal des fournisseurs.
- Les coordonnées bancaires pour le paiement correspondent à celles déjà enregistrées pour ce fournisseur. Un changement ici est un contrôle de fraude, pas un contrôle de données.
- La devise est celle dans laquelle vous négociez réellement.
- Le numéro de bon de commande existe, et ses quantités et prix correspondent si vous effectuez un rapprochement à deux ou trois voies.
- Chaque champ requis est présent et non vide.
Tout ce qui échoue est envoyé à une personne, pas dans la comptabilité :
def validate(invoice):
errors = []
if abs(invoice.subtotal + invoice.tax - invoice.total) > 0.01:
errors.append("totals_do_not_add_up")
line_sum = sum(item.amount for item in invoice.line_items)
if invoice.line_items and abs(line_sum - invoice.subtotal) > 0.01:
errors.append("line_items_do_not_sum_to_subtotal")
if not invoice.invoice_number:
errors.append("missing_invoice_number")
return errors
Dix lignes d'arithmétique détecteront plus de vrais problèmes que n'importe quelle quantité d'ajustement de prompt.
Qu'en est-il de invoice2data et des autres bibliothèques ?
invoice2data mérite une mention honnête, car c'est le premier résultat sur lequel beaucoup de gens atterrissent et c'est un très bon logiciel. C'est un outil en ligne de commande et une bibliothèque Python qui fait correspondre les factures à des modèles YAML que vous écrivez par fournisseur, avec les règles de correspondance dans le contrôle de version plutôt qu'enfouies dans votre code. Si vous avez une douzaine de fournisseurs qui ne modifient jamais leur mise en page, il vous servira bien pendant des années.
La limite réside dans la conception, pas dans la qualité. Un modèle par fournisseur signifie que votre maintenance croît avec votre liste de fournisseurs, et les modèles se cassent exactement pour les raisons énumérées ci-dessus. Quelque part entre vingt et trente mises en page actives, la personne qui maintient les modèles fait plus de travail que la personne qui retapait les factures.
Le même raisonnement s'applique à l'ensemble des bibliothèques plus larges. pdfplumber, PyMuPDF, pdftotext et pytesseract excellent tous dans leur travail réel, qui consiste à extraire des caractères et des coordonnées d'une page. Aucune d'entre elles n'a jamais été conçue pour savoir quel nombre est le total. Nous comparons cet ensemble plus large dans notre sélection des best PDF parsers et des best data extraction APIs.
Collecter les données extraites
Avec Python, vous pouvez itérer sur les fichiers de factures dans un dossier donné et en extraire les données. Disons que nous extrayons le numéro de facture et le montant total, et que nous affichons le résultat au format CSV :
import os
import re
import pdftotext
# Iterate over all the PDF files in the folder
for filename in os.listdir("invoices/"):
if not filename.endswith(".pdf"):
continue
# Load your invoice
with open("invoices/" + filename, "rb") as file_handle:
pdf = pdftotext.PDF(file_handle)
# Print the CSV column header
print("InvoiceNumber,TotalAmount")
# Iterate over all the pages
for page in pdf:
# Extract the invoice number
invoice_number = re.search(r"Invoice number: (\w+)", page).group(1)
total_amount = re.search(r"Total amount: (\w+)", page).group(1)
print(invoice_number, total_amount, sep=",")
Nommez ce script extract_to_csv.py et exécutez-le, vous obtiendrez le numéro de facture et le montant total dans la sortie standard,
que vous pourrez rediriger vers un fichier CSV que vous pourrez ouvrir plus tard avec votre tableur préféré, comme Excel :
$ python extract_to_csv.py > invoices.csv
Un dossier de fichiers CSV est là où s'arrêtent la plupart des scripts de facturation, et c'est aussi là que commence l'honnête comptabilité. Quelqu'un doit encore les importer, faire correspondre les fournisseurs, rechercher les lignes que le système comptable a rejetées et déterminer quoi faire avec la facture dont la validation a échoué à 2h du matin. Ce travail n'apparaît pas dans votre script et il n'apparaît pas dans votre facture de jetons d'IA.
Quand s'arrêter de construire
Voici la partie que la plupart des vendeurs sautent, alors nous la dirons en premier. Si vous avez une poignée de fournisseurs réguliers, un ingénieur qui peut surveiller un pipeline, et personne qui attend les données, alors écrivez le script. Un modèle de vision plus un schéma plus les dix lignes de validation ci-dessus vous mèneront loin pour très peu d'argent, et vous comprendrez chaque partie.
La facture de la construction arrive plus tard, et jamais dans l'extraction. Elle arrive dans les parties que personne ne prototype :
- La file d'attente des exceptions. Votre équipe comptable a besoin d'un écran où cliquer sur un champ le met en évidence sur la facture afin qu'ils puissent le corriger en quatre secondes au lieu de quarante. C'est un produit, pas un script.
- La correspondance des fournisseurs. "ACME Ltd", "Acme Limited" et "ACME LTD." sont un seul fournisseur, et le grand livre n'en acceptera pas trois.
- La détection des doublons. La même facture arrive en pièce jointe d'un e-mail le mardi et en relevé PDF le vendredi.
- La machine à états. Files d'attente, nouvelles tentatives, échecs partiels et savoir lesquelles des 300 factures de la nuit dernière sont réellement passées.
- La piste d'audit. Ce qui a été extrait, ce qu'un humain a changé, qui l'a approuvé et quand. La finance posera la question, généralement lors d'un audit.
- Tout après le JSON. Le mappage des champs dans le système comptable, le codage analytique, le rapprochement des bons de commande et les lignes qu'il rejette.
Les signaux clairs qu'il est temps d'acheter plutôt que de construire sont les suivants. Vous avez dépassé environ vingt à trente mises en page de fournisseurs actives. Vous avez besoin des articles, pas seulement des champs d'en-tête. Plus qu'une poignée de vos factures arrivent sous forme de scans. Votre équipe comptable, et non votre équipe d'ingénierie, doit corriger les erreurs. Les échecs d'extraction retardent les paiements. Ou, plus couramment, la personne qui maintient le parseur a cessé de livrer quoi que ce soit d'autre.
Comment Parseur gère cela
Parseur est un parseur de documents qui fait toute la boucle, pas seulement l'étape d'extraction. Les factures arrivent à une adresse de messagerie dédiée, via l'API, ou depuis un dossier surveillé. Le moteur Vision AI lit les PDF, les scans et les photographies, et le moteur Text AI lit les e-mails et les documents textuels. Les champs ressortent nommés et typés, et les articles ressortent sous forme de lignes. Il n'y a aucun modèle à écrire et rien à maintenir lorsqu'un fournisseur modifie le design de sa facture.
Ce que vous obtenez en plus du JSON est la moitié contre laquelle cet article vous met en garde. Les données extraites sont révisables et corrigeables sur place, de sorte qu'une personne de la comptabilité fournisseurs corrige un total mal lu sans ouvrir de ticket. Les corrections remontent dans l'extraction. Et les données vont là où elles doivent aller grâce à l'intégration directe par webhook, Make, Zapier ou Microsoft Power Automate, ou directement de l'API sous forme de JSON.
Si vous souhaitez voir l'ensemble des champs que nous extrayons des factures par défaut, cela est documenté sur notre page sur l'OCR des factures, et le flux de travail plus large est couvert dans la capture de données de factures.
Conclusion
Extraire les données d'une facture à partir d'un PDF avec Python est un problème résolu pour environ cent factures et un problème non résolu pour dix mille. Le code n'est pas ce qui change entre ces deux nombres. Ce qui change, c'est le nombre de mises en page de fournisseurs que vous vous engagez discrètement à maintenir, et qui est appelé lorsque l'une d'entre elles change.
Écrivez le script. Cela vaut vraiment la peine de le faire une fois, ne serait-ce que pour découvrir exactement lequel des sept modes d'échec ci-dessus vous frappe en premier. Ensuite, décidez honnêtement si vos six prochains mois de temps sont mieux dépensés sur le huitième. Si la réponse est non, Parseur fait cela depuis 2016 et vous déchargera du dossier.
Dernière mise à jour le





