Esiste un modulo Python per convertire i file PDF in testo? Ho provato un pezzo di codice trovato in Activestate che utilizza pypdf ma il testo generato non aveva spazio tra di loro e non era di alcuna utilità.
Esiste un modulo Python per convertire i file PDF in testo? Ho provato un pezzo di codice trovato in Activestate che utilizza pypdf ma il testo generato non aveva spazio tra di loro e non era di alcuna utilità.
Risposte:
Prova PDFMiner . Può estrarre testo da file PDF in formato HTML, SGML o "Tagged PDF".
Il formato PDF con tag sembra essere il più pulito e l'eliminazione dei tag XML lascia solo il testo nudo.
Una versione di Python 3 è disponibile sotto:
Il pacchetto PDFMiner è cambiato da quando è stato pubblicato codeape .
MODIFICA (di nuovo):
PDFMiner è stato nuovamente aggiornato nella versione 20100213
È possibile verificare la versione installata con quanto segue:
>>> import pdfminer
>>> pdfminer.__version__
'20100213'
Ecco la versione aggiornata (con commenti su ciò che ho modificato / aggiunto):
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()
Modifica (ancora una volta):
Ecco un aggiornamento per la versione più recente di PyPI , 20100619p1
. In breve sostituito LTTextItem
con LTChar
e passai un'istanza LAParams al costruttore 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()
MODIFICA (ancora una volta):
Aggiornato per la versione 20110515
(grazie a 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()
LTTextItem
in LTChar
. unixuser.org/~euske/python/pdfminer/index.html#changes
20110515
per il tuo commento.
Poiché nessuna di queste soluzioni supporta l'ultima versione di PDFMiner, ho scritto una soluzione semplice che restituirà il testo di un pdf utilizzando PDFMiner. Questo funzionerà per coloro che ottengono errori di importazione conprocess_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])
Vedi sotto il codice che funziona per 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])
python3
, oltre alle ovvie parentesi dopo il print
comando, si deve sostituire il file
comando con open
e importarlo StringIO
dal pacchettoio
Pdftotext Un programma open source (parte di Xpdf) che potresti chiamare da Python (non quello che hai chiesto ma potrebbe essere utile). L'ho usato senza problemi. Penso che Google lo usi nel desktop di Google.
-layout
opzione di mantenere il testo nella stessa posizione del PDF. Ora, se solo potessi capire come convogliare il contenuto di un PDF in esso.
pdftotext
sembra funzionare molto bene, ma ha bisogno di un secondo argomento che sia un trattino, se vuoi vedere i risultati su stdout.
find . -iname "*.pdf" -exec pdftotext -enc UTF-8 -eol unix -raw {} \;
per impostazione predefinita, i file generati assumono il nome originale con l' .txt
estensione.
pyPDF funziona bene (supponendo che tu stia lavorando con PDF ben formati). Se tutto ciò che vuoi è il testo (con spazi), puoi semplicemente fare:
import pyPdf
pdf = pyPdf.PdfFileReader(open(filename, "rb"))
for page in pdf.pages:
print page.extractText()
Puoi anche accedere facilmente ai metadati, ai dati delle immagini e così via.
Un commento nelle note del codice extractText:
Individua tutti i comandi di disegno del testo, nell'ordine in cui sono forniti nel flusso di contenuto, ed estrai il testo. Funziona bene con alcuni file PDF, ma male per altri, a seconda del generatore utilizzato. Questo sarà perfezionato in futuro. Non fare affidamento sull'ordine del testo che esce da questa funzione, poiché cambierà se questa funzione viene resa più sofisticata.
Se questo è un problema o meno dipende da cosa stai facendo con il testo (ad es. Se l'ordine non ha importanza, va bene, o se il generatore aggiunge testo allo stream nell'ordine in cui verrà visualizzato, va bene) . Ho un codice di estrazione pyPdf nell'uso quotidiano, senza problemi.
Puoi anche usare PDFminer abbastanza facilmente come libreria. Hai accesso al modello di contenuto del pdf e puoi creare la tua estrazione di testo. Ho fatto questo per convertire i contenuti pdf in testo separato da punti e virgola, usando il codice qui sotto.
La funzione ordina semplicemente gli oggetti di contenuto TextItem in base alle loro coordinate y e x e genera elementi con la stessa coordinata y di una riga di testo, separando gli oggetti sulla stessa riga con ';' personaggi.
Usando questo approccio, sono stato in grado di estrarre il testo da un pdf che nessun altro strumento è stato in grado di estrarre il contenuto adatto per ulteriori analisi. Altri strumenti che ho provato includono pdftotext, ps2ascii e lo strumento online pdftextonline.com.
pdfminer è uno strumento prezioso per la raschiatura di 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()
AGGIORNAMENTO :
Il codice sopra è scritto su una vecchia versione dell'API, vedi il mio commento qui sotto.
pdfminer
, non pdflib
). Ti suggerisco di dare un'occhiata alla fonte di pdf2txt.py
PDFminer, il codice sopra è stato ispirato dalla vecchia versione di quel file.
slate
è un progetto che semplifica l'utilizzo di PDFMiner da una libreria:
>>> with open('example.pdf') as f:
... doc = slate.PDF(f)
...
>>> doc
[..., ..., ...]
>>> doc[1]
'Text from page 2...'
Avevo bisogno di convertire un PDF specifico in testo normale all'interno di un modulo Python. Ho usato PDFMiner 20110515, dopo aver letto il loro strumento pdf2txt.py ho scritto questo semplice frammento:
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()
C:\Python27\Scripts\pdfminer\tools\pdf2txt.py
Riproporre il codice pdf2txt.py fornito con pdfminer; puoi fare una funzione che prenderà un percorso per il pdf; opzionalmente, un outtype (txt | html | xml | tag) e opta come la riga di comando pdf2txt {'-o': '/path/to/outfile.txt' ...}. Per impostazione predefinita, è possibile chiamare:
convert_pdf(path)
Verrà creato un file di testo, un fratello sul filesystem nel pdf originale.
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
PDFminer mi ha dato forse una riga [pagina 1 di 7 ...] su ogni pagina di un file pdf che ho provato con esso.
La migliore risposta che ho finora è pdftoipe, o il codice c ++ è basato su Xpdf.
vedi la mia domanda su come appare l'output di pdftoipe.
Inoltre c'è PDFTextStream che è una libreria Java commerciale che può essere utilizzata anche da Python.
Ho usato pdftohtml
l' -xml
argomento, ho letto il risultato con subprocess.Popen()
, che ti darà x coord, y coord, larghezza, altezza e carattere, di ogni frammento di testo nel pdf. Penso che questo sia ciò che probabilmente "evince" usa anche perché vengono emessi gli stessi messaggi di errore.
Se hai bisogno di elaborare dati colonnari, diventa leggermente più complicato in quanto devi inventare un algoritmo adatto al tuo file pdf. Il problema è che i programmi che creano file PDF non strutturano necessariamente il testo in alcun formato logico. Puoi provare semplici algoritmi di ordinamento e a volte funziona, ma possono esserci piccoli "sbandati" e "randagi", pezzi di testo che non vengono messi nell'ordine che pensavi avrebbero fatto. Quindi devi essere creativo.
Mi ci sono volute circa 5 ore per capirne uno per il pdf a cui stavo lavorando. Ma ora funziona abbastanza bene. In bocca al lupo.
Ho trovato quella soluzione oggi. Funziona benissimo per me. Anche il rendering di pagine PDF in immagini PNG. http://www.swftools.org/gfx_tutorial.html