Python-modul for å konvertere PDF til tekst

stemmer
346

Hvilke er de beste Python-moduler for å konvertere PDF-filer til tekst?

Publisert på 25/08/2008 klokken 04:44
kilden bruker
På andre språk...                            


13 svar

stemmer
133

Den PDFMiner pakken har endret seg siden codeape postet.

EDIT (igjen):

PDFMiner har blitt oppdatert igjen i versjon 20100213

Du kan sjekke hvilken versjon du har installert med følgende:

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

Her er den oppdaterte versjonen (med kommentarer om hva jeg endret / lagt til):

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 (nok en gang):

Her er en oppdatering for siste versjon i PyPI , 20100619p1. Kort sagt erstattes I LTTextItemmed LTCharog passerte en forekomst av LAParams til CsvConverter konstruktøren.

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 (en gang):

Oppdatert for versjon 20110515(takk til 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()
Svarte 10/08/2009 kl. 20:47
kilden bruker

stemmer
121

Prøv PDFMiner. Det kan trekke ut tekst fra PDF-filer som HTML, SGML eller "kodet PDF" format.

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

Den Tagged PDF format synes å være den reneste, og stripping ut XML-koder etterlater bare den nakne teksten.

En Python 3-versjonen er tilgjengelig under:

Svarte 25/08/2008 kl. 05:21
kilden bruker

stemmer
50

Siden ingen av disse løsningene støtter den nyeste versjonen av PDFMiner skrev jeg en enkel løsning som vil returnere teksten i en pdf bruke PDFMiner. Dette vil fungere for de som får importfeil medprocess_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])  

Se nedenfor kode som fungerer for 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])  
Svarte 04/02/2014 kl. 22:16
kilden bruker

stemmer
42

pyPDF fungerer fint (forutsatt at du jobber med velformet PDF-filer). Hvis alt du ønsker er teksten (med mellomrom), kan du bare gjøre:

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

Du kan også enkelt få tilgang til metadata, bildedata, og så videre.

En kommentar i extractText kode notater:

Finne alle tekst tegnekommandoer, i den rekkefølgen de er gitt i innholdsstrømmen, og trekke ut teksten. Dette fungerer godt for noen PDF-filer, men dårlig for andre, avhengig av generatoren brukes. Dette vil bli videreutviklet i fremtiden. Ikke stol på rekkefølgen av teksten som kommer ut av denne funksjonen, som det vil endre seg dersom denne funksjonen er gjort mer sofistikert.

Hvorvidt dette er et problem, avhenger av hva du gjør med teksten (for eksempel hvis ordren ikke spiller noen rolle, det er greit, eller om generatoren legger tekst til bekken i den rekkefølgen det vil bli vist, er det greit) . Jeg har pyPdf utvinning kode i daglig bruk, uten problemer.

Svarte 07/09/2008 kl. 04:47
kilden bruker

stemmer
39

Pdftotext En åpen kildekode-program (del av Xpdf) som du kan ringe fra python (ikke det du ba om, men kan være nyttig). Jeg har brukt det uten problemer. Jeg tror google bruke den i google desktop.

Svarte 28/08/2008 kl. 09:46
kilden bruker

stemmer
21

Du kan også ganske enkelt bruke pdfminer som et bibliotek. Du har tilgang til pdf innhold modell, og kan lage din egen tekst utvinning. I gjorde dette for å omdanne pdf innhold til semikolon separert tekst, ved hjelp av koden under.

Funksjonen sorterer bare de TextItem innholdsobjekter i henhold til deres y og x-koordinater, og utmater element med samme y-koordinaten som en tekstlinje, separering av de objekter på den samme linje med ';' tegn.

Ved hjelp av denne fremgangsmåte, var i stand til å trekke ut tekst fra et pdf at ingen andre verktøy var i stand til å trekke ut innholdet egnet for videre analysering fra. Andre verktøy jeg prøvde inkluderer pdftotext, ps2ascii og elektroniske verktøyet pdftextonline.com.

pdfminer er et uvurderlig verktøy for pdf-skraping.


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 :

Koden ovenfor er skrevet mot en gammel versjon av API, se min kommentar nedenfor.

Svarte 24/11/2008 kl. 14:20
kilden bruker

stemmer
16

slate er et prosjekt som gjør det svært enkelt å bruke PDFMiner fra et bibliotek:

>>> with open('example.pdf') as f:
...    doc = slate.PDF(f)
...
>>> doc
[..., ..., ...]
>>> doc[1]
'Text from page 2...'   
Svarte 31/01/2011 kl. 00:27
kilden bruker

stemmer
8

Jeg trengte å konvertere en bestemt PDF til ren tekst i et python-modul. Jeg brukte PDFMiner 20110515, etter å ha lest gjennom sin pdf2txt.py verktøyet jeg skrev denne enkle tekstutdrag:

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() 
Svarte 28/05/2013 kl. 16:01
kilden bruker

stemmer
5

Gjenbruk av pdf2txt.py kode som følger med pdfminer; du kan lage en funksjon som vil ta en bane til pdf; eventuelt en outtype (txt | html | xml | tag) og opts som kommando PDF2TXT { '-o': '/path/to/outfile.txt' ...}. Som standard kan du ringe:

convert_pdf(path)

En tekstfil blir opprettet, et søsken på filsystemet til den opprinnelige pdf.

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
Svarte 18/07/2010 kl. 19:17
kilden bruker

stemmer
1

Jeg har brukt pdftohtml med '-xml' argument, lese resultatet med subprocess.Popen (), som vil gi deg x koord, y koord, bredde, høyde og skrift, for hver 'tekstutdrag' av teksten i pdf. Jeg tror dette er hva evince 'bruker trolig også fordi de samme feilmeldingene spyr ut.

Hvis du trenger å behandle kolonne data, blir det litt mer komplisert som du har å oppfinne en algoritme som passer din pdf-fil. Problemet er at programmene som gjør PDF-filer egentlig ikke nødvendigvis legge ut teksten i noen logisk format. Du kan prøve enkle sorteringsalgoritme og det fungerer noen ganger, men det kan være litt 'etternølere' og 'spredt', biter av tekst som ikke blir benyttet i den rekkefølgen du trodde de ville ... så du har til å bli kreativ.

Det tok meg ca 5 timer for å finne ut en for pdf-er jeg jobbet på. Men det fungerer ganske bra nå. Lykke til.

Svarte 12/11/2010 kl. 22:34
kilden bruker

stemmer
1

I tillegg er det PDFTextStream som er et kommersielt Java-bibliotek som også kan brukes fra Python.

Svarte 12/11/2008 kl. 17:08
kilden bruker

stemmer
0

Fant at løsningen i dag. Fungerer bra for meg. Selv gjengi PDF-sider til PNG-bilder. http://www.swftools.org/gfx_tutorial.html

Svarte 31/01/2011 kl. 00:22
kilden bruker

stemmer
0

PDFminer ga meg kanskje en linje [side 1 av 7 ...] på hver side av en pdf-fil jeg prøvde med det.

Det beste svaret jeg har så langt er pdftoipe, eller c ++ kode det er basert på Xpdf.

se mitt spørsmål for hva resultatet av pdftoipe ser ut.

Svarte 26/08/2008 kl. 02:04
kilden bruker

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more