Текст любой ценой: DOCX и ODT

    Недавно возникла задача получения чистого текста из различных форматов документооборота — будь-то документы Microsoft Word или PDF. Задача была выполнена даже с чуть более широким списком возможных входных данных. Итак, этой статьёй я открываю список публикаций о чтении текста из следующих типов файлов: DOC, DOCX, RTF, ODT и PDF — с помощью PHP без использования сторонних утилит.

    Для начала отвечу на вполне разумный вопрос: «Зачем это, собственно, надо?» Правильно, чистый текст, полученный из, к примеру, документа Word представляет собой достаточно перемешанную кашу. Но этого «бардака» вполне достаточно для построения, например, индекса для поиска по обширному хранилищу офисных документов.

    Другой вполне разумный вопрос: «Почему не использовать сторонние утилиты, например, antiword или xpdf, ну или в крайнем случае OLE под Windows?» Таковы уж были поставленные условия, да и OLE работает люто-бешено медленно, даже если задачу можно решить с помощью этой технологии.

    Сегодня, в качестве «затравки», я расскажу о достаточно простых для поставленной задачи форматах — это Office Open XML, больше известный как DOCX от Microsoft и OpenDocument Format, он же ODT от ODF Aliance.

    Для начала заглянем вовнутрь парочки файлов и увидим буквально следующее (сзади docx, спереди odt):



    Самое важное, что мы здесь видим, это первые два символа PK в начале данных. Это значит, что оба файла представляют собой переименованный в .docx/.odt zip-архив. Открываем, например, по Ctrl+PageDown в Total Commander и лицезреем вполне приемлемую структуру (слева odt, справа docx):



    Итак, нужные нам файлы с данными — это content.xml в ODT и word/document.xml в DOCX. Чтобы прочитать текстовые данные из них напишем несложный код:

    1. function odt2text($filename) {
    2.     return getTextFromZippedXML($filename, "content.xml");
    3. }
    4. function docx2text($filename) {
    5.     return getTextFromZippedXML($filename, "word/document.xml");
    6. }
    7. function getTextFromZippedXML($archiveFile, $contentFile) {
    8.     // Создаёт "реинкарнацию" zip-архива...
    9.     $zip = new ZipArchive;
    10.     // И пытаемся открыть переданный zip-файл
    11.     if ($zip->open($archiveFile)) {
    12.         // В случае успеха ищем в архиве файл с данными
    13.         if (($index = $zip->locateName($contentFile)) !== false) {
    14.             // Если находим, то читаем его в строку
    15.             $content = $zip->getFromIndex($index);
    16.             // Закрываем zip-архив, он нам больше не нужен
    17.             $zip->close();
    18.  
    19.             // После этого подгружаем все entity и по возможности include'ы других файлов
    20.             // Проглатываем ошибки и предупреждения
    21.             $xml = DOMDocument::loadXML($content, LIBXML_NOENT | LIBXML_XINCLUDE | LIBXML_NOERROR | LIBXML_NOWARNING);
    22.             // После чего возвращаем данные без XML-тегов форматирования
    23.  
    24.             return strip_tags($xml->saveXML());
    25.         }
    26.         $zip->close();
    27.     }
    28.     // Если что-то пошло не так, возвращаем пустую строку
    29.     return "";
    30. }
    Всего каких-то 30 строк, и мы получаем текстовые данные из файла. Код работает под PHP 5.2+ и требует php_zip.dll под Windows или ключика --enable-zip под Linux. При отсутствии возможности использования ZipArchive (старая версия PHP или отсутствие библиотек) вполне может сгодиться библиотека PclZip, реализующая чтение zip-файлов без соответствующих средств в системе.

    Отмечу, что данный код является лишь заготовкой для решения задач чтения текста. После череды статей под лозунгом «Текст любой ценой», я постараюсь описать принципы и реализацию чтения форматированного текста.

    По теме:В следующий раз я расскажу о чтении текста из PDF без помощи xpdf. Более сложной, но вполне посильной для PHP задачи.
    Поделиться публикацией

    Похожие публикации

    Комментарии 60
      0
      Интересно, как читать DOC средствами PHP, ведь, если я не ошибаюсь, он имеет двоичный формат, без архивов.
        +1
        DOC-файл — это своя файловая система внутри файла-контейнера, с файлами и каталогами; так что читать ее в принципе можно, нужно только научиться навигироваться по смещениям внутри файла.
          +13
          DOC будет на закуску, обещаю.
            0
            ждем. скоро уж? и ссылку в конце статьи полиз проставьте на следующие а в следующих на предыдущие пожалуйста
              0
              Пожалуйста.
              0
              если без OLE и внешних сервисов, то это просто мега полезно. скоро ли? =)
                0
                Без OLE, без antiword. Завершит цикл статей «Текст любой ценой». Следите за обновлениями :)
              0
              Верно. Для того же Total Commander есть соответствующий плагин.
                0
                Конечно, верно :) Мы писали свой контейнер, аналогичный doc, и читалку-писалку для него. А вообще в терминологии microsoft это все MultiStreamFile (MSF); но тут возникает путаница с multiple streams, что в NTFS
                0
                у меня тоже стояла задача вытянуть с doc файла текст, для этого воспользовался внешним консольным приложением catdoc
            • НЛО прилетело и опубликовало эту надпись здесь
                +2
                Спросите у гугла про Windows 7 wallpapers
                  0
                  Да, из стандартной поставки Seven.
                +1
                Это хорошо, часто сталкиваюсь с такой задачей, из крайне нужных остается DOC и PDF
                  0
                  PDF будет на днях. DOC на сладкое.
                    +1
                      –1
                      Не путайте создание PDF с его чтением.
                        +1
                        А почитать там коменты лениво очень ага? и поэтому минус ??? я же не буду вас носом тыкать что вот именно этот комент показываает как прочитать а не СОЗДАТЬ смотри тут все умнее и умнее народ и все ленивее и ленивее
                          +2
                          Я читал эти комментарии. Я знаю, что там есть способы чтения PDF. Но а) Вы дали ссылку на страницу, которая в первую очередь описывает функционал создания PDF; б) Вы не дали прямую ссылку на комментарий, коль хотели указать именно на тот или иной кусок кода для чтения PDF; в) статья не о PDF, а DOCX и ODT.

                          И не злитесь.
                            +1
                            Не кричите :)
                            а) один минус ничего не означает б) если кто-то не нашел то, что искал там, где это есть, то он ССЗБ; не более.
                            0
                            я был там и видел разные варианты, некоторыми даже пользовался, но меня в основном они не устроили, часть из них, например, использует сторонние программы, что мне не подходило.
                            Скажем так — я бы не отказался увидеть еще один способ
                              0
                              Ну да если это будет сделано нативными методами ПХП я бы сам не против почитать
                          0
                          Отличное объяснение «как оно», спасибо!

                          С нетерпением жду «сладкого».
                            +1
                            Я сожет и не прав, но вот что смущает. У вас return при успешном парсинге до закрытия архива. Вас это не смущает? Или я чего то не понимаю?
                              +1
                              Да, стоит всё же закрыть. Поправил, спасибо.
                              0
                              Спасибо за статью, буду следить дальше. На мне в качестве социальной нагрузки лежит администрирование одного научного ресурса. Максимум что можно выжать из товарищей профессоров это pdf или doc. Приходится тратить несколько часов что бы «спарсить» вручную их статью в текст, а потом разметить в html. Существующие решения не годятся. Они как правило делают ужаснейший код на выходе. Я уже не говорю про всякие графики, картинки и формулы которыми нагружен документ и которые приходится вырезать и вставлять в ручную. Поэтому идея написать свое решение под свои нужды, у меня в голове давно. Попробую использовать ваши статьи как катализатор для лени :)
                                +1
                                Мне лестно (в хорошем смысле) слышать Ваши слова. Благое дело делаете.
                                  +3
                                  А фильтрованный html из ворда? мусора все равно многовато, но уже приемлимо
                                    0
                                    Сохранить в ворде «как html страницу»? Получается хуже. Кроме очевидного мусора в html, который можно еще как то подчистить с помощью утилит, появляются «не очевидные» проблемы. Например иногда текст внутри файла бывает набран в разных кодировках. Часть например в 1251 а часть в utf-8. В редакторе вы этого не увидите, но когда сервер отдаст клиенту страницу в которой принудительно стоит кодировка UTF-8 то пользователь видит «кракозябы». Потом зачастую html форматирование при конвертирование получается не валидное. Приходится «переверстывать» в ручную. В результате способ «сохранить в текст» -> «разметить в html ручками» -> «сделать скриншоты формул и таблиц и добавить их ручками» хоть и выглядит на данном этапе сложным, но более правильный. Во всяком случае для дальнейшего развития такой подход боле правильный. Поскольку написать парсер для автоматизации этих действий будет намного проще, чем оптимизировать неправильный код статей в базе и отлавливать ошибки.
                                    0
                                    Если вы таки создадите решение для таких задач, его с руками оторвут разработчики CMS для сайтов. Т.к. вменяемой процедуры «вставить текст с картинками из Word на сайт» до сих пор не сделали. (SharePoint если только, но это я не знаю)
                                      0
                                      Тут скорее кто то другой сделает. Поскольку как я сказал у меня это социальная нагрузка, а мне еще семью кормить надо а работаю я совсем не программистом :) А в целом разрозненные решения уже есть. Например картинки из формул в формате LaTex умеет парсить Википедия, и такие скрипты есть на php в свободном доступе. Как получить текст скриптом на PHP написал автор в этой статье. Остается понять как конвертнуть формулы из вордовского формата в Latex. И как разметить текст в html. Причем тут будет самое сложное с разметкой. Поскольку стандарта на написание текста в ворде нету. Кто то ставит 5 пробелов, а кто то нажимает TAB :) Ну и прочие моменты.
                                      И кстати, как майкрософт отнесется к такой разработке? Там наверняка все перепантентовано и запатентовано. То есть написать то можно, но распространять будет нельзя :)
                                        0
                                        а как же публикация из ворда? — вставляет чистый (валидный) html, с рисунками, ссылками, графиками и т.п.
                                      0
                                      На сладкое лучше ppt, вот с ним действительно геморрой.
                                        +1
                                        Из PPT вытаскивать, что? Изображения? Тогда у нас несколько разные задачи — мне нужно получить текст. Если же вы про текст из презентации, то… надо подумать — поставлю в список TODO у себя в голове :)
                                        0
                                        Ждём продолжения. От себя скажу, что сталкивался с проблемой HTML 2 PDF, и как только ни пробовал её решать (здесь уже поднимались соотв. темы), но всё было не то, ибо сложные таблицы. Уже даже через Open Office пытались, да всё не то.
                                        Решили через xhtml2pdf
                                          +1
                                          Для Явы есть тулса Apache Tika, которая понимает много форматов (использует разные библиотеки для этого), причем извлекает не только текст, но и мета-данные (заголовок, автор и т.п.). Еще может автоматически распознавать формат.

                                          lucene.apache.org/tika/formats.html
                                            +2
                                            Читайте спецификации. Основной документ следует искать по relationships, а не по имени, иначе есть шанс напороться на то, что завтра он будет лежать в другом месте.

                                            А то в данном случае вы читаете не OOXML, а microsoft word 2007 document.
                                              0
                                              Я ждал этого комментария — что меня ткнут хотя бы в то, что ooxml может состоять из одного XML-файла, а не zipped xml. Это достойное уточнение, но данная статья даёт лишь повод для размышлений и не претендует на решение из коробки.

                                              Да, я в первую очередь читаю DOCX от Microsoft.
                                              +2
                                              Позволю себе процитировать классику жанра про офисные форматы: Почему форматы Microsoft Office такие сложные? (И как это обойти)
                                              Скорее в качестве иллюстрации, насколько тяжеловесна ваша задача и насколько ценен работающий результат.
                                                0
                                                На мой взгляд, не менее интересна задача генерации этих документов.
                                                  0
                                                  Zend_PDF, те же PDF_* функции из функционала PDFLib. Парсить сложнее, уж поверьте.
                                                    0
                                                    Может, вам пригодятся
                                                    TCPDF
                                                    и
                                                    DomPDF
                                                    0
                                                    Если вас устаривает решение под Windows:
                                                    Мы в одном проекте использовали встроенный в Windows Search механизм извлечения текста из разных типов файлов. Поищите в Google по названию интерфейса IFilter.
                                                    Если установлен MS Office Professional то выдергивает текст даже из TIFF файлов (используется встроенный механизм распознавания текста). В понедельник (на работе) могу попробовать найти пример реализации, если общественность попросит ;)
                                                      0
                                                      Идея хорошая, но изначально была поставлена задача получения текста нативным PHP без сторонних утилит и под любую ОСь.
                                                        0
                                                        Уважаемый, прошу Вас добавить хоть что-нибудь о «Текст любой ценой»!.
                                                        Пока речь лишь о компрессии.
                                                        Как насчёт docx xml-формате? Прошу Вас
                                                      0
                                                      zip://'. dirname(__FILE__). '/test.odt#meta.xml
                                                        +1
                                                        Упс: случайно отправил кимент :(
                                                        Как по мне функцию можна записать getTextFromZippedXML так
                                                        function getTextFromZippedXML($archiveFile, $contentFile) {
                                                            return strip_tags(file_get_contents('zip://'.realpath($archiveFile).'#'.$contentFile));
                                                        }
                                                        
                                                          0
                                                          Хм, решение красивое, про wrapper'ы я не подумал. Возможно, чуть менее удобное для последующего расширения для получения разметки документов.
                                                      0
                                                      Разрешите околотемный вопрос.
                                                      Есть ли готовая (опенсорсная) библиотека, которая поможет переделать html+css в pdf. Я сейчас делаю это с iTextSharp, но получается кривовато :).
                                                        0
                                                        есть вопрос (наперед) как вы парсите форматирование текста? средствами xslt? я просто одно время пол интернета перерыл в поисках xslt файла для преобразования вордовского xml в xhtml но так ничего и не нашел…
                                                          0
                                                          Я, кажется, видел решение odt2xhtml, он был на основе трансформаций XSLT. Да, точно, вот оно: www.phpclasses.org/browse/package/3071/. DOCX не искал, попадётся, напишу.
                                                          0
                                                          Товарищи, верители вы, что есть решение проще? И верители ли вы, что это простое решение может годится для форматов XML, RTF, PDF, MP3, FLASH, MS Word, MS Excel, MS PowerPoint? И поверите ли вы, что это решение может быть кроссплатформенное? ;))))
                                                            0
                                                            Просто поверите ли Вы в то, что это одно решение для всех этих форматов?
                                                              0
                                                              Извините, Вы о чём вообще сейчас говорите? Я не говорю, что будет одно решение на все форматы — каждый приходится разбирать по своему.
                                                              0
                                                              В общем решение получается такое:

                                                              Используем Yandex.Server.Enterprise 2009.05. Натравливаем его на каталог, в котором лежат нужные файлы и ждём окончания индексации. С помощью утилиты tarcview можно получить текстовое представление файла, которое и было проиндексировано этой программой. Только утилиту tarcview нужно запускать со следующими параметрами:

                                                              /usr/local/bin/tarcview -ew /*/*/workindex/index N
                                                              Где N — номер документа во внутренней коллекции Yandex.Server. Параметр -ew дополнительно ещё выдаст в запросе имя проиндексированного файла. Будет выдано немного текстовой лапши перед основным текстом, но и в таком виде результат просто СУПЕР!!!
                                                                0
                                                                Вы опять же путаете мух с котлетами — исходная задача «получить данные из текстового формата без использования каких-либо внешних средств средств». Читайте внимательней.
                                                                  0
                                                                  Да? А php не стороннее средство? Если по-вашему, я совсем не в тему, то приношу извинения. Подобного решения ведь, как у Вас, в общем-то никто, вроде, не предлагал, поэтому я и написал свой ответ сюда. Но ведь и PHP тоже не все разработчики жалуют, чтобы писать решение исключительно для него. Просто я хотел немного добавить и углубить ;), т.к. Вы же сами в начале написали

                                                                  «Правильно, чистый текст, полученный из, к примеру, документа Word представляет собой достаточно перемешанную кашу. Но этого «бардака» вполне достаточно для построения, например, индекса для поиска по обширному хранилищу офисных документов.»

                                                                  Индекс-то для чего нужен? Задача-то в конце концов состоит в том, чтобы найти нужный текст? Так? Данные в разных форматах типа Word, Excel, ODT некоторые разработчики хранят в базе данных. А сегодня нету индексатора не html/plain-текстовых форматов напрямую в базе данных, вот и приходится так выкручиваться, чтобы получать «шистый» текст, опять затолкать его в базу данных и там уже проиндексировать plain/html/text парсером. Вот ноги растут именно отсюда, т.к. для индексации по файлам использование программ на php вообще не нужно и с этой задачей лучше справится тот же Yandex.Server, а если немного покумекать, то и Google.Desktop можно на сервер прикрутить и сделать корпоративный поиск по файлообменнику, что собственно я уже давно сделал. И индексируется шустро и отдельно текстовую лапшу хранить не надо. Всё-таки если и выбирать по результату поиска между собственной системой и Yandex.Server, то я бы предпочёл Yandex.Server. Ведь всё равно и у Вас и у него будет лапша, а если нет разницы, зачем платить больше? (это был риторический вопрос ;)
                                                                0
                                                                Есть перевод документации Open Document для офиса (версия 1.0), кому любопытно:
                                                                www.i-rs.ru/content/download/1449/8170/file/OpenDocument-v1.0-os-ru.pdf

                                                                Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                                                                Самое читаемое