Чтение, извлечение и разделение страниц в PDF-файлах Python
Сегодня Portable Document Format (PDF) относится к наиболее часто используемым форматам данных. В 1990 году структура документа PDF была определена компанией Adobe. Идея формата PDF заключается в том, что передаваемые данные и документы выглядят одинаково для обеих сторон, участвующих в процессе коммуникации – создателя, автора или отправителя и получателя. PDF является преемником формата PostScript и стандартизирован как ISO 32000-2: 2017.
Обработка PDF-документов
Для Linux доступны мощные инструменты командной строки, такие как pdftk и pdfgrep. Как разработчик, с огромным энтузиазмом создает собственное программное обеспечение, основанное на Python и использующее библиотеки PDF, которые находятся в свободном доступе.
Эта статья – начало небольшой серии, в которой будут рассмотрены эти полезные библиотеки Python. В ней мы сосредоточимся на работе с существующими PDF-файлами. Вы узнаете, как читать и извлекать содержимое (как текст, так и изображения), вращать отдельные страницы и разбивать документы на отдельные страницы.
Инструменты и библиотеки
- PyPDF2: библиотека Python для извлечения информации и содержимого документа, разделения документов по страницам, объединения документов, обрезки страниц и добавления водяных знаков. PyPDF2 поддерживает как незашифрованные, так и зашифрованные документы.
- PDFMiner: полностью написан на Python и хорошо работает с Python 2.4. Для Python 3 используйте клонированный пакет PDFMiner.six. Оба пакета позволяют анализировать и конвертировать PDF-документы. Это включает поддержку PDF 1.7, а также языков CJK (китайский, японский и корейский) и различных типов шрифтов (Type1, TrueType, Type3 и CID).
- PDFQuery: он описывает себя как «быструю и удобную библиотеку для парсинга PDF», которая реализована как оболочка для PDFMiner, lxml и pyquery. Его цель дизайна – «надежно извлекать данные из наборов PDF-файлов с минимальным количеством кода».
- tabula-py: это простая оболочка Python для tabula-java, которая может читать таблицы из PDF-файлов и преобразовывать их в Pandas DataFrames. Он также позволяет конвертировать файл PDF в файл CSV/TSV/JSON.
- pdflib для Python: расширение библиотеки Poppler, которое предлагает для него привязки Python. Он позволяет анализировать и конвертировать PDF-документы. Не путать с его коммерческим кулоном с таким же названием.
- PyFPDF: библиотека для создания PDF-документов на Python. Портировано из библиотеки FPDF PHP, хорошо известного замены расширения PDFlib с множеством примеров, скриптов и производных.
- PDFTables: коммерческий сервис, предлагающий извлечение из таблиц в виде PDF-документа. Предлагает API, позволяющий использовать PDFTables, как SAAS.
- PyX – графический пакет Python: PyX – это пакет Python для создания файлов PostScript, PDF и SVG. Он сочетает в себе абстракцию модели рисования PostScript с интерфейсом TeX или LaTeX. На основе этих примитивов строятся сложные задачи, такие как создание 2D- и 3D-графиков в готовом для публикации качестве.
- ReportLab: амбициозная промышленная библиотека, в основном ориентированная на точное создание PDF-документов. Доступно бесплатно, как версия с открытым исходным кодом, а также как коммерческая расширенная версия под названием ReportLab PLUS.
- PyMuPDF (также известный как «fitz»): привязки Python для MuPDF, который представляет собой легкую программу просмотра PDF и XPS. Библиотека может получить доступ к файлам в форматах PDF, XPS, OpenXPS, epub, и она известна своей высочайшей производительностью и высоким качеством рендеринга.
- pdfrw: синтаксический анализатор PDF на чистом Python для чтения и записи PDF. Он точно воспроизводит векторные форматы без растеризации. В сочетании с ReportLab он помогает повторно использовать части существующих PDF-файлов в новых PDF-файлах, созданных с помощью ReportLab.
Ниже мы сосредоточимся на PyPDF2 и PyMuPDF и объясним, как извлекать текст и изображения наиболее простым способом. Чтобы понять использование PyPDF2, помогли сочетание официальной документации и множества примеров, доступных из других ресурсов. Напротив, официальная документация PyMuPDF намного яснее и значительно быстрее при использовании библиотеки.
Извлечение текста с помощью PyPDF2
PyPDF2 можно установить как обычный программный пакет или с помощью pip3 (для Python3). Тесты здесь основаны на пакете для предстоящего выпуска Debian GNU или Linux 10 «Buster». Имя пакета Debian – python3-pypdf2.
В примере 1 сначала импортируется класс PdfFileReader. Затем, используя этот класс, он открывает документ и извлекает информацию о документе с помощью метода getDocumentInfo(), количество страниц с помощью getDocumentInfo() и содержимое первой страницы.
Обратите внимание, что PyPDF2 начинает подсчет страниц с 0, и поэтому вызов pdf.getPage (0) возвращает первую страницу документа. В конце концов, извлеченная информация выводится на стандартный вывод.
#!/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, и вы устанавливаете пакет с помощью следующей команды в терминале:
Отображение информации о документе, печать количества страниц и извлечение текста из PDF-документа выполняется аналогично PyPDF2 (см. пример 2). Импортируемый модуль называется fitz и восходит к предыдущему имени 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 .
С помощью PyMuPDF
PyMuPDF упрощает извлечение изображений из документов PDF с помощью метода getPageImageList(). Пример 3 основан на примере вики-страницы PyMuPDF и извлекает и сохраняет все изображения из PDF-файла в виде файлов PNG на постраничной основе. Если изображение имеет цветовое пространство CMYK, оно сначала будет преобразовано в RGB.
#!/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.
Разделение PDF-файлов на страницы с помощью PyPDF2
В этом примере сначала необходимо импортировать классы PdfFileReader и PdfFileWriter. Затем мы открываем PDF-файл, создаем объект-читатель и просматриваем все страницы в цикле, используя метод getNumPages объекта-читателя.
Внутри цикла for мы создаем новый экземпляр PdfFileWriter, который пока не содержит никаких страниц. Затем мы добавляем текущую страницу к нашему объекту записи, используя метод pdfWriter.addPage(). Этот метод принимает объект страницы, который мы получаем с помощью метода PdfFileReader.getPage().
Следующим шагом является создание уникального имени файла, что мы делаем, используя исходное имя файла плюс слово «страница» плюс номер страницы. Мы добавляем 1 к текущему номеру страницы, потому что PyPDF2 считает номера страниц, начиная с нуля.
Наконец, мы открываем новое имя файла в режиме «двоичной записи» (режим wb) и используем метод write() класса pdfWriter для сохранения извлеченной страницы на диск.
#!/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, скрипт возвращает все номера страниц, которые содержат заданную строку поиска. Страницы загружаются одна за другой, и с помощью метода searchFor() обнаруживаются все вхождения строки поиска. В случае совпадения соответствующее сообщение выводится на стандартный вывод.
#!/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-страничной книге.
Заключение
Показанные здесь методы довольно мощные. Благодаря сравнительно небольшому количеству строк кода легко получить результат.