Cheerio на AWS Lambda
Хотя локально мы можем легко создать проект Crawlee с помощью npx crawlee create
, для запуска на AWS Lambda потребуются некоторые изменения.
Изменение кода
При создании нового краулера необходимо передавать уникальный экземпляр Configuration
. По умолчанию все краулеры Crawlee используют общее хранилище - это удобно локально, но может вызвать проблемы с состоянием Lambda-функции.
Также при создании Configuration важно указать параметр persistStorage: false
. Это указывает Crawlee использовать хранилище в памяти, так как файловая система Lambda доступна только для чтения.
// Подробнее на https://crawlee.dev/
import { CheerioCrawler, Configuration, ProxyConfiguration } from 'crawlee';
import { router } from './routes.js';
const startUrls = ['https://crawlee.dev'];
const crawler = new CheerioCrawler({
requestHandler: router,
}, new Configuration({
persistStorage: false,
}));
await crawler.run(startUrls);
Теперь обернем всю логику в функцию handler
. Это и есть Lambda-функция, которую будет выполнять AWS.
// Подробнее на https://crawlee.dev/
import { CheerioCrawler, Configuration } from 'crawlee';
import { router } from './routes.js';
const startUrls = ['https://crawlee.dev'];
export const handler = async (event, context) => {
const crawler = new CheerioCrawler({
requestHandler: router,
}, new Configuration({
persistStorage: false,
}));
await crawler.run(startUrls);
};
Всегда создавайте новый экземпляр краулера для каждой Lambda-функции. AWS сохраняет среду выполнения некоторое время после первого запуска Lambda (для уменьшения времени холодного старта) - поэтому последующие вызовы будут использовать уже существующий экземпляр краулера.
Коротко: Lambda-функция должна быть без состояния.
И наконец, нужно вернуть собранные данные из Lambda после завершения работы краулера.
В итоге ваш скрипт main.js
должен выглядеть примерно так:
// Подробнее на https://crawlee.dev/
import { CheerioCrawler, Configuration } from 'crawlee';
import { router } from './routes.js';
const startUrls = ['https://crawlee.dev'];
export const handler = async (event, context) => {
const crawler = new CheerioCrawler({
requestHandler: router,
}, new Configuration({
persistStorage: false,
}));
await crawler.run(startUrls);
return {
statusCode: 200,
body: await crawler.getData(),
}
};
Развертывание проекта
Теперь пора развернуть наш скрипт на AWS!
Создадим ZIP-архив проекта (включая папку node_modules
), выполнив команду zip -r package.zip .
в папке проекта.
node_modules
?AWS имеет ограничение в 50МБ для прямой загрузки файлов. Обычно проекты Crawlee не достигают этого лимита, но с большим количеством зависимостей это возможно.
Лучший способ установки зависимостей - использование Lambda Layers. С помощью слоев можно также делиться файлами между несколькими Lambda-функциями и сохранять сам код максимально компактным.
Для создания Lambda Layer необходимо:
- Упаковать папку
node_modules
в отдельный ZIP-файл (архив должен содержать одну папку с именемnode_modules
) - Создать новый Lambda Layer из этого архива. Вероятно, потребуется загрузить файл в AWS S3 и создать Layer оттуда
- После создания просто указать использование этого слоя в Lambda-функции
Для развертывания кода загружаем архив package.zip
как исходный код.
В настройках Runtime указываем handler
на главную функцию краулера. Используйте слеши для описания структуры каталогов и точку для именованного экспорта. Наша функция называется handler
и экспортируется из src/main.js
, поэтому укажем src/main.handler
.
Готово! Нажав кнопку Test, можно отправить тестовое событие в новую Lambda-функцию. Содержимое события пока не важно - при желании можно параметризовать работу краулера, анализируя объект event
, который AWS передает первым аргументом в handler.
На вкладке Configuration в панели управления AWS Lambda можно настроить объем памяти и размер временного хранилища.
Объем памяти может существенно влиять на скорость выполнения Lambda.
Подробнее о том, как производительность и стоимость масштабируются с увеличением памяти, смотрите в официальной документации.