Доброе время суток.
Хочу поделиться своим опытом написания скрипта на PHP для поиска по файлам форматов PDF, DOC и DOCX, используя Sphinx. Все ниже изложенное написано для людей, которые уже имели опыт работы с связкой Sphinx и PHP. Если нет, то в свое время мне очень помогла статья Мартина Стрейчера «Создание собственной поисковой системы с помощью PHP», опубликованная на сайте IBM.
Совсем недавно на одном из проектов, который является веб-интерфейсом для базы пользователей, я получил задание организовать поиск по документам. Проект был написан до меня и уже использовал Sphinx.
Задача вроде тривиальная, но погуглив немного, я не смог найти внятную инструкцию с конкретным примером поиска по файлам форматов PDF, DOC и DOCX, что и стало причиной написания этой статьи.
Sphinx может обрабатывать текстовые файлы только формата XML. Для обработки данных в этом формате используется драйвер xmlpipe или xmlpipe2. То есть, для индексации информации в файлах формата PDF, DOC и DOCX, ее нужно отдать sphinx-у в формате XML.
Для этого надо написать скрипт, который будет ходить файлам, считывать данные и отдавать их в нужном формате.
Ниже приведу пример конфигурации и кода, который поможет нам с индексацией.
В конфигурационном файле sphinx.conf указываем следующее:
Содержимое файла xml-parser.php, который генерирует XML для Sphinx-a:
Приведенный выше код является модифицированной версией скрипта с официального сайта Sphinx-a.
Для считывания текста из PDF файла используется утилита pdftotext. Для DOC файлов — catdoc. Для считывания текста из DOCX файлов используется Zend. Про Phar можно прочесть тут. Официальная документация по Sphinx, а так же пример правильного XML для индексера.
Сверху представлен скрипт, который считывает данные из файлов и отдает их в XML формате для индексации.
Теперь при поиске Sphinx будет выдавать id папки в котором найдено совпадение. Можно еще хранить имя файла. rednaxi в своем материале «Создание ознакомительного поискового движка на Sphinx + php» более подробно описал процесс работы с Sphinx и PHP.
Хочу поделиться своим опытом написания скрипта на PHP для поиска по файлам форматов PDF, DOC и DOCX, используя Sphinx. Все ниже изложенное написано для людей, которые уже имели опыт работы с связкой Sphinx и PHP. Если нет, то в свое время мне очень помогла статья Мартина Стрейчера «Создание собственной поисковой системы с помощью PHP», опубликованная на сайте IBM.
Совсем недавно на одном из проектов, который является веб-интерфейсом для базы пользователей, я получил задание организовать поиск по документам. Проект был написан до меня и уже использовал Sphinx.
Задача вроде тривиальная, но погуглив немного, я не смог найти внятную инструкцию с конкретным примером поиска по файлам форматов PDF, DOC и DOCX, что и стало причиной написания этой статьи.
Sphinx может обрабатывать текстовые файлы только формата XML. Для обработки данных в этом формате используется драйвер xmlpipe или xmlpipe2. То есть, для индексации информации в файлах формата PDF, DOC и DOCX, ее нужно отдать sphinx-у в формате XML.
Для этого надо написать скрипт, который будет ходить файлам, считывать данные и отдавать их в нужном формате.
Ниже приведу пример конфигурации и кода, который поможет нам с индексацией.
В конфигурационном файле sphinx.conf указываем следующее:
# Определяем источник данных source SourceParseXml { # Используем xmlpipe2, как источник данных type = xmlpipe2 # Команда, которые должна быть выполнены, чтобы получить XML. Файл xml-parser.php генерирует XML xmlpipe_command = /path/to/php /path/to/xml-parser.php } # Определяем индекс index IndexParseXml { # Указываем, какой источник использовать source = SourceParseXml # Указываем путь где хранить индекс path = /path/where/to/store/index-data # Минимальная длина слова для индексации min_word_len = 1 # Указываем кодировку charset_type = utf-8 } # Определяем индексер indexer { # Максимальный объем используемой памяти для индексации mem_limit = 32M }
Содержимое файла xml-parser.php, который генерирует XML для Sphinx-a:
$allowedMimes = array( 'application/pdf', 'application/zip', // docx 'application/vnd.ms-office', // doc созданный с помощью OpenOffice 'application/msword' ); $allowedExtentions = array('pdf', 'docx', 'doc'); $pdfInfoPath = '/usr/bin/pdfinfo'; // полный путь к pdfinfo $pdfToTextPath = '/usr/bin/pdftotext'; // полный путь к pdftotext $catDocPath = '/usr/bin/catdoc'; // полный путь к catdoc // загружаем библиотеку Zеnd-а для считывания файлов формата docx define('FILE_PATH', realpath(dirname(__FILE__)) . DIRECTORY_SEPARATOR); if (strpos(get_include_path(), 'Zend.phar.gz') === false) { ini_set('include_path', ini_get('include_path') . ':phar\://' . FILE_PATH . 'Zend.phar.gz'); } $phar = new Phar(FILE_PATH . 'Zend.phar.gz', 0, 'Zend.phar.gz'); if (isset($phar['Zend/Search/Lucene/Document/Docx.php'])) { require_once($phar['Zend/Search/Lucene/Document/Docx.php']); } else { echo 'ERROR: can\'t load "Zend/Search/Lucene/Document/Docx.php" !' . PHP_EOL; die; } // Начинаем формирование XML $xmlWriter = new xmlWriter(); $xmlWriter->openMemory(); $xmlWriter->setIndent(true); $xmlWriter->startDocument('1.0', 'UTF-8'); $xmlWriter->startElement('sphinx:docset'); $xmlWriter->startElement('sphinx:schema'); $xmlWriter->startElement('sphinx:field'); $xmlWriter->writeAttribute('name', 'content'); $xmlWriter->endElement(); // field $xmlWriter->endElement(); // schema /* Предположим что файлы лежат в папке files, в которой находятся другие папки с названиями, которые соответсвуют id пользователя. Например: files/01/file.pdf */ // Запускаем цикл по папке files foreach (new DirectoryIterator(dirname(__FILE__) . '/files') as $folder) { // Проверяем, является ли выбраные обьект папкой if (!$folder->isDir() || $folder->isDot()) { continue; } // Запускаем цикл по папке в которой теоретически должны находится файлы foreach (new DirectoryIterator($folder->getPathname()) as $file) { // Проверяем выбранный объект является ли файлом разрешенного типа if ($file->isDir() || !in_array(strtolower(pathinfo($file, PATHINFO_EXTENSION)), $allowedExtentions) || !in_array(mime_content_type($file->getPathname()), $allowedMimes)) { continue; } $text = ''; $filePath = $file->getPathname(); $filePathEscape = escapeshellarg($filePath); try { switch (mime_content_type($filePath)) { case 'application/pdf': { $pdfInfo = array(); $key = ''; $val = ''; // Считываем информацию о выбранном PDF файле foreach (explode("\n", shell_exec(escapeshellcmd($pdfInfoPath . ' ' . $filePathEscape))) as $str) { list($key, $val) = count(explode(':', $str)) == 2 ? explode(':', $str) : array('', ''); if (trim($key) && trim($val)) { $pdfInfo[trim($key)] = trim($val); } } // Проверяем на ошибки if (empty($pdfInfo) || (isset($pdfInfo['Error']) && $pdfInfo['Error'])) { continue; } // С помощью pdftotext считываем содержимое файла $text = shell_exec(escapeshellcmd($pdfToTextPath . ' -nopgbrk ' . $filePathEscape . ' -')); break; } case 'application/zip' : { $file = Zend_Search_Lucene_Document_Docx::loadDocxFile($filePath); // С помощью Zend_Search_Lucene_Document_Docx считываем содержимое файла $text = $file->getFieldValue('body'); break; } case ('application/vnd.ms-office' || 'application/msword'): { // С помощью catdoc считываем содержимое файла $text = shell_exec(escapeshellcmd($catDocPath . ' ' . $filePathEscape)); break; } } if (empty($text)) { continue; } $text = strip_tags($text); $givenEncode = mb_detect_encoding($text); // Текст должен быть в кодировке UTF-8 $text = $givenEncode ? iconv($givenEncode, 'UTF-8', $text) : mb_convert_encoding($text, 'UTF-8'); } catch (Exception $e) { echo $e->getMessage() . PHP_EOL; continue; } $xmlWriter->startElement('sphinx:document'); // $folder->getBasename() - вернет название папки в которой хранятся наши файлы. // Название папки является идентификатором пользователя $xmlWriter->writeAttribute('id', $folder->getBasename()); $xmlWriter->startElement('content'); $xmlWriter->writeCData($text); $xmlWriter->endElement(); // content $xmlWriter->endElement(); // field } } $xmlWriter->endElement(); $xml = $xmlWriter->outputMemory(); $tidy = tidy_repair_string($xml, array( 'output-xml' => true, 'input-xml' => true ), 'utf8'); echo $tidy;
Приведенный выше код является модифицированной версией скрипта с официального сайта Sphinx-a.
Для считывания текста из PDF файла используется утилита pdftotext. Для DOC файлов — catdoc. Для считывания текста из DOCX файлов используется Zend. Про Phar можно прочесть тут. Официальная документация по Sphinx, а так же пример правильного XML для индексера.
Сверху представлен скрипт, который считывает данные из файлов и отдает их в XML формате для индексации.
Теперь при поиске Sphinx будет выдавать id папки в котором найдено совпадение. Можно еще хранить имя файла. rednaxi в своем материале «Создание ознакомительного поискового движка на Sphinx + php» более подробно описал процесс работы с Sphinx и PHP.
