Lire un fichier pdf avec Python

Partager cet article

Dans la série des types de documents nous avons vu entres autre les fichiers structurés au format YAML. Mais qu’en est-il de l’un des formats de fichier texte les plus courant : le pdf ? comment récupérer les données de ce format semi-structuré et ce bien sur en toute simplicité ? Bien sur si vous tapez dans Google Python et pdf vous trouverez plusieurs librairies candidates pour manipuler ces fichier. Nous allons voir ensemble et dans ce court article comment utiliser l’une d’elle pyPDF2.

Qu’est-ce que pyPDF2 ?

PyPDF2 est une librairie Python trés utilisée permettant la lecture et quelques manipulations de fichiers au format pdf. Cette librairie est elle-même issue du projet pyPdf. Cette librairie est actuellement maintenue par Phaseit, Inc. et permet l’extraction des données provenant de fichiers PDF ou alors tout simplement manipuler des PDF existants dans l’idée de produire un nouveau fichier pdf (concaténation, filtrage de page, etc.). PyPDF2 est compatible avec les versions 2.6, 2.7 et 3.2 – 3.5 de Python.

NB: Utilisant la version 3.7 je n’ai pas rencontré de soucis particuliers …

L’installation est simple via l’utilitaire pip :

pip install pypdf2

Ouvrir un fichier pdf

L’utilisation de la librairie est plutôt simple et repose sur deux objets principaux : l’un en lecture et l’autre en écriture de fichier pdf.

from PyPDF2 import PdfFileReader
from PyPDF2 import PdfFileWriter

Ensuite pour lire un fichier pdf, rien de plus simple, il suffit de l’ouvrir comme n’importe quel fichier en Python et d’utiliser l’objet PdfFileReader.

Nous allons dans le cadre de notre exemple lire un fichier très simple :

Contenu du fichier pdf
document = PdfFileReader(open(myFile, 'rb'))

Le fichier est maintenant ouvert, regardons les méta-données :

metadata = document.getDocumentInfo()
print (metadata)
{'/Author': 'Benoit Cayla',
 '/Creator': 'Microsoft® Word pour Office\xa0365',
 '/CreationDate': "D:20201110115707+01'00'",
 '/ModDate': "D:20201110115707+01'00'",
 '/Producer': 'Microsoft® Word pour Office\xa0365'}

On peut les récupérer aussi directement comme ceci :

author = metadata.author if metadata.author else u'Unknown'
title = metadata.title if metadata.title else myFile
subject = metadata.subject if metadata.subject else "No Subject"
print (author + "|" + title + "|" + subject)
Benoit Cayla|test.pdf|No Subject

D’autres méthodes permettent de récupérer des informations importantes comme le nombre de page :

print(document.getNumPages())
1

Récupération des champs interactifs :

fields = document.getFields()

Mais aussi :

  • Le type d’affichage avec getPageLayout()
  • Le mode d’affichage avec getPageMode()

N’hésitez pas à consulter la documentation ici.

Lire le contenu du fichier pdf

On lit le contenu bien sur après avoir ouvert le fichier pdf avec PdfFileReader. Il faut bien sur tenir aussi compte de la pagination, la lecture se faisant page par page :

pdftext = ""
for page in range(document.numPages):
    pageObj = document.getPage(page)
    pdftext += pageObj.extractText().replace('\n','')
Bonjour ceci est un test !  Benoit Cayla 

Vous remarquerez dans la dernière ligne que je retire les retours chariots (\n) car il sont récupérés tels quels par l’interpréteur.

Vous pouvez aussi regarder le détail (hiérarchique) des données qui ont été récupérées en affichant l’objet. Ce n’est pas d’une grande utilité tel quel mais celà vous montre que tout le contenu et méta-contenu à bien été récupéré.

print(pageObj)
{'/Type': '/Page',
 '/Parent': {'/Type': '/Pages',
  '/Count': 3,
  '/Kids': [IndirectObject(3, 0), IndirectObject(4, 0), IndirectObject(5, 0)]},
 '/Resources': {'/Font': {'/F1': {'/Type': '/Font',
    '/Subtype': '/TrueType',
    '/Name': '/F1',
    '/BaseFont': '/BCDEEE+Calibri',
    '/Encoding': '/WinAnsiEncoding',
    '/FontDescriptor': {'/Type': '/FontDescriptor',
     '/FontName': '/BCDEEE+Calibri',
     '/Flags': 32,
     '/ItalicAngle': 0,
     '/Ascent': 750,
     '/Descent': -250,
     '/CapHeight': 750,
     '/AvgWidth': 521,
     '/MaxWidth': 1743,
     '/FontWeight': 400,
     '/XHeight': 250,
     '/StemV': 52,
     '/FontBBox': [-503, -250, 1240, 750],
     '/FontFile2': {'/Filter': '/FlateDecode', '/Length1': 93840}},
    '/FirstChar': 32,
    '/LastChar': 117,
    '/Widths': [226,
     0,
     ...
     0,
     525]}},
  '/ExtGState': {'/GS7': {'/Type': '/ExtGState', '/BM': '/Normal', '/ca': 1},
   '/GS8': {'/Type': '/ExtGState', '/BM': '/Normal', '/CA': 1}},
  '/ProcSet': ['/PDF', '/Text', '/ImageB', '/ImageC', '/ImageI']},
 '/MediaBox': [0, 0, 595.2, 841.92],
 '/Contents': {'/Filter': '/FlateDecode'},
 '/Group': {'/Type': '/Group', '/S': '/Transparency', '/CS': '/DeviceRGB'},
 '/Tabs': '/S',
 '/StructParents': 0}

Créons maintenant un nouveau fichier pdf

Dans cet exemple nous allons concaténer 3 fichiers pdf en un seul. Voici les 3 fichiers pdf que l’on va dans un premier temps ouvrir :

pdflist = ["test.pdf" , "test2.pdf", "test3.pdf"]

Une fois ouvert, nous créons un nouveau fichier pdf avec PdfFileWriter:

pdfWriter = PdfFileWriter()
for filename in pdflist:
    pdfFileObj = open(filename,'rb')
    pdfReader = PdfFileReader(pdfFileObj)
    for pageNum in range(pdfReader.numPages):
        pageObj = pdfReader.getPage(pageNum)
        pdfWriter.addPage(pageObj)

pdfOutput = open('final.pdf', 'wb')
pdfWriter.write(pdfOutput)
pdfOutput.close() 

Nous rajoutons simplement les pages des pdf existants dans le nouveau pdf.

Conclusion

Nous avons vu dans cet article comment et en toute simplicité on pouvait lire des données textuelles à partir d’un fichier pdf. Attention car nous sommes parti du principe que ces données étaient du « vrai » texte et non pas des images (scannées) de texte placées dans un pdf. Dans ce cas de figure, il faudrait alors extraire l’image du pdf puis ensuite utiliser un OCR tel que Tesseract. Pour celà je vous suggère de lire mon article ici.

Comme d’habitude vous trouverez les sources de cet article sur GitHub.

Partager cet article

2 Replies to “Lire un fichier pdf avec Python”

  1. Bonjour à tous,
    Je suis actuellement sur un projet dans le domaine du Machine Learning, le but est de faire une classification supervisée sur un ensemble de données. Mes données sont un grand nombre de fichiers pdf , chaque fichier à une classe precise, le but est d’utiliser ces fichiers la comme jeu de données d’apprentissage afin de faire de la prediction de la classe sur de nouveaux fichiers.
    Mon probléme c’est que je ne sais pas comment construire mon jeu de données d’entrainement vu que l’algrithme de classification doit s’entrainer sur le contenu de chaque fichier et dans mon data Frame d’entrainement j’ai la classe de chaque fichier et le nom du fichier en question. comment faire pour inclure le contenu de chaque fichier pdf dans mon Data Frame d’entrainement ?
    Merci par avance pour vorte aide

  2. Bonjour,
    Si j’ai bien compris votre question, il n’y a pas vraiment de soucis en réalité. Dans votre phase (partie de code) d’entraînement vous allez lire votre dataframe (pour recuperer le nom de fichier) et immédiatement lire le contenu du fichier, pour en exploiter directement le contenu … pas réellement besoin en fait de constituer un nouveau dataframe avec les features + labels puisque vous allez le construire à la volée.

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée.

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur comment les données de vos commentaires sont utilisées.