Добрый вечер всем!
Возможно выбрал не лучшее время для охвата аудитории, но тем не менее главное чтоб продукт был хороший, а не статья о нем. Последние несколько недель я пишу приложение в рамках которого надо собирать огромное количество информации из сети(запросы к API/парсинг HTML кода) и под конец 4-ой интеграции я подумал что надо бы это максимально облегчить(не дело это пересобирать приложение под каждый чих интеграции), возможно это не лучшая преамбула, но хотя бы была реальная проблема решение к которой хотелось показать и заопенсорнуть.
Итак Fitter = сшиватель достаточно жаргонный перевод, но мне он кажется что лучше всего подходит. Я делал эту штуку исходя из следующих предположений:
-
Данные могут меняться => нужен механизм обновления
-
Данные могут быть в нескольких источниках => данные надо сшить(map/reduce)
-
Нужна авторизация => API ключ/OAuth/Login+Pass
-
Есть не только Server-side rendering => Данные могут появиться после загрузки клиентской части(я понимаю что иногда можно с эмулировать запрос)
-
Данные могут быть не валидны => поле может отсутствовать или разметка поменялась
-
Мы не знаем где это будет развернуто => отсутствовать нужны абстракции
-
Нужна конфигурация которую +- легко менять(не в текущем варианте)
-
Нужно иметь возможность привести данные к одному виду
И так DEMO:
Немного расскажу что он умеет:
-
Брать данные по HTTP запросам с авторизацией по Header
-
Брать данные с бинарника Chromium
-
Брать данные с Docker браузеров
-
Брать данные с Playwright
-
Парсить данные HTML/Json/XPath
-
Пробрасывать данные/связывать с разных источников
Планы на будущее(Roadmap):
-
Добавить сценарии: некоторые сайты для парсинга требуют авторизацию/принять cookie и тд тп, будет набор команд которые можно будет прогнать до парсинга и после
-
Добавить способы отдачи информации: чтобы проект смог отправлять Webhook/Queue сообщения и тд тп
-
Добавить способы сбора информации: сегодня пришла идея чтоб источником информации может быть любая штука например телеграм канал: и для этого нам нужен допустим бот с доступам к сообщениям
-
Добавить способы запуска: тот же Webhook/Queue
-
Валидация — отсеевать не валидные данные
-
Редактор конфигурации — смотри ниже
Текущие точки боли(pain-points):
Пока что он один: конфигурация — для простых ситуаций это просто, однако если хочется связать несколько источников разобраться тяжело.
{ "limits": { "playwright_instance": 3 }, "item": { "connector_config": { "response_type": "HTML", "connector_type": "server", "server_config": { "method": "GET", "url": "http://www.citymayors.com/gratis/uk_topcities.html" } }, "model": { "type": "array", "array_config": { "root_path": "table table tr:not(:first-child)", "item_config": { "fields": { "name": { "base_field": { "path": "td:nth-of-type(1) font", "type": "string" } }, "population": { "base_field": { "path": "td:nth-of-type(2) font", "type": "string" } }, "temperature": { "base_field": { "path": "td:first-child font", "type": "string", "generated": { "model": { "type": "string", "path": "temp.temp", "model": { "type": "object", "object_config": { "fields": { "temp": { "base_field": { "type": "string", "path": "//div[@id='forecast_list_ul']//td/b/a/@href", "generated": { "model": { "type": "string", "model": { "type": "object", "object_config": { "fields": { "temp": { "base_field": { "type": "string", "path": "div.current-temp span.heading" } } } } }, "connector_config": { "response_type": "HTML", "connector_type": "browser", "attempts": 4, "browser_config": { "url": "https://openweathermap.org{PL}", "playwright": { "timeout": 30, "wait": 30, "install": false, "browser": "FireFox", "type_of_wait": "networkidle" } } } } } } } } } }, "connector_config": { "response_type": "xpath", "connector_type": "browser", "attempts": 3, "browser_config": { "url": "https://openweathermap.org/find?q={PL}", "playwright": { "timeout": 30, "wait": 30, "install": false, "browser": "Chromium" } } } } } } } } } } } } }
Как мы видим это больно, но давайте расскажу что тут происходит:
-
Мы настраиваем лимиты параллельных запуска Playwright: 3 штуки
-
Мы будем читать GET запросом HTML c сайта: http://www.citymayors.com/gratis/uk_topcities.html
-
На выходе мы ожидаем массив следующего вида:
Array<City> City = { name: string; population: string; temperature: string }
-
Если с полями name/population +- все понятно мы их берем из таблицы с сайта указанного выше, то temperature это сгенерированное поле
-
Мы берем название города из таблицы и пробрасываем его на сайт: https://openweathermap.org/find?q={PL} — где {PL} — имя города , для этого мы используем Playwright так как там client-side rendering
-
С результатов поиска мы берем relative ссылку на страницу города, пример: /city/2950159 и подставляем https://openweathermap.org{PL} — где {PL} ссылка, для этого мы используем Playwright так как там client side-rendering
-
По ссылке выше выдергиваем температуру по селектору: `div.current-temp span.heading`
-
И разворачиваем поле путем парсинга: `temp.temp` из сгенирированных данных
-
Пример элемента массива
{ "name": "Exeter", "population": "107,729", "temperature": "4°C" },
Ну и принципе и выводим результат.
PS: я понимаю что для достижения результата можно было связать с API попроще, но мне хотелось показать сложный кейс.
PS2: я не выбрал я пиарюсь, потому как коммерческой ценности 0, проект будет открытыми бесплатным, но было бы хорошо собрать отзывы
Спасибо большое за внимание, очень хочется услышать критику ну и идеи на будущее!
Проект и примеры:
Примеры для поиграться(скачиваете Fitter_CLI из релизов и можете следовать Readme
Там есть разные примеры: Docker/Server side HTTP request/Chromium можно поиграться
ссылка на оригинал статьи https://habr.com/ru/post/718972/
Добавить комментарий