Создайте и используйте индекс полнотекстового поиска HTML (C++)

Я должен создать поисковый индекс для набора страниц HTML.

У меня нет опыта в реализации поискового индекса вообще, таким образом, любая общая информация, как создать один, какую информацию хранить, как реализовать расширенный поиск, такой как "вся фраза", рейтинг результатов и т.д.

Я не боюсь создать его сам, хотя я был бы рад снова использовать существующий компонент (или использовать тот для начала работы с прототипом). Я ищу решение, доступное от C++, предпочтительно не требуя дополнительных установок во времени выполнения. Содержание статично (таким образом, оно имеет смысл к совокупной поисковой информации), но поиску, возможно, придется накопить результаты нескольких таких репозиториев.


Я могу высказать несколько образованных предположений, хотя: создайте карту word ==> pages для всех (соответствующих) слов разряд может быть присвоен отображению promincence (h1> h2>...>

) и близость к вершине. Расширенный поиск мог быть создан к тому же: поиск фразы "homo sapiens" мог перечислить все страницы, которые содержат "homo" и "sapiens", затем отсканируйте все страницы, возвращенные для местоположений, где они происходят вместе. Однако существует много проблематичных сценариев и оставшихся без ответа вопросов, таким образом, я ищу ссылки на то, что должно быть огромным объемом существующей работы, которая так или иначе выходит из моего google-fu.


[редактирование для щедрости]
Лучший ресурс, который я нашел до сих пор, является этим и ссылками оттуда. У меня действительно есть дорожная карта реализации для экспериментальной системы, однако, я все еще ищу:

  • Ссылочный материал относительно создания индекса и отдельных шагов
  • доступные реализации отдельных шагов
  • допускающие повторное использование реализации (с вышеупомянутыми ограничениями среды)

19
задан peterchen 25 June 2010 в 11:09
поделиться

4 ответа

Этот процесс обычно известен как поиск информации . Вы, вероятно, найдете эту онлайн-книгу полезной.

Существующие библиотеки

Вот два существующих решения, которые можно полностью интегрировать в приложение, не требуя отдельного процесса (я считаю, что оба будут компилироваться с VC ++).

Xapian является зрелым и может делать многое из того, что вам нужно, от индексации до ранжированного поиска.Отдельный синтаксический анализ HTML потребуется, потому что, AFAIK, он не анализирует HTML (у него есть сопутствующая программа Omega , которая является интерфейсом для индексирования веб-сайтов).

Lucene - это библиотека Apache для индексации / поиска на Java с официальной предварительной версией C lucy и неофициальной версией C ++ CLucene .

Реализация поиска информации

Если по какой-то причине вышеперечисленные варианты не работают, вот некоторая информация об отдельных этапах построения и использования индекса. Индивидуальные решения могут быть от простых до очень сложных, в зависимости от того, что вам нужно для вашего приложения. Я разбил процесс на 5 шагов

  1. Обработка HTML
  2. Обработка текста
  3. Индексирование
  4. Получение
  5. Ранжирование

Обработка HTML

Здесь есть два подхода

  • Удаление ] На упомянутой вами странице обсуждается техника, обычно известная как удаление, которая включает удаление всех элементов html, которые не будут отображаться, и перевод других в их форму отображения. Лично я бы предварительно обработал с помощью perl и проиндексировал полученные текстовые файлы. Но для интегрированного решения, особенно такого, в котором вы хотите записывать значимые теги (например,

    ,

    ), вы, вероятно, захотите использовать свои собственные. Вот частичная реализация подпрограммы очистки C ++ (приведена в Thinking in C ++ , последняя версия книги здесь ), из которой вы можете построить.

  • Синтаксический анализ Уровень сложности выше по сравнению с удалением - это синтаксический анализ HTML, который может помочь в вашем случае для записи значимых тегов.Однако хороший синтаксический анализатор C ++ HTML трудно найти . Некоторые параметры могут быть htmlcxx (никогда не использовали, но активны и выглядят многообещающе) или hubbub (библиотека C, часть NetSurf, но претендует на переносимость).

    Если вы имеете дело с XHTML или хотите использовать преобразователь HTML в XML, вы можете использовать один из многих доступных синтаксических анализаторов XML. Но опять же, трудно найти преобразователи HTML в XML, единственный, о котором я знаю, это HTML Tidy . Помимо преобразования в XHTML, его основной целью является исправление отсутствующих / поврежденных тегов, и он имеет API , который, возможно, можно использовать для его интеграции в приложение. Учитывая документы XHTML, существует много хороших парсеров XML, например Xerces-C ++ и tinyXML .

Обработка текста

По крайней мере, для английского языка преобразование текста в слова довольно прямолинейно. Однако при поиске есть несколько сложностей.

  • Стоп-слова - это слова, о которых априори известно, что они не обеспечивают полезного различия между документами в наборе, такими как статьи и предложения. Часто эти слова не индексируются и не фильтруются из потоков запросов. В Интернете доступно множество списков стоп-слов, например этот .

  • Построение включает в себя предварительную обработку документов и запросов для определения корня каждого слова для лучшего обобщения поиска. Например. поиск «foobarred» должен давать «foobarred», «foobarring» и «foobar». Индекс можно построить и искать только по корням.Два основных подхода к поиску корня основаны на словарях (поиск из слова ==> корень) и алгоритмах. Алгоритм Портера очень распространен, и доступно несколько реализаций, например C ++ здесь или C здесь . Создание в библиотеке Snowball C поддерживает несколько языков.

  • Кодирование Soundex Одним из методов повышения устойчивости поиска к орфографическим ошибкам является кодирование слов с помощью фонетического кодирования. Затем, когда в запросах есть фонетические ошибки, они по-прежнему будут отображаться напрямую в проиндексированные слова. Существует множество реализаций, вот одна .

Индексирование

Структура данных слова ==> страницы известна как инвертированный индекс . Его инвертированный, потому что он часто генерируется из прямого индекса страницы ==> слов. Инвертированные индексы обычно бывают двух видов: инвертированный индекс файла , который сопоставляет слова с каждым документом, в котором они встречаются, и полный инвертированный индекс , который сопоставляет слова с каждой позицией в каждом документе, в котором они встречаются. in.

Важным решением является то, какой бэкэнд использовать для индекса, некоторые возможности в порядке простоты реализации:

  1. SQLite или Berkly DB - оба они являются базой данных движки с API C ++, которые интегрированы в проект, не требуя отдельного серверного процесса. Постоянные базы данных по сути являются файлами, поэтому поиск по нескольким индексным наборам можно выполнить, просто изменив связанный файл. Использование СУБД в качестве серверной части упрощает создание, обновление и поиск индексов.
  2. В структуре данных памяти - если вы используете инвертированный индекс файла, который не является чрезмерно большим (потребление памяти и время загрузки), это может быть реализовано как std :: map , используя boost :: serialization для сохранения.
  3. О структуре данных на диске - я слышал о невероятно быстрых результатах при использовании файлов с отображением памяти для такого рода вещей, YMMV.Наличие инвертированного индекса файла потребует наличия двух файлов индекса, один из которых представляет слова с чем-то вроде struct {char word [n]; беззнаковое целое смещение; беззнаковое целое число; }; , а второй представляет кортежи (слово, документ) только с unsigned int s (слова, неявные в смещении файла). Смещение - это смещение файла для идентификатора первого документа для слова во втором файле, count - это количество идентификаторов документов, связанных с этим словом (количество идентификаторов для чтения из второй файл). Затем поиск сводится к двоичному поиску по первому файлу с указателем в файле с отображением в память. Обратной стороной является необходимость дополнения / усечения слов для получения постоянного размера записи.

Процедура индексации зависит от того, какой бэкэнд вы используете. Классический алгоритм создания инвертированного индекса файла ( подробно описан здесь ) начинается с чтения каждого документа и расширения списка кортежей (идентификатор страницы, слово), игнорируя повторяющиеся слова в каждом документе. После обработки всех документов отсортируйте список по словам, затем сверните в (слово, (идентификатор страницы1, идентификатор страницы2, ...)).

Библиотека GNU mifluz реализует инвертированные индексы с хранилищем, но без синтаксического анализа документов или запросов. GPL, поэтому, возможно, не является жизнеспособным вариантом, но даст вам представление о сложностях, связанных с инвертированным индексом, который поддерживает большое количество документов.

Извлечение

Очень распространенным методом является логическое извлечение, которое представляет собой просто объединение / пересечение документов, проиндексированных для каждого из слов запроса, которые соединены с помощью или / и, соответственно. Эти операции эффективны, если идентификаторы документов хранятся в отсортированном порядке для каждого термина, чтобы можно было напрямую применять такие алгоритмы, как std :: set_union или std :: set_intersection .

Существуют варианты поиска, википедия имеет обзор, но стандартное логическое значение подходит для многих / большинства приложений.

Ранжирование

Существует множество методов ранжирования документов, возвращаемых логическим поиском. Общие методы основаны на модели мешка слов, что просто означает, что относительное положение слов игнорируется. Общий подход заключается в оценке каждого извлеченного документа относительно запроса и ранжировании документов на основе их вычисленной оценки. Существует множество методов оценки, но хорошей отправной точкой является формула термина "частота-обратная частота документа" .

Идея, лежащая в основе этой формулы, заключается в том, что если слово запроса часто встречается в документе, этот документ должен иметь более высокий балл, но слово, которое встречается во многих документах, менее информативно, поэтому это слово должно быть понижено. Формула: по условиям запроса i = 1..N и документу j

оценка [j] = sum_over_i (word_freq [i, j] * inv_doc_freq [i])

, где word_freq [i, j] - количество вхождений слова i в документ j, и

inv_doc_freq [i] = log (M / doc_freq [i])

, где M - количество документов, а doc_freq [i] - количество документов, содержащих слово i.Обратите внимание, что слова, встречающиеся во всех документах, не влияют на оценку. Широко используется более сложная скоринговая модель BM25 , которая включена как в Lucene, так и в Xapian.

Часто эффективное ранжирование для конкретной области достигается путем корректировки методом проб и ошибок.Отправной точкой для корректировки ранжирования по контексту заголовка / абзаца может быть увеличение word_freq для слова на основе контекста заголовка / абзаца, например 1 для абзаца, 10 для заголовка верхнего уровня. Что касается некоторых других идей, вы можете найти эту статью интересной, где авторы скорректировали рейтинг BM25 для позиционной оценки (идея состоит в том, что слова ближе к началу документа более релевантны, чем слова ближе к концу).

Объективная количественная оценка эффективности ранжирования получается с помощью кривых точности-отзыва или средней средней точности, подробно здесь . Для оценки требуется идеальный набор запросов, сопряженный со всеми соответствующими документами в наборе.

32
ответ дан 30 November 2019 в 03:24
поделиться

Я бы напал на это с помощью небольшой базы данных sqlite. У вас могут быть таблицы для «страницы», «термина» и «термина страницы». «Страница» будет иметь такие столбцы, как идентификатор, текст, заголовок и URL-адрес. «Термин» будет иметь столбец, содержащий слово, а также основной идентификатор. «Термин страницы» будет иметь внешние ключи к идентификатору страницы и идентификатору термина, а также может хранить вес, рассчитанный на основе расстояния от верха и количества вхождений (или чего угодно).

Возможно, более эффективным способом было бы иметь только две таблицы - «страница», как и раньше, и «термин страницы», которые имели бы идентификатор страницы, вес и хэш слова термина.

Пример запроса - вы хотите найти «foo». Вы хешируете "foo", а затем запрашиваете все строки терминов страницы, которые имеют этот хеш-код. Сортировка по убыванию веса и отображение первой десятки результатов.

Я думаю, что этот запрос должен выполняться достаточно быстро, хотя это, очевидно, зависит от количества и размера рассматриваемых страниц. Sqlite несложно связать и не требует дополнительной установки.

2
ответ дан 30 November 2019 в 03:24
поделиться

Ранжирование страниц - это действительно сложная задача. С большой выборкой страниц вы можете довольно часто использовать ссылки при расчете рангов. В противном случае вам нужно проверить, как кажутся размещенные слова, а также убедиться, что ваш движок не обманывается страницами «словаря».

Удачи!

2
ответ дан 30 November 2019 в 03:24
поделиться

В зависимости от размера и количества статических страниц вам может потребоваться уже существующее поисковое решение.

«Как реализовать полнотекстовый поиск для этой таблицы из 10+ миллионов строк, не отставать от нагрузки и оставаться актуальным? Sphinx умеет разгадывать загадки такого типа».

I выбрал бы механизм Sphinx для полнотекстового поиска . Лицензия GPL , но также доступна коммерческая версия. Он предназначен для автономной работы [2], но он также может быть встроен в приложения путем извлечения необходимой функциональности (будь то индексирование [1], поиск [3], поиск исходных данных и др.).

Данные должны быть получены путем анализа входных файлов HTML и преобразования их в обычный текст с помощью синтаксического анализатора, такого как libxml2's HTMLparser (я не знаю, t использовал его, но говорят, что он может анализировать даже искаженный HTML). Если вы не привязаны к C / C ++ , вы можете взглянуть на Beautiful Soup .

После получения открытых текстов вы можете сохранить их в базе данных, например MySQL или PostgreSQL . Если вы хотите сохранить все встроенным, вам следует использовать sqlite .

Обратите внимание, что Sphinx не работает "из коробки" с sqlite , но есть попытка добавить поддержку ( sphinx-sqlite3 ).

3
ответ дан 30 November 2019 в 03:24
поделиться
Другие вопросы по тегам:

Похожие вопросы: