Gibt es ein Python-Modul zum Konvertieren von PDF-Dateien in Text? Ich habe einen in Activestate gefundenen Code ausprobiert, der pypdf verwendet, aber der generierte Text hatte kein Leerzeichen dazwischen und war nutzlos.
Gibt es ein Python-Modul zum Konvertieren von PDF-Dateien in Text? Ich habe einen in Activestate gefundenen Code ausprobiert, der pypdf verwendet, aber der generierte Text hatte kein Leerzeichen dazwischen und war nutzlos.
Antworten:
Probieren Sie PDFMiner aus . Es kann Text aus PDF-Dateien im HTML-, SGML- oder "Tagged PDF" -Format extrahieren.
Das Tagged PDF-Format scheint das sauberste zu sein, und wenn Sie die XML-Tags entfernen, bleibt nur der bloße Text übrig.
Eine Python 3-Version ist verfügbar unter:
Das PDFMiner- Paket hat sich seit der Veröffentlichung von codeape geändert .
BEARBEITEN (erneut):
PDFMiner wurde in der Version erneut aktualisiert 20100213
Sie können die installierte Version folgendermaßen überprüfen:
>>> import pdfminer
>>> pdfminer.__version__
'20100213'
Hier ist die aktualisierte Version (mit Kommentaren zu dem, was ich geändert / hinzugefügt habe):
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()
Bearbeiten (noch einmal):
Hier ist ein Update für die neueste Version in pypi , 20100619p1
. Kurz ersetzt ich LTTextItem
mit LTChar
und übergeben eine Instanz von LAParams zum CsvConverter Konstruktor.
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()
BEARBEITEN (noch einmal):
Für die Version aktualisiert 20110515
(danke an 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
zu LTChar
. unixuser.org/~euske/python/pdfminer/index.html#changes
20110515
gemäß Ihrem Kommentar einen weiteren Abschnitt hinzugefügt .
Da keine dieser Lösungen die neueste Version von PDFMiner unterstützt, habe ich eine einfache Lösung geschrieben, die mit PDFMiner den Text eines PDFs zurückgibt. Dies funktioniert für diejenigen, bei denen Importfehler auftretenprocess_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])
Siehe folgenden Code, der für Python 3 funktioniert:
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
, muss man neben den offensichtlichen Klammern nach dem print
Befehl den file
Befehl durch das Paket ersetzen open
und StringIO
aus dem Paket importierenio
Pdftotext Ein Open-Source-Programm (Teil von Xpdf), das Sie von Python aus aufrufen können (nicht das, wonach Sie gefragt haben, aber möglicherweise nützlich sind). Ich habe es ohne Probleme benutzt. Ich denke, Google verwendet es in Google Desktop.
-layout
Option, Text an derselben Position wie im PDF zu halten. Wenn ich nur herausfinden könnte, wie ich den Inhalt einer PDF-Datei darin ableiten kann.
pdftotext
scheint sehr gut zu funktionieren, aber es braucht ein zweites Argument, das ein Bindestrich ist, wenn Sie die Ergebnisse auf stdout sehen wollen.
find . -iname "*.pdf" -exec pdftotext -enc UTF-8 -eol unix -raw {} \;
Standardmäßig nehmen die generierten Dateien den ursprünglichen Namen mit der .txt
Erweiterung an.
pyPDF funktioniert einwandfrei (vorausgesetzt, Sie arbeiten mit wohlgeformten PDFs). Wenn Sie nur den Text (mit Leerzeichen) möchten, können Sie einfach Folgendes tun:
import pyPdf
pdf = pyPdf.PdfFileReader(open(filename, "rb"))
for page in pdf.pages:
print page.extractText()
Sie können auch problemlos auf Metadaten, Bilddaten usw. zugreifen.
Ein Kommentar im Code "extractText" enthält folgende Hinweise:
Suchen Sie alle Textzeichnungsbefehle in der Reihenfolge, in der sie im Inhaltsdatenstrom angegeben sind, und extrahieren Sie den Text. Dies funktioniert für einige PDF-Dateien gut, für andere jedoch schlecht, je nach verwendetem Generator. Dies wird in Zukunft verfeinert. Verlassen Sie sich nicht auf die Reihenfolge des Texts, der aus dieser Funktion kommt, da sich dieser ändert, wenn diese Funktion komplexer wird.
Ob dies ein Problem ist oder nicht, hängt davon ab, was Sie mit dem Text tun (z. B. wenn die Reihenfolge keine Rolle spielt, ist es in Ordnung, oder wenn der Generator dem Stream Text in der Reihenfolge hinzufügt, in der er angezeigt wird, ist es in Ordnung). . Ich habe pyPdf-Extraktionscode im täglichen Gebrauch, ohne Probleme.
Sie können pdfminer auch ganz einfach als Bibliothek verwenden. Sie haben Zugriff auf das Inhaltsmodell des PDFs und können Ihre eigene Textextraktion erstellen. Ich habe dies getan, um PDF-Inhalte mit dem folgenden Code in durch Semikolon getrennten Text zu konvertieren.
Die Funktion sortiert die TextItem-Inhaltsobjekte einfach nach ihren y- und x-Koordinaten und gibt Elemente mit derselben y-Koordinate wie eine Textzeile aus, wobei die Objekte in derselben Zeile mit ';' getrennt werden. Figuren.
Mit diesem Ansatz konnte ich Text aus einem PDF extrahieren, aus dem kein anderes Tool Inhalte extrahieren konnte, die zum weiteren Parsen geeignet waren. Andere Tools, die ich ausprobiert habe, sind pdftotext, ps2ascii und das Online-Tool pdftextonline.com.
pdfminer ist ein unschätzbares Werkzeug für das PDF-Scraping.
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 :
Der obige Code wurde gegen eine alte Version der API geschrieben, siehe meinen Kommentar unten.
pdfminer
nicht mehr pdflib
). Ich schlage vor, Sie werfen einen Blick auf die Quelle pdf2txt.py
in der PDFminer-Quelle. Der obige Code wurde von der alten Version dieser Datei inspiriert.
slate
ist ein Projekt, das die Verwendung von PDFMiner aus einer Bibliothek sehr einfach macht:
>>> with open('example.pdf') as f:
... doc = slate.PDF(f)
...
>>> doc
[..., ..., ...]
>>> doc[1]
'Text from page 2...'
Ich musste eine bestimmte PDF-Datei innerhalb eines Python-Moduls in einfachen Text konvertieren. Ich habe PDFMiner 20110515 verwendet, nachdem ich das Tool pdf2txt.py gelesen hatte , habe ich dieses einfache Snippet geschrieben:
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
Umnutzung des mit pdfminer gelieferten Codes pdf2txt.py; Sie können eine Funktion erstellen, die einen Pfad zum PDF nimmt. Optional ein Outtype (txt | html | xml | tag) und Optionen wie die Befehlszeile pdf2txt {'-o': '/path/to/outfile.txt' ...}. Standardmäßig können Sie Folgendes aufrufen:
convert_pdf(path)
Es wird eine Textdatei erstellt, ein Geschwister im Dateisystem zum Original-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
PDFminer gab mir vielleicht eine Zeile [Seite 1 von 7 ...] auf jeder Seite einer PDF-Datei, die ich damit ausprobiert habe.
Die beste Antwort, die ich bisher habe, ist pdftoipe oder der C ++ - Code, der auf Xpdf basiert.
Siehe meine Frage, wie die Ausgabe von pdftoipe aussieht.
Zusätzlich gibt es PDFTextStream , eine kommerzielle Java-Bibliothek, die auch von Python aus verwendet werden kann.
Ich habe verwendet pdftohtml
mit dem -xml
Argument, lesen Sie das Ergebnis mit subprocess.Popen()
, das Ihnen x coord, y - Koordinaten zum Runterladen, Breite, Höhe und Schriftart, jeder Schnipsel des Textes im pdf. Ich denke, das ist es, was 'evince' wahrscheinlich auch verwendet, weil die gleichen Fehlermeldungen ausgegeben werden.
Wenn Sie Säulendaten verarbeiten müssen, wird dies etwas komplizierter, da Sie einen Algorithmus erfinden müssen, der zu Ihrer PDF-Datei passt. Das Problem ist, dass die Programme, die PDF-Dateien erstellen, den Text nicht unbedingt in einem logischen Format auslegen. Sie können einfache Sortieralgorithmen ausprobieren und es funktioniert manchmal, aber es kann kleine "Nachzügler" und "Streuner" geben, Textteile, die nicht in der Reihenfolge angeordnet werden, in der Sie sie erwartet haben. Man muss also kreativ werden.
Ich brauchte ungefähr 5 Stunden, um eines für die PDFs herauszufinden, an denen ich arbeitete. Aber jetzt funktioniert es ziemlich gut. Viel Glück.
Habe diese Lösung heute gefunden. Funktioniert gut für mich. Sogar das Rendern von PDF-Seiten in PNG-Bilder. http://www.swftools.org/gfx_tutorial.html