Я должен создать поисковый индекс для набора страниц HTML.
У меня нет опыта в реализации поискового индекса вообще, таким образом, любая общая информация, как создать один, какую информацию хранить, как реализовать расширенный поиск, такой как "вся фраза", рейтинг результатов и т.д.
Я не боюсь создать его сам, хотя я был бы рад снова использовать существующий компонент (или использовать тот для начала работы с прототипом). Я ищу решение, доступное от C++, предпочтительно не требуя дополнительных установок во времени выполнения. Содержание статично (таким образом, оно имеет смысл к совокупной поисковой информации), но поиску, возможно, придется накопить результаты нескольких таких репозиториев.
Я могу высказать несколько образованных предположений, хотя: создайте карту word ==> pages
для всех (соответствующих) слов разряд может быть присвоен отображению promincence (h1> h2>...> ) и близость к вершине. Расширенный поиск мог быть создан к тому же: поиск фразы
"homo sapiens"
мог перечислить все страницы, которые содержат "homo"
и "sapiens"
, затем отсканируйте все страницы, возвращенные для местоположений, где они происходят вместе. Однако существует много проблематичных сценариев и оставшихся без ответа вопросов, таким образом, я ищу ссылки на то, что должно быть огромным объемом существующей работы, которая так или иначе выходит из моего google-fu.
[редактирование для щедрости]
Лучший ресурс, который я нашел до сих пор, является этим и ссылками оттуда. У меня действительно есть дорожная карта реализации для экспериментальной системы, однако, я все еще ищу:
Этот процесс обычно известен как поиск информации . Вы, вероятно, найдете эту онлайн-книгу полезной.
Вот два существующих решения, которые можно полностью интегрировать в приложение, не требуя отдельного процесса (я считаю, что оба будут компилироваться с VC ++).
Xapian является зрелым и может делать многое из того, что вам нужно, от индексации до ранжированного поиска.Отдельный синтаксический анализ HTML потребуется, потому что, AFAIK, он не анализирует HTML (у него есть сопутствующая программа Omega , которая является интерфейсом для индексирования веб-сайтов).
Lucene - это библиотека Apache для индексации / поиска на Java с официальной предварительной версией C lucy и неофициальной версией C ++ CLucene .
Если по какой-то причине вышеперечисленные варианты не работают, вот некоторая информация об отдельных этапах построения и использования индекса. Индивидуальные решения могут быть от простых до очень сложных, в зависимости от того, что вам нужно для вашего приложения. Я разбил процесс на 5 шагов
Здесь есть два подхода
Удаление ] На упомянутой вами странице обсуждается техника, обычно известная как удаление, которая включает удаление всех элементов html, которые не будут отображаться, и перевод других в их форму отображения. Лично я бы предварительно обработал с помощью perl и проиндексировал полученные текстовые файлы. Но для интегрированного решения, особенно такого, в котором вы хотите записывать значимые теги (например,
Синтаксический анализ Уровень сложности выше по сравнению с удалением - это синтаксический анализ 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.
Важным решением является то, какой бэкэнд использовать для индекса, некоторые возможности в порядке простоты реализации:
std :: map
, используя boost :: serialization для сохранения. 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 для позиционной оценки (идея состоит в том, что слова ближе к началу документа более релевантны, чем слова ближе к концу).
Объективная количественная оценка эффективности ранжирования получается с помощью кривых точности-отзыва или средней средней точности, подробно здесь . Для оценки требуется идеальный набор запросов, сопряженный со всеми соответствующими документами в наборе.
Я бы напал на это с помощью небольшой базы данных sqlite. У вас могут быть таблицы для «страницы», «термина» и «термина страницы». «Страница» будет иметь такие столбцы, как идентификатор, текст, заголовок и URL-адрес. «Термин» будет иметь столбец, содержащий слово, а также основной идентификатор. «Термин страницы» будет иметь внешние ключи к идентификатору страницы и идентификатору термина, а также может хранить вес, рассчитанный на основе расстояния от верха и количества вхождений (или чего угодно).
Возможно, более эффективным способом было бы иметь только две таблицы - «страница», как и раньше, и «термин страницы», которые имели бы идентификатор страницы, вес и хэш слова термина.
Пример запроса - вы хотите найти «foo». Вы хешируете "foo", а затем запрашиваете все строки терминов страницы, которые имеют этот хеш-код. Сортировка по убыванию веса и отображение первой десятки результатов.
Я думаю, что этот запрос должен выполняться достаточно быстро, хотя это, очевидно, зависит от количества и размера рассматриваемых страниц. Sqlite несложно связать и не требует дополнительной установки.
Ранжирование страниц - это действительно сложная задача. С большой выборкой страниц вы можете довольно часто использовать ссылки при расчете рангов. В противном случае вам нужно проверить, как кажутся размещенные слова, а также убедиться, что ваш движок не обманывается страницами «словаря».
Удачи!
В зависимости от размера и количества статических страниц вам может потребоваться уже существующее поисковое решение.
«Как реализовать полнотекстовый поиск для этой таблицы из 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 ).