
Extraire du texte et des tableaux d’un PDF en R
Extrayez texte, positions, métadonnées et tableaux de fichiers PDF avec pdftools — plus le chemin honnête 2026 pour les documents désordonnés ou scannés
Une recette pratique et actuelle pour sortir du texte — et des tableaux — de fichiers PDF vers R. Lisez des pages entières avec pdftools::pdf_text(), obtenez les positions au niveau du mot avec pdf_data(), lisez les métadonnées avec pdf_info(), et nettoyez le résultat en un data frame. Pour les tableaux : reconstruisez des tableaux numériques propres à partir des positions, faites appel à tabulapdf pour les tableaux à lignes/à flux, et utilisez l’OCR (tesseract) ou une passe LLM/vision pour les PDF scannés ou désordonnés. Bâti sur le cœur maintenu pdftools — pas sur le tabulizer archivé.
- Lire le texte d’un PDF avec
pdftools— les pages entières avecpdf_text(), les données de position au niveau du mot avecpdf_data(), les métadonnées du document avecpdf_info(). - Nettoyer le texte brut — supprimer les sauts de page, réduire les espaces et découper en lignes avec le R base et
stringr. - Extraire les tableaux de trois façons honnêtes : reconstruire des tableaux numériques propres à partir des positions de
pdf_data(), utilisertabulapdfpour les tableaux à lignes/à flux, et recourir à l’OCR ou à une passe LLM/vision sur les PDF scannés ou désordonnés. - Quelle approche pour quel PDF — un court guide de décision pour ne pas passer un après-midi à faire du regex sur un scan.
- Pourquoi le cœur maintenu pdftools est la bonne base en 2026 — et pourquoi vous ne devriez pas commencer avec le
tabulizerarchivé.
Vous avez reçu un relevé bancaire, un rapport de laboratoire, un tableau administratif ou un PDF de revue — et les chiffres dont vous avez besoin sont prisonniers à l’intérieur. En R, le premier outil maintenu pour cette tâche est pdftools (de rOpenSci) : il lit le texte, les positions des mots et les métadonnées directement à partir d’un PDF numérique, sans Java ni service externe. Ce guide couvre toute la colonne vertébrale pratique — lire le texte, le nettoyer, en tirer un data frame, puis traiter les cas de tableaux — et il est honnête sur la réalité de 2026 : les PDF numériques propres livrent leur texte facilement, mais les documents désordonnés ou scannés nécessitent l’OCR ou une passe LLM, et aucun regex n’y changera rien.
D’une page PDF à un data frame bien rangé en R — pdftools lit le texte et les positions des mots, et vous les mettez en forme en colonnes.
pdftools, tabulapdf et tesseract reposent sur des bibliothèques système (poppler, Java, Tesseract) qui ne sont pas installées sur le moteur de rendu léger de cette page ; les blocs R sont donc présentés ici comme des listings illustratifs plutôt qu’exécutés en direct. Installez le package correspondant à la section qui vous intéresse (ci-dessous) et chaque extrait s’exécute tel quel dans votre propre session R. Là où nous montrons une sortie extraite, il s’agit d’un exemple réaliste capturé, clairement étiqueté — les formes et les noms de colonnes correspondent à ce que vous verrez.
Installer pdftools
pdftools enveloppe la bibliothèque PDF C++ poppler ; sous Linux vous installez donc d’abord un paquet système, tandis que sur macOS et Windows le binaire CRAN embarque ce qu’il faut. Suivez les notes d’installation de pdftools :
# Linux (Debian/Ubuntu) needs the poppler dev headers first:
# sudo apt-get install libpoppler-cpp-dev
# Fedora: sudo dnf install poppler-cpp-devel
# macOS/Windows: the CRAN binary just works.
install.packages("pdftools")Ensuite, chargez-le. pdftools — le package rOpenSci pour lire et afficher des documents PDF — suffit pour les sections consacrées au texte :
library(pdftools)Lire le texte : pdf_text()
La bête de somme, c’est pdf_text() : donnez-lui un chemin de fichier (ou une URL) et elle renvoie un vecteur de caractères avec un élément par page. Chaque élément contient tout le texte brut de cette page, sauts de ligne compris.
# One string per page
pages <- pdf_text("statement.pdf")
length(pages) # number of pages
#> [1] 3
# Peek at the first page
cat(pages[[1]])
#> Datanovia Bank — Monthly Statement
#> Account 0042 · Period 2026-06
#>
#> Date Description Amount
#> 2026-06-03 Opening balance 1,200.00
#> 2026-06-07 Grocery -54.20
#> 2026-06-19 Salary 2,400.00pdf_text() conserve la disposition visuelle de la page grâce à des suites d’espaces, ce qui est souvent utile — les colonnes restent à peu près alignées, si bien que vous pouvez découper sur les espaces plus tard. Pour obtenir le document entier sous forme d’une seule chaîne, réduisez le vecteur :
full_text <- paste(pages, collapse = "\n")C’est le cas dans 80 % des situations : pour un PDF normal, généré numériquement, pdf_text() suffit à en sortir les mots.
Nettoyer le texte brut
Le texte extrait d’un PDF arrive avec des sauts de page, des espaces irréguliers et des lignes vides. Le nettoyer relève du travail ordinaire sur les chaînes — découpez la page en lignes, puis rognez et compactez avec le R base ou stringr :
library(stringr)
lines <- pages[[1]] |>
str_split("\n") |> # one page-string -> a vector of lines
unlist() |>
str_squish() # trim ends + collapse internal runs of spaces
lines <- lines[lines != ""] # drop blank lines
head(lines)
#> [1] "Datanovia Bank — Monthly Statement"
#> [2] "Account 0042 · Period 2026-06"
#> [3] "Date Description Amount"
#> [4] "2026-06-03 Opening balance 1,200.00"
#> [5] "2026-06-07 Grocery -54.20"À partir de lignes propres, vous pouvez extraire exactement ce qu’il vous faut. Pour récupérer chaque montant, faites correspondre un motif de nombre ; pour ne garder que les lignes de transaction, filtrez sur une date en début de ligne :
# All amounts on the page (regex for a number with optional thousands + decimals)
str_extract_all(lines, "-?[0-9,]+\\.[0-9]{2}") |> unlist()
#> [1] "1,200.00" "-54.20" "2,400.00"
# Only the rows that start with an ISO date
txn <- lines[str_detect(lines, "^\\d{4}-\\d{2}-\\d{2}")]C’est le schéma de tous les jours : pdf_text() pour obtenir les mots, stringr pour les mettre en forme, le R base pour filtrer. Pour un document au format libre (un contrat, un article), cela suffit généralement — vous cherchez des passages, pas une grille.
Positions au niveau du mot : pdf_data()
Quand la disposition compte — vous essayez de reconstruire un tableau — le texte de la page entière jette la géométrie. pdf_data() la conserve : elle renvoie un data frame par page, avec une ligne pour chaque mot et sa boîte englobante. Introduite dans pdftools 2.0, chaque ligne porte ces colonnes :
| Colonne | Signification |
|---|---|
x, y |
Position du coin supérieur gauche du mot sur la page |
width, height |
Taille de la boîte englobante du mot |
space |
Le mot est-il suivi d’un espace (plutôt que d’un saut de ligne) ? |
text |
Le mot lui-même |
# A list with one data frame per page
tokens <- pdf_data("statement.pdf")
head(tokens[[1]])
#> width height x y space text
#> 1 64 11 72 60 TRUE Datanovia
#> 2 30 11 140 60 TRUE Bank
#> 3 12 11 72 96 TRUE Date
#> 4 78 11 150 96 TRUE Description
#> 5 46 11 360 96 FALSE Amount
#> 6 58 11 72 120 TRUE 2026-06-03x et y sont la clé de la structure : les mots d’une même ligne partagent un y, et les colonnes s’alignent sur x. C’est exactement ce qu’il faut pour reconstruire un tableau — voyons cela.
Lire les métadonnées : pdf_info()
Avant de traiter un fichier, vous voulez souvent connaître ses caractéristiques — nombre de pages, chiffrement éventuel, date de création. pdf_info() les renvoie sous forme de liste :
info <- pdf_info("statement.pdf")
info$pages #> [1] 3
info$encrypted #> [1] FALSE
info$created #> [1] "2026-06-30 09:14:00 UTC"Utilisez info$pages pour dimensionner une boucle, info$encrypted pour décider s’il vous faut un mot de passe (pdf_text(..., upw = "…")), et les horodatages pour la provenance. (pdf_toc() et pdf_fonts() complètent la famille lorsque vous avez besoin du sommaire ou de la liste des polices.)
Tableaux, cas 1 : reconstruire un tableau numérique propre à partir des positions
Pour un tableau propre, généré numériquement, vous n’avez pas besoin d’une bibliothèque de tableaux du tout — les positions issues de pdf_data() suffisent. La recette : regroupez les mots en lignes par y, ordonnez les mots de chaque ligne par x, et attribuez les colonnes selon les endroits où les valeurs de x se regroupent.
Comme les lignes se situent (presque) au même y, arrondissez y pour aligner les mots sur la même ligne, puis découpez :
page <- pdf_data("statement.pdf")[[1]]
# 1. Group words into rows: round y so a line's words share a key
page$row <- round(page$y / 5) * 5 # tolerance for tiny y jitter
# 2. Split into rows, then order each row left-to-right by x
rows <- split(page, page$row)
rows <- lapply(rows, function(r) r[order(r$x), ])
# 3. Turn each row into its cells. For a fixed-column table, cut on x breaks
# you read off the header positions (here: Date ~72, Description ~150, Amount ~360)
breaks <- c(-Inf, 130, 340, Inf)
to_cells <- function(r) {
col <- cut(r$x, breaks, labels = c("date", "description", "amount"))
tapply(r$text, col, paste, collapse = " ")
}
cells <- lapply(rows, to_cells)
tbl <- do.call(rbind, cells)L’idée est délibérément transparente : y trouve les lignes, x trouve les colonnes. Vous relevez une fois les positions de rupture de colonne sur l’en-tête (ou détectez les écarts dans les valeurs de x triées), et les mêmes ruptures s’appliquent à chaque ligne. C’est robuste pour les relevés, factures et rapports générés par un logiciel — la géométrie est exacte. Cela devient délicat quand les colonnes se replient ou fusionnent, et c’est là qu’un extracteur de tableaux dédié fait la différence.
Tableaux, cas 2 : tabulapdf pour les tableaux à lignes (lattice) et à flux (stream)
Quand la logique positionnelle devient pénible — tableaux à lignes (« lattice ») avec bordures, ou tableaux à flux (« stream ») denses — tournez-vous vers tabulapdf, les liaisons R rOpenSci vers le moteur d’extraction de tableaux Java Tabula. Sa fonction extract_tables() renvoie une liste de data frames, un par tableau détecté :
# install.packages("tabulapdf") # needs Java (rJava) — see the note below
library(tabulapdf)
tables <- extract_tables("report.pdf", pages = 2)
tables[[1]] # the first table on page 2, as a data frameextract_tables() possède un argument method — "lattice" pour les tableaux tracés avec des lignes de séparation, "stream" pour les tableaux alignés uniquement par les espaces — ainsi qu’un extract_areas() interactif qui vous laisse dessiner à la main la zone du tableau quand la détection peine.
tabulizer archivé
Les tutoriels plus anciens vous orientent vers tabulizer — ne commencez pas par là. Il a été archivé sur le CRAN en 2021 (ses vérifications de package ont été laissées sans correctif). Son successeur maintenu est tabulapdf, les mêmes liaisons Tabula tenues à jour pour un Java moderne. Les deux exigent toujours Java sur votre système — cette dépendance est la raison de préférer le pdftools purement C++ pour le texte, et de ne recourir à tabulapdf que lorsque vous avez spécifiquement besoin de sa détection de tableaux.
Tableaux, cas 3 : les PDF scannés ou désordonnés nécessitent l’OCR ou un LLM
Voici la partie honnête que la plupart des tutoriels passent sous silence. Si votre PDF est un scan — la photo ou le fax d’une page — il n’y a aucune couche de texte à extraire. pdf_text() renvoie des chaînes vides et pdf_data() ne renvoie rien, car le « texte » n’est que des pixels. Aucun regex n’y remédiera. Vous avez deux voies modernes.
La reconnaissance optique de caractères (OCR) — lire le texte contenu dans une image — avec tesseract (liaisons rOpenSci vers le moteur Tesseract de Google). pdftools fournit même une fonction utilitaire, pdf_ocr_text(), qui rend chaque page en image et lui applique l’OCR en un seul appel :
# install.packages("tesseract") # needs the Tesseract + Leptonica system libs
library(pdftools)
# Renders each page to an image, then runs Tesseract OCR
scanned_text <- pdf_ocr_text("scanned-report.pdf", language = "eng")
# tesseract can also return word-level boxes (like pdf_data, but for a scan).
# ocr_data() needs an image *file*, so render the page to a PNG first.
library(tesseract)
png <- pdf_convert("scanned-report.pdf", pages = 1, dpi = 300)
boxes <- ocr_data(png[1])L’OCR est imparfait — attendez-vous à des caractères parasites, et vérifiez toujours les nombres par sondage avant de leur faire confiance — mais pour un scan lisible, il récupère l’essentiel du texte, et ocr_data() vous donne des positions pour reconstruire les tableaux de la même manière avec x/y que ci-dessus.
Une passe d’extraction par LLM (grand modèle de langage) / vision est l’autre option de 2026, et c’est souvent le chemin le plus rapide pour un document véritablement désordonné — un formulaire multi-colonnes, un tableau à cellules fusionnées et notes de bas de page, un scan de mauvaise qualité. Vous envoyez la page (sous forme de texte ou d’image rendue) à un modèle capable de vision et demandez une sortie structurée — disons, du JSON avec des champs nommés — puis vous lisez celle-ci dans un data frame. C’est exactement la tâche du moment : « alimenter un pipeline d’analyse avec un document non structuré ».
Le piège, et c’est tout l’enjeu : un modèle inventera volontiers un nombre plausible. Traitez donc toute extraction par LLM comme un brouillon et bouclez la boucle — vérifiez les chiffres récupérés par rapport à la source. Deux garde-fous économiques :
- Rapprochez d’un total. Si le document comporte une somme, un comptage ou un sous-total, calculez-le à partir des lignes extraites et comparez. Un relevé dont les transactions ne correspondent pas au solde annoncé est un signal d’alerte : l’extraction a laissé tomber ou mal lu une ligne.
- Vérifiez avec
pdf_text()là où une couche de texte existe. Pour un PDF hybride (du vrai texte plus une zone d’image), récupérez la couche de texte avecpdftoolset confirmez que les nombres du modèle y apparaissent réellement.
Cette étape de vérification — juger la réponse générée par rapport à la source plutôt que de lui faire confiance — fait la différence entre un pipeline que vous pouvez mettre dans un rapport et un autre qui corrompt silencieusement vos données. Recourez à un LLM pour analyser le désordre, mais laissez les nombres être vérifiés par quelque chose de déterministe.
Quelle approche pour quel PDF
Un guide de décision rapide pour choisir le bon outil du premier coup :
| Votre PDF | Utilisez | Pourquoi |
|---|---|---|
| Texte numérique, au format libre (contrat, article) | pdf_text() + stringr |
Les mots sont là ; vous voulez des passages, pas une grille |
| Tableau numérique, généré par logiciel | positions de pdf_data() (ou tabulapdf) |
Géométrie exacte — y donne les lignes, x donne les colonnes |
| Tableau à lignes / dense, disposition délicate | tabulapdf (extract_tables, extract_areas) |
Détection de tableaux dédiée (lattice/stream) |
| Image scannée, sans couche de texte | OCR tesseract (pdf_ocr_text) |
Il n’y a pas de texte à extraire — vous devez lire les pixels |
| Désordonné, fusionné ou de qualité mixte | passe LLM / vision, puis vérifiez | Le plus rapide sur le chaos — mais rapprochez chaque nombre de la source |
L’ordre de préférence est aussi l’ordre de confiance : une vraie couche de texte est exacte, l’OCR est approximatif, et un LLM n’est qu’un brouillon tant que vous ne l’avez pas vérifié. Commencez par pdftools ; n’escaladez que lorsque le document vous y oblige.
Problèmes courants
pdf_text() renvoie des chaînes vides ("") pour chaque page. Le PDF est presque certainement un scan — une image sans couche de texte. Rien dans pdftools ne peut extraire un texte qui n’existe pas. Confirmez avec pdf_data() (vide aussi), puis passez à l’OCR (pdf_ocr_text() / tesseract).
Les colonnes sont embrouillées quand je découpe pdf_text() sur les espaces. Le texte de la page entière perd la géométrie exacte, si bien que l’alignement multi-espaces n’est qu’approximatif — les cellules repliées et les polices proportionnelles cassent un découpage naïf. Utilisez plutôt pdf_data() et reconstruisez les colonnes à partir des positions de x, ou confiez le tableau à tabulapdf.
tabulapdf refuse de s’installer ou échoue au chargement. Il a besoin de Java (via rJava), un accroc d’installation fréquent, surtout sous Windows. Vérifiez qu’un JDK est présent sur votre système et que rJava se charge (library(rJava)) avant tabulapdf. Si vous n’avez besoin que de texte (pas de détection de tableaux), vous pouvez éviter Java entièrement en restant avec pdftools.
La sortie de l’OCR est pleine de caractères parasites. La qualité de l’OCR dépend de la résolution de l’image — effectuez le rendu à un DPI plus élevé (pdf_ocr_text(..., dpi = 600)), assurez-vous d’utiliser les bonnes données de language, et vérifiez toujours les nombres extraits par rapport à un total connu.
Questions fréquentes
Cela dépend du tableau. Pour un tableau propre, généré par logiciel, utilisez pdftools::pdf_data() pour obtenir la position x/y de chaque mot, puis regroupez les mots en lignes par y et en colonnes par x — la géométrie est exacte. Pour les tableaux à lignes ou denses, utilisez tabulapdf::extract_tables() (le moteur de détection de tableaux de Tabula), ou extract_areas() pour sélectionner la zone de façon interactive. Pour un tableau scanné, il n’y a pas de texte à extraire — vous devez d’abord lui appliquer l’OCR avec tesseract. Aucun appel unique ne gère tous les PDF ; adaptez l’outil au document.
Utilisez pdftools comme base : c’est le package rOpenSci maintenu pour lire le texte, les positions et les métadonnées d’un PDF, il est purement C++ (poppler) sans Java, et il est activement développé. Ne commencez pas avec tabulizer — il a été archivé sur le CRAN en 2021 et vise une version de Java obsolète. Si vous avez spécifiquement besoin d’une détection de tableaux automatique au-delà de ce que donnent les positions de pdf_data(), utilisez tabulapdf, le successeur maintenu de tabulizer (même moteur Tabula, Java moderne).
Un PDF scanné est une image, donc pdf_text() ne renvoie rien — il vous faut l’OCR (reconnaissance optique de caractères). Le chemin le plus simple est pdftools::pdf_ocr_text("scan.pdf", language = "eng"), qui rend chaque page en image et lui applique le moteur Tesseract (installez d’abord le package tesseract et ses bibliothèques système). Effectuez le rendu à un dpi plus élevé pour des résultats plus propres, et vérifiez toujours les nombres extraits — l’OCR est approximatif, pas exact.
Appelez pdftools::pdf_text("file.pdf"). Cela renvoie un vecteur de caractères avec une chaîne par page, chacune contenant le texte brut de cette page. Réduisez-le avec paste(pages, collapse = "\n") pour le document entier, puis nettoyez-le avec stringr::str_squish() (rognage et réduction des espaces) et str_split("\n") pour travailler ligne par ligne.
Oui, et pour les documents désordonnés ou scannés, c’est souvent le chemin le plus rapide — envoyez le texte de la page ou une image rendue à un modèle capable de vision et demandez une sortie structurée (par ex. du JSON), puis lisez-la dans un data frame. La réserve essentielle : un modèle peut fabriquer un nombre convaincant, donc ne faites jamais confiance à la sortie à l’aveugle. Rapprochez les valeurs extraites d’un total ou d’un sous-total du document, et recoupez avec la couche de texte de pdftools là où elle existe. Utilisez le LLM pour analyser le chaos ; laissez quelque chose de déterministe vérifier les nombres.
Testez votre compréhension
Vous recevez trois PDF et vous avez besoin des nombres de chacun en R :
- Un relevé bancaire exporté depuis une banque en ligne — texte net et sélectionnable, colonnes soignées.
- Un article de revue de 1998 scanné à partir du papier, avec un tableau de résultats à mi-hauteur de la page 4.
- Un rapport PDF moderne dont le tableau comporte des cellules d’en-tête fusionnées, des notes de bas de page et du texte pivoté.
Quelle approche d’extraction convient à chacun — et de laquelle devez-vous vérifier la sortie avec le plus de soin ?
pdftools— les positions depdf_data()(lignes pary, colonnes parx) reconstruisent exactement le tableau du relevé ; c’est un PDF propre, généré par logiciel, donc ni OCR ni Java nécessaires.- OCR — c’est un scan, donc il n’y a pas de couche de texte ;
pdftools::pdf_ocr_text()(Tesseract) lit les pixels. Effectuez le rendu à un DPI élevé et vérifiez les résultats par sondage — l’OCR est approximatif. - Une passe LLM / vision est le choix pragmatique pour le désordre fusionné/pivoté — mais sa sortie demande le plus de vigilance : rapprochez chaque nombre extrait d’un total ou de la source avant de lui faire confiance. (Vous pouvez aussi essayer
tabulapdf::extract_areas()pour dessiner la zone à la main.)
L’ordre de confiance reflète l’ordre des outils : positions exactes → OCR approximatif → un brouillon de LLM que vous devez vérifier.
Conclusion
Sortir des données d’un PDF en R, c’est une affaire d’adéquation entre l’outil et le document. Pour tout ce qui possède une vraie couche de texte, pdftools fait le travail sans Java ni service : pdf_text() pour les mots, pdf_data() pour les positions x/y au niveau du mot, pdf_info() pour les métadonnées — puis stringr et le R base pour nettoyer et filtrer. Reconstruisez les tableaux propres directement à partir des positions ; faites appel à tabulapdf (le successeur maintenu du tabulizer archivé) pour les tableaux à lignes (lattice) et à flux (stream) délicats. Et soyez honnête au sujet des scans et des documents désordonnés : ils nécessitent l’OCR (tesseract) ou une passe LLM/vision — avec des nombres toujours vérifiés par rapport à la source, car une réponse générée est un brouillon, pas un fait. Commencez par pdftools, n’escaladez que lorsque le PDF vous y oblige, et vérifiez ce à quoi vous ne pouvez pas vous fier.
Pour aller plus loin
- Web scraping en R avec rvest — extraire des données de pages web, le cousin HTML de cette tâche. · Lire et écrire le presse-papier en R avec clipr — la façon la plus rapide de faire entrer et sortir de petits tableaux de R. · Analyse exploratoire des données en R — que faire des données une fois sorties du PDF. · Importer et exporter des données et Manipulation de données — la série
/learncomplète sur l’importation et la remise en forme des données.
Demandez à Prova « J’ai un relevé bancaire au format PDF — extrais le tableau des transactions dans un data frame en R avec pdftools, en utilisant les positions des mots issues de pdf_data() » — elle répond avec du code que vous pouvez exécuter sur votre propre fichier, et rapproche les montants du solde. The runtime is the judge. Demander à Prova →
Citation
@online{kassambara2026,
author = {Kassambara, Alboukadel},
title = {Extraire du texte et des tableaux d’un PDF en R},
date = {2026-07-19},
url = {https://www.datanovia.com/blog/extract-text-and-tables-from-pdf-in-r},
langid = {fr}
}