Автор оригинала: 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-файл.