Tokopedia, один из крупнейших онлайн-маркетплейсов Индонезии с десятками миллионов активных покупателей и сотнями тысяч продавцов, предлагающих товары в категориях электроники, одежды, продуктов питания и товаров для дома. Публичные страницы поиска и товаров, богатый источник сигналов спроса: названия, цены, продавцы, рейтинги и количество продаж по любому запросу, именно те данные, которые используются командами для отслеживания цен, исследования конкурентов и анализа трендов категорий.
Это руководство показывает, как парсить данные Tokopedia с помощью JavaScript и Node.js, используя cheerio. Вы создадите небольшой рабочий скрапер, который получает листинги поиска и страницы товаров Tokopedia через Crawling API, извлекает название, цену, магазин, рейтинг, количество продаж и ссылку для каждого товара, обрабатывает пагинацию и экспортирует результат в форматах JSON и CSV. Всё руководство ограничено публичными данными листинга товаров, и раздел о правовых аспектах в конце стоит прочитать, прежде чем применять скрапер на реальных объёмах.
Что вы создадите
Скрипт на Node.js, который принимает публичный URL поиска Tokopedia, получает отрисованный HTML через Crawling API и извлекает структурированную запись для каждого товара в сетке результатов. Для примера используем поиск гарнитуры и извлекаем следующие поля для каждого элемента:
- Title, название товара, отображаемое на карточке.
- Price, цена в том виде, в каком она отображается, например "Rp178.000".
- Shop, название магазина или продавца, предлагающего товар.
- Rating, текст звёздного рейтинга, если он отображается на карточке.
- Sold, количество проданных единиц, если указано, например "60+ terjual".
- Link, URL страницы отдельного товара.
Почему обычный запрос не работает на Tokopedia
Если запросить URL поиска Tokopedia обычным HTTP-клиентом, вы почти никогда не получите сетку товаров в ответе. Здесь действуют два фактора. Во-первых, Tokopedia рендерит карточки листинга в браузере с помощью JavaScript, поэтому исходный HTML, почти пустая оболочка, пока скрипты страницы не запустятся и не загрузят данные о товарах. Во-вторых, платформа реагирует на автоматизированный трафик: IP-адреса датацентров и паттерны запросов, не похожие на настоящий браузер, подвергаются ограничению скорости или блокировке ещё до того, как доберутся до отрисованных листингов.
Значит, рабочий скрапер Tokopedia должен решать в одном запросе две задачи: браузер, который действительно рендерит страницу, и IP-адрес, который платформа воспринимает как реального посетителя. Можно собрать это самостоятельно с помощью headless-браузера и пула ротирующихся резидентских прокси, но поддержание их в рабочем состоянии, основная часть работы. Crawling API объединяет оба компонента в одном вызове: вы передаёте ему URL, он рендерит страницу за доверенным IP-адресом и возвращает готовый HTML для парсинга с помощью cheerio.
Crawlbase выдаёт два токена: обычный для статичных сайтов и JavaScript-токен для контента, рендеримого браузером. Tokopedia загружает товары через JavaScript, поэтому здесь используйте JavaScript-токен. Бесплатный уровень даёт до 20 000 запросов без привязки карты, что позволяет протестировать весь процесс до оплаты.
Предварительные требования
Прежде чем писать код, необходимо подготовить несколько вещей. Ни одна не займёт много времени.
Базовые знания JavaScript и Node.js. Вы должны уметь писать и запускать Node-скрипты, а также устанавливать пакеты с помощью npm. Если вы новичок в Node, руководство по созданию веб-скрапера с Node.js охватывает уровень, который предполагает данное руководство.
Node.js 16 или новее. Проверьте версию командой node --version. Если Node.js не установлен, установите его с официального сайта или через менеджер версий, например nvm.
Аккаунт Crawlbase и токен. Зарегистрируйтесь, откройте дашборд и скопируйте ваш JavaScript-токен. Бесплатный уровень даёт до 20 000 запросов без привязки карты. Обращайтесь с токеном как с паролем: он аутентифицирует ваши запросы, поэтому не добавляйте его в систему контроля версий.
Настройка проекта
Создайте папку проекта, инициализируйте её и установите две библиотеки, которые нужны скраперу.
node --version mkdir tokopedia-scraper && cd tokopedia-scraper npm init -y npm install crawlbase cheerio
Две зависимости выполняют всю работу: crawlbase, официальный Node-клиент для Crawling API, а cheerio парсит возвращаемый HTML с помощью jQuery-подобного API, что позволяет извлекать отдельные поля по CSS-селекторам. Создайте файл tokopedia-scraper.js в этой папке и добавьте код из следующих шагов.
Шаг 1: Получение отрисованной страницы поиска
Начните с получения готовой страницы. Импортируйте класс CrawlingAPI, инициализируйте его вашим токеном и запросите URL поиска. Tokopedia загружает результаты лениво, поэтому передайте параметры ajax_wait и page_wait, чтобы дать странице время на рендеринг до захвата API. Проверка кода статуса перед парсингом позволяет сразу замечать ошибки.
const { CrawlingAPI } = require('crawlbase'); const api = new CrawlingAPI({ token: 'YOUR_CRAWLBASE_TOKEN' }); const options = { ajax_wait: 'true', page_wait: '5000' }; const searchURL = 'https://www.tokopedia.com/search?q=headset'; api .get(searchURL, options) .then((response) => { if (response.statusCode === 200) { console.log(response.body.slice(0, 500)); } }) .catch((error) => console.error('API request error:', error));
Запустите скрипт командой node tokopedia-scraper.js, и вверху тела ответа должна появиться реальная разметка товаров Tokopedia, а не пустая оболочка. Это подтверждает, что рендеринг работает, ещё до написания первого селектора. Флаг ajax_wait указывает API дождаться завершения внутристраничных запросов, а page_wait добавляет дополнительные пять секунд, чтобы лениво загружаемые карточки успели появиться.
Первый запрос только что вернул полностью отрисованную страницу поиска Tokopedia без headless-браузера и без прокси на вашей стороне. Crawling API запускает страницу в настоящем браузере, ожидает стабилизации загружаемых JavaScript-карточек и ротирует резидентские IP-адреса на стороне сервера, и всё это в одном вызове. Попробуйте на любом публичном поисковом запросе на бесплатном уровне.
Шаг 2: Парсинг каждого товара с помощью cheerio
Получив отрисованный HTML, загрузите его в cheerio и пройдитесь по карточкам товаров. Tokopedia размещает каждый результат в повторяющемся контейнере внутри области результатов поиска, поэтому вы выбираете все карточки, а затем считываете название, цену, магазин, рейтинг, количество продаж и ссылку из каждой из них. Защитное чтение каждого поля не позволяет одному отсутствующему значению сломать весь запуск.
const cheerio = require('cheerio'); function parseSearchListings(html) { const $ = cheerio.load(html); const products = []; const cards = $( 'div[data-testid="divSRPContentProducts"] div.css-5wh65g' ); cards.each((index, element) => { const card = $(element); const title = card .find('span.OWkG6oHwAppMn1hIBsC3pQ\\=\\=') .text() .trim(); const price = card .find('div.ELhJqP-Bfiud3i5eBR8NWg\\=\\=') .text() .trim(); const shop = card .find('span.X6c-fdwuofj6zGvLKVUaNQ\\=\\=') .text() .trim(); // Rating and sold count sit in small text rows under the price const rating = card.find('span.nBBbPk2cBpbZJ2nFPN8jKA\\=\\=').text().trim(); const sold = card.find('span.eLNb-rRDe6X9p64ZsQAx9w\\=\\=').text().trim(); const link = card.find('a.Nq8NlC5Hk9KgVBJzMYBUsg\\=\\=').attr('href'); if (title) { products.push({ title: title, price: price || 'N/A', shop: shop || 'N/A', rating: rating || 'N/A', sold: sold || 'N/A', link: link || 'N/A', }); } }); return products; }
Несколько деталей обеспечивают точность по отношению к странице. Карточки находятся под test id divSRPContentProducts, и каждая карточка содержит название в элементе span с классом OWkG6oHwAppMn1hIBsC3pQ==, цену в элементе div с классом ELhJqP-Bfiud3i5eBR8NWg== и название магазина в элементе span с классом X6c-fdwuofj6zGvLKVUaNQ==. Ссылка на товар считывается из атрибута href якорного элемента карточки. Эти имена классов содержат буквальные символы ==, поэтому они экранируются как \\=\\= внутри строк селекторов cheerio. Селекторы рейтинга и количества продаж следуют тому же паттерну небольшого текста под ценой.
Хешированные имена классов Tokopedia (OWkG6oHwAppMn1hIBsC3pQ== и остальные) генерируются и меняются без предупреждения. Воспринимайте приведённые выше селекторы как шаблон, а не как неизменный контракт. Если поле возвращается пустым, заново изучите страницу в инструментах разработчика браузера и обновите селектор. Периодическое обслуживание селекторов, норма для любого производственного скрапера, а не признак поломки.
Шаг 3: Обработка пагинации
Tokopedia распределяет результаты поиска по нескольким страницам, каждая из которых доступна через параметр page в URL, например &page=2. Пройдитесь в цикле от первой до последней нужной страницы, получайте каждую через Crawling API, парсите с помощью функции из Шага 2 и собирайте всё в один массив. Если страницу не удалось получить, остановитесь досрочно, чтобы не добавлять пустые результаты.
async function fetchHtml(url) { const response = await api.get(url, options); if (response.statusCode === 200) return response.body; console.error(`Failed to fetch page: ${response.statusCode}`); return null; } async function scrapeMultiplePages(baseUrl, maxPages) { const allProducts = []; for (let page = 1; page <= maxPages; page++) { const paginatedUrl = `${baseUrl}&page=${page}`; const html = await fetchHtml(paginatedUrl); if (!html) break; const products = parseSearchListings(html); allProducts.push(...products); console.log(`Page ${page}: ${products.length} products`); } return allProducts; }
Цикл проходит запрошенные страницы, парсит листинги с каждой и агрегирует результаты. Поскольку все страницы поиска используют одинаковую структуру карточек, написанный на Шаге 2 парсер работает со всеми страницами без изменений.
Шаг 4: Сборка полного скрипта с экспортом в JSON и CSV
Теперь соедините получение данных, парсер и пагинацию в один рабочий скрипт, затем сохраните записи на диск в форматах JSON и CSV. JSON сохраняет полную вложенную структуру; CSV сразу открывается в таблице.
const fs = require('fs'); const { CrawlingAPI } = require('crawlbase'); const cheerio = require('cheerio'); const api = new CrawlingAPI({ token: 'YOUR_CRAWLBASE_TOKEN' }); const options = { ajax_wait: 'true', page_wait: '5000' }; // Paste parseSearchListings, fetchHtml, and scrapeMultiplePages here function toCsv(rows) { const headers = ['title', 'price', 'shop', 'rating', 'sold', '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 baseUrl = 'https://www.tokopedia.com/search?q=headset'; const maxPages = 5; const products = await scrapeMultiplePages(baseUrl, maxPages); fs.writeFileSync( 'tokopedia_search_results.json', JSON.stringify(products, null, 2) ); fs.writeFileSync('tokopedia_search_results.csv', toCsv(products)); console.log(`Saved ${products.length} products to JSON and CSV`); } main();
Вставьте функции parseSearchListings, fetchHtml и scrapeMultiplePages в тот же файл, чтобы main мог их вызвать. Запустите скрипт командой node tokopedia-scraper.js и получите два файла: tokopedia_search_results.json с полными структурированными записями и tokopedia_search_results.csv, готовый к открытию в таблице. Вспомогательная функция toCsv оборачивает каждое поле в кавычки и экранирует встроенные кавычки двойными, что особенно важно, так как названия товаров Tokopedia длинные и часто содержат запятые.
Как выглядит результат
JSON-файл содержит по одному объекту на каждый товар в порядке поиска, каждый с названием, ценой, магазином, рейтингом, количеством продаж и ссылкой.
[ { "title": "Ipega PG-R008 Gaming Headset for P4 /X1 series/N-Switch Lite/Mobile", "price": "Rp178.000", "shop": "ipegaofficial", "rating": "4.9", "sold": "250+ terjual", "link": "https://www.tokopedia.com/ipegaofficial/ipega-pg-r008-gaming-headset" }, { "title": "Hippo Toraz Handsfree Earphone Stereo Sound - headset, Putih", "price": "Rp13.000", "shop": "HippoCenter", "rating": "4.8", "sold": "1rb+ terjual", "link": "https://www.tokopedia.com/hippocenter88/hippo-toraz-handsfree-earphone" } ]
CSV повторяет те же строки с заголовком, поэтому он сразу открывается в Excel, Google Sheets или любом конвейере данных, работающем с файлами с разделителями.
title,price,shop,rating,sold,link "Ipega PG-R008 Gaming Headset for P4 /X1 series","Rp178.000","ipegaofficial","4.9","250+ terjual","https://www.tokopedia.com/ipegaofficial/ipega-pg-r008-gaming-headset" "Hippo Toraz Handsfree Earphone Stereo Sound - headset, Putih","Rp13.000","HippoCenter","4.8","1rb+ terjual","https://www.tokopedia.com/hippocenter88/hippo-toraz-handsfree-earphone"
Парсинг отдельных страниц товаров
Поисковые листинги дают ширину охвата; страницы товаров дают глубину. Получив ссылку из скрапера поиска, вы можете получить страницу товара через тот же Crawling API и извлечь более богатые поля. На странице товара Tokopedia название находится в элементе h1 с атрибутом data-testid="lblPDPDetailProductName", цена, в элементе div с атрибутом data-testid="lblPDPDetailProductPrice", магазин, в элементе a с атрибутом data-testid="llbPDPFooterShopName", описание, в элементе div с атрибутом data-testid="lblPDPDescriptionProduk", а миниатюры изображений, в тегах img внутри кнопок с атрибутом data-testid="PDPImageThumbnail".
async function scrapeProductPage(url) { const html = await fetchHtml(url); if (!html) return null; const $ = cheerio.load(html); const images = $('button[data-testid="PDPImageThumbnail"] img') .map((i, el) => $(el).attr('src')) .get(); return { name: $('h1[data-testid="lblPDPDetailProductName"]').text().trim(), price: $('div[data-testid="lblPDPDetailProductPrice"]').text().trim(), shop: $('a[data-testid="llbPDPFooterShopName"]').text().trim(), description: $('div[data-testid="lblPDPDescriptionProduk"]').text().trim(), images: images, }; }
Функция повторно использует тот же вспомогательный метод fetchHtml из скрапера поиска, поэтому скрапер страниц товаров наследует рендеринг и ротацию IP-адресов без дополнительной настройки. Передайте ей любую ссылку, собранную скрапером поиска, и она вернёт единый структурированный объект, который можно сохранить тем же способом: с помощью JSON.stringify или строкой в вашем CSV.
Как оставаться незаблокированным
Даже при обработке рендеринга Tokopedia отслеживает трафик, характерный для скраперов. Несколько привычек помогают поддерживать работоспособность запуска; они применимы к любой серьёзной коммерческой цели.
- Дозируйте запросы. Вводите задержку между получением страниц, а не бомбардируйте результаты поиска в тесном цикле. Распределение запросов, главный фактор соблюдения лимитов скорости платформы.
- Используйте ротацию. Пул резидентских IP-адресов распределяет запросы по множеству адресов реальных пользователей, не позволяя ни одному из них превысить лимит. Crawling API берёт это на себя; если вы строите собственный стек, именно в эту часть стоит вкладываться.
- Читайте коды статусов. Запуск, который начинает возвращать не-200 ответы, сигнализирует о том, что текущая скорость или уровень IP больше недостаточны. Воспринимайте это как сигнал к замедлению, а не как шум, который можно игнорировать.
Более подробный план действий см. в руководстве по обходу блокировок при скрапинге и расширенном руководстве по сканированию JavaScript-сайтов, которые более подробно охватывают сторону рендеринга и ротации.
Законно ли парсить Tokopedia?
Разрешён ли скрапинг Tokopedia, зависит от условий использования сервиса, вашей юрисдикции и того, что вы делаете с данными. Условия Tokopedia ограничивают автоматизированный доступ, поэтому скрапинг может противоречить этим условиям вне зависимости от тщательности ваших инструментов. Ни один из приведённых здесь кодов не меняет этого; он просто делает техническую часть рабочей. Ознакомьтесь с Условиями использования Tokopedia и её файлом robots.txt и воспринимайте оба как границы того, что вы собираете.
Несколько правил, которых стоит придерживаться. Собирайте только публичные данные о товарах: название, цену, название магазина, рейтинг, количество продаж и ссылку на товар, которые любой может видеть на странице поиска или товара без аккаунта. Соблюдайте заявленные ожидания Tokopedia по скорости запросов и держите объём достаточно низким, чтобы не перегружать серверы. Избегайте персональных данных, включая всё, что связано с идентифицируемыми покупателями или рецензентами, кроме публичного текста отзывов и звёздных оценок, показанных на странице. Не распространяйте защищённые авторским правом материалы Tokopedia, такие как фотографии товаров продавцов, как если бы они были вашими.
Это руководство намеренно ограничено публичными поисковыми листингами и страницами товаров, так как это та линия, которая делает работу правомерной. Оно не распространяется на что-либо за логином, личные данные покупателей или продавцов, историю заказов, чат или любые попытки обойти аутентификацию или CAPTCHA, которую вы не должны были проходить. Если ваш проект требует большего, чем публичные листинги, правильный путь, санкционированное соглашение о данных или любой официальный API-канал, предлагаемый Tokopedia, а не более изощрённый скрапер. При сомнениях предпочитайте получение разрешения, а не предположение, что молчание означает согласие.
Ключевые выводы
- Tokopedia рендерит листинги на стороне клиента и жёстко ограничивает скорость. Обычный запрос возвращает пустую оболочку, поэтому необходимо рендерить страницу за доверенным IP-адресом с JavaScript-токеном.
-
Crawling API делает оба в одном вызове. Он рендерит страницу с параметрами
ajax_waitиpage_wait, ротирует резидентские IP-адреса и отдаёт готовый HTML для парсинга с помощью cheerio. -
cheerio извлекает поля. Выберите каждую карточку товара, затем считывайте название, цену, магазин, рейтинг, количество продаж и ссылку, экранируя хешированные имена классов с
==и ожидая их изменения со временем. -
Пагинация и страницы товаров расширяют скрапер. Перебирайте параметр
pageдля охвата, затем повторно используйте тот же вспомогательный метод получения для извлечения имени, цены, магазина, описания и изображений из отдельных страниц товаров. - Работайте только с публичными данными. Соблюдайте Условия использования и robots.txt Tokopedia, дозируйте запросы, экспортируйте в JSON и CSV, и избегайте всего за логином и любых персональных данных.
Часто задаваемые вопросы
Законно ли парсить данные с Tokopedia?
Парсинг Tokopedia допустим, если вы остаётесь в рамках публичных данных о товарах и соблюдаете условия использования сервиса. Сначала ознакомьтесь с правилами сайта и файлом robots.txt, держите объём запросов разумным и избегайте персональных или скрытых за логином данных. Используйте собранные данные в законных целях, таких как исследование, отслеживание цен или анализ, а не для чего-либо, что нарушает политику Tokopedia или местное законодательство.
Зачем нужен Crawling API для парсинга Tokopedia?
Tokopedia загружает товары через JavaScript, поэтому обычный HTTP-запрос обычно возвращает пустую оболочку, а не карточки товаров. Crawling API рендерит страницу в настоящем браузере, ожидает лениво загружаемого контента с параметрами ajax_wait и page_wait и ротирует резидентские IP-адреса, чтобы вы получали готовые листинги без необходимости управлять парком браузеров или пулом прокси.
Какие данные можно извлечь с Tokopedia?
Из поисковых листингов можно извлечь название товара, цену, название магазина, рейтинг, количество продаж и ссылку. Со страниц отдельных товаров можно получить имя, цену, магазин, полное описание и URL изображений. Вместе они охватывают публичные поля, которые большинству команд необходимы для мониторинга цен, исследования конкурентов и анализа трендов товаров.
Почему мои селекторы Tokopedia возвращают пустые значения?
Почти наверняка изменилась разметка. Хешированные имена классов Tokopedia, такие как OWkG6oHwAppMn1hIBsC3pQ==, генерируются и меняются без предупреждения, поэтому селекторы, работавшие в прошлом месяце, могут сломаться. Заново изучите страницу в инструментах разработчика браузера и обновите селекторы. Периодическое обслуживание селекторов, норма для любого производственного скрапера.
Как обработать пагинацию в результатах поиска Tokopedia?
Добавьте параметр page к URL поиска, например &page=2, и пройдитесь в цикле от первой до последней нужной страницы. Получайте каждую страницу через Crawling API, парсите её с помощью той же функции cheerio и собирайте результаты в один массив перед экспортом. Останавливайтесь досрочно, если страницу не удалось получить, чтобы не дополнять данные пустыми результатами.
Можно ли парсить персональные данные покупателей или продавцов с Tokopedia?
Нет, и это руководство не охватывает данный аспект. Аккаунты покупателей, история заказов, чат и всё, что находится за логином, не являются публичными данными. Парсинг контента за логином, персональных данных о покупателях или рецензентах, выходящих за рамки публичного текста отзывов, или обход аутентификации выходит за рамки данного руководства и противоречит условиям Tokopedia. Для более широких потребностей см. руководство по скрапингу в сфере электронной коммерции или обзор веб-скрапинга для анализа цен, и предпочитайте санкционированное соглашение о данных для всего, что выходит за рамки публичных листингов.
Обходите любой сайт в масштабе, без борьбы с инфраструктурой.
Crawlbase берёт на себя прокси, отпечатки и CAPTCHA, чтобы ваша команда выпускала конвейеры данных вместо поддержки обвязки краулинга. 1 000 запросов бесплатно, без карты.
