Страница результатов поиска Amazon является одним из богатейших публичных датасетов в e-commerce: каждый запрос возвращает ранжированную сетку товаров с названиями, ценами, рейтингами, количеством отзывов и ссылкой на каждый листинг. Отслеживание изменений этой сетки во времени показывает, что продаётся, где конкурент располагается по ключевому слову и как цены движутся в категории. Данные публичны, но надёжно извлекать их скриптом сложно, поскольку Amazon рендерит большую часть страницы через JavaScript и быстро перехватывает автоматизированный трафик.

Это руководство покажет, как скрейпить Amazon SERP с помощью Next.js, полноценным способом. Вы создадите небольшое рабочее Next.js-приложение, серверное действие которого вызывает Crawling API для загрузки отрендеренной страницы поиска Amazon, разбирает каждый товар с помощью Cheerio и отображает результаты в React-сетке. Токен Crawlbase остаётся на стороне сервера всё время и никогда не попадает в браузер. Руководство ограничено публичными данными поиска, а раздел о законности в конце не является формальностью, поэтому прочитайте его до применения в реальных объёмах. Если вместо этого вам нужна версия в виде обычного скрипта, см. статью о том, как скрейпить страницы поиска Amazon с Crawling API.

Что вы создадите

Next.js-приложение (App Router) с серверным действием, которое принимает поисковое ключевое слово, загружает отрендеренный Amazon SERP через Crawling API, разбирает его с помощью Cheerio и возвращает структурированную запись на каждый товар клиентскому компоненту, отображающему сетку. Извлекаем следующие поля на позицию:

  • Название заголовок товара на карточке, например "Apple iPhone 15 Pro Max 256GB".
  • Цена указанная цена в отображаемом виде, например "$1,199.00".
  • Изображение URL миниатюры товара для рендеринга карточки.
  • Рейтинг текст звёздного рейтинга при наличии, например "4.7 out of 5 stars".
  • Отзывы количество отзывов покупателей рядом с рейтингом.
  • URL абсолютная ссылка на страницу отдельного товара.

Почему простой запрос не работает на Amazon

Если вы запрашиваете URL поиска Amazon обычным HTTP-клиентом, чистая сетка товаров, видимая в браузере, появляется редко. Два фактора работают против вас. Во-первых, Amazon рендерит цены, рейтинги и части каждой карточки результата в браузере через JavaScript, поэтому сырой HTML может возвращаться неполным. Во-вторых, Amazon быстро помечает автоматизированный трафик: IP дата-центров и паттерны запросов, непохожие на реальный браузер, получают CAPTCHA, промежуточную страницу «robot check» или прямую блокировку ещё до того, как добираются до товаров.

Таким образом, работающий скрейпер Amazon SERP требует в одном запросе двух вещей: браузера, который реально рендерит страницу, и IP, воспринимаемого платформой как реальный посетитель. Можно собрать это самостоятельно из headless-браузера и пула ротирующих жилых прокси, но объединить их и поддерживать в рабочем состоянии составляет большую часть работы. Crawling API сворачивает всё это в один вызов: вы отправляете ему URL с JavaScript-токеном, он рендерит страницу за доверенным IP и возвращает готовый HTML для парсинга. Выполнение этого из серверного действия Next.js держит токен вне клиента и загрузку на сервере, что является именно правильным местом.

Зачем нужен JS-токен

Crawlbase предлагает два типа токенов. Обычный токен загружает статический HTML; JavaScript (JS) токен сначала рендерит страницу в настоящем браузере. Amazon загружает ключевые поля результатов на стороне клиента, поэтому JS-токен даёт наиболее полную страницу. Использование обычного токена может вернуть частичную сетку с отсутствующими ценами или рейтингами, оставляя нечего надёжно парсить.

Предварительные требования

Перед написанием кода нужно подготовить несколько вещей. Это не займёт много времени.

Node.js 18.17 или выше. Next.js требует современной среды выполнения Node. Проверьте версию командой node --version. Если Node.js не установлен, установите его с официального сайта или через менеджер версий вроде nvm.

Базовый React и Next.js. Вы должны быть знакомы с React-компонентами, хуками и запуском Next.js-приложения. Если App Router и серверные действия для вас новы, официальная документация Next.js охватывает оба, а наше руководство по созданию веб-скрейпера с Node.js станет хорошим дополнением для части, связанной со скрейпингом.

Аккаунт Crawlbase и JS-токен. Зарегистрируйтесь, откройте личный кабинет и скопируйте свой JavaScript (JS) токен со страницы документации аккаунта. Обращайтесь с токеном как с паролем: он аутентифицирует ваши запросы, поэтому храните его на стороне сервера и вне системы контроля версий. Мы будем читать его из переменной окружения, никогда не хардкодя в клиентском компоненте.

Настройка проекта

Создайте каркас Next.js-приложения, затем установите две необходимые библиотеки. Когда мастер создания спросит, выберите App Router; остальные настройки по умолчанию подойдут.

bash
node --version

npx create-next-app@latest amazon-serp-scraper
cd amazon-serp-scraper

npm install crawlbase cheerio

Две зависимости выполняют всю работу: crawlbase, официальный Node-клиент для Crawling API, а cheerio разбирает возвращаемый HTML с jQuery-подобным API, позволяя извлекать отдельные поля по CSS-селектору. Если селекторы вам незнакомы, вводная статья по XPath и CSS-селекторам станет хорошим дополнением. Теперь поместите токен в файл окружения в корне проекта, чтобы он оставался на стороне сервера:

bash
# .env.local (never commit this file)
CRAWLBASE_JS_TOKEN=YOUR_CRAWLBASE_TOKEN

Поскольку у CRAWLBASE_JS_TOKEN нет префикса NEXT_PUBLIC_, Next.js держит его на сервере и никогда не включает в клиентский JavaScript. В этом весь смысл выполнения загрузки в серверном действии.

Шаг 1: Загрузка отрендеренного SERP в серверном действии

Создайте файл app/actions.js. Директива 'use server' в начале помечает всё в файле как серверное действие, поэтому этот код выполняется только на сервере, где токен в безопасности. Импортируйте клиент CrawlingAPI, прочитайте токен из переменной окружения и запросите URL поиска Amazon, сформированный из ключевого слова. Проверка кода статуса перед парсингом делает ошибки явными, а не скрытыми.

javascript
'use server';

const { CrawlingAPI } = require('crawlbase');

const api = new CrawlingAPI({ token: process.env.CRAWLBASE_JS_TOKEN });

async function fetchSerpHtml(keyword) {
  const query = encodeURIComponent(keyword.trim());
  const pageUrl = `https://www.amazon.com/s?k=${query}`;
  const options = { ajax_wait: 'true', page_wait: 5000 };
  const response = await api.get(pageUrl, options);
  if (response.statusCode === 200) {
    return response.body;
  }
  console.error(`Request failed: ${response.statusCode}`);
  return null;
}

Два параметра ожидания важны для цели, рендерящейся на стороне клиента. ajax_wait указывает API дождаться завершения загрузки асинхронного контента, а page_wait выдерживает фиксированное количество миллисекунд после загрузки, чтобы элементы, рендерящиеся с задержкой, отобразились до захвата страницы. Пять секунд, разумное начало; увеличьте это значение, если цены или рейтинги возвращаются пустыми. На этом этапе у вас есть отрендеренный HTML Amazon, полученный на стороне сервера с токеном, не попадающим в браузер. Это подтверждает работу рендеринга ещё до написания единственного селектора.

Crawlbase Amazon Scraper

Этот вызов api.get выполняет сложную часть, которую Amazon ставит перед вами: принимает ваш JS-токен, запускает страницу поиска в настоящем браузере, чтобы отрендерить цены и рейтинги, ротирует жилые IP на стороне сервера, чтобы запрос читался как реальный посетитель, и передаёт серверному действию готовый HTML. Вам не нужно самостоятельно управлять парком headless-браузеров и пулом прокси. Начните с публичной страницы поиска на бесплатном тарифе.

Шаг 2: Парсинг каждого товара с помощью Cheerio

Имея отрендеренный HTML, загрузите его в Cheerio и обойдите карточки результатов. Amazon размещает каждый результат поиска в повторяющемся блоке с тегом div[data-component-type="s-search-result"], поэтому выбираете все карточки, а затем читаете название, цену, изображение, рейтинг, количество отзывов и ссылку на товар изнутри неё. Защитное чтение каждого поля предотвращает сбой из-за одного пропущенного значения. Добавьте эту функцию в тот же файл app/actions.js.

javascript
const cheerio = require('cheerio');

function parseProducts(html) {
  const $ = cheerio.load(html);
  const products = [];

  $('div[data-component-type="s-search-result"]').each((_, el) => {
    const card = $(el);
    const name = card.find('h2 span').first().text().trim();
    if (!name) return;

    const path = card.find('h2 a').attr('href');

    products.push({
      name,
      price: card.find('.a-price .a-offscreen').first().text().trim() || null,
      image: card.find('img.s-image').attr('src') || null,
      rating: card.find('.a-icon-alt').first().text().trim() || null,
      reviews: card.find('.a-size-base.s-underline-text').first().text().trim() || null,
      url: path ? `https://www.amazon.com${path}` : null,
    });
  });

  return products;
}

Несколько деталей обеспечивают надёжность. Цена находится в скрытом теге .a-offscreen внутри блока .a-price, что является чистейшим единственным источником отображаемой суммы, поэтому мы читаем её, а не склеиваем символ и цифры. Каждое поле возвращает null при отсутствии элемента, что характерно, поскольку спонсированные карточки и некоторые листинги не имеют рейтинга или количества отзывов. Ссылка на товар является относительным путём, поэтому мы добавляем к ней источник Amazon для получения абсолютного, кликабельного URL.

Селекторы дрейфуют

Имена классов и макет Amazon (s-search-result, a-price, a-offscreen, s-image и прочие) меняются без предупреждения и варьируются в зависимости от региона и типа запроса. Относитесь к приведённым выше селекторам как к стартовому шаблону, а не к договору. Когда поле возвращает null, переизучите живую страницу в инструментах разработчика браузера и обновите селектор. Периодическое обслуживание селекторов, норма для любого продакшн-скрейпера, а не признак поломки.

Шаг 3: Предоставление одного серверного действия и формы данных

Теперь соедините загрузку и парсинг в единое экспортируемое серверное действие, которое может вызывать UI. Оно принимает ключевое слово, возвращает разобранные товары и никогда не позволяет токену утечь к клиенту, поскольку всё это остаётся в файле 'use server'. Добавьте это в конец файла app/actions.js.

javascript
export async function scrapeAmazonProducts(keyword) {
  if (!keyword || !keyword.trim()) return { products: [], error: 'Empty search' };
  try {
    const html = await fetchSerpHtml(keyword);
    if (!html) return { products: [], error: 'Fetch failed' };
    const products = parseProducts(html);
    return { products, error: null };
  } catch (err) {
    console.error(err);
    return { products: [], error: 'Something went wrong' };
  }
}

Возврат простого объекта { products, error } вместо генерации исключений упрощает клиентский компонент: он может отображать сетку при наличии строк в products и показывать сообщение при установленном error, без try/catch, разбросанных по UI. Всё вышеперечисленное выполняется на сервере, поэтому токен, вызов Crawling API и парсинг Cheerio не попадают в браузерный бандл.

Шаг 4: Отображение результатов в React-сетке

Клиентская часть невелика. Один клиентский компонент хранит состояние поиска, вызывает серверное действие при отправке формы и отображает возвращённые товары в виде карточек. Создайте app/page.jsx с директивой 'use client', чтобы использовать React-хуки, затем импортируйте и вызывайте действие напрямую; Next.js обрабатывает серверный round-trip за вас.

javascript
'use client';

import { useState } from 'react';
import { scrapeAmazonProducts } from './actions';

export default function Home() {
  const [keyword, setKeyword] = useState('');
  const [products, setProducts] = useState([]);
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState(null);

  async function handleSubmit(event) {
    event.preventDefault();
    setLoading(true);
    setError(null);
    const result = await scrapeAmazonProducts(keyword);
    setProducts(result.products);
    setError(result.error);
    setLoading(false);
  }

  return (
    <main className="container">
      <form onSubmit={handleSubmit}>
        <input
          value={keyword}
          onChange={(e) => setKeyword(e.target.value)}
          placeholder="Search Amazon (iPhone, laptop, headphones...)"
        />
        <button type="submit" disabled={loading}>
          {loading ? 'Searching...' : 'Search'}
        </button>
      </form>

      {error && <p className="error">{error}</p>}

      <div className="grid">
        {products.map((product, i) => (
          <a key={i} href={product.url} target="_blank" className="card">
            {product.image && <img src={product.image} alt={product.name} />}
            <h3>{product.name}</h3>
            <p className="price">{product.price}</p>
            <p className="rating">{product.rating} ({product.reviews || 0})</p>
          </a>
        ))}
      </div>
    </main>
  );
}

Это весь фронтенд. Форма обновляет keyword при каждом нажатии клавиши; при отправке переключается флаг загрузки, ожидается серверное действие и сохраняются возвращённые товары и ошибка. Сетка отображает каждый товар в виде карточки со ссылкой на листинг Amazon в новой вкладке. Стилизуйте с помощью Tailwind или обычного CSS по вкусу; здесь важен поток данных. Запустите приложение командой npm run dev, откройте localhost:3000, найдите что-нибудь вроде "iPhone 15 Pro Max", и отрендеренные, разобранные товары появятся в сетке.

Как выглядит результат

Серверное действие возвращает чистый массив записей, по одной на товар, ещё до того, как они попадут в сетку. Выведите result.products в лог и получите структуру вроде следующей, готовую для рендеринга, записи в JSON или сохранения в базу данных.

json
[
  {
    "name": "Apple iPhone 15 Pro Max 256GB Natural Titanium",
    "price": "$1,199.00",
    "image": "https://m.media-amazon.com/images/I/81fxjeu8fdL._AC_UL320_.jpg",
    "rating": "4.7 out of 5 stars",
    "reviews": "1,284",
    "url": "https://www.amazon.com/dp/B0CHX1W1XY"
  },
  {
    "name": "Samsung Galaxy S24 Ultra 256GB Unlocked",
    "price": "$1,099.99",
    "image": "https://m.media-amazon.com/images/I/71CXi9gZ4mL._AC_UL320_.jpg",
    "rating": "4.5 out of 5 stars",
    "reviews": "912",
    "url": "https://www.amazon.com/dp/B0CMDRCZBP"
  }
]

Масштабирование на несколько страниц результатов

Одна страница результатов, демонстрация; реальная задача обходит пагинацию. Amazon предоставляет номер страницы через параметр запроса page, поэтому можно загружать каждую страницу через то же серверное действие, разбирать её той же функцией и собирать строки. Поскольку каждая страница результатов имеет одинаковую структуру карточек, написанный парсер работает на всех страницах без изменений. Добавьте вариант с пагинацией в app/actions.js.

javascript
export async function scrapeAmazonPages(keyword, totalPages) {
  const all = [];
  const query = encodeURIComponent(keyword.trim());
  for (let page = 1; page <= totalPages; page++) {
    const url = `https://www.amazon.com/s?k=${query}&page=${page}`;
    const response = await api.get(url, { ajax_wait: 'true', page_wait: 5000 });
    if (response.statusCode === 200) {
      all.push(...parseProducts(response.body));
    }
  }
  return all;
}

Чтобы обогатить каждую строку полными деталями товара (полным описанием, всеми изображениями, блоком покупки, полными данными отзывов), возьмите url из каждой карточки, загрузите эту страницу отдельного товара через тот же клиент API, затем напишите небольшой парсер для макета товара. Паттерн идентичен: рендеринг, затем парсинг. Подробнее о целях с интенсивным рендерингом см. в статье о том, как краулить JavaScript-сайты.

Как не попасть в блок

Даже при решённом вопросе рендеринга Amazon отслеживает трафик, характерный для скрейпера. Несколько привычек помогают запуску оставаться здоровым, и они применимы к любой защищённой коммерческой цели.

  • Соблюдайте темп запросов. Бомбардировка страниц в тесном цикле, быстрейший способ получить CAPTCHA. Распределяйте запросы во времени и варьируйте ключевые слова вместо полноскоростного краулинга одного пути.
  • Опирайтесь на ротацию. Пул жилых IP распределяет запросы по множеству реальных пользовательских адресов, поэтому ни один из них не достигает лимита скорости. Crawling API берёт это на себя; если вы собираете собственный стек, именно это нужно настроить правильно.
  • Читайте коды статуса. Запуск, начавший возвращать блокировки или ответы со статусом, отличным от 200, сигнализирует о недостаточности текущей скорости или IP-уровня. Воспринимайте это как сигнал к отступлению, а не как шум, который можно игнорировать.

Общая методология описана в статье как скрейпить сайты без блокировок. Если вы предпочитаете маршрутизировать собственный трафик через ротирующий пул вместо использования управляемого API, Smart AI Proxy предоставляет ту же ротацию жилых IP в качестве подключаемого прокси-эндпоинта.

Законно ли скрейпить Amazon?

Допустимость скрейпинга Amazon зависит от условий использования Amazon, вашей юрисдикции и того, как вы используете данные. Условия Amazon ограничивают автоматизированный доступ, поэтому скрейпинг может противоречить этим условиям независимо от аккуратности вашего инструментария. Ни один из приведённых здесь фрагментов кода не изменяет этого; он лишь обеспечивает техническую работоспособность. Ознакомьтесь с Условиями использования Amazon и его robots.txt и рассматривайте оба документа как границы допустимого сбора данных.

Несколько принципов, которых стоит придерживаться. Собирайте только публичные данные поиска: названия товаров, цены, рейтинги, количество отзывов, миниатюры и ссылки, видимые любому пользователю без аккаунта. Соблюдайте заявленные Amazon ограничения скорости и держите объём запросов достаточно низким, чтобы не перегружать его серверы. Избегайте персональных данных, включая всё, связанное с идентифицируемыми рецензентами, помимо публичного текста отзывов и счётчиков на странице результатов. Не распространяйте охраняемые авторским правом изображения товаров или описания оптом; ссылайтесь на них, а не переиздавайте как собственные. Если планируете коммерческое повторное использование данных, получите разрешение или официальное соглашение, а не считайте молчание согласием.

Для объёмного или коммерческого использования Amazon предлагает официальные каналы, включая Product Advertising API для аффилиатов и API для продавцов и рекламодателей для зарегистрированных предприятий, и именно они являются правильными инструментами, когда нужны большие объёмы, гарантированная структура или коммерческие права. Это руководство намеренно ограничено публичными страницами поиска и листингов. Оно не охватывает ничего за логином, данных аккаунтов покупателей или продавцов, истории заказов, частных сообщений и любых попыток обойти аутентификацию или CAPTCHA как средство доступа. Если ваш проект требует большего, чем публичные данные поиска, официальные API Amazon или соглашение о данных являются верным путём, а не более умный скрейпер.

Итоги

Ключевые выводы

  • Amazon рендерит SERP на стороне клиента. Простой запрос возвращает неполную или заблокированную страницу, поэтому её необходимо отрендерить перед парсингом.
  • Выполняйте загрузку в серверном действии. Файл 'use server' хранит токен Crawlbase в process.env на сервере, полностью вне браузерного бандла.
  • Один вызов обеспечивает рендеринг и доверенный IP. Crawling API с JS-токеном делает и то, и другое; ajax_wait и page_wait управляют временем ожидания контента.
  • Cheerio выполняет извлечение. Выбирайте каждую карточку s-search-result, затем сопоставляйте название, цену, изображение, рейтинг, отзывы и URL с текущими селекторами и ожидайте их дрейфа.
  • Работайте только с публичными данными. Соблюдайте условия и robots.txt Amazon, предпочитайте официальные API для объёмного или коммерческого использования и никогда не трогайте логины, персональные данные или историю заказов.

Часто задаваемые вопросы

Зачем использовать серверное действие Next.js вместо загрузки на стороне клиента?

Потому что токен Crawlbase никогда не должен попадать в браузер. Серверное действие выполняется только на сервере, поэтому можно читать токен из process.env.CRAWLBASE_JS_TOKEN, делать вызов Crawling API и парсить с Cheerio, и ничего из этого не будет включено в клиентский JavaScript. Клиентский компонент просто вызывает действие и рендерит то, что приходит обратно. Это также держит Cheerio, являющийся Node-библиотекой, вне браузера, где он не может работать.

Почему простой запрос возвращает неполные данные с Amazon?

Потому что Amazon рендерит цены, рейтинги и части каждой карточки результата на стороне клиента через JavaScript, а автоматизированный трафик перехватывает CAPTCHA и проверками на роботов. Сырой HTTP-запрос может вернуться с отсутствующими ключевыми полями или быть заблокирован полностью. Чтобы получить полную страницу, необходимо отрендерить её за доверенным IP, и именно это делает JS-токен Crawling API.

Нужен ли обычный токен или JS-токен для Amazon?

Используйте JS-токен. Обычный токен загружает статический HTML, который на Amazon может вернуться с отсутствующими ценами или рейтингами. JS-токен рендерит страницу в настоящем браузере перед возвратом HTML, поэтому поля результатов присутствуют при парсинге Cheerio.

Мои селекторы возвращают null. Что изменилось?

Почти наверняка разметка Amazon. Карточки s-search-result, блоки a-price и теги a-offscreen меняются без предупреждения и варьируются в зависимости от региона и типа запроса, поэтому селекторы, работавшие в прошлом месяце, могут сломаться. Переизучите живую страницу в инструментах разработчика браузера и обновите селекторы. Периодическое обслуживание селекторов, норма для любого продакшн-скрейпера.

Чем этот подход отличается от обычного Python или Node-скрипта?

Данные и вызов Crawling API одинаковы; меняется обрамление. Здесь загрузка и парсинг находятся в серверном действии Next.js, а React-сетка отображает результаты в браузерном UI с токеном, безопасно хранящимся на сервере. Если вам нужен только скрипт, печатающий JSON, более простой путь, скрейпить страницы поиска Amazon с Crawling API напрямую или создать веб-скрейпер с Node.js без фреймворка вокруг него.

Можно ли скрейпить персональные данные покупателей или продавцов с Amazon?

Нет, и это руководство этого не охватывает. Данные аккаунтов, история заказов и частные сообщения находятся за логином, то есть не являются публичными данными. Скрейпинг контента за логином, персональных данных или обход аутентификации или CAPTCHA для их получения выходит за рамки данного руководства и противоречит условиям Amazon. Для санкционированного доступа правильный путь, официальные API Amazon или лицензионное соглашение.

Начать создавать

Обходите любой сайт в масштабе, без борьбы с инфраструктурой.

Crawlbase берёт на себя прокси, отпечатки и CAPTCHA, чтобы ваша команда выпускала конвейеры данных вместо поддержки обвязки краулинга. 1 000 запросов бесплатно, без карты.

Самообслуживание · Звонок отдела продаж не требуется · Доступны корпоративные объёмы краулинга