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

Хранение запросов

Crawlee предлагает несколько типов хранилищ запросов для различных задач. Запросы сохраняются локально на диск в директорию, указанную в переменной окружения CRAWLEE_STORAGE_DIR. Если эта переменная не задана, Crawlee по умолчанию использует директорию ./storage в текущей рабочей папке.

Очередь запросов

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

Каждый запуск проекта Crawlee связан с очередью запросов по умолчанию. Обычно она используется для хранения URL-адресов, которые нужно обойти в рамках конкретного запуска краулера. Её использование необязательно.

В Crawlee очередь запросов представлена классом RequestQueue.

Очередью запросов управляет класс MemoryStorage. Данные хранятся в памяти и периодически сохраняются в локальную директорию, указанную в переменной CRAWLEE_STORAGE_DIR, по следующему пути:

{CRAWLEE_STORAGE_DIR}/request_queues/{QUEUE_ID}/entries.json
примечание

{QUEUE_ID} - это имя или идентификатор очереди запросов. У очереди по умолчанию ID равен default, если не переопределить его через переменную окружения CRAWLEE_DEFAULT_REQUEST_QUEUE_ID.

примечание

entries.json содержит массив запросов.

Вот пример использования очереди запросов:

import { CheerioCrawler } from 'crawlee';

// Краулер автоматически обрабатывает запросы из очереди.
// Он используется таким же образом для краулеров Puppeteer/Playwright.
const crawler = new CheerioCrawler({
// Обратите внимание, что мы здесь не указываем requestQueue
async requestHandler({ $, crawler, enqueueLinks }) {
// Добавляем новый запрос в очередь
await crawler.addRequests([{ url: 'https://example.com/new-page' }]);
// Добавляем найденные ссылки на странице в очередь
await enqueueLinks();
},
});

// Добавляем начальные запросы.
// Обратите внимание, что мы здесь не открываем очередь запросов явно перед этим.
await crawler.addRequests([
{ url: 'https://example.com/1' },
{ url: 'https://example.com/2' },
{ url: 'https://example.com/3' },
// ...
]);

// Запускаем краулер
await crawler.run();

Более подробный пример использования очереди запросов с краулером можно найти в разделе Puppeteer Crawler.

Список запросов

Список запросов создается исключительно для запуска краулера и только если его использование явно указано в коде. Использование списка необязательно.

В Crawlee список запросов представлен классом RequestList.

Вот пример базовых операций со списком запросов:

import { RequestList, PuppeteerCrawler } from 'crawlee';

// Подготовим массив источников с URL для посещения
const sources = [
{ url: 'http://www.example.com/page-1' },
{ url: 'http://www.example.com/page-2' },
{ url: 'http://www.example.com/page-3' },
];

// Открываем список запросов
// Имя списка используется для сохранения источников и состояния в key-value хранилище
const requestList = await RequestList.open('my-list', sources);

// Краулер автоматически обработает запросы из списка
// Работает аналогично для Cheerio/Playwright краулеров
const crawler = new PuppeteerCrawler({
requestList,
async requestHandler({ page, request }) {
// Обработка страницы (извлечение данных, создание скриншота и т.д.)
// На этом этапе новые запросы в список добавить уже нельзя
},
});

Что выбрать?

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

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

Обратите внимание, что очередь и список запросов могут использоваться одним краулером одновременно. В таких случаях каждый запрос из списка сначала помещается в очередь (на первую позицию, даже если очередь не пуста), а затем обрабатывается из нее. Это необходимо для предотвращения повторной обработки одного и того же URL (сначала из списка, а затем возможно из очереди). На практике такая комбинация может быть полезна, когда есть множество начальных URL, но краулер будет динамически добавлять новые URL.

подсказка

В Crawlee практически нет необходимости комбинировать очередь запросов со списком запросов (хотя технически это возможно).

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

Однако теперь мы можем использовать функцию addRequests(), которая добавляет запросы пакетами. Таким образом, вместо комбинирования очереди и списка запросов, мы можем использовать только очередь запросов для таких сценариев. Смотрите примеры ниже.

// Это рекомендуемый способ.
// Обратите внимание, что мы не используем список запросов вообще,
// и не используем очередь запросов явно здесь.

import { PuppeteerCrawler } from 'crawlee';

// Подготавливаем массив источников с URL для посещения (он может содержать миллионы URL)
const sources = [
{ url: 'http://www.example.com/page-1' },
{ url: 'http://www.example.com/page-2' },
{ url: 'http://www.example.com/page-3' },
// ...
];

// Краулер автоматически обрабатывает запросы из очереди.
// Он используется таким же образом для краулеров Cheerio/Playwright
const crawler = new PuppeteerCrawler({
async requestHandler({ crawler, enqueueLinks }) {
// Добавляем новый запрос в очередь
await crawler.addRequests(['http://www.example.com/new-page']);

// Добавляем найденные ссылки на странице в очередь
await enqueueLinks();

// Запросы выше будут добавлены в очередь
// и будут обработаны после того, как начальные запросы будут обработаны.
},
});

// Добавляем начальный массив источников в очередь
// и запускаем краулер
await crawler.run(sources);

Очистка хранилищ

Хранилища по умолчанию очищаются перед запуском краулера, если не указано иное. Это происходит при первой попытке открыть хранилище (например, через RequestQueue.open()) или при использовании вспомогательных методов для работы с хранилищем по умолчанию (например, crawler.addRequests()). Если мы не работаем с хранилищами явно в нашем коде, очистка произойдет при выполнении метода run краулера. Если нам нужно очистить хранилища раньше, мы можем явно использовать вспомогательную функцию purgeDefaultStorages():

import { purgeDefaultStorages } from 'crawlee';

await purgeDefaultStorages();

Вызов этой функции очистит директорию хранилища запросов по умолчанию (а также список запросов, хранящийся в key-value хранилище по умолчанию). Это сокращение для выполнения (опционального) метода purge интерфейса StorageClient. Другими словами, будет вызван метод purge текущей реализации хранилища. Можно гарантировать, что очистка произойдет только один раз для данного контекста выполнения, если установить onlyPurgeOnce в значение true в объекте options.