Я реализовал первый прототип собственного механизма поиска, который сокращённо назвал 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 также есть различные организованные списки, в которых может присутствовать интересное содержимое для сбора и индексирования. Думаю, нужно будет реализовать механизм сопоставления выдаваемых системой предложений с определённым списком исключений. Например, нет смысла пытаться собрать содержимое платёжного шлюза или коммерческих сайтов в целом.

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

Комментарии (20)


  1. NickDoom
    05.01.2024 13:52
    +4

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

    А, и система рейтингов, чтобы быстро давить поисковый спам, внедрившийся под видом «своих».


    1. ednersky
      05.01.2024 13:52
      +2

      ИМХО в такой системе самый сложный вопрос - надёжность работы ботнета в условиях, что каждый узел может быть недоступен (комп/телефон/итп выключен/вне сети)

      От ботнета же чего хотим? распределённого хранения индекса/инфы (чтобы каждый не хранил свою копию)
      От поиска чего хотим? Устойчивой работы.

      И здесь противоречие для добровольного ботнета на десктопах/телефонах пользователей


      1. NickDoom
        05.01.2024 13:52
        +1

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

        Ещё сложная штука там — приватность vs рейтинг. Зашли мы на какой-то сайт, бот собрал ключевые слова, зашифровал и отправил по цепочке узлов так, что на приёмном конце фиг поймёшь, кто реально заходил на этот сайт, а кто просто промежуточное звено (непонятно, где зародилось начало). Легко.

        Но теперь стали приходить жалобы, что такое-то соответствие «слово + сайт» — фейковое. Как организовать обратную цепочку, чтобы все соседи поискового спамера знали, что он — поисковый спамер? Найти, от кого исходила цепочка N, но не говорить, что в ней было?

        UPD: там ещё как-то надо различать невинных жертв, попавших на поисковый спам методом «лучший, новый, главный, самый, секс, Москва и реферат» ®©™, и злонамеренных спамеров, которые эти ключевые слова сами выдумали. В первом случае надо понижать страничку, вплоть до блэклиста, во втором — рейтинг узла, вплоть до бана.


      1. mxr
        05.01.2024 13:52

        Проблему распределенного хранения данных от части решили, например в Storj. IPFS так же может подойти для этих целей. На основе их решений вполне себе можно создать что-то подобное.


    1. tester12
      05.01.2024 13:52

      «добровольный ботнет», где несколько участников отвечают за каждое ключевое слово

      Обычно люди ищут не по одному слову. а по нескольким. И в индексированном тексте не одно слово, а тысячи.

      А, и система рейтингов

      А тут нужна какая-то организация, назначающая рейтинги.


      1. NickDoom
        05.01.2024 13:52

        Ну как, если человек кликнул ссылку, увидел, что там, и ломанулся назад тут же — вот уже повод усомниться… а если нажал репорт — то сразу можно снимать балл, не изучая массовые реакции :)

        Тут крайняя степень опенсорса нужна, потому что такой поисковик по определению ооочень много знает о браузере, в который интегрирован. И нужны сложные гарантии того, что он не поделится этим ни с кем.


  1. 13werwolf13
    05.01.2024 13:52
    +3

    как-то под пиво прокрастинировали с одним коллегой на тему поисковика на базе блокчейна, где майнер=краулер а поисковый запрос утекает в цепочку 100% проверенных ссылок. но пришли к выводу что за пару лет использования (при условии что он станет популярным) размер блокчейна перевалит за петабайт и станет неюзабельным для масс..


    1. uranik
      05.01.2024 13:52

      Значит делайте распереденный блокчейн, чтоб на каждой ноде по 100 мегабайт хранилось, разумеется с многократным дублированием.


    1. maximw
      05.01.2024 13:52
      +3

      А зачем для этого блокчейн? Какие он свойства системе добавляет?


      1. si1v3r
        05.01.2024 13:52
        +14

        Повысит вероятность найти инвестора.


      1. 13werwolf13
        05.01.2024 13:52

        поисковики хостящиеся "у дяди" имеют несколько недостатков:
        1) спонсорские выдачи которые всегда наверху независимо от того а подходят ли они вообще под поисковый запрос
        2) реклама
        3) безполезные интеграции с прочими сервисами "дяди"
        4) единственный web ui который невозможно подкрутить под себя и который имеет свойство меняться (зачастую не в лучшую сторону) по воле того самого "дяди"
        5) чистку от неугодных выдач по запросу непойми кого из страны в которой этот самый "дядя" обитает

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


        1. tester12
          05.01.2024 13:52

          Без "дяди", следящего за контентом, весь этот блокчейновый поиск будет завален спамом, да и искаться всё будет с жуткими тормозами. А "дядя" за свою работу захочет денег (в т.ч. в форме рекламы, привязки к своим сервисам и т.п.). И за "дядей" будет следить другой "дядя" - в штатском, но со званием.


          1. 13werwolf13
            05.01.2024 13:52

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


  1. johnfound
    05.01.2024 13:52
    +1

    Мёртвые ссылки в файле pages.txt значительно замедляют процесс сбора содержимого. Мне нужно найти способ исключить попадание таких ссылок в этот файл или наладить их удаление из него.

    Ссылки могут быть мертвыми временно. Надо просто использовать много экземпляров wget. Пусть некоторые из них ждут (это ничего не стоит) другие будут работать.


  1. SergioT4
    05.01.2024 13:52
    +2

    Основная проблема персональных поисковиков - это огромная непродуктивная нагрузка на инфраструктуру.

    Если миллион обладателей личных краулеров будет с хоть какой-то переодичностью обходить даже часть веба, то сколько ненужных вычислительных ресурсов будет выбрасываться в мусор.

    Надо создать open-source проекты которые делают краулинг веба, может какой-препроцессинг. А потом отдают базы желающим. Вот по этим базам личный поиск реализовывать, вот это дело.

    Личный же краулинг можно делать если ты через пейвол к специфичным ресурсам под своим аккаунтом ходишь.


  1. akakoychenko
    05.01.2024 13:52
    +1

    Какой же бред в контексте современного веба.

    Идея то хорошая - часто пытаешься вспомнить что-то, что видел в интернете, но не помнишь где. Но через wget качать страницы с истории браузинга при условии, что и так все было локально отрендерено в момент чтения, очень тупо. Учитывая, что современный интернет весь завешан пейволами, авторизациями, защитами от ботов, необходимостью надеть впн конкретной страны, да и, кроме того, даже пробившись через все эти защитные сооружения, с учетом повсеместного испольбзования ajax, не факт, что страница будет прямо соответствовать урлу, а, если и будет, что контент выйдет загрузить без исполнения js.

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

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


    1. REPISOT
      05.01.2024 13:52
      +2

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

      стартап называется «Товарищ майор»?


    1. ahabreader
      05.01.2024 13:52

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

      Уже. Подобное реализовано в расширение ArchiveWeb.page (только Chromium-браузеры). Но:

      • По названию понятно, что акцент на архивацию, а не поиск.

      • Дампит не DOM, а запросы и ответы, если я правильно понимаю, как оно опирается на WARC-файлы - в общем, даже лучше.

      • По ключевым словам "WARC MITM proxy" можно найти близкие проекты вроде warcprox, но среди нет самодостаточных, поэтому ими не интересовался.


  1. hMartin
    05.01.2024 13:52

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


  1. Manrus
    05.01.2024 13:52
    +1

    Немного не по теме, но Yacy