К содержимому

Документация

JavaScript SDK: Обход страниц и категорий

Задача обхода — пройти все нужные страницы, не пропустить товары и не зациклиться на последней странице. Сначала определите, откуда берётся номер страницы: из URL, кнопки «Далее» или ответа JSON. Затем задайте верхнюю границу и стабильный ключ записи.

Номер страницы в URL

Если адрес содержит параметр page, используйте ctx.url().withParam(). После каждого запроса проверяйте HTTP-статус, наличие карточек и отмену. Остановка по пустой странице и максимум страниц дополняют друг друга.

async function run(ctx) {
  const base = ctx.param('catalog_url', '');
  if (!base) ctx.abort('Укажите адрес каталога');
  const maxPages = Math.min(100, Math.max(1, Number(ctx.param('max_pages', 10))));
  const seen = new Set();

  for (let number = 1; number <= maxPages; number++) {
    if (await ctx.cancelled()) ctx.abort('Сбор остановлен');
    const url = ctx.url().withParam(base, 'page', number);
    const response = await ctx.http().get(url);
    if (!response.ok()) ctx.fail(`Страница ${number}: HTTP ${response.status}`);
    const cards = response.dom().qa('.product-card');
    if (!cards.length) break;

    let added = 0;
    for (const card of cards) {
      const sku = card.attr('data-sku');
      if (!sku || seen.has(sku)) continue;
      seen.add(sku);
      added++;
      ctx.emit({ sku, name: card.q('.title')?.text() ?? '' }, sku);
    }
    ctx.progress(number, maxPages, 'Страницы каталога');
    if (added === 0) break;
  }
}

Селекторы .product-card, data-sku и .title — примеры. Выберите их на своём сайте и проверьте пробным прогоном. Set удаляет повторы только внутри текущего запуска; стабильный ключ в ctx.emit() связывает результаты разных запусков.

Несколько категорий

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

async function run(ctx) {
  const categories = ctx.param('category_urls', []);
  if (!Array.isArray(categories)) ctx.abort('Укажите список категорий');
  const seen = new Set();
  for (const categoryUrl of categories.slice(0, 20)) {
    if (await ctx.cancelled()) ctx.abort('Сбор остановлен');
    const response = await ctx.http().get(String(categoryUrl));
    if (!response.ok()) ctx.fail(`Категория: HTTP ${response.status}`);
    for (const card of response.dom().qa('.product-card')) {
      const sku = card.attr('data-sku');
      if (!sku || seen.has(sku)) continue;
      seen.add(sku);
      ctx.emit({ sku, category: String(categoryUrl), name: card.text() }, sku);
    }
  }
}

Если источник использует кнопку, бесконечную прокрутку или XHR, применяйте действия браузера и дождитесь новой карточки или ответа сети. Сам клик не доказывает, что данные изменились.

Жизненный цикл · HttpClient · Работа с JSON