Личный опыт парсинга pdf с помощью PHP
Задача извлечения данных из pdf-документа с помощью PHP является довольно распространенной и решается с помощью готовых библиотек. Однако, в связи с тем, что формат такой, не самой однозначный так скажем, могут возникнуть некоторые сложности. В статье ниже я расскажу о процессе парсинга и решениях подробнее.
Задача
Необходимо из полисов pdf страховых компаний извлекать типовую информацию, например, номер полиса ОСАГО. Список компаний
- Абсолют Страхование
- АльфаСтрахование
- Астро-Волга
- БОРОВИЦКОЕ
- АО ВСК
- ЕВРОИНС
- Ингосстрах
- Макс
- Ренессанс
- Ресо Гарантия
- РГС
- Сбербанк
- Совкомбанк
- СОГАЗ
- Согласие
- Тинькоф
- ЭНЕРГОГАРАН
- Югория
Библиотека Smalot
Подключать к проекту пришлось по старинке, многими инклудами, у меня получилась вот такая цепочка:
require_once('PdfParser/Config.php'); require_once('PdfParser/RawData/FilterHelper.php'); require_once('PdfParser/RawData/RawDataParser.php'); require_once('PdfParser/Encoding/PDFDocEncoding.php'); require_once('PdfParser/Document.php'); require_once('PdfParser/Header.php'); require_once('PdfParser/Element.php'); require_once('PdfParser/Element/ElementNumeric.php'); require_once('PdfParser/Element/ElementArray.php'); require_once('PdfParser/Element/ElementMissing.php'); require_once('PdfParser/Element/ElementXRef.php'); require_once('PdfParser/Element/ElementName.php'); require_once('PdfParser/Element/ElementBoolean.php'); require_once('PdfParser/Element/ElementString.php'); require_once('PdfParser/Element/ElementDate.php'); require_once('PdfParser/Element/ElementHexa.php'); require_once('PdfParser/Element/ElementStruct.php'); require_once('PdfParser/Element/ElementNull.php'); require_once('PdfParser/PDFObject.php'); require_once('PdfParser/Page.php'); require_once('PdfParser/XObject/Image.php'); require_once('PdfParser/XObject/Form.php'); require_once('PdfParser/Encoding.php'); require_once('PdfParser/Pages.php'); require_once('PdfParser/Font.php'); require_once('PdfParser/Parser.php');Сам запуск тоже не совсем обычный, пришлось использовать магическую константу __FILE__
require_once dirname(dirname(__FILE__))."/include/Smalot/all-pdf.php"; $parser = new \Smalot\PdfParser\Parser(); $pdf = $parser->parseFile('Ренесанс полис.pdf');Все прекрасно работает, читает большинство файлов. Однако, появились и сложности.
Кодировка Smalot
У некоторые файлов Smalot не мог определить кодировку. Точнее, там была UTF-8, но вместо символов русского языка парсер выдавал крякозябры. Так получилось с полисами от АльфаБанка.
Манипуляции с текстом, который получался после парсера (функции mb_convert_encoding() и iconv()) ничего не дали, пришлось залезть в исходный код Smalot и поискать там. В файле PdfParser/Font.php в методе decodeContentByEncoding(string $text) я заменил
if (!mb_check_encoding($text, 'UTF-8')) { return mb_convert_encoding($text, 'UTF-8', 'Windows-1252'); }На
if (!mb_check_encoding($text, 'UTF-8')) { $text = iconv('windows-1251', 'UTF-8', $text); }После этого полис Альфа Страхования стал парситься нормально.
Другие проблемы Smalot
Однако, дальше с некоторыми другими типами полисов также Smalot не смог справиться:
- Согласие – защищенный pdf
- Росгосстрах – вообще ничего не выводит
- Сбербанк и Иногсстрах – проблемы с кодировкой
- Макс – вообще непонятная ошибка
Библиотека Poppler
Целый комбайн. Легко ставится и прост в использовании. Попробовал у себя на сервере – читает и ингосстрах и согласие, все норм, можно работать. Возникло затруднение, состоящее в том, что у заказчика хостинг шаред и ставить туда Poppler проблематично. Пришлось набросать подобие апи к своему серверу. Отправка pdf и прием ответа выглядят так:
$ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $target_url); curl_setopt($ch, CURLOPT_POST, 1); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); curl_setopt($ch, CURLOPT_HEADER, true); curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: application/pdf')); $file = new CURLFile($file_name_with_full_path,'application/pdf','MyFile'); curl_setopt($ch, CURLOPT_POSTFIELDS, ['pdf' => $file]); $result = curl_exec($ch); curl_close ($ch);Само типа апи на PHP
copy("php://input", "1.pdf"); sleep(2); $output = shell_exec('pdftotext 1.pdf 1.txt'); sleep(2); echo file_get_contents("1.txt"); sleep(2); unlink("1.pdf"); unlink("1.txt"); die();Изо всех полисов кроме Макс удалось таким образом извлечь текст; Макс не хотел никак отдавать свою информацию. После нескольких проб было решено не тратить времени на него, в принципе мы уже обрабатывали таким образом больше 99 процентов поступавших полисов.
Но задача интересная, если будет время, то обязательно расковыряю Макс и напишу об этом.
Автор этого материала - я - Пахолков Юрий. Я оказываю услуги по написанию программ на языках Java, C++, C# (а также консультирую по ним) и созданию сайтов. Работаю с сайтами на CMS OpenCart, WordPress, ModX и самописными. Кроме этого, работаю напрямую с JavaScript, PHP, CSS, HTML - то есть могу доработать ваш сайт или помочь с веб-программированием. Пишите сюда.
Отправляя сообщение я подтверждаю, что ознакомлен и согласен с политикой конфиденциальности данного сайта.