Модуль Python для преобразования PDF в текст [закрыт]
-
09-06-2019 - |
Вопрос
Какие модули Python лучше всего подходят для преобразования PDF-файлов в текст?
Решение
Пытаться PDFMiner.Он может извлекать текст из файлов PDF в формате HTML, SGML или «Tagged PDF».
Формат PDF с тегами кажется самым чистым, и при удалении тегов XML остается только чистый текст.
Версия Python 3 доступна по адресу:
Другие советы
А PDFMiner пакет изменился с тех пор Кодейп разместил.
РЕДАКТИРОВАТЬ (снова):
PDFMiner снова обновлён в версии 20100213
Проверить версию, которую вы установили, можно следующим образом:
>>> import pdfminer
>>> pdfminer.__version__
'20100213'
Вот обновленная версия (с комментариями о том, что я изменил/добавил):
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()
Редактировать (еще раз):
Вот обновление для последней версии в пипи, 20100619p1
.Короче я заменил LTTextItem
с LTChar
и передал экземпляр LAParams конструктору 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()
РЕДАКТИРОВАТЬ (еще раз):
Обновлено для версии 20110515
(спасибо Уфкоку Пентеано!):
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()
Поскольку ни одно из этих решений не поддерживает последнюю версию PDFMiner, я написал простое решение, которое будет возвращать текст PDF-файла с помощью PDFMiner.Это будет работать для тех, кто получает ошибки импорта с 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])
См. ниже код, который работает для 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])
PDF-текст Программа с открытым исходным кодом (часть Xpdf), которую вы можете вызвать из Python (не то, что вы просили, но может быть полезно).Я использовал его без проблем.Я думаю, что Google использует его в Google Desktop.
pyPDF работает нормально (при условии, что вы работаете с правильно сформированными PDF-файлами).Если все, что вам нужно, это текст (с пробелами), вы можете просто сделать:
import pyPdf
pdf = pyPdf.PdfFileReader(open(filename, "rb"))
for page in pdf.pages:
print page.extractText()
Вы также можете легко получить доступ к метаданным, данным изображения и т. д.
Комментарий в коде ExtractText отмечает:
Найдите все команды текстового чертежа, в порядке, которые они представлены в потоке контента, и извлеките текст.Это хорошо работает для некоторых файлов PDF, но плохо для других, в зависимости от используемого генератора.Это будет уточнено в будущем.Не полагайтесь на порядок текста, выходящий из этой функции, так как он изменится, если эта функция сделана более сложной.
Является ли это проблемой, зависит от того, что вы делаете с текстом (например,если порядок не имеет значения, это нормально, или если генератор добавляет текст в поток в том порядке, в котором он будет отображаться, это нормально).У меня есть код извлечения pyPdf, который я использую ежедневно, без каких-либо проблем.
Вы также можете легко использовать pdfminer в качестве библиотеки.У вас есть доступ к модели содержимого PDF-файла и вы можете создать собственное извлечение текста.Я сделал это, чтобы преобразовать содержимое PDF в текст, разделенный точкой с запятой, используя приведенный ниже код.
Функция просто сортирует объекты контента TextItem в соответствии с их координатами Y и X, и выводит элементы с той же координат Y, что и одна текстовая линия, разделяя объекты на той же строке с ';'; ' персонажи.
Используя этот подход, я смог извлечь текст из PDF-файла, из которого ни один другой инструмент не смог извлечь контент, пригодный для дальнейшего анализа.Другие инструменты, которые я пробовал, включают pdftotext, ps2ascii и онлайн-инструмент pdftextonline.com.
pdfminer — бесценный инструмент для парсинга 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()
ОБНОВЛЯТЬ:
Приведенный выше код написан для старой версии API, см. мой комментарий ниже.
slate
— это проект, который упрощает использование PDFMiner из библиотеки:
>>> with open('example.pdf') as f:
... doc = slate.PDF(f)
...
>>> doc
[..., ..., ...]
>>> doc[1]
'Text from page 2...'
Мне нужно было преобразовать определенный PDF-файл в обычный текст в модуле Python.я использовал PDFMiner 20110515, прочитав их pdf2txt.py инструмент, я написал этот простой фрагмент:
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()
Перепрофилирование кода pdf2txt.py, поставляемого с pdfminer;вы можете создать функцию, которая будет определять путь к PDF-файлу;опционально, outtype (txt|html|xml|tag) и выбор типа командной строки pdf2txt {'-o':'/путь/к/outfile.txt' ...}.По умолчанию вы можете позвонить:
convert_pdf(path)
Будет создан текстовый файл, аналог исходного 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 дал мне, возможно, одну строку [страница 1 из 7...] на каждой странице PDF-файла, который я пробовал с ним.
Лучший ответ, который у меня есть на данный момент, — это pdftoipe или код C++, основанный на Xpdf.
видеть мой вопрос как выглядит вывод pdftoipe.
Кроме того, есть PDFTextStream это коммерческая библиотека Java, которую также можно использовать из Python.
я использовал pdftohtml
с -xml
аргумент, прочитайте результат с помощью subprocess.Popen()
, который даст вам координату x, координату y, ширину, высоту и шрифт каждого фрагмент текста в PDF.Я думаю, что это то, что, вероятно, использует и 'evince', потому что выдаются те же сообщения об ошибках.
Если вам нужно обработать столбчатые данные, это становится немного сложнее, поскольку вам придется изобрести алгоритм, подходящий для вашего PDF-файла.Проблема в том, что программы, создающие PDF-файлы, на самом деле не обязательно преобразуют текст в какой-либо логический формат.Вы можете попробовать простые алгоритмы сортировки, и иногда это работает, но могут быть небольшие «отставшие» и «отстающие» фрагменты текста, которые не располагаются в том порядке, в котором вы предполагали.Так что вам придется проявить творческий подход.
Мне потребовалось около 5 часов, чтобы найти один вариант для PDF-файла, над которым я работал.Но сейчас это работает довольно хорошо.Удачи.
Сегодня нашел это решение.У меня отлично работает.Даже рендеринг PDF-страниц в изображения PNG.http://www.swftools.org/gfx_tutorial.html