Frontera: архитектура фреймворка для обхода веба и текущие проблемы

Всем привет, я занимаюсь разработкой Frontera, первым в истории фреймворком для масштабного обхода интернета сделанным на Python-е, с открытым исходным кодом. С помощью Фронтеры можно легко сделать робота который сможет выкачивать контент со скоростью тысяч страниц в секунду, при этом следуя вашей стратегии обхода и используя обычную реляционную БД или KV-хранилище для хранения базы ссылок и очереди.

Разработка Фронтеры финансируется компанией Scrapinghub Ltd., имеет полностью открытый исходный код (находится на GitHub, BSD 3-clause лицензия) и модульную архитектуру. Мы стараемся чтобы и процесс разработки тоже был максимально прозрачным и открытым.

В этой статье я собираюсь рассказать о проблемах с которыми мы столкнулись при разработке Фронтеры и эксплуатации роботов на ее основе.

Устройство распределенного робота на базе Фронтеры выглядит так:
image

На картинке изображена конфигурация с хранилищем Apache HBase и шиной данных Apache Kafka. Процесс запускается из воркера стратегии обхода (SW), который планирует URL-ы, с которых нужно начать обход. Эти URL-ы попадают «scoring log», топик кафки (выделенный канал сообщений), потребляются воркером БД и им же планируются в топик «new batches», откуда поступают в пауки на основе Scrapy. Паук Scrapy разрешает DNS имя, поддерживает пул открытых соединений, получает контент и посылает его в топик «spider log». Откуда контент потребляется снова воркером стратегии и в зависимости от логики закодированной в стратегии обхода он планирует новые URL-ы.

Если кому-то интересно, то вот видео моего доклада о том, как мы обходили испанский интернет с помощью Фронтеры.



На текущий момент мы столкнулись с тем, что людям которые пытаются развернуть робота у себя очень тяжело дается конфигурирование распределенных компонент. К сожалению, развертывание Фронтеры на кластере предполагает понимание ее архитектуры, чтение документации, правильную настройку шины данных (message bus) и хранилища под нее. Все это очень затратно по времени и отнюдь не для начинающих. Поэтому мы хотим автоматизировать развертывание на кластере. Мы выбрали Kubernetes и Docker для достижения этих целей. Большой плюс Kubernetes в том, что он поддерживается многими облачными провайдерами (AWS, Rackspace, GCE и др.) Т.е. с помощью него можно будет развернуть Фронтеру даже не имея настроенного Kubernetes кластера.

Другая проблема в том, что Фронтера она как система водоснабжения ядерной электростанции. В ней есть потребители и производители для них очень важно контролировать такие характеристики, как скорость потока и производительность, в разных местах системы. Здесь следует напомнить, что Frontera это онлайн система. Классические роботы (Apache Nutch), напротив, работают в пакетном режиме: сначала порция планируется, затем скачивается, затем парсится и снова планируется. Одновременный парсинг и планирование не предусмотрены в таких системах. Таким образом, у нас встает проблема синхронизации скорости работы разных компонент. Довольно тяжело спроектировать робота, который обходит страницы с постоянной производительностью с большим количеством потоков, при этом сохраняет их в хранилище и планирует новые. Разнится скорость ответа веб-серверов, размер и количество страниц на сайте, количество ссылок, все это делает невозможным точную подгонку компонент по производительности. В качестве решения этой проблемы мы хотим сделать веб-интерфейс на базе Django. Он будет отображать основные параметры и если получится подсказывать какие нужно принять меры оператору робота.

Я уже упоминал о модульной архитектуре. Как и любой проект с открытым кодом мы стремимся к разнообразию технологий, которые мы поддерживаем. Поэтому сейчас у нас в Pull Request’ах готовится поддержка распределенного Redis, было уже две попытки сделать поддержку Apache Cassandra, ну и мы бы хотели поддержку RabbitMQ в качестве шины данных.

Все эти проблемы мы рассчитываем хотя бы частично решить в рамках Google Summer Of Code 2017. Если Вам что-то кажется интересным, и Вы студент, то дайте нам знать на frontera@scrapinghub.com и подайте заявку через форму GSoC. Подача заявок начинается с 20 марта и продлится до 3 апреля. Полное расписание GSoC 2017 здесь.

Конечно же, есть еще и проблемы производительности. На данный момент в Scrapinghub в пилотном режиме работает сервис по массивному скачиванию страниц на основе Фронтеры. Клиент нам предоставляет набор доменов, к которым он бы хотел получить контент, а мы их скачиваем и помещаем в S3 хранилище. Оплата за скачанный объем. В таких условиях мы стараемся качать как можно больше страниц в единицу времени. При попытке масштабировать Фронтеру до 60 спайдеров мы с толкнулись с тем, что HBase (через Thrift интерфейс) деградирует при записи в него всех найденных ссылок. Эти данные мы называем ссылочной базой, а нужна она для того, чтобы знать когда и что мы скачали, какой ответ получили и т.п. На кластере из 7 регион серверов у нас кол-во запросов доходит до 40-50K в секунду и при таких объемах время ответа сильно увеличивается. Производительность записи сильно падает. Решать такую проблему можно разными способами: например сохранять в отдельный быстрый лог, а позже его пакетными методами записывать в HBase, или же обращаться в HBase напрямую минуя Thrift, через собственный Java клиент.

Также мы постоянно работаем над улучшением надежности. Слишком большой скачанный документ или внезапные сетевые проблемы не должны приводить к остановке компонент. Тем не менее это иногда случается. Для диагностики таких случаев мы сделали обработчик сигнала ОС SIGUSR1, который сохраняет в лог текущий стек процесса. Несколько раз нам это очень сильно помогло разобраться в чем была проблема.

Мы планируем и дальше улучшать Фронтеру, и надеемся что сообщество активных разработчиков будет расти.
Поделиться публикацией
Комментарии 34
    0
    Интересный продукт, спасибо!
    Подскажите, а Scrapinghub умеет работать с «динамическими» страницами с большим количеством JS? Например, ждать появления какого либо элемента страницы, прежде чем начать парсить страницу.
      0
      Это крайне нетривиальная задача, а тут обычный паук, вам нужен куда более «сообразительный» парсер.
        0
        Тривиальная но крайне требовательная к ресурсам — так имхо правильнее. На фантоме пишется за вечер. Я писал.
          0
          Для одного сайта. После того как вы посмотрели его глазами и нашли к чему привязаться в xpath (теги, id, вложенность). И написали скрипты именно для этого сайта. А сайтов тысячи.
            0
            Мы похоже про разные вещи говорим.
            дернули спашку, отрендерили в фантоме, слили построенный дом — дальше можно индексировать как обычную статику. Про эмуляцию действий пользователя речи не шло.
              0
              Ок. Отрендерили вы DOM, а если на живом сайте скролить вниз происходит подгрузка данных. То есть на первом шаге вы не получает все данные. Если это не критично, то конечно так можно поступить.
                0
                В целом согласен с вами фантом не предоставляет подобного функционала и обычно делаеться это либо ожиданием появления какого то элемента либо функцией которая запускаеться после полной загрузки страницы и переодически проверяет не появились ли новые запросы в течении какого то времени и если нет то считается что сайт отренедрился
                  0
                  а это уже селениум c драйвером фантома. Но тут да — под каждый сайт (движок) свои хаки. Ну или подождать сильный ИИ )
                    0

                    Если сайт не сильно мудреный — можно делать AJAX запрос руками. В большынстве случаев такой способ роботоспособный.

                    0
                    Да, в таком юзкейсе Вы правы и это очевидно. Я говорил именно об индексации страниц, генерируемых js без действий пользователя. Их тоже немало, других простых способов решить вопрос — нет, гугль, насколько мне известно поступает также.
                      0
                      Колесико мышки-то сейчас имеет смысл эмулировать для всех сайтов.
                      На 99,9999999% решит вопрос подгрузки страницы.
              0
              Пока что только применительно к конкретному веб-сайту. У нас есть своя разработка https://github.com/scrapinghub/splash
              0
              Возможно перед HBase поставить фильтр Блума, чтобы поменьше его кантовать? Или там иного рода проблемы?
                0
                В HBase он уже есть встроенный.
                –1
                Я далек от Web'а, и сейчас, наверное, моя карма уйдет в небытие =), но подскажите, правильно ли я понял, разрабатывается фреймворк, который исполняет роль поисковика?
                  0
                  Этот фреймворк скачивает документы из сети. Скачанный контент может использоваться для построения поисковика.
                  • НЛО прилетело и опубликовало эту надпись здесь
                      0
                      Это тоже верно. Просто по мере разработки мы поняли, что стратегия обхода отдельно от хранилища и от других компонент, отвечающих за обмен данными не представляет большой пользы. В результате мы разработали эти компоненты и приняли решение, что Фронтера будет навязывать архитектуру всего робота. При этом мы старались сделать её максимально расширяемой.
                      • НЛО прилетело и опубликовало эту надпись здесь
                          0
                          Использование Scrapy не является обязательным при построении роботов на Fronter'е. Но если Вы берете воркер стратегии, разрабатываете под него стратегию обхода, то должны будете считаться с тем, как во Fronter'е реализован менеджмент состояний, очередь и обмен данными между компонентами.
                    0
                    Он роль поискового робота выполняет.
                  • НЛО прилетело и опубликовало эту надпись здесь
                      0
                      Скорее наоборот, это понимание у них не появится пока они не возьмут и не попробуют. А вот с этим у нас не все хорошо.
                      • НЛО прилетело и опубликовало эту надпись здесь
                    • НЛО прилетело и опубликовало эту надпись здесь
                        0
                        >Разнится скорость ответа веб-серверов, размер и количество страниц на сайте

                        Учитываете ли при работе данные с robots.txt (второй версии желательно) Если от ваших запросов сервер начинает тупить — мониторите/замеряете/корректируете частоту запросов?

                        Писал паука для одного из проектов, имхо — если не самая сложная то одна из по головоломности частей получилась как раз работа с robots.txt Но у меня там еще загон был — разные политики по юзер-агенту (статичный, round-robin, кастом) и юзерагент мог как учитываться так и нет при обработке robots (в зависимости от настроек)
                          0
                          Статья про фреймворк, а не про конкретный робот. Есть два способа обрабатывать robots.txt в рамках Fronter'ы и Scrapy. Самое простое, это использовать RobotsTxtMiddleware в Scrapy. Перед обработкой URL запрашивается robots.txt с домена, кешируется, и все запросы к домену проверяются если допустимы в рамках robots.txt домена.
                          Второй способ, это интегрировать обработку robots.txt в стратегию обхода, и попросту не планировать URL, которые запрещены в robots.txt.
                            0
                            allow/disallow отрабатываются тривиально, речь шла о нормальной работе с Request-rate и Visit-time. Но ответ Ваш я понял, спасибо!
                          0
                          Люди, которые научатся доставать исходные данные из react-кода без выполнения js — озолотяться
                            0
                            Что вы под этим подразумеваете? Какой юзкейс?
                              0
                              Юзкейс прост — экономить деньги/время. Вместо запуска фантомов для выполнения js — простой curl
                            0

                            Я так понимаю, автор имеет отношение к Scrapinghub. Скажите, а ваша Portia — она насколько стабильно работает и насколько сложный парсинг поддерживает?


                            Попробовал вчера hosted версию — при работе с переменным числом блоков эвристики постоянно промахиваются, и получается мусор. При попытке выгрузить "накликанный" парсер в формате Scrapy — выгружает что-то похожее на mock'овый код — модельки описаны, а алгоритм парсинга — нет.

                              0
                              Насколько знаю, внутри portia — библиотечка https://github.com/scrapy/scrapely. Ну т.е. там еще куча всего есть, но вроде дефолтное извлечение выполняется именно через scrapely. Это и есть алгоритм парсинга.
                                0
                                Пришлите конкретные поля/запросы, мы посмотрим со своей стороны.

                              Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                              Самое читаемое