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/


Интересные статьи

Интересные статьи

Рассматривается проблема COVID-19 и респираторная терапия дыхательной недостаточности, ассоциированной с COVID-19. Даны примеры клинических алгоритмов на медицинском алгоритмическом языке ДРАКОН. Счет...
Привет, Хабр! Мы продолжаем серию докладов Deep Dive Into Deep Link. Как думаете, что будет, если перейти по deep link на Samsung A70 и Pixel 2? А на Android 10.0 и 8.0? Кажется, что все будет нормаль...
Данная статья, является продолжением, описания хода учебного исследовательского проекта по разработке рекомендательной системы.В первой части мы остановились на том, что модель, полученная с использов...
Мы двигаемся к финалу нашей саги об интеграции Raspberry Pi 4 в выделенные серверы. В первом тексте я рассказал об отличиях процесса загрузки «малинок» от «классических» серверов. Во втором — собрал...
Привет всем. Это продолжение истории про переход команды PVS-Studio на работу по методике kanban. На этот раз речь пойдет про YouTrack, как мы выбирали и внедряли этот трекер задач и с какими вызовами...