Получение данных из PDF-файла с тем же макетом, что и при копировании+вставке

У меня есть процедура, которую я хочу автоматизировать, которая включает в себя получение серии таблиц из файла PDF. В настоящее время я могу сделать это, открыв файл в любом средстве просмотра (Adobe, Sumatra, okular и т. д.) и просто нажав Ctrl+A, Ctrl+C, Ctrl+V в блокноте, и он поддерживает каждую строку, выровненную с разумным достаточного формата, чтобы затем я мог просто запустить регулярное выражение, скопировать и вставить его в Excel для всего, что понадобится впоследствии.

Пытаясь сделать это с помощью python, я пробовал различные модули, PDFminer - основной, который работает с использованием например, этот пример. Но он возвращает данные в одном столбце. Другие варианты включают только получение его в виде html-таблицы, но в этом случае он добавляет дополнительную разделяющую среднюю таблицу, которая усложняет синтаксический анализ или даже иногда переключает столбцы между первой и второй страницами.

У меня есть временное решение, работающее на данный момент, но я беспокоюсь, что заново изобретаю колесо, когда мне, вероятно, просто не хватает основной опции в синтаксическом анализаторе или что мне нужно рассмотреть какой-то фундаментальный вариант того, как визуализатор PDF работает, чтобы решить эту проблему.

Есть идеи, как к этому подойти?


person Drexer    schedule 26.11.2016    source источник
comment
Нашли ли вы обходной путь/решение с использованием библиотеки Python pdfminer для сохранения макета выходного текста таким же, как в документе PDF? В исходном коде есть класс LAParams который может управлять параметрами макета, но указание правильных значений — это попытка проб и ошибок. Пример использования: extract_text_to_fp. Думаю, я буду использовать pdftotext -layout input.pdf output.txt , см.: askubuntu.com/q/52040   -  person Alex Bitek    schedule 01.01.2017
comment
Я нашел это да, но забыл дать ответ из-за спешки в реализации. Я проверю код и предоставлю его через несколько минут.   -  person Drexer    schedule 02.01.2017


Ответы (2)


В итоге я реализовал решение на основе этого, которое само по себе было модифицировано из кода tgray. Он работает последовательно во всех случаях, которые я тестировал до сих пор, но мне еще предстоит определить, как напрямую манипулировать параметрами pdfminer для получения желаемого поведения.

person Drexer    schedule 02.01.2017

Публикация этого только для того, чтобы получить фрагмент кода, который работает с py35 для синтаксического анализа, подобного csv. Разбиение на столбцы проще всего, но у меня сработало.

В этом ответе в качестве отправной точки важно использовать tgray.

Также добавьте openpyxl, так как я предпочитаю получать результаты непосредственно в Excel.

# works with py35 & pip-installed pdfminer.six in 2017
def pdf_to_csv(filename):
    from io import StringIO
    from pdfminer.converter import LTChar, TextConverter
    from pdfminer.layout import LAParams
    from pdfminer.pdfdocument import PDFDocument
    from pdfminer.pdfpage import PDFPage
    from pdfminer.pdfparser import 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):
                    (_,_,x,y) = child.bbox
                    line = lines[int(-y)]
                    line[x] = child.get_text()
                    # the line is now an unsorted dict

            for y in sorted(lines.keys()):
                line = lines[y]
                # combine close letters to form columns
                xpos = tuple(sorted(line.keys()))
                new_line = []
                temp_text = ''
                for i in range(len(xpos)-1):
                    temp_text += line[xpos[i]]
                    if xpos[i+1] - xpos[i] > 8:
                        # the 8 is representing font-width
                        # needs adjustment for your specific pdf
                        new_line.append(temp_text)
                        temp_text = ''
                # adding the last column which also manually needs the last letter
                new_line.append(temp_text+line[xpos[-1]])

                self.outfp.write(";".join(nl for nl in new_line))
                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())

    fp = open(filename, 'rb')
    parser = PDFParser(fp)
    doc = PDFDocument(parser)
    password = ""
    maxpages = 0
    caching = True
    pagenos=set()

    interpreter = PDFPageInterpreter(rsrc, device)

    for i, page in enumerate(PDFPage.get_pages(fp,
                                pagenos, maxpages=maxpages,
                                password=password,caching=caching,
                                check_extractable=True)):
        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()

fn = 'your_file.pdf'
result = pdf_to_csv(fn)

lines = result.split('\n')
import openpyxl as pxl
wb = pxl.Workbook()
ws = wb.active
for line in lines:
    ws.append(line.split(';'))
    # appending a list gives a complete row in xlsx
wb.save('your_file.xlsx')
person ahed87    schedule 11.02.2017