Модуль Python для преобразования PDF в текст [закрыто]
какие модули Python лучше всего конвертировать PDF-файлы в текст?
13 ответов:
Попробуйте PDFMiner. Он может извлекать текст из PDF-файлов в формате HTML, SGML или "Tagged PDF".
http://www.unixuser.org / ~euske/python/pdfminer/index.html
Формат PDF с тегами кажется самым чистым, и удаление тегов XML оставляет только голый текст.
версия Python 3 доступна в разделе:
The PDFMiner изменилась с codeape выложил.
изменить (еще раз):
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()
изменить (еще раз):
вот это обновление для последней версии pypi,
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
(спасибо 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()
поскольку ни один из этих решений не поддерживает последнюю версию 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])
pyPDF отлично работает (при условии, что вы работаете с хорошо сформированными PDF-файлами). Если все, что вы хотите, это текст (с пробелами), вы можете просто сделать:
import pyPdf pdf = pyPdf.PdfFileReader(open(filename, "rb")) for page in pdf.pages: print page.extractText()
вы также можете легко получить доступ к метаданным, данные изображения, и так далее.
комментарий в коде extractText отмечает:
найдите все команды рисования текста, в порядке, в котором они представлены в поток содержимого и извлечение текста. Это хорошо работает для некоторых PDF файлов, но плохо для других, в зависимости от используемый генератор. Это будет доработано в будущем. Не полагайтесь на порядок текста, выходящего из этого функция, как она изменится, если это функции более сложные.
является ли это проблемой, зависит от того, что вы делаете с текстом (например, если порядок не имеет значения, это нормально, или если генератор добавляет текст в поток в том порядке, в котором он будет отображаться, это нормально). У меня есть извлечение pyPdf код в ежедневном использовании, без каких-либо проблем.
Pdftotext программа с открытым исходным кодом (часть Xpdf), которую вы можете вызвать из python (не то, что вы просили, но может быть полезно). Я использовал его без проблем. Я думаю, что google использует его в Google desktop.
вы также можете довольно легко использовать 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': '/path/to/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
дополнительно PDFTextStream который является коммерческой библиотекой Java, которая также может быть использована из Python.
Я использовал pdftohtml с аргументом '- xml', прочитайте результат с подпроцессом.Popen (), который даст вам x coord, y coord, width, height и font, каждого "фрагмента" текста в pdf. Я думаю, что это то, что "evince", вероятно, тоже использует, потому что те же сообщения об ошибках извергаются.
Если вам нужно обрабатывать столбчатые данные, это становится немного сложнее, так как вам нужно придумать алгоритм, который соответствует вашему pdf-файлу. Проблема в том, что программы, которые делают PDF-файлов не обязательно выкладывать текст в любом логическом формате. Вы можете попробовать простые алгоритмы сортировки, и это иногда работает, но могут быть маленькие "отставшие" и "заблудшие", фрагменты текста, которые не помещаются в том порядке, в котором вы думали, что они будут... поэтому вы должны проявить творческий подход.
Мне потребовалось около 5 часов, чтобы выяснить один для pdf-файлов, над которыми я работал. Но сейчас это работает довольно хорошо. Удача.
PDFminer дал мне, возможно, одну строку [Страница 1 из 7...] на каждой странице PDF-файла, я пробовал с ним.
лучший ответ, который у меня есть до сих пор, - это pdftoipe, или код c++, основанный на Xpdf.
посмотреть у меня вопрос для того, что выход pdftoipe выглядит.
нашел это решение сегодня. Отлично работает для меня. Даже рендеринг PDF страниц в PNG изображений. http://www.swftools.org/gfx_tutorial.html