Создание и использование индекса полнотекстового поиска HTML (C ++) - PullRequest
18 голосов
/ 19 июня 2010

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

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

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


Хотя я могу сделать несколько образованных предположений: создать картуword ==> pages для всех (релевантных) слов ранг может быть назначен отображению по условию (h1> h2> ...> <p>) и близости к вершине.Вдобавок к этому может быть построен расширенный поиск: поиск по фразе "homo sapiens" может перечислить все страницы, которые содержат "homo" и "sapiens", а затем отсканировать все возвращенные страницы на предмет мест, где они встречаются вместе.Однако есть много проблемных сценариев и вопросов без ответов, поэтому я ищу ссылки на то, что должно быть огромным количеством существующей работы, которая каким-то образом ускользает от моего гугл-фу.


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

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

Ответы [ 4 ]

32 голосов
/ 28 июня 2010

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

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

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

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

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

Реализация информацииretrieval

Если вышеуказанные опции по какой-либо причине не являются жизнеспособными, вот некоторая информация об отдельных шагах построения и использования индекса.Индивидуальные решения могут быть как простыми, так и очень сложными, в зависимости от того, что вам нужно для вашего приложения.Я разбил процесс на 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».Индекс можно построить и искать только по корням.Два основных подхода к основам - это словарь (поиск по слову ==> root) и алгоритм.Алгоритм Портера очень распространен, и доступно несколько реализаций, например, C ++ здесь или C здесь .Stemming в библиотеке Snowball C поддерживает несколько языков.

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

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

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

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

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

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

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

Извлечение

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

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

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

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

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

score [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 - числоDocuments and doc_freq [i] - количество документов, содержащих слово i.Обратите внимание, что слова, встречающиеся во всех документах, не влияют на оценку.Более сложная модель оценки, которая широко используется, это BM25 , которая включена как в Lucene, так и в Xapian.

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

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

3 голосов
/ 01 июля 2010

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

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

Я бы выбрал двигатель Sphinx дляfull text searching.Лицензия GPL, но также доступна версия commercial.Предполагается, что он будет работать автономно [2] , но его также можно встраивать в приложения путем извлечения необходимых функций (будь то indexing [1] , searching [3] , stemming и т. Д.).

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

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

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

2 голосов
/ 19 июня 2010

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

Удачи!

2 голосов
/ 19 июня 2010

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

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

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

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

...