Автор оригинала: 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 выше, извлеченный текст печатается на постоянной основе. Нет ни абзацев, ни разделительных предложений. Как указано в документации 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 ).
Извлечение изображений из 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 .
Разделение 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)
Найти Все Страницы, Содержащие Текст
Этот вариант использования довольно практичен и работает аналогично 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-страничной книге.
Вывод
Методы, показанные здесь, довольно мощные. При сравнительно небольшом количестве строк кода легко получить результат. Другие варианты использования рассматриваются во второй части (скоро!) это касается добавления водяного знака в PDF-файл.