Был в долгом отпуске :)
Кажется мозги заржавели, чувствуется.
Захотел себе сделать парсер небольшой, но задачка оказалась не тривиальной из-за одного нюансика.
Задача парсить схожие сайты с сервиса similarweb.
Есть стартовый набор сайтов, с которых будет начинаться сбор схожих сайтов. А после этого, к стартовому набору добавляются и сами спаршенные схожие сайты и процесс парсинга пошел дальше. Парсим схожие сайты из схожих спаршенных ранее и так вглубь "бесконечно", пока перестанут появляться новые схожие сайты, а будут попадаться одни дубли.
Например, идем по ссылке https://www.similarweb.com/website/youtube.com/
Крутим вниз и видим раздел Competitors & Similar Sites там блок с 10 схожими сайтами.
Берем эти 10 сайтов и добавляем их в очередь на парсинг. А исходный сайт youtube.com добавляем в черный список, чтобы не парсить в дальнейшем, то что уже спаршено. И пошли по кругу снова.
Думал использую под хранение очереди из сайтов для парсинга РЕСУРС. Но не тут то было, огорчился , когда понял, что в ресурс возможно добавить дубликат и все простота и удобство ресурса улетучилась. Откуда дубли будут? Например в 20 потоков идет парсинг, и среди схожих сайтов будут дубликаты, которые будут добавлены в очередь. Т.е. дубликаты нужно фильтровать перед этапом добавление в очередь парсинга.
Потом вспомнил про встроенную БД, проверил, но и там можно добавить запись-дубль. А возможности указать колонку, как уникальный ключ не нашел. По этому и БД отпали.
Потом увидел , что добавился функционал "асинхронные функции", да, через них такое можно провернуть, но много городить придется. Поскольку очередь будет расширяться , то придется химичить с циклами вызова асинх. функций. Т.е. брать кусок 20 строк из очереди, отправлять их в асинхронные функции, там по этих 20 сайтах получили схожие (с кучей дублей). Вышли в основной поток, обработали эти данные, убрали дубли, удалили те, сайты, что уже ранее были спаршены сверка с blacklist. Потом опять входим в цикл берем следующий кусок данных из очереди. Но проблемка то в том, что очередь динамичная, по этому сколько раз цикл делать по вызову асинх.функции? Сделать бесконечный цикл while(True) ? И если в очереди нет данных, то выходить из цикла, завершать работу? Ну да, вроде рабочий вариант. А что тогда делать, если захочется скрипт остановить и возобновить работу завтра? Можно перед каждой итерацией цикла по вызову асинхр.функции читать файл, если так написано СТОП, значит выходить из цикла сохранять текущую очередь в файл. Но опять же - костыли.
За глобальные переменные тоже думал, но они тут не в тему вроде как, еще больше костылей.
Всего этого можно было бы избежать, если бы в ресурс нельзя было добавлять дубли. Как в js есть set - в которую, если добавляешь запись и она уже там есть, то новая (дубль) не добавится.
Кто как решал бы красиво, с минимум костылей и без использования внешней БД ?