Вот как я ползаю

Почему в Мире?

Когда меня наняла компания для работы над большой устаревшей системой, я столкнулся с проблемой. QA-специалисты тратили чрезмерное количество времени на тестирование плохо написанного приложения, управляемого данными. Наша команда не могла заблаговременно выявлять и устранять ошибки в среде кодирования, столь подверженной сбоям. Нам нужно было решение. Вот тогда меня осенило: что, если я создам бота, который будет сканировать наши продукты, чтобы помочь нашим тестерам искать ошибки и производить более качественную работу с большей эффективностью? Хотя для таких языков, как Python и Javascript, существовало несколько фреймворков, в то время не было ничего, что помогло бы моей команде поддерживать продукты, использующие PHP. Эта необходимость вдохновила меня на создание собственной среды сканирования PHP. CrawlZone.

Итак, в этом посте я хотел бы рассказать о некоторых архитектурных решениях и ошибках, которые я допустил при создании библиотеки, а также о некоторых рекомендациях по использованию и юридических соображениях. Он ориентирован на инженеров-программистов, но может быть интересен людям, которые хотят получить общее представление о парсинге данных.

1. Тестирование

Увидев, как мои коллеги по тестированию с саркастической улыбкой разбивают мои вещи, я решил внести некоторые изменения и максимально автоматизировать свое тестирование. Это дает вам огромное преимущество и большую уверенность на многих этапах создания и обработки вашего кода.

Мне потребовалось некоторое время, чтобы понять, что мне нужно для настройки среды тестирования, поскольку вы должны иметь возможность сканировать несколько доменов, фильтровать URI, а также тестировать задержки и перенаправления.

Во всяком случае, короче говоря, Докер это все, что тебе нужно. Он позволяет настроить многодоменную инфраструктуру за считанные минуты без необходимости погружаться в ад настройки. Кроме того, TravisCI, которая является моей любимой платформой непрерывной интеграции, может запускать и создавать образы Docker. Если вам интересно, как я устанавливаю TravisCI с Docker, вот ссылка на мой .travis.yml файл и докер-compose.yml файл. я использую Makefile чтобы создать несколько полезных ярлыков.

2. Архитектура

Гит знает только, сколько раз я полностью переписывал эту архитектуру, начиная с нуля. Он превратился из простой рекурсивной функции в то, что вы видите в фигура 1.

В его основе лежит планировщик запросов (движок), который выполняет асинхронные запросы, используя Жрать HTTP-клиент координирует события и запускает стек промежуточного программного обеспечения.
Архитектура веб-краулера.svg
Рисунок 1. Двигатель

Вот что происходит для одного запроса при запуске клиента:

  1. Клиент ставит в очередь первоначальный запрос (start_uri).
  2. Движок просматривает очередь и проверяет, есть ли какие-либо запросы.
  3. Движок получает запрос из очереди и выдает BeforeRequestSent мероприятие. Если в конфиге задана глубина, то RequestDepth extension проверяет глубину запроса. Если в конфиге установлена ​​опция подчиняться robots.txt, то RobotTxt расширение проверяет, соответствует ли запрос правилам. В случае, когда запрос не выполняется, движок выдает RequestFailed событие и получает следующий запрос из очереди.
  4. Движок использует стек промежуточного программного обеспечения запроса, чтобы передать запрос через него.
  5. Движок отправляет асинхронный запрос с помощью HTTP-клиента Guzzle.
  6. Двигатель излучает AfterRequestSent событие и сохраняет запрос в истории, чтобы избежать повторного сканирования одного и того же запроса.
  7. Когда заголовки ответа получены, но тело еще не начало загружаться, механизм выдает сообщение ResponseHeadersReceived мероприятие.
  8. Двигатель излучает TransferStatisticReceived мероприятие. Если в конфиге установлена ​​опция автозапуска, то AutoThrottle расширение выполняется.
  9. Механизм использует стек промежуточного программного обеспечения ответа, чтобы передать ответ через него.
  10. Двигатель излучает ResponseReceived мероприятие. Кроме того, если код состояния запроса больше или равен 400, механизм выдает RequestFailed мероприятие.
  11. ResponseReceived запускает ExtractAndQueueLinks расширение, которое извлекает и ставит в очередь ссылки. Процесс начинается сначала, пока очередь не опустеет.

3. Расширения

Как видно из обзора, архитектура сильно зависит от событий, чтобы отделить логику и обеспечить расширяемость кода. Расширения — это не что иное, как прослушиватели событий, основанные на Диспетчер событий Symfony составная часть.

Чтобы создать расширение, все, что вам нужно сделать, это расширить Extension class и добавьте его в клиент:

use Crawlzone\Client;
use Crawlzone\Extension\Extension;
...
$config = [
    'start_uri' => ['
];

$client = new Client($config);

$client->addExtension(new class() extends Extension {
...
});

$client->run();

Все расширения имеют доступ к Queue так что вы можете поставить в очередь дополнительные запросы и к HTTP-клиенту, чтобы сделать новые запросы (для аутентификации для пример).

Существует довольно много событий, которые можно подключить к процессу выполнения для самых разных целей:

  1. BeforeEngineStarted — Полезно, когда вы хотите что-то инициализировать, прежде чем начать сканирование. Я использую его для создания базы данных SQLite для хранения очереди и истории.

  2. BeforeRequestSent — Вы можете использовать его для проверки запроса перед его отправкой. Я использую его для проверки глубины запроса и соблюдения robots.txt.

  3. AfterRequestSent — Полезно, когда вы хотите выполнить определенные действия после того, как запрос был отправлен, но ответ еще не получен.

  4. TransferStatisticReceived — Это интересно. Он отправляется, когда обработчик завершает отправку запроса, что позволяет вам получить доступ к деталям передачи более низкого уровня. Он используется для автоматического регулирования задержки между запросами в расширении Autothrottle.

  5. ResponseHeadersReceived — Генерируется, когда HTTP-заголовки ответа получены, но загрузка тела еще не началась. Полезно, если вы хотите, например, отклонить ответы, превышающие определенный размер.

  6. RequestFailed — Отправляется, когда запрос не выполнен (код состояния 5xx) или когда расширение или промежуточное программное обеспечение вызывает InvalidRequestException. Вы можете использовать его, например, для регистрации ошибок сервера 5xx.

  7. ResponseReceived — Полезно для регистрации ответа и получения данных. Я использую его для извлечения и постановки в очередь ссылок. Кроме того, вместо того, чтобы следовать перенаправлениям, я планирую их для последующего использования. Он обеспечивает более согласованное поведение при обработке запросов.

  8. AfterEngineStopped — Происходит, когда очередь пуста и больше нет запросов на отправку. Используйте его для выполнения очистки или отправки уведомлений.

4. Промежуточное ПО

Хотя расширения позволяют вам подключиться к процессу сканирования, они не позволят вам изменить запрос или ответ (например, добавить дополнительные заголовки). Вот где промежуточное ПО может пригодиться. Для этой цели я построил простой стек промежуточного программного обеспечения. Чтобы создать промежуточное ПО запроса или ответа, просто реализуйте RequestMiddleware или же ResponseMiddleware интерфейс и добавить его в клиент. Вот пример:

use Psr\Http\Message\RequestInterface;
use Crawlzone\Client;
use Crawlzone\Middleware\RequestMiddleware;
...
$config = [
    'start_uri' => ['https://httpbin.org/ip']
];
$client = new Client($config);
$client->addRequestMiddleware(
    new class implements RequestMiddleware {
        public function processRequest(RequestInterface $request): RequestInterface
        {
            $request = $request->withHeader("User-Agent", "Mybot/1.1");
            return $request;
        }
    }
);
$client->run();

5. Хранение

Сначала я попытался сохранить историю и очередь в памяти. Он хорошо работал для небольших веб-сайтов, но с трудом масштабировался с объемом. Если вам нужно просканировать тысячи страниц, этот подход не работает, потому что вы теряете прогресс, когда процесс прерывается.

Затем я начал писать свой обработчик для хранения истории и очереди на диске, но вскоре понял, что для эффективного извлечения данных необходимо кодировать B-дерево.

Мне нужен был механизм хранения для хранения и индексации данных в памяти для тестирования или на диске с простым API для извлечения данных. Это где SQLite сияет. Согласно их веб-сайту, это наиболее часто используемый движок базы данных в мире. Однако привязка конкретного механизма базы данных к вашей архитектуре является огромной ошибкой. Абстрагирование вашего хранилища — это правильный путь, поскольку это дает вам возможность позже поменять механизм базы данных, если вам это нужно.

Еще одна проблема, с которой я столкнулся, заключалась в том, чтобы не ставить в очередь один и тот же запрос дважды. Первоначально я думал, что будет достаточно просто нормализовать абсолютный URI и сохранить его в качестве первичного ключа. Однако, если вы хотите, например, поставить в очередь запрос POST, этот подход не работает. Изучив тему и посмотрев другие подобные фреймворки, в частности, Python Скрапия обнаружил, что использование отпечатка пальца запроса было лучшим и наиболее гибким вариантом.

запросить отпечаток пальца это хэш, однозначно идентифицирующий ресурс, на который указывает запрос. Например, возьмем следующие три запроса:

 1. GET 
 2. GET 
 3. POST 

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

Заголовки авторизации — еще один отличный пример. Многие сайты используют файлы cookie для хранения идентификатора сеанса, что добавляет к запросу случайную часть.

Вычислить отпечаток несложно, это просто хэш метода, URI и тела запроса. По умолчанию заголовки запроса не включаются в вычисление отпечатка пальца, потому что вы можете поставить один и тот же запрос в очередь несколько раз. Я оставил эту опцию настраиваемой на случай, если она понадобится мне позже.

6. Вежливость: важность ползания белых шляп

В мои первоначальные требования не входило делать краулера милым, поскольку иногда бывает удобно быть грубым. Я осознал важность этического сканирования только на этапе тестирования, когда завалил свою тестовую среду кучей запросов, которые фактически сделали ее недоступной.

Моей первой попыткой решить проблему было использование статической задержки между каждым запросом. По большей части это работало нормально, за исключением того, что краулер стал очень медленным, что, по сути, было идеальным для производства. Суть в том, чтобы никоим образом не влиять на производительность сайта. Однако при локальном тестировании вы хотите получить обратную связь как можно скорее.

Поэтому в идеале сканер должен динамически изменять задержку между запросами в зависимости от текущей загрузки сервера. Вот тут-то и вступает в игру расширение автоматического дросселя. Алгоритм автоматически регулирует задержку на основе среднего времени ответа, состояния ответа и параллелизма.

Соблюдение правил файла robots.txt было еще одной отличной функцией, которую легко реализовать с помощью расширений, что сделало поисковый робот еще более успешным.

Когда я начал работать над поисковым роботом, я был немного наивен, полагая, что я могу использовать CrawlZone для сканирования всего, что захочу. Я подумал, что раз вы можете получить доступ к данным через браузер, то они общедоступны, и я могу собирать их любым способом, каким захочу. Оказывается, очистка данных может быть потенциально рискованным занятием.
Взгляните на часть Пользовательское Соглашение взято с www.linkedin.com:

8.2. Не

м. Использовать ботов или другие автоматизированные методы для доступа к Услугам, добавления или загрузки контактов, отправки или перенаправления сообщений;

у фейсбука есть похожие условия:

Чем вы можете поделиться и что делать на Facebook:

Вы не можете получать доступ или собирать данные из наших Продуктов с помощью автоматизированных средств (без нашего предварительного разрешения) или пытаться получить доступ к данным, на доступ к которым у вас нет разрешения.

Поэтому, прежде чем вы начнете использовать сканер в дикой природе, в качестве меры предосторожности изучите и соблюдайте условия их использования, чтобы избежать возможных судебных исков. И я не даю вам никаких советов по этому поводу, пожалуйста, проконсультируйтесь со своим адвокатом.

Вывод

Работа над Crawlzone была интересным и сложным проектом. Я изучил многие аспекты веб-сканирования и очистки данных. Это также помогло командам контроля качества, с которыми я сотрудничаю, сделав их жизнь намного комфортнее.

Не стесняйтесь проверить библиотеку зона обхода/зона обхода.
Дайте мне знать, что вы думаете о статье. Я приветствую ваши отзывы. Спасибо!

Похожие записи

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

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