404 votes

Module Python pour la conversion de PDF en texte

Existe-t-il un module python pour convertir les fichiers PDF en texte ? J'ai essayé un morceau de code trouvé dans Activestate qui utilise pypdf mais le texte généré n'avait pas d'espace entre les deux et n'était d'aucune utilité.

1 votes

Je cherchais une solution similaire. J'ai juste besoin de lire le texte du fichier pdf. Je n'ai pas besoin des images. pdfminer est un bon choix mais je n'ai pas trouvé d'exemple simple sur la façon d'extraire le texte. Finalement, j'ai trouvé cette réponse SO ( stackoverflow.com/questions/5725278/ ) et l'utilise maintenant.

4 votes

Comme la question a été fermée, je l'ai repostée sur le Stack Exchange dédié aux recommandations de logiciels au cas où quelqu'un voudrait écrire une nouvelle réponse : Module Python pour la conversion de PDF en texte

1 votes

La seule solution qui a fonctionné pour moi pour le contenu UTF-8 : Apache Tika

154voto

David Crow Points 7704

Pruebe PDFMiner . Il peut extraire le texte des fichiers PDF au format HTML, SGML ou "Tagged PDF".

Le format PDF balisé semble être le plus propre, et l'élimination des balises XML ne laisse que le texte brut.

Une version de Python 3 est disponible sous :

2 votes

Je viens d'ajouter une réponse décrivant comment utiliser pdfminer comme une bibliothèque.

26 votes

Pas de support pour python 3 :(

1 votes

La réponse que j'ai fournie dans ce fil pourrait être utile aux personnes qui regardent cette réponse et se demandent comment utiliser la bibliothèque. Je donne un exemple de l'utilisation de la bibliothèque PDFMiner pour extraire du texte d'un PDF. Comme la documentation est un peu éparse, j'ai pensé que cela pourrait aider quelques personnes.

138voto

tgray Points 4002

En PDFMiner a changé depuis codeape affiché.

EDIT (encore) :

PDFMiner a été à nouveau mis à jour en version 20100213

Vous pouvez vérifier la version que vous avez installée à l'aide de ce qui suit :

>>> import pdfminer
>>> pdfminer.__version__
'20100213'

Voici la version mise à jour (avec des commentaires sur ce que j'ai changé/ajouté) :

def pdf_to_csv(filename):
    from cStringIO import StringIO  #<-- added so you can copy/paste this to try it
    from pdfminer.converter import LTTextItem, TextConverter
    from pdfminer.pdfparser import PDFDocument, PDFParser
    from pdfminer.pdfinterp import PDFResourceManager, PDFPageInterpreter

    class CsvConverter(TextConverter):
        def __init__(self, *args, **kwargs):
            TextConverter.__init__(self, *args, **kwargs)

        def end_page(self, i):
            from collections import defaultdict
            lines = defaultdict(lambda : {})
            for child in self.cur_item.objs:
                if isinstance(child, LTTextItem):
                    (_,_,x,y) = child.bbox                   #<-- changed
                    line = lines[int(-y)]
                    line[x] = child.text.encode(self.codec)  #<-- changed

            for y in sorted(lines.keys()):
                line = lines[y]
                self.outfp.write(";".join(line[x] for x in sorted(line.keys())))
                self.outfp.write("\n")

    # ... the following part of the code is a remix of the 
    # convert() function in the pdfminer/tools/pdf2text module
    rsrc = PDFResourceManager()
    outfp = StringIO()
    device = CsvConverter(rsrc, outfp, codec="utf-8")  #<-- changed 
        # becuase my test documents are utf-8 (note: utf-8 is the default codec)

    doc = PDFDocument()
    fp = open(filename, 'rb')
    parser = PDFParser(fp)       #<-- changed
    parser.set_document(doc)     #<-- added
    doc.set_parser(parser)       #<-- added
    doc.initialize('')

    interpreter = PDFPageInterpreter(rsrc, device)

    for i, page in enumerate(doc.get_pages()):
        outfp.write("START PAGE %d\n" % i)
        interpreter.process_page(page)
        outfp.write("END PAGE %d\n" % i)

    device.close()
    fp.close()

    return outfp.getvalue()

Modifier (encore une fois) :

Voici une mise à jour pour la dernière version en pypi , 20100619p1 . En bref, j'ai remplacé LTTextItem con LTChar et passé une instance de LAParams au constructeur de CsvConverter.

def pdf_to_csv(filename):
    from cStringIO import StringIO  
    from pdfminer.converter import LTChar, TextConverter    #<-- changed
    from pdfminer.layout import LAParams
    from pdfminer.pdfparser import PDFDocument, PDFParser
    from pdfminer.pdfinterp import PDFResourceManager, PDFPageInterpreter

    class CsvConverter(TextConverter):
        def __init__(self, *args, **kwargs):
            TextConverter.__init__(self, *args, **kwargs)

        def end_page(self, i):
            from collections import defaultdict
            lines = defaultdict(lambda : {})
            for child in self.cur_item.objs:
                if isinstance(child, LTChar):               #<-- changed
                    (_,_,x,y) = child.bbox                   
                    line = lines[int(-y)]
                    line[x] = child.text.encode(self.codec)

            for y in sorted(lines.keys()):
                line = lines[y]
                self.outfp.write(";".join(line[x] for x in sorted(line.keys())))
                self.outfp.write("\n")

    # ... the following part of the code is a remix of the 
    # convert() function in the pdfminer/tools/pdf2text module
    rsrc = PDFResourceManager()
    outfp = StringIO()
    device = CsvConverter(rsrc, outfp, codec="utf-8", laparams=LAParams())  #<-- changed
        # becuase my test documents are utf-8 (note: utf-8 is the default codec)

    doc = PDFDocument()
    fp = open(filename, 'rb')
    parser = PDFParser(fp)       
    parser.set_document(doc)     
    doc.set_parser(parser)       
    doc.initialize('')

    interpreter = PDFPageInterpreter(rsrc, device)

    for i, page in enumerate(doc.get_pages()):
        outfp.write("START PAGE %d\n" % i)
        if page is not None:
            interpreter.process_page(page)
        outfp.write("END PAGE %d\n" % i)

    device.close()
    fp.close()

    return outfp.getvalue()

EDIT (une fois de plus) :

Mis à jour pour la version 20110515 (merci à Oeufcoque Penteano !):

def pdf_to_csv(filename):
    from cStringIO import StringIO  
    from pdfminer.converter import LTChar, TextConverter
    from pdfminer.layout import LAParams
    from pdfminer.pdfparser import PDFDocument, PDFParser
    from pdfminer.pdfinterp import PDFResourceManager, PDFPageInterpreter

    class CsvConverter(TextConverter):
        def __init__(self, *args, **kwargs):
            TextConverter.__init__(self, *args, **kwargs)

        def end_page(self, i):
            from collections import defaultdict
            lines = defaultdict(lambda : {})
            for child in self.cur_item._objs:                #<-- changed
                if isinstance(child, LTChar):
                    (_,_,x,y) = child.bbox                   
                    line = lines[int(-y)]
                    line[x] = child._text.encode(self.codec) #<-- changed

            for y in sorted(lines.keys()):
                line = lines[y]
                self.outfp.write(";".join(line[x] for x in sorted(line.keys())))
                self.outfp.write("\n")

    # ... the following part of the code is a remix of the 
    # convert() function in the pdfminer/tools/pdf2text module
    rsrc = PDFResourceManager()
    outfp = StringIO()
    device = CsvConverter(rsrc, outfp, codec="utf-8", laparams=LAParams())
        # becuase my test documents are utf-8 (note: utf-8 is the default codec)

    doc = PDFDocument()
    fp = open(filename, 'rb')
    parser = PDFParser(fp)       
    parser.set_document(doc)     
    doc.set_parser(parser)       
    doc.initialize('')

    interpreter = PDFPageInterpreter(rsrc, device)

    for i, page in enumerate(doc.get_pages()):
        outfp.write("START PAGE %d\n" % i)
        if page is not None:
            interpreter.process_page(page)
        outfp.write("END PAGE %d\n" % i)

    device.close()
    fp.close()

    return outfp.getvalue()

1 votes

In [6] : import pdfminer In [7] : pdfminer.__version__ Out[7] : '20100424' In [8] : from pdfminer.converter import LTTextItem ImportError : cannot import name LTTextItem .... LITERALS_DCT_DECODE LTChar LTImage LTPolygon LTTextBox LITERAL_DEVICE_GRAY LTContainer LTLine LTRect LTTextGroup LITERAL_DEVICE_RGB LTFigure LTPage LTText LTTextLine

0 votes

@skyl, le code ci-dessus est pour la version précédente '20100213'. D'après la liste des changements sur leur site web, il semble qu'ils aient changé LTTextItem a LTChar . unixuser.org/~euske/python/pdfminer/index.html#changes

0 votes

Si quelqu'un tombe sur ce post et a besoin d'un moyen décent de lire ligne par ligne les pdfs comme je le voulais et n'ai pas réussi à le faire sur la version la plus récente, des ajustements très mineurs dans ce code le rendront fonctionnel en 20110515 : Ajout d'un dir pour voir ce qui se passe a fait tout le travail. A la ligne 19, changez for child in self.cur_item.objs: a for child in self.cur_item._objs: (ajoutez simplement un trait de soulignement à objs) ; et à la ligne 23 faites de même pour text c'est-à-dire : line[x] = child.text.encode(self.codec) devrait être remplacé par line[x] = child._text.encode(self.codec)

52voto

Jamie Points 1985

Pdftotext Un programme open source (faisant partie de Xpdf) que vous pourriez appeler depuis python (ce n'est pas ce que vous avez demandé mais cela pourrait être utile). Je l'ai utilisé sans problème. Je pense que google l'utilise dans google desktop.

8 votes

Cet outil semble être le plus utile des outils énumérés ici, avec le -layout pour conserver le texte dans la même position que dans le PDF. Maintenant, si seulement je pouvais trouver le moyen d'y intégrer le contenu d'un PDF.

0 votes

Après avoir testé plusieurs solutions, celle-ci semble être l'option la plus simple et la plus robuste. Elle peut facilement être intégrée à Python en utilisant un fichier temporaire pour déterminer où la sortie est écrite.

1 votes

Cerin utiliser '-' comme nom de fichier pour rediriger la sortie vers stdout. De cette façon, vous pouvez utiliser le simple subprocess.check_output et cet appel sera considéré comme une fonction interne.

45voto

Tony Meyer Points 4700

pyPDF fonctionne bien (en supposant que vous travaillez avec des PDF bien formés). Si tout ce que vous voulez est le texte (avec les espaces), vous pouvez simplement faire :

import pyPdf
pdf = pyPdf.PdfFileReader(open(filename, "rb"))
for page in pdf.pages:
    print page.extractText()

Vous pouvez également accéder facilement aux métadonnées, aux données des images, etc.

Un commentaire dans les notes de code d'extractText :

L l'ordre dans lequel elles sont fournies dans le flux de contenu, et extraire le texte. Cela fonctionne bien pour certains fichiers PDF, mais mal pour d'autres, en fonction du le générateur utilisé. Cette méthode sera affiné à l'avenir. Ne pas se fier à l'ordre du texte qui sort de cette fonction car il changera si cette fonction fonction est rendue plus sophistiquée.

Le fait que ce soit un problème ou non dépend de ce que vous faites avec le texte (par exemple, si l'ordre n'a pas d'importance, c'est bon, ou si le générateur ajoute le texte au flux dans l'ordre où il sera affiché, c'est bon). J'ai un code d'extraction pyPdf en usage quotidien, sans aucun problème.

8 votes

Pas de support unicode :(

8 votes

PyPdf supporte UTF maintenant.

2 votes

Cette bibliothèque ressemble à une poubelle. Le test sur un PDF aléatoire me donne l'erreur "pyPdf.utils.PdfReadError : EOF marker not found"

21voto

codeape Points 38576

Vous pouvez aussi très facilement utiliser pdfminer comme bibliothèque. Vous avez accès au modèle de contenu du pdf, et pouvez créer votre propre extraction de texte. Je l'ai fait pour convertir le contenu d'un pdf en texte séparé par des points-virgules, en utilisant le code ci-dessous.

La fonction trie simplement les objets de contenu TextItem en fonction de leurs coordonnées y et x, et produit les éléments ayant la même coordonnée y sur une ligne de texte, en séparant les objets sur la même ligne par des caractères ';'.

En utilisant cette approche, j'ai pu extraire du texte d'un pdf dont aucun autre outil n'était capable d'extraire un contenu adapté à une analyse ultérieure. Les autres outils que j'ai essayés sont pdftotext, ps2ascii et l'outil en ligne pdftextonline.com.

pdfminer est un outil précieux pour le scraping de pdf.

def pdf_to_csv(filename):
    from pdflib.page import TextItem, TextConverter
    from pdflib.pdfparser import PDFDocument, PDFParser
    from pdflib.pdfinterp import PDFResourceManager, PDFPageInterpreter

    class CsvConverter(TextConverter):
        def __init__(self, *args, **kwargs):
            TextConverter.__init__(self, *args, **kwargs)

        def end_page(self, i):
            from collections import defaultdict
            lines = defaultdict(lambda : {})
            for child in self.cur_item.objs:
                if isinstance(child, TextItem):
                    (_,_,x,y) = child.bbox
                    line = lines[int(-y)]
                    line[x] = child.text

            for y in sorted(lines.keys()):
                line = lines[y]
                self.outfp.write(";".join(line[x] for x in sorted(line.keys())))
                self.outfp.write("\n")

    # ... the following part of the code is a remix of the 
    # convert() function in the pdfminer/tools/pdf2text module
    rsrc = PDFResourceManager()
    outfp = StringIO()
    device = CsvConverter(rsrc, outfp, "ascii")

    doc = PDFDocument()
    fp = open(filename, 'rb')
    parser = PDFParser(doc, fp)
    doc.initialize('')

    interpreter = PDFPageInterpreter(rsrc, device)

    for i, page in enumerate(doc.get_pages()):
        outfp.write("START PAGE %d\n" % i)
        interpreter.process_page(page)
        outfp.write("END PAGE %d\n" % i)

    device.close()
    fp.close()

    return outfp.getvalue()

UPDATE :

Le code ci-dessus est écrit avec une ancienne version de l'API, voir mon commentaire ci-dessous.

0 votes

De quels types de plugins avez-vous besoin pour que ça fonctionne ? J'ai téléchargé et installé pdfminer mais ça ne suffit pas...

1 votes

Le code ci-dessus est écrit avec une ancienne version de PDFminer. L'API a changé dans les versions plus récentes (par exemple, le paquetage est maintenant pdfminer pas pdflib ). Je vous suggère de jeter un coup d'œil à la source de pdf2txt.py dans le source de PDFminer, le code ci-dessus a été inspiré par l'ancienne version de ce fichier.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X