Pull to refresh

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

Level of difficultyEasy
Reading time4 min
Views3K

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

Возможно выбрал не лучшее время для охвата аудитории, но тем не менее главное чтоб продукт был хороший, а не статья о нем. Последние несколько недель я пишу приложение в рамках которого надо собирать огромное количество информации из сети(запросы к 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 можно поиграться

Tags:
Hubs:
Total votes 5: ↑4 and ↓1+5
Comments9

Articles