Рубрики
Без рубрики

Работа с PDF-файлами на Python: Чтение и разбиение страниц

Автор оригинала: Frank Hofmann.

Эта статья является первой в серии статей по работе с PDF файлами на Python:

Формат документа PDF

Сегодня формат переносимого документа (PDF) относится к наиболее часто используемым форматам данных. В 1990 году структура PDF-документа была определена компанией Adobe. Идея формата PDF заключается в том, что передаваемые данные/документы выглядят совершенно одинаково для обеих сторон, участвующих в процессе коммуникации – создателя, автора или отправителя, и получателя. PDF является преемником формата PostScript и стандартизирован как ISO 32000-2:2017 .

Обработка PDF-документов

Для Linux доступны мощные инструменты командной строки, такие как pdftk и pdfgrep . Как разработчик, вы испытываете огромное волнение, создавая свое собственное программное обеспечение, основанное на Python и использующее библиотеки PDF, которые находятся в свободном доступе.

Эта статья является началом небольшой серии и будет посвящена этим полезным библиотекам Python. В первой части мы сосредоточимся на манипуляциях с существующими PDF-файлами. Вы научитесь читать и извлекать содержимое (как текст, так и изображения), поворачивать отдельные страницы и разбивать документы на отдельные страницы. Вторая часть будет посвящена добавлению водяного знака на основе наложений. Третья часть будет посвящена исключительно написанию/созданию PDF-файлов, а также удалению и повторному объединению отдельных страниц в новый документ.

Инструменты и библиотеки

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

PyPDF2 Чтение
PyMuPDF Чтение
pdflib Чтение
Таблицы PDF Чтение
табула-пи Чтение
табула-пи Чтение
Запрос PDF Чтение
pdfrw Чтение, Письмо/Создание
Reportlab Написание/Создание
Пикс Написание/Создание
PyFPDF Написание/Создание

Ниже мы сосредоточимся на PyPDF2 и PyMuPDF и объясним, как извлечь текст и изображения самым простым способом. Для того чтобы понять использование PyPDF2, нам помогло сочетание официальной документации и множества примеров, доступных из других ресурсов. В отличие от этого, официальная документация PyMuPDF гораздо понятнее и значительно быстрее использует библиотеку.

Извлечение текста с помощью PyPDF2

PyPDF2 можно установить как обычный программный пакет, так и с помощью pip3 (для Python3). Тесты здесь основаны на пакете для предстоящего выпуска Debian GNU/Linux 10 “Buster”. Имя пакета Debian – python3-pypdf2 .

Листинг 1 сначала импортирует класс Pdf File Reader . Затем, используя этот класс, он открывает документ и извлекает информацию о документе с помощью метода getDocumentInfo () , количество страниц с помощью getDocumentInfo () и содержимое первой страницы.

Обратите внимание, что PyPDF2 начинает подсчет страниц с 0, и именно поэтому вызов pdf.getPage(0) извлекает первую страницу документа. В конце концов извлеченная информация печатается в stdout .

Листинг 1: Извлечение информации и содержимого документа.

#!/usr/bin/python

from PyPDF2 import PdfFileReader

pdf_document = "example.pdf"
with open(pdf_document, "rb") as filehandle:
    pdf = PdfFileReader(filehandle)
    info = pdf.getDocumentInfo()
    pages = pdf.getNumPages()

    print (info)
    print ("number of pages: %i" % pages)

    page1 = pdf.getPage(0)
    print(page1)
    print(page1.extractText())
Рис. 1: Извлеченный текст из PDF-файла с помощью PyPDF2

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

Извлечение текста с помощью PyMuPDF

PyMuPDF доступен с веб-сайта PyPI, и вы устанавливаете пакет со следующей командой в терминале:

$ pip3 install PyMuPDF

Отображение информации о документе, печать количества страниц и извлечение текста PDF-документа выполняются аналогично PyPDF2 (см. Листинг 2 ). Импортируемый модуль называется fitz и возвращается к предыдущему имени PyMuPDF.

Листинг 2: Извлечение содержимого из PDF-документа с помощью PyMuPDF.

#!/usr/bin/python

import fitz

pdf_document = "example.pdf"
doc = fitz.open(pdf_document):
print ("number of pages: %i" % doc.pageCount)
print(doc.metadata)

page1 = doc.loadPage(0)
page1text = page1.getText("text")
print(page1text)

Самое приятное в PyMuPDF то, что он сохраняет исходную структуру документа нетронутой – целые абзацы с разрывами строк сохраняются так же, как и в PDF-документе (см. Рис. 2 ).

Рис. 2: Извлеченные текстовые данные

Извлечение изображений из PDF-файлов с помощью PyMuPDF

PyMuPDF упрощает извлечение изображений из PDF-документов с помощью метода getPageImage List() . Листинг 3 основан на примере из вики-страницы PyMuPDF и извлекает и сохраняет все изображения из PDF-файлов в формате PNG постранично. Если изображение имеет цветовое пространство CMYK, то сначала оно будет преобразовано в RGB.

Листинг 3: Извлечение изображений.

#!/usr/bin/python

import fitz

pdf_document = fitz.open("file.pdf")
for current_page in range(len(pdf_document)):
    for image in pdf_document.getPageImageList(current_page):
        xref = image[0]
        pix = fitz.Pixmap(pdf_document, xref)
        if pix.n < 5:        # this is GRAY or RGB
            pix.writePNG("page%s-%s.png" % (current_page, xref))
        else:                # CMYK: convert to RGB first
            pix1 = fitz.Pixmap(fitz.csRGB, pix)
            pix1.writePNG("page%s-%s.png" % (current_page, xref))
            pix1 = None
        pix = None

Запустив этот скрипт Python на 400 страницах PDF, он извлек 117 изображений менее чем за 3 секунды, что удивительно. Отдельные изображения хранятся в формате PNG. Чтобы сохранить исходный формат и размер изображения, вместо преобразования в PNG, посмотрите расширенные версии скриптов в PyMuPDF wiki .

Рис. 3: Извлеченные изображения

Разделение PDF-файлов на страницы с помощью PyPDF2

В этом примере сначала необходимо импортировать классы Pdf File Reader и PdfFileWriter . Затем мы открываем PDF-файл, создаем объект reader и перебираем все страницы с помощью метода объекта reader getNumPages .

Внутри цикла for мы создаем новый экземпляр PdfFileWriter , который пока не содержит никаких страниц. Затем мы добавляем текущую страницу в наш объект writer с помощью метода pdf Writer.addPage () . Этот метод принимает объект page, который мы получаем с помощью метода Pdf File Reader.getPage () .

Следующим шагом является создание уникального имени файла, которое мы делаем, используя исходное имя файла плюс слово “страница”, плюс номер страницы. Мы добавляем 1 к текущему номеру страницы, потому что PyPDF2 подсчитывает номера страниц, начинающиеся с нуля.

Наконец, мы открываем новое имя файла в режиме “write binary” (mode wb ) и используем метод write() класса PdfWriter для сохранения извлеченной страницы на диск.

Листинг 4: Разбиение PDF-файла на отдельные страницы.

#!/usr/bin/python

from PyPDF2 import PdfFileReader, PdfFileWriter

pdf_document = "example.pdf"
pdf = PdfFileReader(pdf_document)

for page in range(pdf.getNumPages()):
    pdf_writer = PdfFileWriter
    current_page = pdf.getPage(page)
    pdf_writer.addPage(current_page)

    outputFilename = "example-page-{}.pdf".format(page + 1)
    with open(outputFilename, "wb") as out:
        pdf_writer.write(out)

        print("created", outputFilename)
Рис. 4: Разбиение PDF-файла

Найти Все Страницы, Содержащие Текст

Этот вариант использования довольно практичен и работает аналогично pdfgrep . Используя PyMuPDF, скрипт возвращает все номера страниц, содержащие заданную строку поиска. Страницы загружаются одна за другой, и с помощью метода search For() обнаруживаются все вхождения поисковой строки. В случае совпадения соответствующее сообщение печатается на stdout .

Листинг 5: Поиск заданного текста.

#!/usr/bin/python

import fitz

filename = "example.pdf"
search_term = "invoice"
pdf_document = fitz.open(filename):

for current_page in range(len(pdf_document)):
    page = pdf_document.loadPage(current_page)
    if page.searchFor(search_term):
        print("%s found on page %i" % (search_term, current_page))

На рисунке 5 ниже показан результат поиска термина “Debian GNU/Linux” в 400-страничной книге.

Рис. 5: Поиск PDF-документа

Вывод

Методы, показанные здесь, довольно мощные. При сравнительно небольшом количестве строк кода легко получить результат. Другие варианты использования рассматриваются во второй части (скоро!) это касается добавления водяного знака в PDF-файл.