Перейти к основному содержимому

Добавление URL-адресов

В предыдущем разделе вы создали простой краулер, который загружает HTML одной страницы, читает её заголовок и выводит его в консоль. Вот исходный код:

import { CheerioCrawler } from 'crawlee';

const crawler = new CheerioCrawler({
async requestHandler({ $, request }) {
const title = $('title').text();
console.log(`Заголовок страницы "${request.url}": ${title}.`);
}
})

await crawler.run(['https://crawlee.dev']);

Теперь мы улучшим этот пример. Добавим больше URL-адресов в очередь, благодаря чему краулер будет находить новые ссылки, добавлять их в RequestQueue и обрабатывать их.

Как работает краулинг

Процесс довольно прост:

  1. Находим новые ссылки на странице.
  2. Фильтруем только те, которые ведут на тот же домен (в нашем случае crawlee.dev).
  3. Добавляем их в RequestQueue.
  4. Посещаем новые ссылки.
  5. Повторяем процесс.

Далее вы познакомитесь с функцией enqueueLinks, которая упрощает краулинг до одного вызова функции. Для сравнения мы также покажем эквивалентное решение без использования enqueueLinks.

Контекстная осведомленность enqueueLinks

Функция enqueueLinks учитывает контекст. Это означает, что она автоматически получает информацию о текущей обрабатываемой странице из контекста, и вам не нужно явно указывать аргументы. Она находит ссылки с помощью функции Cheerio $ и автоматически добавляет их в RequestQueue работающего краулера.

Ограничение краулинга с помощью maxRequestsPerCrawl

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

const crawler = new CheerioCrawler({
maxRequestsPerCrawl: 20,
// ...
});

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

Поиск новых ссылок

Существует множество подходов к поиску ссылок при краулинге. В нашем случае мы будем искать элементы <a> с атрибутом href, так как это наиболее распространенный случай. Например:

<a href="https://crawlee.dev/docs/introduction">Это ссылка на введение в Crawlee</a>

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

src/main.mjs
import { CheerioCrawler } from 'crawlee';

const crawler = new CheerioCrawler({
// Let's limit our crawls to make our
// tests shorter and safer.
maxRequestsPerCrawl: 20,
// enqueueLinks является аргументом requestHandler
async requestHandler({ $, request, enqueueLinks }) {
const title = $('title').text();
console.log(`The title of "${request.url}" is: ${title}.`);
// Функция enqueueLinks является контекстно-зависимой,
// поэтому не требует никаких параметров.
await enqueueLinks();
},
});

await crawler.run(['https://crawlee.dev']);

Если вам нужно переопределить стандартный выбор элементов в enqueueLinks, вы можете использовать аргумент selector:

await enqueueLinks({
selector: 'div.has-link'
});

Фильтрация ссылок на тот же домен

Веб-сайты обычно содержат множество ссылок, ведущих на другие ресурсы. Это нормально, но при сканировании сайта мы обычно хотим обрабатывать только один конкретный сайт, а не позволять нашему краулеру уходить на Google, Facebook или Twitter. Поэтому нам нужно отфильтровать внешние ссылки и оставить только те, которые ведут на тот же домен.

src/main.mjs
import { CheerioCrawler } from 'crawlee';

const crawler = new CheerioCrawler({
maxRequestsPerCrawl: 20,
async requestHandler({ $, request, enqueueLinks }) {
const title = $('title').text();
console.log(`The title of "${request.url}" is: ${title}.`);
// По умолчанию enqueueLinks работает на том же имени хоста,
// поэтому не требует никаких параметров.
// Это гарантирует, что поддомен останется тем же.
await enqueueLinks();
},
});

await crawler.run(['https://crawlee.dev']);

По умолчанию enqueueLinks работает только с тем же хостом. Это не включает поддомены. Чтобы включить поддомены в сканирование, используйте аргумент strategy:

await enqueueLinks({
strategy: 'same-domain'
});

При запуске кода вы увидите, как краулер сначала выводит заголовок первой страницы, затем сообщение о добавлении в очередь с количеством URL-адресов, после чего заголовок первой обработанной страницы из очереди и так далее.

Пропуск повторяющихся URL-адресов

Пропуск повторяющихся URL-адресов критически важен, поскольку повторное посещение одной и той же страницы приведет к дублированию результатов. Это автоматически обрабатывается RequestQueue, который исключает дубликаты запросов, используя их uniqueKey. Этот uniqueKey автоматически генерируется из URL запроса путем приведения URL к нижнему регистру, лексической сортировки параметров запроса, удаления фрагментов и некоторых других преобразований, гарантирующих, что в очереди будут только уникальные URL-адреса.

Расширенные аргументы фильтрации

Хотя настройки по умолчанию для enqueueLinks часто могут быть именно тем, что вам нужно, функция также предоставляет точный контроль над тем, какие URL-адреса должны быть добавлены в очередь. Один из способов мы уже упоминали выше - использование EnqueueStrategy. Вы можете использовать стратегию All, если хотите следовать по всем ссылкам независимо от домена, или добавлять в очередь ссылки, ведущие на тот же домен, используя стратегию SameDomain.

await enqueueLinks({
strategy: 'all', // исследовать весь интернет
});

Фильтрация URL-адресов с помощью шаблонов

Для еще большего контроля вы можете использовать globs, regexps и pseudoUrls для фильтрации URL-адресов. Каждый из этих аргументов всегда является массивом, но их содержимое может принимать различные формы. Смотрите справочник для получения дополнительной информации о них и других опциях.

Переопределение настроек по умолчанию

Если вы указываете один из этих параметров, стратегия по умолчанию same-hostname не будет применяться, если явно не указана в опциях.

await enqueueLinks({
globs: ['http?(s)://apify.com/*/*'],
});

Преобразование запросов

Для полного контроля у нас есть transformRequestFunction. Непосредственно перед созданием нового Request и добавлением его в RequestQueue, эта функция может быть использована для его пропуска или изменения содержимого, такого как userData, payload или, что наиболее важно, uniqueKey. Это полезно, когда вам нужно добавить в очередь несколько запросов с одинаковым URL, но разными методами или полезной нагрузкой. Другой вариант использования - динамическое обновление или создание userData.

await enqueueLinks({
globs: ['http?(s)://apify.com/*/*'],
transformRequestFunction(req) {
// игнорировать все ссылки, заканчивающиеся на `.pdf`
if (req.url.endsWith('.pdf')) return false;
return req;
},
});

Вот и всё! enqueueLinks() - это только один пример мощных вспомогательных функций Crawlee. Все они разработаны, чтобы упростить вашу работу, позволяя сосредоточиться на получении данных и оставив рутинное управление сканированием инструментам.

Следующие шаги

Далее вы начнете свой проект по сбору данных с реального веб-сайта и узнаете еще несколько приемов работы с Crawlee.