Apify и Node.js: Как автоматизировать веб-скрапинг и управление браузером?

Что такое Apify и его основные возможности

Apify представляет собой облачную платформу для веб-скрапинга и автоматизации задач в интернете. Она предоставляет разработчикам инструменты и инфраструктуру для создания, запуска и управления веб-краулерами (Actors). Ключевые возможности включают:

  • Apify Actors: Переиспользуемые серверные облачные программы для выполнения задач скрапинга или автоматизации.
  • Apify SDK: Библиотека для Node.js, упрощающая разработку Actors.
  • Apify Store: Маркетплейс готовых Actors для различных задач.
  • Прокси-серверы: Встроенные решения для обхода блокировок (датацентровые, резидентные, мобильные).
  • Управление данными: Хранение и экспорт собранных данных в различных форматах (JSON, CSV, Excel).
  • Планировщик: Автоматический запуск Actors по расписанию.

Преимущества использования Node.js для веб-скрапинга

Node.js, благодаря своей асинхронной природе и событийно-ориентированной архитектуре, идеально подходит для задач веб-скрапинга, требующих обработки множества I/O операций.

  • Производительность: Асинхронность позволяет эффективно обрабатывать множество одновременных HTTP-запросов.
  • Экосистема: Огромное количество библиотек (npm) для работы с HTTP, парсинга HTML (Cheerio), управления браузерами (Puppeteer, Playwright).
  • JavaScript: Единый язык для фронтенда и бэкенда, включая скрипты автоматизации браузера.
  • Apify SDK: Нативная интеграция с платформой Apify через официальный SDK.

Сравнение Apify с другими инструментами веб-скрапинга

По сравнению с библиотеками типа Requests + BeautifulSoup (Python) или Scrapy (Python), Apify предлагает комплексное платформенное решение.

  • Инфраструктура: Apify берет на себя управление прокси, масштабирование, хранение данных и мониторинг, что отсутствует в отдельных библиотеках.
  • Управление браузером: Легкая интеграция с Puppeteer/Playwright для скрапинга динамических сайтов, что сложнее настроить с нуля.
  • Облако vs. Локально: Apify ориентирован на облачное выполнение, тогда как Scrapy или связка Requests/BeautifulSoup чаще используются для локальной разработки и запуска (хотя их можно развернуть в облаке).
  • Модель Actors: Позволяет легко переиспользовать и делиться кодом, в отличие от более монолитных проектов на Scrapy.

Настройка Apify SDK в Node.js

Установка Apify CLI и Node.js

Предполагается, что Node.js (версия 16 или выше) и npm уже установлены. Установка Apify CLI выполняется глобально через npm:

npm install -g apify-cli

После установки проверьте доступность команды:

apify --version

Инициализация нового проекта Apify

Для создания структуры проекта Actor используется команда apify create:

apify create my-scraper

CLI предложит выбрать шаблон. Для Node.js проектов обычно выбирают Getting started with Actor source code in Node.js. Это создаст базовую структуру проекта.

Для входа в аккаунт Apify (необходимо для развертывания в облаке) используется команда:

apify login

Обзор структуры проекта Apify

Стандартная структура проекта Apify Actor на Node.js включает:

  • .actor/: Папка с конфигурацией Actor (actor.json).
  • storage/: Локальное хранилище данных (эмулирует Apify Cloud Storage).
  • src/: Исходный код Actor (основной файл — main.js).
  • package.json: Зависимости Node.js проекта.
  • Dockerfile: (Опционально) для кастомных сборок.
  • README.md: Описание Actor.

Файл main.js является точкой входа. .actor/actor.json определяет метаданные Actor, включая входную схему (inputSchema).

Автоматизация веб-скрапинга с помощью Apify и Node.js

Использование Apify Actors для извлечения данных

Основной инструмент — Apify SDK. Он предоставляет классы для управления процессом скрапинга:

  • Apify.Actor.main(): Основная функция для запуска логики Actor.
  • RequestQueue: Управление очередью URL для обхода.
  • BasicCrawler, CheerioCrawler, PuppeteerCrawler, PlaywrightCrawler: Классы для реализации логики обхода и парсинга.
  • Dataset: Хранение извлеченных данных.

Обход блокировок и ограничений веб-сайтов

Apify предоставляет встроенные механизмы:

  • Apify Proxy: Автоматическая ротация IP-адресов. Настраивается при запуске Crawler или глобально.
  • User Agents: Использование реалистичных User-Agent строк.
  • Задержки и параллелизм: Настройка задержек между запросами (maxRequestRetries, maxRequestsPerMinute) для имитации человеческого поведения.
  • Браузерные отпечатки: При использовании PuppeteerCrawler или PlaywrightCrawler, можно применять техники маскировки отпечатков (fingerprinting).

Примеры кода для скрапинга данных с популярных веб-сайтов

Пример: Скрапинг заголовков новостей с помощью CheerioCrawler

// src/main.js
import { Actor } from 'apify';
import { CheerioCrawler, log } from '@crawlee/cheerio';

/**
 * @typedef {object} Input
 * @property {string} startUrl - The starting URL, e.g., a news site.
 */

/**
 * Main Actor function
 */
Actor.main(async () => {
    const input = await Actor.getInput();

    // Type assertion for input
    const { startUrl } = /** @type {Input} */ (input);

    if (!startUrl) {
        log.error('Missing startUrl in input.');
        await Actor.fail();
        return;
    }

    const requestQueue = await Actor.openRequestQueue();
    await requestQueue.addRequest({ url: startUrl });

    log.info('Starting crawler...');

    const crawler = new CheerioCrawler({
        requestQueue,
        maxRequestsPerCrawl: 100, // Limit crawl depth

        /**
         * Handles each page request.
         * @param {import('@crawlee/cheerio').CheerioCrawlingContext} context
         */
        async requestHandler({ request, $, log }) {
            log.info(`Processing ${request.url}...`);

            // Example: Extract news headlines (selector depends on the target site)
            const titles = [];
            $('h2 a').each((index, element) => {
                const title = $(element).text().trim();
                if (title) {
                    titles.push(title);
                }
            });

            // Save results to dataset
            await Actor.pushData({
                url: request.url,
                extractedTitles: titles,
            });
        },

        /**
         * Handles failed requests.
         * @param {import('@crawlee/core').FailedRequestContext} context
         */
        failedRequestHandler({ request, log }) {
            log.error(`Request ${request.url} failed too many times.`);
        },
    });

    await crawler.run();

    log.info('Crawler finished.');
});
Реклама

Управление браузером с помощью Apify и Puppeteer/Playwright

Интеграция Puppeteer или Playwright в Apify Actors

Apify SDK (через @crawlee/puppeteer или @crawlee/playwright) предоставляет классы PuppeteerCrawler и PlaywrightCrawler, которые абстрагируют управление браузером.

// Пример использования PlaywrightCrawler
import { Actor } from 'apify';
import { PlaywrightCrawler, log } from '@crawlee/playwright';

Actor.main(async () => {
    // ... (input handling, requestQueue setup) ...

    const crawler = new PlaywrightCrawler({
        requestQueue,
        launchContext: {
            // Use Apify Proxy Configuration for browser requests
            proxyConfiguration: await Actor.createProxyConfiguration(),
            // Options for Playwright launch
            launchOptions: {
                headless: true, // Run in headless mode
            },
        },
        maxRequestsPerCrawl: 50,

        /**
         * Handles page navigation and interaction.
         * @param {import('@crawlee/playwright').PlaywrightCrawlingContext} context
         */
        async requestHandler({ page, request, log }) {
            log.info(`Navigated to ${request.url}`);
            const title = await page.title();
            log.info(`Page title: ${title}`);

            // Further interactions or data extraction...
            await Actor.pushData({ url: request.url, title });
        },

        // ... (failedRequestHandler etc.)
    });

    await crawler.run();
});

Автоматизация действий пользователя на веб-страницах (клики, заполнение форм)

Используя page объект из PuppeteerCrawler или PlaywrightCrawler, можно эмулировать действия пользователя.

// Внутри requestHandler
/**
 * @param {import('@crawlee/playwright').PlaywrightCrawlingContext} context
 */
async requestHandler({ page, request, log }) {
    log.info(`Processing form on ${request.url}`);

    // Example: Fill and submit a login form
    await page.waitForSelector('#username');
    await page.type('#username', 'your_username');

    await page.waitForSelector('#password');
    await page.type('#password', 'your_password');

    await page.waitForSelector('button[type="submit"]');
    // Use Promise.all for concurrent navigation wait and click
    await Promise.all([
        page.waitForNavigation(), // Wait for navigation triggered by click
        page.click('button[type="submit"]'),
    ]);

    log.info('Form submitted successfully.');

    // Extract data after login or action
    const dashboardData = await page.locator('.dashboard-metric').textContent();
    await Actor.pushData({ url: request.loadedUrl, data: dashboardData });
}

Скрапинг динамически генерируемого контента

Браузерные Crawler (PuppeteerCrawler, PlaywrightCrawler) идеально подходят для сайтов, активно использующих JavaScript для рендеринга контента (SPA — Single Page Applications).

Ключевые моменты:

  • Ожидание элементов: Использование page.waitForSelector(), page.waitForFunction(), page.waitForResponse() для ожидания появления нужных данных или завершения AJAX-запросов.
  • Выполнение JS: page.evaluate() для выполнения произвольного JavaScript в контексте страницы и извлечения данных, недоступных через DOM селекторы.
  • Перехват запросов: page.route() для модификации или анализа сетевых запросов, например, для извлечения данных из API-вызовов, инициированных страницей.

Продвинутые техники и советы по работе с Apify и Node.js

Параллелизация скрапинга для повышения производительности

Apify SDK автоматически управляет параллелизмом через настройки Crawler:

  • maxConcurrency: Максимальное количество одновременно обрабатываемых страниц/запросов.
  • maxRequestsPerMinute: Ограничение скорости для избежания блокировок.

Подбор оптимальных значений зависит от целевого сайта и ограничений инфраструктуры Apify (или локальной машины).

Обработка ошибок и повторные попытки

Crawler автоматически обрабатывает сетевые ошибки и ошибки HTTP (статусы 5xx) с помощью повторных попыток (maxRequestRetries). Логику обработки специфичных ошибок (например, страница бана) следует реализовывать в failedRequestHandler или внутри requestHandler с использованием блоков try...catch.

Можно использовать request.pushErrorMessage() для сохранения информации об ошибках.

Использование Apify Cloud для масштабирования и мониторинга

  • Развертывание: apify push загружает код Actor в Apify Cloud.
  • Масштабирование: Запуск Actor в облаке позволяет использовать инфраструктуру Apify (прокси, вычислительные ресурсы) для обработки больших объемов данных.
  • Мониторинг: Веб-интерфейс Apify предоставляет логи, статистику выполнения, просмотр хранилища данных (Dataset, KeyValueStore, RequestQueue).
  • Веб-хуки и API: Интеграция с другими системами через API Apify или веб-хуки по завершении выполнения Actor.

Интеграция с другими сервисами и базами данных

Собранные данные (Dataset) можно экспортировать вручную или автоматически через API/веб-хуки.

  • API Apify: Использовать клиент Apify (apify-client в Node.js) для программного доступа к данным.
  • Веб-хуки: Настроить веб-хук, который будет отправлять уведомление (например, с ID датасета) на ваш сервер по завершении Actor. Ваш сервер затем может скачать данные и импортировать их в БД (PostgreSQL, MongoDB и т.д.) или отправить в другие сервисы (Google Sheets, BI-системы).
  • Прямая запись: В коде Actor можно использовать соответствующие Node.js библиотеки для прямой записи в базу данных, но это требует осторожного управления соединениями и обработкой ошибок, особенно при параллельном выполнении.

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