Недавно я боролся с похожей проблемой, хотя структура моего pdf была немного проще.
PDFMiner использует классы, называемые «устройствами», для анализа страниц в pdf-файле. Базовым классом устройства является класс PDFPageAggregator, который просто анализирует текстовые поля в файле. Классы преобразователя, например. TextConverter, XMLConverter и HTMLConverter также выводят результат в файл (или в поток строк, как в вашем примере) и выполняют более сложный анализ содержимого.
Проблема с TextConverter (и PDFPageAggregator) заключается в том, что они недостаточно глубоко рекурсивно обращаются к структуре документа для правильного извлечения различных столбцов. Двум другим преобразователям требуется некоторая информация о структуре документа для целей отображения, поэтому они собирают более подробные данные. В вашем примере pdf оба упрощенных устройства только анализируют (примерно) все текстовое поле, содержащее столбцы, что делает невозможным (или, по крайней мере, очень сложным) правильное разделение разных строк. Решение этого, которое я нашел, работает очень хорошо, либо
- Создайте новый класс, наследуемый от PDFPageAggregator, или
- Используйте XMLConverter и проанализируйте полученный XML-документ, используя, например. Красивый суп
В обоих случаях вам придется объединить различные текстовые сегменты в строки, используя их координаты y ограничивающей рамки.
В случае нового класса устройств (я думаю, это более красноречиво) вам придется переопределить метод receive_layout
, который вызывается для каждой страницы в процессе рендеринга. Затем этот метод рекурсивно анализирует элементы на каждой странице. Например, что-то вроде этого может помочь вам начать:
from pdfminer.pdfdocument import PDFDocument, PDFNoOutlines
from pdfminer.pdfparser import PDFParser
from pdfminer.pdfinterp import PDFResourceManager, PDFPageInterpreter
from pdfminer.converter import PDFPageAggregator
from pdfminer.layout import LTPage, LTChar, LTAnno, LAParams, LTTextBox, LTTextLine
class PDFPageDetailedAggregator(PDFPageAggregator):
def __init__(self, rsrcmgr, pageno=1, laparams=None):
PDFPageAggregator.__init__(self, rsrcmgr, pageno=pageno, laparams=laparams)
self.rows = []
self.page_number = 0
def receive_layout(self, ltpage):
def render(item, page_number):
if isinstance(item, LTPage) or isinstance(item, LTTextBox):
for child in item:
render(child, page_number)
elif isinstance(item, LTTextLine):
child_str = ''
for child in item:
if isinstance(child, (LTChar, LTAnno)):
child_str += child.get_text()
child_str = ' '.join(child_str.split()).strip()
if child_str:
row = (page_number, item.bbox[0], item.bbox[1], item.bbox[2], item.bbox[3], child_str) # bbox == (x1, y1, x2, y2)
self.rows.append(row)
for child in item:
render(child, page_number)
return
render(ltpage, self.page_number)
self.page_number += 1
self.rows = sorted(self.rows, key = lambda x: (x[0], -x[2]))
self.result = ltpage
В приведенном выше коде каждый найденный элемент LTTextLine хранится в упорядоченном списке кортежей, содержащих номер страницы, координаты ограничивающей рамки и текст, содержащийся в этом конкретном элементе. Затем вы должны сделать что-то похожее на это:
from pprint import pprint
from pdfminer.pdfparser import PDFParser
from pdfminer.pdfdocument import PDFDocument
from pdfminer.pdfpage import PDFPage
from pdfminer.pdfinterp import PDFResourceManager, PDFPageInterpreter
from pdfminer.layout import LAParams
fp = open('pdf_doc.pdf', 'rb')
parser = PDFParser(fp)
doc = PDFDocument(parser)
doc.initialize('password') # leave empty for no password
rsrcmgr = PDFResourceManager()
laparams = LAParams()
device = PDFPageDetailedAggregator(rsrcmgr, laparams=laparams)
interpreter = PDFPageInterpreter(rsrcmgr, device)
for page in PDFPage.create_pages(doc):
interpreter.process_page(page)
# receive the LTPage object for this page
device.get_result()
pprint(device.rows)
Переменная device.rows содержит упорядоченный список, в котором все текстовые строки упорядочены по номерам страниц и координатам Y. Вы можете перебирать текстовые строки и группировать строки с одинаковыми координатами Y, чтобы формировать строки, сохранять данные столбца и т. д.
Я попытался проанализировать ваш PDF-файл, используя приведенный выше код, и столбцы в основном анализируются правильно. Однако некоторые столбцы расположены так близко друг к другу, что стандартная эвристика PDFMiner не может разделить их на отдельные элементы. Вероятно, вы можете обойти это, настроив параметр поля слова (флаг -W в инструменте командной строки pdf2text.py). В любом случае, вы можете прочитать (плохо документированный) PDFMiner API, а также просмотрите исходный код PDFMiner, который вы можете получить на github. (Увы, я не могу вставить ссылку, потому что у меня недостаточно точек репутации: '‹, но вы, надеюсь, можете найти правильный репозиторий в Google)
person
lindblandro
schedule
04.10.2013
retstr = StringIO.StringIO()
? - person Stedy   schedule 01.04.2013