Мой первый прототип поискового движка

от автора


Я реализовал первый прототип собственного механизма поиска, который сокращённо назвал PSE (Personal Search Engine). Создал я его с помощью трёх скриптов Bash, возложив всю основную работу на sqlite3, wget и PageFind.

Браузер Firefox вместе с Newsboat сохраняют полезную информацию в базах данных SQLite. В moz_places.sqlite содержатся все посещённые URL-адреса и адреса закладок (то есть moz_bookmarks.sqlite базы данных SQLite). У меня получилось около 2000 закладок. Это меньше, чем я предполагал, так как многие оказались нерабочими из-за битых ссылок.

Нерабочие URL-адреса страниц сильно замедляют процесс сбора, так как wget приходится ожидать истечения различных таймаутов (например, DNS, ответа сервера, время скачивания). URL-адреса из «истории» составили бы интересную коллекцию для сбора, но тут не обойтись без списка исключений (например, нет смысла сохранять запросы к поисковым системам, веб-почте, онлайн-магазинам). Изучение этого вопроса я отложу до следующего прототипа.

Кэш Newsboat cache.db обеспечил богатый источник контента и намного меньше мёртвых ссылок (что не удивительно, поскольку я мониторю этот список URL-адресов гораздо активнее, нежели закладки). Из обоих этих источников я собрал 16,000 страниц. Затем с помощью SQLite 3 я запросил из разных БД значения URL, упорядочив их в одном текстовом файле построчно.

После создания списка страниц, по которым я хотел выполнять поиск, нужно было скачать их в каталог с помощью wget. У этого инструмента есть множество настроек, из которых я решил включить регистрацию временны́х меток, а также создание каталогов на основе протокола, сопровождаемого доменом и путём каждого элемента. Это позволило в дальнейшем преобразовать локальные пути элементов в URL-адреса.

После сбора содержимого я использовал PageFind для его индексирования. Поскольку я стал использовать PageFind изначально, то сопроводил этот инструмент опцией --serve, которая предоставляет веб-службу localhost на порту 1414. Всё, что мне требовалось – это добавить файл index.html в каталог, где я собрал всё содержимое и сохранил индексы PageFind. После этого я снова использовал PageFind для предоставления собственного механизма поиска на базе localhost.

И хотя общее число страниц было невелико (16,000), мне удалось получить интересные результаты, просто опробуя случайные слова. Так что прототип получился перспективный.

▍ Текущие компоненты прототипа

Я использую простой скрипт Bash, который получает URL-адреса из закладок Firefox и кэша Newsboat, после чего генерирует файл pages.txt с уникальными URL.

Далее, используя этот файл, с помощью wget я собираю и организую всё содержимое в структуру дерева:

  • htdocs
    • http (все URL-адреса с типом соединения HTTP);
    • https (все URL-адреса с типом соединения HTTPS);
    • pagefind (здесь содержатся индексы PageFind и JavaScript-код интерфейса поиска);
    • index.html (здесь находится страница для интерфейса поиска, использующего библиотеки из pagefind).

Поскольку я скачал только HTML, 16K страниц заняли на диске не сильно много места.

▍ Реализация прототипа

Вот скрипты Bash для получения URL-адресов, сбора контента и запуска локального движка поиска на основе PageFind.

После сбора URL-адресов я использую две переменные среды для обнаружения различных баз данных SQLite 3 (то есть PSE_MOZ_PLACES и PSE_NEWSBOAT):

1.#!/bin/bash 2. 3.if [ "$PSE_MOZ_PLACES" = "" ]; then 4.    printf "the PSE_MOZ_PLACES environment variable is not set." 5.    exit 1 6.fi 7.if [ "$PSE_NEWSBOAT" = "" ]; then 8.    printf "the PSE_NEWSBOAT environment variable is not set." 9.    exit 1 10.fi 11. 12.sqlite3 "$PSE_MOZ_PLACES" \ 13.    'SELECT moz_places.url AS url FROM moz_bookmarks JOIN moz_places ON moz_bookmarks.fk = moz_places.id WHERE moz_bookmarks.type = 1 AND moz_bookmarks.fk IS NOT NULL' \ 14.    >moz_places.txt 15.sqlite3 "$PSE_NEWSBOAT" 'SELECT url FROM rss_item' >newsboat.txt 16.cat moz_places.txt newsboat.txt | 17.    grep -E '^(http|https):' | 18.    grep -v '://127.0.' | 19.    grep -v '://192.' | 20.    grep -v 'view-source:' | 21.    sort -u >pages.txt

Следующим шагом я с помощью wget получаю страницы:

1.#!/bin/bash 2.# 3.if [ ! -f "pages.txt" ]; then 4.    echo "missing pages.txt, skipping harvest" 5.    exit 1 6.fi 7.echo "Output is logged to pages.log" 8.wget --input-file pages.txt \ 9.    --timestamping \ 10.    --append-output pages.log \ 11.    --directory-prefix htdocs \ 12.    --max-redirect=5 \ 13.    --force-directories \ 14.    --protocol-directories \ 15.    --convert-links \ 16.    --no-cache --no-cookies

Наконец, у меня есть скрипт, который генерирует страницу index.html и XML-файл в формате OpenSearch Description, индексирует собранные сайты и запускает PageFind в режиме сервера:

1.#!/bin/bash 2.mkdir -p htdocs 3. 4.cat <<OPENSEARCH_XML >htdocs/pse.osdx 5.<OpenSearchDescription xmlns="http://a9.com/-/spec/opensearch/1.1/" 6.                       xmlns:moz="http://www.mozilla.org/2006/browser/search/"> 7.  <ShortName>PSE</ShortName> 8.  <Description>A Personal Search Engine implemented via wget and PageFind</Description> 9.  <InputEncoding>UTF-8</InputEncoding> 10.  <Url rel="self" type="text/html" method="get" template="http://localhost:1414/index.html?q={searchTerms}" /> 11.  <moz:SearchForm>http://localhost:1414/index.html</moz:SearchForm> 12.</OpenSearchDescription> 13.OPENSEARCH_XML 14. 15.cat <<HTML >htdocs/index.html 16.<html> 17.<head> 18.<link 19.  rel="search" 20.  type="application/opensearchdescription+xml" 21.  title="A Personal Search Engine" 22.  href="http://localhost:1414/pse.osdx" /> 23.<link href="/pagefind/pagefind-ui.css" rel="stylesheet"> 24.</head> 25.<body> 26.<h1>A personal search engine</h1> 27.<div id="search"></div> 28.<script src="/pagefind/pagefind-ui.js" type="text/javascript"></script> 29.<script> 30.    window.addEventListener('DOMContentLoaded', function(event) { 31.        let page_url = new URL(window.location.href), 32.            query_string = page_url.searchParams.get('q'), 33.            pse = new PagefindUI({ element: "#search" }); 34.        if (query_string !== null) { 35.            pse.triggerSearch(query_string); 36.        } 37.    }); 38.</script> 39.</body> 40.</html> 41.HTML 42. 43.pagefind \ 44.--source htdocs \ 45.--serve

Затем я просто указываю в браузере адрес http://localhost:1414/index.html. При желании я могу даже передать строку запроса ?q=....

С функциональной точки зрения это очень примитивная система, и 16К страниц явно недостаточно для того, чтобы сделать её привлекательной для использования (думаю, для этого нужно где-то 100К).

▍ Чему я научился на этом прототипе

Текущий прототип имеет несколько ограничений:

  1. Мёртвые ссылки в файле pages.txt значительно замедляют процесс сбора содержимого. Мне нужно найти способ исключить попадание таких ссылок в этот файл или наладить их удаление из него.
  2. В выводе PageFind используются страницы, скачанные мной на локальную машину. Было бы лучше, если бы получаемые ссылки переводились и указывали на фактический источник страницы. Думаю, это можно реализовать с помощью JS-кода в файле index.html при настройке элемента PageFind, отвечающего за результат поиска. Этот вопрос нужно изучить подробнее.

16K страниц – это очень скромный объём. В ходе тестирования я получил интересные результаты, но недостаточно хорошие для того, чтобы начинать реально использовать эту систему поиска. Предполагаю, что для более-менее полноценного её применения нужно собрать хотя бы 100К страниц.

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

На данный момент значительное время занимает сбор содержимого страниц в индекс. Я пока не знаю, сколько конкретно места потребуется для планируемого объёма в 100К страниц.

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

▍ Предстоящие доработки

Я вижу несколько способов расширения поискового массива. Первый подразумевает создание зеркал для нескольких сайтов, которые я использую в качестве ссылок. В wget есть функция зеркала. Опираясь на список из sites.txt, я мог бы периодически отображать эти сайты, делая их содержимое доступным для индексирования.

Экспериментируя с опцией зеркала, я заметил, что получаю PDF-файлы, привязанные к отображаемым страницам. Если я использую команду Linux find для обнаружения всех этих PDF, то смогу применить другой инструмент для извлечения их текста. Таким образом я расширю свой поиск за пределы простого текста и HTML. Нужно хорошенько продумать этот вариант, так как в конечном итоге я хочу иметь возможность восстанавливать путь к PDF при отображении этих результатов.

Ещё один подход будет заключаться в работе со всей историей браузера и его закладками. Это позволит значительно расширить массив страниц для поиска. Тогда я также смогу проверять «шапку» HTML на наличие ссылок на фиды, которые можно будет агрегировать в общий банк фидов. Это позволит захватывать содержимое из интересных для чтения источников, не пропуская посты, которые оказались бы упущены из-за ограничения времени чтения.

Для просмотра интересных страниц из моего RSS-агрегатора я использую приложение Pocket. У этого приложения есть API, и я могу получать из него дополнительные интересные страницы. В Pocket также есть различные организованные списки, в которых может присутствовать интересное содержимое для сбора и индексирования. Думаю, нужно будет реализовать механизм сопоставления выдаваемых системой предложений с определённым списком исключений. Например, нет смысла пытаться собрать содержимое платёжного шлюза или коммерческих сайтов в целом.

Помоги спутнику бороться с космическим мусором в нашей новой игре! ?


ссылка на оригинал статьи https://habr.com/ru/articles/784504/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *