Fitter —  сшиватель API/Website’s, часть личного проекта которую хотел опенсорснуть

от автора

Добрый вечер всем!

Возможно выбрал не лучшее время для охвата аудитории, но тем не менее главное чтоб продукт был хороший, а не статья о нем. Последние несколько недель я пишу приложение в рамках которого надо собирать огромное количество информации из сети(запросы к API/парсинг HTML кода) и под конец 4-ой интеграции я подумал что надо бы это максимально облегчить(не дело это пересобирать приложение под каждый чих интеграции), возможно это не лучшая преамбула, но хотя бы была реальная проблема решение к которой хотелось показать и заопенсорнуть.

Итак Fitter = сшиватель достаточно жаргонный перевод, но мне он кажется что лучше всего подходит. Я делал эту штуку исходя из следующих предположений:

  1. Данные могут меняться => нужен механизм обновления

  2. Данные могут быть в нескольких источниках => данные надо сшить(map/reduce)

  3. Нужна авторизация => API ключ/OAuth/Login+Pass

  4. Есть не только Server-side rendering =>  Данные могут появиться после загрузки клиентской части(я понимаю что иногда можно с эмулировать запрос)

  5. Данные могут быть не валидны => поле может отсутствовать или разметка поменялась

  6. Мы не знаем где это будет развернуто => отсутствовать нужны абстракции

  7. Нужна конфигурация которую +- легко менять(не в текущем варианте)

  8. Нужно иметь возможность привести данные к одному виду

И так DEMO:

Немного расскажу что он умеет:

  1. Брать данные по HTTP запросам с авторизацией по Header

  2. Брать данные с бинарника Chromium

  3. Брать данные с Docker браузеров

  4. Брать данные с Playwright

  5. Парсить данные HTML/Json/XPath

  6. Пробрасывать данные/связывать с разных источников

Планы на будущее(Roadmap):

  1. Добавить сценарии: некоторые сайты для парсинга требуют авторизацию/принять cookie и тд тп, будет набор команд которые можно будет прогнать до парсинга и после

  2. Добавить способы отдачи информации: чтобы проект смог отправлять Webhook/Queue сообщения и тд тп

  3. Добавить способы сбора информации: сегодня пришла идея чтоб источником информации может быть любая штука например телеграм канал: и для этого нам нужен допустим бот с доступам к сообщениям

  4. Добавить способы запуска: тот же Webhook/Queue

  5. Валидация — отсеевать не валидные данные

  6. Редактор конфигурации — смотри ниже

Текущие точки боли(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"                         }                       }                     }                   }                 }               }             }           }         }       }     }   } }

Как мы видим это больно, но давайте расскажу что тут происходит:

  1. Мы настраиваем лимиты параллельных запуска Playwright: 3 штуки

  2. Мы будем читать GET запросом HTML c сайта: http://www.citymayors.com/gratis/uk_topcities.html

  3. На выходе мы ожидаем массив следующего вида:

Array<City>  City = {   name: string;   population: string;   temperature: string }
  1. Если с полями name/population +- все понятно мы их берем из таблицы с сайта указанного выше, то temperature это сгенерированное поле

  2. Мы берем название города из таблицы и пробрасываем его на сайт: https://openweathermap.org/find?q={PL} — где {PL} — имя города , для этого мы используем Playwright так как там client-side rendering

  3. С результатов поиска мы берем relative ссылку на страницу города, пример: /city/2950159 и подставляем https://openweathermap.org{PL} — где {PL} ссылка, для этого мы используем Playwright так как там client side-rendering

  4. По ссылке выше выдергиваем температуру по селектору: `div.current-temp span.heading`

  5. И разворачиваем поле путем парсинга: `temp.temp` из сгенирированных данных

  6. Пример элемента массива

{   "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/


Комментарии

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

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