Module Python pour la conversion de PDF en texte [fermé]

Quels sont les meilleurs modules Python pour convertir des fichiers PDF en texte?

346
demandé sur adamk 2008-08-25 08:44:06

13 réponses

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

http://www.unixuser.org/~euske/python/pdfminer/index.html

le format PDF étiqueté semble être le plus propre, et enlever les étiquettes XML ne laisse que le texte nu.

une version de Python 3 est disponible sous:

122
répondu David Crow 2017-09-30 15:34:46

le paquet PDFMiner a changé depuis codeape affiché.

MODIFIER (encore une fois):

PDFMiner a été à nouveau mis à jour dans la version 20100213

vous pouvez vérifier la version que vous avez installée avec 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()

Edit (encore une fois):

Voici une mise à jour pour la dernière version de pypi , 20100619p1 . En bref, j'ai remplacé LTTextItem par LTChar et j'ai passé une instance de LAParams au constructeur 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()

MODIFIER (une fois de plus):

mise à jour pour la version 20110515 (merci à oeuf 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()
133
répondu tgray 2017-05-23 10:31:37

comme aucune de ces solutions ne supporte la dernière version de PDFMiner, j'ai écrit une solution simple qui va retourner le texte d'un pdf en utilisant PDFMiner. Cela fonctionnera pour ceux qui obtiennent des erreurs d'importation avec process_pdf

import sys
from pdfminer.pdfinterp import PDFResourceManager, PDFPageInterpreter
from pdfminer.pdfpage import PDFPage
from pdfminer.converter import XMLConverter, HTMLConverter, TextConverter
from pdfminer.layout import LAParams
from cStringIO import StringIO

def pdfparser(data):

    fp = file(data, 'rb')
    rsrcmgr = PDFResourceManager()
    retstr = StringIO()
    codec = 'utf-8'
    laparams = LAParams()
    device = TextConverter(rsrcmgr, retstr, codec=codec, laparams=laparams)
    # Create a PDF interpreter object.
    interpreter = PDFPageInterpreter(rsrcmgr, device)
    # Process each page contained in the document.

    for page in PDFPage.get_pages(fp):
        interpreter.process_page(page)
        data =  retstr.getvalue()

    print data

if __name__ == '__main__':
    pdfparser(sys.argv[1])  

voir ci-dessous le code qui fonctionne pour Python 3:

import sys
from pdfminer.pdfinterp import PDFResourceManager, PDFPageInterpreter
from pdfminer.pdfpage import PDFPage
from pdfminer.converter import XMLConverter, HTMLConverter, TextConverter
from pdfminer.layout import LAParams
import io

def pdfparser(data):

    fp = open(data, 'rb')
    rsrcmgr = PDFResourceManager()
    retstr = io.StringIO()
    codec = 'utf-8'
    laparams = LAParams()
    device = TextConverter(rsrcmgr, retstr, codec=codec, laparams=laparams)
    # Create a PDF interpreter object.
    interpreter = PDFPageInterpreter(rsrcmgr, device)
    # Process each page contained in the document.

    for page in PDFPage.get_pages(fp):
        interpreter.process_page(page)
        data =  retstr.getvalue()

    print(data)

if __name__ == '__main__':
    pdfparser(sys.argv[1])  
51
répondu user3272884 2017-10-03 08:42:29

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

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

vous pouvez aussi facilement accéder aux métadonnées, aux données d'image, et ainsi de suite.

Un commentaire dans le extractText notes de code:

Localisez toutes les commandes de dessin de texte, dans l'ordre qu'ils sont fournis dans le flux de contenu, et extraire le texte. Cela fonctionne bien pour certains fichiers PDF, mais mal pour d'autres, selon le générateur utilisé. Ce sera raffiné dans l'avenir. Ne comptez pas sur l'ordre du texte en sortant de cette fonction, car il va changer, si ce la fonction est plus sophistiquées.

si oui ou non c'est un problème 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 du texte dans le flux la commande affiche, c'est très bien). J'ai le code d'extraction pyPdf en usage quotidien, sans aucun problème.

42
répondu Tony Meyer 2008-09-07 04:47:09

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

40
répondu Jamie 2008-08-28 09:46:53

vous pouvez également très facilement utiliser pdfminer comme une bibliothèque. Vous avez accès au modèle de contenu du pdf, et pouvez créer votre propre extraction de texte. J'ai fait ceci pour convertir le contenu pdf en texte semi-colon séparé, en utilisant le code ci-dessous.

la fonction trie simplement les objets TextItem content en fonction de leurs coordonnées y et x, et affiche les éléments avec la même coordonnée y qu'une ligne de texte, en séparant les objets sur la même ligne avec les caractères';'.

en utilisant cette approche, j'ai pu extraire du texte d'un pdf qu'aucun autre outil n'était capable d'extraire du contenu approprié pour une analyse ultérieure. Parmi les autres outils que j'ai essayés, on retrouve pdftotext, ps2ascii et l'outil en ligne pdftextonline.com.

pdfminer est un outil précieux pour le raclage 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()

mise à JOUR :

le code ci-dessus est écrit contre une ancienne version de L'API, voir mon commentaire ci-dessous.

21
répondu codeape 2011-07-25 06:05:07

slate est un projet qui rend très simple D'utiliser PDFMiner à partir d'une bibliothèque:

>>> with open('example.pdf') as f:
...    doc = slate.PDF(f)
...
>>> doc
[..., ..., ...]
>>> doc[1]
'Text from page 2...'   
16
répondu Tim McNamara 2011-01-31 00:27:17

j'avais besoin de convertir un PDF spécifique en texte brut dans un module python. J'ai utilisé PDFMiner 20110515, après avoir lu leur pdf2txt.py tool I wrote this simple snippet:

from cStringIO import StringIO
from pdfminer.pdfinterp import PDFResourceManager, process_pdf
from pdfminer.converter import TextConverter
from pdfminer.layout import LAParams

def to_txt(pdf_path):
    input_ = file(pdf_path, 'rb')
    output = StringIO()

    manager = PDFResourceManager()
    converter = TextConverter(manager, output, laparams=LAParams())
    process_pdf(manager, converter, input_)

    return output.getvalue() 
8
répondu gonz 2013-10-28 16:46:27

reconvertir le pdf2txt.py code qui vient avec pdfminer; vous pouvez faire une fonction qui prendra un chemin vers le pdf; optionnellement, un outtype (txt|html|xml|tag) et opte comme la ligne de commande pdf2txt {'-o': '/path/to/outfile.txt' ...}. Par défaut, vous pouvez appeler:

convert_pdf(path)

un fichier texte sera créé, un jumeau sur le système de fichiers au pdf original.

def convert_pdf(path, outtype='txt', opts={}):
    import sys
    from pdfminer.pdfinterp import PDFResourceManager, PDFPageInterpreter, process_pdf
    from pdfminer.converter import XMLConverter, HTMLConverter, TextConverter, TagExtractor
    from pdfminer.layout import LAParams
    from pdfminer.pdfparser import PDFDocument, PDFParser
    from pdfminer.pdfdevice import PDFDevice
    from pdfminer.cmapdb import CMapDB

    outfile = path[:-3] + outtype
    outdir = '/'.join(path.split('/')[:-1])

    debug = 0
    # input option
    password = ''
    pagenos = set()
    maxpages = 0
    # output option
    codec = 'utf-8'
    pageno = 1
    scale = 1
    showpageno = True
    laparams = LAParams()
    for (k, v) in opts:
        if k == '-d': debug += 1
        elif k == '-p': pagenos.update( int(x)-1 for x in v.split(',') )
        elif k == '-m': maxpages = int(v)
        elif k == '-P': password = v
        elif k == '-o': outfile = v
        elif k == '-n': laparams = None
        elif k == '-A': laparams.all_texts = True
        elif k == '-D': laparams.writing_mode = v
        elif k == '-M': laparams.char_margin = float(v)
        elif k == '-L': laparams.line_margin = float(v)
        elif k == '-W': laparams.word_margin = float(v)
        elif k == '-O': outdir = v
        elif k == '-t': outtype = v
        elif k == '-c': codec = v
        elif k == '-s': scale = float(v)
    #
    CMapDB.debug = debug
    PDFResourceManager.debug = debug
    PDFDocument.debug = debug
    PDFParser.debug = debug
    PDFPageInterpreter.debug = debug
    PDFDevice.debug = debug
    #
    rsrcmgr = PDFResourceManager()
    if not outtype:
        outtype = 'txt'
        if outfile:
            if outfile.endswith('.htm') or outfile.endswith('.html'):
                outtype = 'html'
            elif outfile.endswith('.xml'):
                outtype = 'xml'
            elif outfile.endswith('.tag'):
                outtype = 'tag'
    if outfile:
        outfp = file(outfile, 'w')
    else:
        outfp = sys.stdout
    if outtype == 'txt':
        device = TextConverter(rsrcmgr, outfp, codec=codec, laparams=laparams)
    elif outtype == 'xml':
        device = XMLConverter(rsrcmgr, outfp, codec=codec, laparams=laparams, outdir=outdir)
    elif outtype == 'html':
        device = HTMLConverter(rsrcmgr, outfp, codec=codec, scale=scale, laparams=laparams, outdir=outdir)
    elif outtype == 'tag':
        device = TagExtractor(rsrcmgr, outfp, codec=codec)
    else:
        return usage()

    fp = file(path, 'rb')
    process_pdf(rsrcmgr, device, fp, pagenos, maxpages=maxpages, password=password)
    fp.close()
    device.close()

    outfp.close()
    return
5
répondu Skylar Saveland 2010-07-18 19:17:56

de plus il y a PDFTextStream qui est une bibliothèque Java commerciale qui peut également être utilisée à partir de Python.

1
répondu msanders 2008-11-12 17:08:47

j'ai utilisé pdftohtml avec l'argument '-xml', lire le résultat avec subprocess.Popen(), qui vous donnera x coord, y coord, la largeur, la hauteur et la police, de chaque "fragment" de texte dans le document pdf. Je pense que c'est ce que 'prouver' utilise probablement aussi parce que les mêmes messages d'erreur dégorger.

si vous avez besoin de traiter des données en colonnes, cela devient légèrement plus compliqué que vous devez inventer un algorithme qui convient à votre fichier pdf. Le problème est que les programmes qui font Les fichiers PDF ne présentent pas nécessairement le texte dans un format logique. Vous pouvez essayer des algorithmes de tri simples et cela fonctionne parfois, mais il peut y avoir de petits "traînards" et "errants", des morceaux de texte qui ne sont pas mis dans l'ordre que vous pensiez qu'ils le seraient... donc, vous devez faire preuve de créativité.

Il m'a fallu environ 5 heures pour comprendre l'un pour les pdf je travaillais. Mais ça marche plutôt bien maintenant. Bonne chance.

1
répondu Decora 2010-11-12 22:39:25

PDFminer m'a donné peut-être une ligne [page 1 de 7...] sur chaque page d'un fichier pdf, j'ai essayé avec elle.

la meilleure réponse que j'ai jusqu'à présent est pdftoipe, ou le code c++ qu'il est basé sur Xpdf.

voir ma question pour que la sortie de pdftoipe ressemble.

0
répondu sphereinabox 2017-05-23 12:34:45

a trouvé cette solution aujourd'hui. Fonctionne très bien pour moi. Même le rendu des pages PDF aux images PNG. http://www.swftools.org/gfx_tutorial.html

0
répondu Andrey Shipilov 2011-01-31 00:22:57