Simple crawler.
Необходимо создать простой интернет crawler, который будет доставать из страниц информацию о названии сайта. Это должно быть приложение с http эндпоинтом. На этот эндпоинт поступает список http url'ов. Приложение должно пройтись по всем предоставленным урлам и достать оттуда название. Названием условно будем считать содержимое тэга title. После изъятия информации из всех страниц эндпоинт должен вернуть ответ, в котором каждому входному урлу соответствует найденное название. Все недостающие требования или неоднозначности начальной формулировки задачи вы должны разрешить самостоятельно - это является частью задания. Единственное требование к реализации - приложение должно быть написано на языке Scala.
Было решено сделать два варианта исполнения запросов: синхронный и асинхронный.
При использовании синхронного api на эндпоинт шлется POST-запрос вида:
curl \
-X POST \
--data-urlencode 'urls=<url1>,<url2>,...,<urlN>' \
'http://<hostname>:<port_number>/crawler/sync/query'
где
- url1,...,urlN - урлы страниц с названиями сайтов, которые необходимо запарсить
- hostname - имя хоста или ip-адрес эндпоинта
- port - номер порта привязки эндпоинта
Примечание: здесь и далее предполагается, что команды выполняются в POSIX-совместимой оболочке командной строки (bash, zsh, etc...)
Этот запрос является блокирующим до окончания парса всей информации по урлам из запроса. После окончания парса клиенту возвращается извлеченная со страниц информация в формате:
{
"result" : {
"results" : [
{
"info" : {
"title" : "Google"
},
"url" : "https://google.com"
},
{
"info" : {
"title" : "Яндекс"
},
"url" : "https://yandex.ru"
},
{
"info" : {
"title" : "НГС.НОВОСИБИРСК"
},
"url" : "https://ngs.ru"
}
],
"status" : "complete"
},
"status" : "OK",
"code" : 200
}
При использовании асинхронного api на эндпоинт шлется POST-запрос вида:
curl \
-X POST \
--data-urlencode 'urls=<url1>,<url2>,...,<urlN>' \
'http://<hostname>:<port_number>/crawler/async/query'
Возврат из запроса происходит мгновенно и клиенту возвращается id (строка в формате UUID) запроса для последующего извлечения информации:
{
"result" : {
"query_id" : "bba04c4b-4bb7-4f1e-a543-6bcf33068943"
},
"status" : "OK",
"code" : 200
}
Получение информации об исполнении запроса делается с помощью следующего запроса:
curl \
-X GET \
'http://<hostname>:<port_number>/crawler/async/query/bba04c4b-4bb7-4f1e-a543-6bcf33068943'
Формат вывода аналогичен формату синхронного запроса.
Результаты асинхронных запросов хранятся внутри api определенное время, по истечении которого удаляются. Кэш запросов также ограничен по количеству запросов.
- Для реализации http эндпоинта был выбран фреймворк
akka-http. - Парс информации о сайтах внутри эндпоинта реализован в виде отдельной подсистемы
на основе фреймворка
akka. Когда подсистема получает задание в виде списка урлов, она разбивает этот список на отдельные элементы и парсит каждый урл в отдельном потоке с помощью пулов акторов, завязанных на пулы потоков через диспетчеры фреймворка. - Для загрузки страниц используется HTTP-клиент
OkHttpClient. - Для парса HTML используется библиотека JSoup.
- Для работы с json используется библиотека Argonaut.
Проект собран в самодостаточный uberjar с помощью системы сборки sbt с использованием плагина sbt-assembly.
Jar-файл запускается с указанием двух параметров:
java -jar crawler-api.jar -h <host> -p <port>
где
- - имя хоста привязки
- - номер порта привязки