Noon является одним из крупнейших маркетплейсов электронной коммерции на Ближнем Востоке, обслуживающим миллионы покупателей в ОАЭ, Саудовской Аравии и Египте. Каталог охватывает электронику, одежду, косметику и продукты питания, а цены и рейтинги на страницах листингов представляют собой чистый публичный сигнал для всех, кто отслеживает конкурентов, изучает категорию или создаёт мониторинг цен. Данные находятся прямо на каждой странице поиска: названия товаров, цены, звёздные рейтинги, бренды и ссылки на каждый товар.
В этом руководстве показано, как парсить данные Noon с помощью JavaScript и Node.js, используя cheerio. Вы создадите небольшой рабочий скрапер, который получает листинг поиска Noon через Crawling API, извлекает название, цену, рейтинг, бренд и ссылку для каждого товара, обрабатывает пагинацию по страницам результатов и экспортирует результат в формате JSON и CSV. Всё руководство ограничено публичными данными листингов товаров, а раздел о законности в конце не является шаблонным текстом, поэтому прочитайте его перед тем, как направить скрапер на реальные объёмы данных.
Что вы создадите
Скрипт на Node.js, который принимает публичный URL поиска Noon и запрос, получает отрендеренный HTML через Crawling API и извлекает структурированную запись для каждого товара в листинге. В качестве примера мы используем витрину ОАЭ и запрос "smartphones" и извлекаем следующие поля для каждого товара:
-
Title название товара, считанное из элемента
data-qa="product-name". - Price числовая сумма, отображаемая на карточке, например "1,799".
- Currency метка валюты рядом с суммой, например "AED".
- Rating текст звёздного рейтинга, когда у товара есть отзывы.
- Brand метка бренда, отображаемая над названием товара на карточке.
- Link абсолютный URL страницы конкретного товара.
Почему обычный запрос не работает на Noon
Если запросить URL поиска Noon с помощью простого HTTP-клиента, сетку товаров вы получите редко. Против вас работают два фактора. Во-первых, Noon рендерит карточки листинга в браузере с помощью JavaScript, поэтому исходный HTML представляет собой почти пустую оболочку до тех пор, пока скрипты страницы не запустятся и данные о товарах не загрузятся через AJAX. Во-вторых, Noon отмечает автоматизированный трафик: IP-адреса датацентров и паттерны запросов, не похожие на реальный браузер, получают CAPTCHA, ограничение скорости или блокировку прежде, чем достигают отрендеренных листингов.
Таким образом, рабочему скраперу Noon нужны две вещи в одном запросе: браузер, который реально рендерит страницу и ожидает AJAX-контент, и IP-адрес, который платформа воспринимает как реального посетителя. Можно собрать это самостоятельно с помощью headless-браузера и пула ротирующихся резидентских прокси, но сборка и поддержание их в рабочем состоянии составляет большую часть работы. Crawling API объединяет оба компонента в одном вызове: вы отправляете URL, API рендерит страницу за доверенным IP, ожидает динамический контент и возвращает готовый HTML для парсинга с помощью cheerio.
Noon загружает карточки товаров асинхронно, поэтому наиболее важные параметры, ajax_wait и page_wait. Установка ajax_wait: 'true' указывает Crawling API удерживать запрос до завершения фоновых запросов, а page_wait добавляет фиксированную задержку (в миллисекундах), чтобы медленные карточки успели отрисоваться до захвата HTML.
Предварительные требования
Перед написанием кода необходимо подготовить несколько вещей. Ни одна из них не займёт много времени.
Базовые знания JavaScript и Node.js. Вы должны уметь писать и запускать Node-скрипты, устанавливать пакеты с помощью npm. Если вы новичок в Node, официальная документация или любой вводный курс выведут вас на уровень, который предполагает данное руководство. Сопутствующее руководство по созданию веб-скрапера с Node.js охватывает основы, если вам нужно повторение.
Node.js версии 16 или выше. Проверьте версию командой node --version. Если она не установлена, установите с сайта Node.js или через менеджер версий, например nvm.
Аккаунт Crawlbase и токен. Зарегистрируйтесь, откройте панель управления и скопируйте токен со страницы документации аккаунта. Бесплатный тариф даёт до 20 000 бесплатных запросов без привязки карты. Относитесь к токену как к паролю: он аутентифицирует ваши запросы, поэтому держите его вне системы контроля версий.
Настройка проекта
Создайте папку проекта, инициализируйте её и установите две библиотеки, необходимые скраперу.
node --version mkdir noon-scraper && cd noon-scraper npm init -y npm install crawlbase cheerio
Две зависимости выполняют всю работу: crawlbase, официальный Node-клиент для Crawling API, а cheerio парсит возвращённый HTML с API в стиле jQuery, позволяя извлекать отдельные поля по CSS-селектору. Создайте файл noon-scraper.js в этой папке и добавьте код из приведённых ниже шагов.
Шаг 1: Получение отрендеренной страницы поиска
Начните с получения готовой страницы. Импортируйте класс CrawlingAPI, инициализируйте его с вашим токеном и запросите URL поиска Noon с установленными параметрами ожидания AJAX. Проверка кода статуса перед парсингом позволяет обнаруживать сбои явно, а не скрыто.
const { CrawlingAPI } = require('crawlbase'); const api = new CrawlingAPI({ token: 'YOUR_CRAWLBASE_TOKEN' }); function searchUrl(query, page) { return `https://www.noon.com/uae-en/search/?q=${query}&page=${page}`; } const options = { ajax_wait: 'true', page_wait: '5000' }; api .get(searchUrl('smartphones', 1), options) .then((response) => { if (response.statusCode === 200) { console.log(response.body.slice(0, 500)); } }) .catch((error) => console.error('API request error:', error));
Запустите скрипт командой node noon-scraper.js, в начале тела вы должны увидеть реальную разметку листинга Noon, а не урезанную оболочку. Это подтверждает, что рендеринг и ожидание AJAX работают ещё до написания первого селектора. Если вам не нужны пользовательские поля и вы предпочитаете получать структурированный JSON без написания парсера, передайте autoparse: 'true' в параметрах, и API вернёт разобранные данные напрямую.
Первый запрос вернул полностью отрендеренную страницу поиска Noon с AJAX-карточками, без headless-браузера и прокси на вашей стороне. Crawling API запускает страницу в реальном браузере, ожидает динамический контент с помощью ajax_wait и page_wait, ротирует резидентские IP на стороне сервера и обрабатывает CAPTCHA, которые Noon подбрасывает скраперам, так что вы получаете готовый HTML из одного вызова. Попробуйте сначала поиск смартфонов на бесплатном тарифе.
Шаг 2: Парсинг каждого товара с помощью cheerio
Имея в распоряжении отрендеренный HTML, загрузите его в cheerio и обойдите карточки товаров. Noon размещает каждый результат поиска в повторяющемся контейнере, поэтому вы выбираете каждую карточку, а затем считываете название, цену, валюту, рейтинг, бренд и ссылку из её содержимого. Защитное чтение каждого поля предотвращает сбой всего запуска из-за одного отсутствующего значения.
const cheerio = require('cheerio'); function extractProducts(html) { const $ = cheerio.load(html); const products = []; $('div.grid > span.productContainer').each((index, element) => { const card = $(element); const title = card .find('div[data-qa="product-name"]') .text() .trim(); const price = card.find('strong.amount').text().trim(); const currency = card.find('span.currency').text().trim(); const rating = card.find('div.dGLdNc').text().trim(); const brand = card.find('div[data-qa="product-brand"]').text().trim(); const href = card.find('a').attr('href'); const link = href ? new URL(href, 'https://www.noon.com').href : ''; if (title && price) { products.push({ title, price, currency, rating, brand, link }); } }); return products; }
Несколько деталей обеспечивают точность парсинга. Название берётся из элемента data-qa="product-name", числовая цена из strong.amount, а метка валюты считывается отдельно из span.currency, чтобы "AED" не попадала в сумму. Текст рейтинга находится в div.dGLdNc и пуст для товаров без отзывов, бренд в элементе data-qa="product-brand", а ссылка считывается из anchor href карточки и преобразуется в абсолютный URL для работы вне страницы. Защита в конце добавляет запись только при наличии и названия, и цены, тем самым отсеивая рекламные слоты и пустые карточки-заглушки.
Хэшированные имена классов Noon (например, dGLdNc) генерируются при сборке и меняются без предупреждения. Считайте приведённые выше селекторы отправной точкой, а не постоянным контрактом. Когда поле возвращается пустым, повторно проинспектируйте живую страницу в инструментах разработчика браузера и обновите селектор. Атрибуты data-qa как правило живут дольше хэшированных классов, поэтому предпочитайте их там, где страница их предлагает. Периодическое обслуживание селекторов нормально для любого продакшн-скрапера.
Шаг 3: Обработка пагинации по страницам результатов
Одна страница поиска, это демо; в реальном запуске нужно пройти весь набор результатов. Noon пагинирует поиск с помощью параметра запроса page, поэтому вы последовательно получаете каждую страницу, парсите её функцией из шага 2 и останавливаетесь, когда страница возвращается без товаров. Именно эта проверка пустой страницы не даёт цикличному обходу выйти за пределы результатов.
async function fetchPage(query, page) { const options = { ajax_wait: 'true', page_wait: '5000' }; const response = await api.get(searchUrl(query, page), options); if (response.statusCode === 200) return response.body; console.error(`Failed to fetch page ${page}: ${response.statusCode}`); return null; } async function scrapeAllPages(query, maxPages) { const all = []; for (let page = 1; page <= maxPages; page++) { console.log(`Scraping page ${page}...`); const html = await fetchPage(query, page); if (!html) break; const products = extractProducts(html); if (products.length === 0) { console.log('No more results found. Stopping.'); break; } all.push(...products); } return all; }
Вспомогательная функция fetchPage оборачивает один запрос и возвращает null при статусе, отличном от 200, а scrapeAllPages выполняет цикл от страницы 1 до maxPages, прерываясь в тот момент, когда страница возвращает ноль товаров. Поскольку каждый запрос рендерит полную страницу, во время тестирования разумнее держать maxPages небольшим: каждый JavaScript-рендеринговый запрос расходует больше кредитов, чем обычный, поэтому сначала проверьте парсер на одной-двух страницах, прежде чем увеличивать счётчик.
Шаг 4: Сборка полного скрипта с экспортом в JSON и CSV
Теперь соедините получение данных, парсинг и пагинацию в один запускаемый скрипт, а затем запишите записи на диск в форматах JSON и CSV.
const fs = require('fs'); const { CrawlingAPI } = require('crawlbase'); const cheerio = require('cheerio'); const api = new CrawlingAPI({ token: 'YOUR_CRAWLBASE_TOKEN' }); function searchUrl(query, page) { return `https://www.noon.com/uae-en/search/?q=${query}&page=${page}`; } function extractProducts(html) { const $ = cheerio.load(html); const products = []; $('div.grid > span.productContainer').each((index, element) => { const card = $(element); const title = card.find('div[data-qa="product-name"]').text().trim(); const price = card.find('strong.amount').text().trim(); const currency = card.find('span.currency').text().trim(); const rating = card.find('div.dGLdNc').text().trim(); const brand = card.find('div[data-qa="product-brand"]').text().trim(); const href = card.find('a').attr('href'); const link = href ? new URL(href, 'https://www.noon.com').href : ''; if (title && price) { products.push({ title, price, currency, rating, brand, link }); } }); return products; } async function fetchPage(query, page) { const options = { ajax_wait: 'true', page_wait: '5000' }; const response = await api.get(searchUrl(query, page), options); if (response.statusCode === 200) return response.body; console.error(`Failed to fetch page ${page}: ${response.statusCode}`); return null; } async function scrapeAllPages(query, maxPages) { const all = []; for (let page = 1; page <= maxPages; page++) { console.log(`Scraping page ${page}...`); const html = await fetchPage(query, page); if (!html) break; const products = extractProducts(html); if (products.length === 0) break; all.push(...products); } return all; } function toCsv(rows) { const headers = ['title', 'price', 'currency', 'rating', 'brand', 'link']; const escape = (value) => `"${String(value).replace(/"/g, '""')}"`; const lines = [headers.join(',')]; for (const row of rows) { lines.push(headers.map((h) => escape(row[h])).join(',')); } return lines.join('\n'); } async function main() { const query = 'smartphones'; const maxPages = 3; const products = await scrapeAllPages(query, maxPages); if (products.length === 0) return; fs.writeFileSync('noon-products.json', JSON.stringify(products, null, 2)); fs.writeFileSync('noon-products.csv', toCsv(products)); console.log(`Saved ${products.length} products to JSON and CSV`); } main();
Запустите командой node noon-scraper.js и получите два файла: noon-products.json с полными структурированными записями и noon-products.csv, готовый к открытию в электронной таблице. Вспомогательная функция toCsv заключает каждое поле в кавычки и удваивает встроенные кавычки, что важно, поскольку названия товаров длинные и часто содержат запятые. Измените query на любой поисковый запрос и увеличьте maxPages, когда будете готовы к более широкому сбору данных.
Как выглядит результат
Файл JSON содержит один объект на товар в том порядке, в котором Noon их вернул, с названием, ценой, валютой, рейтингом, брендом и ссылкой.
[ { "title": "Galaxy S25 AI Dual SIM Silver Shadow 12GB RAM 256GB 5G", "price": "3,199", "currency": "AED", "rating": "4.5", "brand": "Samsung", "link": "https://www.noon.com/uae-en/galaxy-s25-ai-dual-sim-silver-shadow-12gb-ram-256gb-5g/N70140511V/p/" }, { "title": "A78 5G Dual SIM Glowing Black 8GB RAM 256GB", "price": "899", "currency": "AED", "rating": "4.3", "brand": "OPPO", "link": "https://www.noon.com/uae-en/a78-5g-dual-sim-glowing-black-8gb-ram-256gb/N70115717V/p/" } ]
CSV отражает те же строки с заголовочной строкой, поэтому его можно сразу открыть в Excel, Google Sheets или любом конвейере данных, читающем файлы с разделителями.
title,price,currency,rating,brand,link "Galaxy S25 AI Dual SIM Silver Shadow 12GB RAM 256GB 5G","3,199","AED","4.5","Samsung","https://www.noon.com/uae-en/galaxy-s25-ai-dual-sim-silver-shadow-12gb-ram-256gb-5g/N70140511V/p/" "A78 5G Dual SIM Glowing Black 8GB RAM 256GB","899","AED","4.3","OPPO","https://www.noon.com/uae-en/a78-5g-dual-sim-glowing-black-8gb-ram-256gb/N70115717V/p/"
Отсюда записи напрямую поступают в работу по отслеживанию цен и исследованиям. Для более широкого взгляда на превращение данных листингов в решения см. руководство по использованию веб-скрапинга для ценовой аналитики, а общее руководство по веб-скрапингу в электронной коммерции охватывает паттерны, актуальные для любого маркетплейса.
Как не попасть под блокировку
Даже при решённом вопросе рендеринга Noon следит за трафиком, похожим на скрапинг. Несколько привычек помогут сохранить работоспособность запуска, и они применимы к любой серьёзной коммерческой цели.
- Регулируйте частоту запросов. Вводите задержку между получением страниц вместо того, чтобы долбить поиск в плотном цикле. Распределение запросов во времени, главный фактор, позволяющий оставаться в пределах лимитов Noon.
- Используйте ротацию. Пул резидентских IP распределяет запросы по множеству адресов реальных пользователей, чтобы ни один не превысил лимит и не получил CAPTCHA. Crawling API делает это за вас; если вы строите собственный стек, это та часть, которую нужно реализовать правильно.
- Читайте коды статусов. Запуск, начавший возвращать проверки или не-200 ответы, сигнализирует, что текущая скорость или IP-уровень больше недостаточны. Воспринимайте это как сигнал к снижению активности, а не как шум, который можно игнорировать.
Поскольку Noon рендерится на клиенте, тот же подход рендеринга с последующим парсингом применим и к другим динамическим магазинам. Общая методика описана в руководстве как парсить сайты, не попадая под блокировку, а если вас интересует базовая техника сама по себе, руководство как краулить JavaScript-сайты разбирает рендеринг подробнее.
Законно ли парсить Noon?
Допустимость парсинга Noon зависит от условий использования Noon, вашей юрисдикции и того, что вы делаете с данными. Условия Noon ограничивают автоматизированный доступ, поэтому парсинг может нарушать эти условия независимо от тщательности вашего инструментария. Ни один из кодов здесь не меняет этого; он лишь делает техническую часть рабочей. Ознакомьтесь с Условиями использования Noon и его robots.txt и воспринимайте оба документа как границу того, что вы собираете.
Несколько линий, которых стоит придерживаться. Собирайте только публичные данные о товарах: название, цену, валюту, рейтинг, бренд и ссылку на товар, которые любой может увидеть на странице поиска без аккаунта. Соблюдайте заявленные ограничения скорости Noon и держите объём запросов достаточно низким, чтобы не перегружать серверы. Избегайте персональных данных, включая всё, что связано с идентифицируемыми рецензентами, кроме публичного текста отзыва и количества звёзд, отображаемых на странице. Не перераспространяйте защищённые авторским правом медиаматериалы Noon, например фотографии товаров, как если бы они были вашими. Если вы планируете коммерческое повторное использование данных, получите разрешение или официальное соглашение, а не предполагайте, что молчание равносильно согласию.
Данное руководство намеренно ограничено публичными данными страниц поиска и листингов, поскольку именно эта граница делает работу защищаемой. Оно не охватывает ничего, что скрыто за авторизацией, личные или платёжные данные клиентов и продавцов, историю заказов или любые попытки обойти аутентификацию или CAPTCHA, которую вы не должны были проходить. Если Noon или один из его партнёров предлагает санкционированный канал данных или официальный API для вашего сценария использования, это правильный инструмент, когда вам нужны большие объёмы, гарантированная структура или коммерческие права. Если вашему проекту нужно больше, чем публичные листинги, официальное соглашение, правильный путь, а не более умный скрапер.
Ключевые выводы
- Noon рендерит листинги на клиенте и жёстко блокирует. Обычный запрос возвращает пустую оболочку или CAPTCHA, поэтому необходимо рендерить страницу и дожидаться AJAX-контента за доверенным IP прежде, чем парсить её.
-
Crawling API делает это за один вызов. Передайте
ajax_wait: 'true'иpage_wait, чтобы динамические карточки загрузились, а API ротирует резидентские IP и обрабатывает CAPTCHA на стороне сервера; добавьтеautoparse: 'true', если хотите получить JSON вместо сырого HTML. -
cheerio извлекает поля. Выберите каждый
span.productContainer, затем считайте название, цену, валюту, рейтинг, бренд и ссылку, отдавая предпочтение стабильным атрибутамdata-qa, поскольку хэшированные имена классов меняются. -
Пагинация, это параметр page. Перебирайте значение запроса
pageи останавливайтесь, когда страница возвращает ноль товаров, держаmaxPagesнебольшим во время тестирования, поскольку каждый рендеринговый запрос стоит больше кредитов. - Оставайтесь на публичных данных. Соблюдайте ToS и robots.txt Noon, регулируйте частоту запросов, избегайте персональных данных и контента за авторизацией, и предпочитайте официальный канал для объёмного или коммерческого использования.
Часто задаваемые вопросы
Какие данные можно парсить со страницы поиска Noon?
Публичные поля каждой карточки поиска: название товара, числовую цену, валюту, звёздный рейтинг при наличии отзывов, бренд и ссылку на страницу товара. Данное руководство считывает именно эти поля из каждого span.productContainer в сетке. Всё, что скрыто за авторизацией, например сведения об аккаунте или история заказов, выходит за рамки и не является публичными данными.
Почему обычный запрос возвращает неполные данные от Noon?
Потому что Noon рендерит сетку товаров на клиенте с помощью JavaScript и загружает карточки через AJAX, а затем оспаривает автоматизированный трафик с помощью CAPTCHA. Сырой HTTP-запрос с IP-адреса датацентра обычно возвращает пустую оболочку или страницу блокировки вместо карточек товаров. Чтобы получить полную страницу, нужно её отрендерить, дождаться AJAX-контента и запросить через доверенный IP, именно это делает за вас Crawling API.
Что делают ajax_wait и page_wait?
Они контролируют, как долго Crawling API ждёт перед захватом HTML. Установка ajax_wait: 'true' удерживает запрос до завершения фоновых запросов, а page_wait добавляет фиксированную задержку в миллисекундах, чтобы медленно загружающиеся карточки успели отрисоваться. Оба параметра важны для Noon, поскольку данные о товарах поступают после первоначальной загрузки страницы, а не в первом ответе.
Как обрабатывать пагинацию на Noon?
Noon пагинирует поиск с помощью параметра запроса page, поэтому вы увеличиваете его и последовательно получаете каждую страницу. Функция scrapeAllPages в данном руководстве выполняет цикл от страницы 1 до предела maxPages и останавливается, как только страница возвращает ноль товаров, что позволяет определить конец результатов без необходимости угадывать количество страниц заранее.
Мои селекторы возвращают пустые значения. Что изменилось?
Почти наверняка разметка Noon. Её хэшированные имена классов, такие как dGLdNc, генерируются при сборке и меняются без предупреждения, поэтому селекторы, работавшие в прошлом месяце, могут сломаться. Повторно проинспектируйте живую страницу в инструментах разработчика браузера и обновите селектор, отдавая предпочтение стабильным атрибутам data-qa там, где страница их предлагает. Периодическое обслуживание селекторов нормально для любого продакшн-скрапера.
Можно ли парсить персональные данные клиентов с Noon?
Нет, и данное руководство этого не охватывает. Данные аккаунта клиента, история заказов и всё, что скрыто за авторизацией, не являются публичными данными. Парсинг контента, закрытого авторизацией, персональных данных рецензентов кроме публичного текста отзыва или обход аутентификации выходит за рамки данного руководства и противоречит условиям Noon. Для санкционированного доступа в больших объёмах официальное соглашение о данных, правильный путь.
Обходите любой сайт в масштабе, без борьбы с инфраструктурой.
Crawlbase берёт на себя прокси, отпечатки и CAPTCHA, чтобы ваша команда выпускала конвейеры данных вместо поддержки обвязки краулинга. 1 000 запросов бесплатно, без карты.
