- PVSM.RU - https://www.pvsm.ru -
Во время учебной сессии (май-июнь и декабрь-январь) пользователи просят нас проверить на наличие заимствований до 500 документов каждую минуту. Документы приходят в файлах различных форматов, сложность работы с каждым из которых различна. Для проверки документа на заимствования нам сперва необходимо извлечь из файла его текст, а заодно и разобраться с форматированием. Задача — реализовать качественное извлечение полутысячи текстов с форматированием в минуту, при этом падать нечасто (а лучше не падать совсем), потреблять мало ресурсов и не платить за разработку и эксплуатацию конечного детища половину галактического бюджета.
Да-да, мы, конечно, знаем, что из трех вещей — быстро, дешево и качественно — нужно выбрать любые две. Но самое противное, что в нашем случае мы ничего не можем вычеркнуть. Вопрос в том, как хорошо у нас это получилось...
Источник изображения: Википедия [1]
Нам часто говорят, что от качества нашей работы зависят судьбы людей. Поэтому приходится воспитывать в себе перфекционистов. Конечно, мы постоянно повышаем качество работы системы (во всех аспектах), так как недобросовестные авторы придумывают все новые пути обходов. И, надеюсь, что близок тот день, когда сложность обмана, с одной стороны, и чувство удовлетворения от качественно выполненной работы, с другой, побудят абсолютное большинство студентов отказаться от столь любимого желания схалтурить. При этом мы понимаем, что ценой ошибки могут быть возможные страдания невинных людей, если вдруг схалтурим мы.
К чему это я? Если бы мы были перфекционистами, то вдумчиво подошли бы к написанию цикла статей о работе системы «Антиплагиат» [2]. Мы бы кропотливо сформировали план публикаций, чтобы изложить все наиболее логичным и ожидаемым для читателя образом:
Внимательный читатель наверняка заметил, что избыточным перфекционизмом мы все-таки не страдаем, поэтому пришло время перейти к рассмотрению первого этапа — извлечения текста и форматирования документов. Этим мы сегодня и займемся, по пути размышляя о бренности бытия и о свете в конце тоннеля, о несуществовании ничего идеального и о стремлении к совершенству, о наличии плана и следовании ему и о компромиссах, к которым нас всегда склоняет жизнь.
Сперва мы извлекали из документов лишь самое необходимое для проверки их на заимствования — сам текст документов. Поддерживались основные форматы — docx, doc, txt, pdf, rtf, html. Затем добавлялись менее распространённые ppt, pptx, odt, epub, fb2, djvu, правда, от работы с большинством из них проприетарность [9] последнего делает работу с ним еще более проблемной). Однако, уже на данном этапе, когда наши желания ограничивались лишь извлечением текста, стало ясно, что любой способ чтения нужных нам форматов несет с собой ряд неприятных особенностей. Самые существенные из них:
Источник нижнего изображения: Статья [12]
Источник верхнего изображения: Хм... [13]
Если для анализа документа на заимствования нам было достаточно текстовой подложки документа, то реализация целого ряда новых возможностей невозможна либо очень затруднительна без извлечения дополнительных данных из документа. Сегодня, помимо текстовой подложки, мы также извлекаем форматирование документа и рендерим [14] изображения страниц. Последние мы используем для оптического распознавания текста (OCR [15]), а также для определения некоторых разновидностей обходов.
Форматирование документа включает геометрическое расположение всех слов и символов на страницах, а также размер шрифта всех символов. Данная информация позволяет нам:
Для унификации процесса обработки документов и набора извлекаемых данных мы конвертируем документы всех поддерживаемых нами форматов в pdf. Таким образом, процедура извлечения данных документа производится в два этапа:
Поскольку нельзя так просто взять и сконвертировать документ в pdf, мы решили не изобретать велосипед и исследовать готовые решения, выбрав наиболее подходящее нам. Дело было в далеком 2017 году.
Критерии отбора кандидатов:
Мы проанализировали доступные решения, отобрав среди них 6 наиболее подходящих под наши задачи:
Библиотека | Проблемы на поверхности |
---|---|
MS Word. Interop | Требует: MS Word. Вызов Microsoft Word через COM. Метод имеет много минусов: необходимость установленного MS Word, неустойчивость, низкая производительность. Есть лицензионные ограничения. |
DevExpress (17) [18] | Не поддерживает ppt, pptx |
GroupDocs [19] | — |
Syncfusion [20] | Не поддерживает ppt, pptx, odt |
Neevia Document Converter Pro [21] | Требует: MS Word. Не поддерживает odt |
DynamicPdf [22] | Требует: MS Word, Internet Explorer. Не поддерживает odt |
MS Word Interop, Neevia Document Converter Pro и DynamicPdf требуют установки MS Office на продакшне, что могло бы окончательно и бесповоротно привязать нас к Windows. Поэтому эти варианты мы больше не рассматривали.
Таким образом, у нас осталось три основных кандидата, причем лишь один из них полностью поддерживает все необходимые нам форматы. Что ж, самое время посмотреть, на что они способны.
Для тестирования библиотек мы сформировали выборку из 120 тысяч реальных пользовательских документов, соотношение форматов в которой примерно соответствует тому, что мы видим каждый день на продакшене.
Итак, первый раунд. Посмотрим, какую долю документов смогут успешно сконвертировать в pdf рассматриваемые библиотеки. Успешно, в нашем случае, — это не кинуть исключение, уложиться в 3-х минутный таймаут и вернуть непустой текст.
Конвертер | Успешно (%) | Не успешно (%) | ||||
---|---|---|---|---|---|---|
Всего | Пустой текст | Exception | Timeout (3 минуты) | Падение процесса | ||
GroupDocs | 99.012 | 0.988 | 0.039 | 0.873 | 0.076 | 0 |
DevExpress | 99.819 | 0.181 | 0.123 | 0.019 | 0.039 | 0 |
Syncfusion | 98.358 | 1.632 | 0.039 | 0.848 | 0.745 | 0.01 |
Сразу выделился Syncfusion, который не только смог успешно обработать наименьшее количество документов, но и на некоторых документах свалил весь процесс (сгенерировав неотлавливаемые без танцев с бубном исключения типа OutOfMemoryException или исключений из нативного кода).
GroupDocs'у не удалось обработать примерно в 5.5 раз больше документов, чем DevExpress'у (все видно на табличке сверху). Это при том, что лицензия на одного разработчика у GroupDocs стоит примерно в 9 раз дороже лицензии на одного разработчика у DevExpress. Это так, к слову.
Второе серьезное испытание — время конвертации, те же самые 120 тысяч документов:
Конвертер | Mean (сек.) | Median (cек.) | Std (сек.) |
---|---|---|---|
GroupDocs | 1.301966 | 0.328000 | 6.401197 |
DevExpress | 0.523453 | 0.252000 | 1.781898 |
Syncfusion | 8.922892 | 4.987000 | 12.929588 |
Заметим, что DevExpress не только значительно быстрее обрабатывает документы в среднем, но и показывает значительно более стабильное время обработки.
Но стабильность и скорость обработки ничего не значат, если на выходе получается плохая pdf'ка. Может, DevExpress пропускает половину текста? Проверяем. Итак, те же 120 тыс документов, на этот раз посчитаем общий объем извлеченного текста и среднюю долю словарных слов (чем больше извлеченных слов являются словарными, тем меньше мусора/некорректно извлеченного текста):
Конвертер | Общий объём текста (в символах) | Средняя доля словарных слов |
---|---|---|
GroupDocs | 6 321 145 966 | 0.949172 |
DevExpress | 6 135 668 416 | 0.950629 |
Syncfusion | 5 995 008 572 | 0.938693 |
Отчасти предположение оказалось верным. Как выяснилось, GroupDocs, в отличие от DevExpress, умеет работать со сносками. DevExpress же их просто пропускает при конвертации документа в pdf. Кстати да, текст из получаемых pdf'ок во всех случаях извлекаем посредством DevExpress'а.
Итак, мы изучили скорость и стабильность рассматриваемых библиотек, теперь тщательно оценим качество конвертации документов pdf. Для этого мы проанализируем не просто объем извлекаемого текста и долю словарных слов в нем, а сравним извлекаемые из полученных pdf'ок тексты с текстами pdf'ок, полученных посредством MS Word. Принимаем результат конвертации документа посредством MS Word за эталонную pdf'ку. Для данного теста было подготовлено около 4500 пар «документ, эталонная pdf'ка».
Конвертер | Текст извлекся (%) | Близость по длине текста | Близость по частоте встречаемости слов | ||||
---|---|---|---|---|---|---|---|
Среднее | Медиана | СКО [23] | Среднее | Медиана | СКО [23] | ||
GroupDocs | 99.131 | 0.985472 | 0.999756 | 0.095304 | 0.979952 | 1.000000 | 0.102316 |
DevExpress | 99.726 | 0.971326 | 0.996647 | 0.075951 | 0.965686 | 0.996101 | 0.082192 |
Syncfusion | 89.336 | 0.880229 | 0.996845 | 0.306920 | 0.815760 | 0.998206 | 0.348621 |
Для каждой пары «эталонная pdf'ка, результат конвертации» мы вычислили схожесть по длине извлеченного текста и по частотам извлеченных слов. Естественно, данные метрики были получены только в тех случаях, когда конвертация была произведена успешно. Поэтому результаты Syncfusion мы здесь не рассматриваем. DevExpress и GroupDocs показали примерно одинаковые показатели. На стороне DevExpress'а — значительно больший процент успешной конвертации, на стороне GD — корректная работа со сносками.
Учитывая полученные результаты, выбор был очевиден. Мы по сей день используем решение от DevExpress и в скором времени планируем обновиться уже до 19-й его версии.
Итак, мы умеем конвертировать документы в pdf. Теперь перед нами стоит другая задача: с помощью DevExpress'а извлечь текст, зная о каждом слове всю необходимую нам информацию. А именно:
На изображении представлено разбиение текста по страницам, а также проиллюстрировано соответствие слова текста области страницы.
Источник изображения: Header Metadata Extraction from Scientific Documents [24]
Казалось бы, все должно быть просто. Смотрим, какой API нам предоставляет DevExpress:
Окей, вроде как все необходимое есть. Только вот как получить необходимые данные по каждому слову в тексте документа, который возвращает DevExpress? Самим собирать текст документа из слов не очень хочется, так как, например, у нас нет информации, где между словами просто пробел, а где перевод строки. Придется придумывать эвристики на основе местоположения слов… Текст же — вот он, перед нами, уже собранный.
Источник изображения: Эврика! [26]
Очевидное решение — сопоставлять слова с текстом документа. Смотрим — действительно, в тексте документа слова расположены в том же порядке, в котором их возвращает итератор по словам документа.
Быстренько реализуем простой алгоритм сопоставления слов с текстом документа, добавляем проверки на то, что все корректно сопоставилось, запускаем...
Действительно, на подавляющем большинстве страниц все корректно работает, но, к сожалению, не на всех страницах.
Источник верхнего изображения: Are you sure? [27]
На части документов мы видим, что слова в тексте расположены не в том порядке, в котором они идут при итерации по словам документа. Более того, видно, что открывающая квадратная скобка в тексте в списке слов представлена как закрывающая скобка и находится в другом «слове». Корректное отображение данного фрагмента текста можно увидеть, открыв документ в MS Word. Что еще более интересно, если документ не конвертировать в pdf, а напрямую извлечь текст из doc'а, то мы получаем третий вариант фрагмента текста, не совпадающий ни с правильным порядком, ни с двумя другими порядками, получаемыми от библиотеки. В данном фрагменте, как и в большинстве остальных, на которых возникает подобная проблема, дело в невидимых «RTL» символах, меняющих порядок следования рядом стоящих символов/слов.
Тут стоит вспомнить о том, что немаловажным при выборе библиотеки мы называли качество техподдержки. Как показала практика, в этом аспекте взаимодействие с DevExpress достаточно эффективно. Проблема с представленным документом была оперативно исправлена после создания нами соответствующего тикета. Также был исправлен ряд других проблем, связанных с исключениями/большим потреблением оперативной памяти/долгой обработкой документов.
Однако, пока DevExpress не предоставляет прямого способа получения текста с нужной информацией по каждому слову, мы продолжаем сопоставлять порой несопоставимое. Если не можем построить точное соответствие слов с текстом, применяем ряд эвристик, допускающих небольшие перестановки слов. Если же ничего не помогло — документ у нас остается без форматирования. Редко, но такое случается.
Пока :)
Автор: Aram
Источник [28]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/c-2/322996
Ссылки в тексте:
[1] Википедия: https://commons.wikimedia.org/wiki/File:P._Oxy._I_29.jpg
[2] системы «Антиплагиат»: https://www.antiplagiat.ru/
[3] пятая публикация: https://habr.com/ru/company/antiplagiat/blog/429634/
[4] в нескольких наших статьях: https://habr.com/ru/company/antiplagiat/
[5] первая статья: https://habr.com/ru/company/antiplagiat/blog/354142/
[6] четвертая: https://habr.com/ru/company/antiplagiat/blog/422941/
[7] вторая: https://habr.com/ru/company/antiplagiat/blog/413361/
[8] седьмая статья: https://habr.com/ru/company/antiplagiat/blog/445952/
[9] проприетарность: https://ru.wikipedia.org/wiki/%D0%9F%D1%80%D0%BE%D0%BF%D1%80%D0%B8%D0%B5%D1%82%D0%B0%D1%80%D0%BD%D0%BE%D0%B5_%D0%BF%D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D0%BD%D0%BE%D0%B5_%D0%BE%D0%B1%D0%B5%D1%81%D0%BF%D0%B5%D1%87%D0%B5%D0%BD%D0%B8%D0%B5
[10] нативный код: https://ru.wikipedia.org/wiki/%D0%9C%D0%B0%D1%88%D0%B8%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BA%D0%BE%D0%B4
[11] формат pdf: https://ru.wikipedia.org/wiki/Portable_Document_Format
[12] Статья: http://old2.adygnet.ru/sites/default/files/%203%20-%204-5_2013.pdf
[13] Хм...: https://imgur.com/gallery/yopBh
[14] рендерим: https://ru.wikipedia.org/wiki/%D0%A0%D0%B5%D0%BD%D0%B4%D0%B5%D1%80%D0%B8%D0%BD%D0%B3
[15] OCR: http://www.unkniga.ru/innovation/tehnology/8017-raspoznavanie-izobrazheniy-na-sluzhbe-u-antiplagiata.html
[16] библиография: https://habr.com/ru/company/antiplagiat/blog/449124/
[17] Обнаруживать попытки обхода системы.: http://www.unkniga.ru/innovation/tehnology/9514-kto-ne-spryatalsya-ya-ne-vinovat.html
[18] DevExpress (17): https://documentation.devexpress.com/OfficeFileAPI/14911/Office-File-API
[19] GroupDocs: https://products.groupdocs.com/conversion/net
[20] Syncfusion: https://www.syncfusion.com/products/file-formats/docio
[21] Neevia Document Converter Pro: https://neevia.com/products/dcpro/
[22] DynamicPdf: https://www.dynamicpdf.com/
[23] СКО: https://ru.wikipedia.org/wiki/%D0%A1%D1%80%D0%B5%D0%B4%D0%BD%D0%B5%D0%BA%D0%B2%D0%B0%D0%B4%D1%80%D0%B0%D1%82%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%BE%D0%B5_%D0%BE%D1%82%D0%BA%D0%BB%D0%BE%D0%BD%D0%B5%D0%BD%D0%B8%D0%B5
[24] Header Metadata Extraction from Scientific Documents: https://scholar.google.ru/scholar?hl=ru&as_sdt=0%2C5&q=Header+Metadata+Extraction+from+Scientific+Documents&btnG=
[25] string: https://ru.wikipedia.org/wiki/%D0%A1%D1%82%D1%80%D0%BE%D0%BA%D0%BE%D0%B2%D1%8B%D0%B9_%D1%82%D0%B8%D0%BF
[26] Эврика!: https://imgur.com/gallery/XfkhXrS
[27] Are you sure?: https://i.pinimg.com/216x146/d5/6f/14/d56f14bf43bb81efc99b892c592e2685.jpg
[28] Источник: https://habr.com/ru/post/458842/?utm_source=habrahabr&utm_medium=rss&utm_campaign=458842
Нажмите здесь для печати.