Wayfair работает по принципу динамического ценообразования. Одна и та же диван может стоить по-разному утром, вечером и на следующий день: всё определяется спросом, остатками на складе и собственной алгоритмической моделью платформы. Для покупателя, ожидающего выгодного предложения, или аналитика, изучающего категорию мебели, важна не текущая цена, а то, как она меняется со временем. Чтобы это отследить, нужно записывать цену по расписанию и сохранять историю.
В этом руководстве показано, как создать трекер цен на Wayfair на JavaScript с использованием Node.js и cheerio. Вы получаете публичную страницу товара или категории на Wayfair через Crawling API, извлекаете название и цену каждого товара, добавляете строку с меткой времени в файл истории и запускаете всё это по расписанию, чтобы файл превратился в журнал цен, который можно строить на графике. Руководство ограничено публичными данными листинга, а раздел о правовых аспектах в конце действительно важен, поэтому прочитайте его перед тем, как начать работу с большими объёмами.
Что вы создадите
Трекер на Node.js, который принимает публичный URL листинга Wayfair, получает отрендеренный HTML через Crawling API, разбирает каждую карточку товара и записывает снимок с меткой времени на диск. Запустив скрипт один раз, вы получите текущие цены; запустив по расписанию, превратите файл истории в временной ряд для анализа. Каждая запись содержит следующие поля:
- Timestamp строка времени в формате ISO, указывающая момент создания снимка, столбец, превращающий строки в временной ряд.
- Title название товара из карточки листинга, например "Mahwah 98'' Chenille Square Arm Sofa".
- Price цена, отображённая на карточке, например "$689.99".
- URL исходная страница листинга, с которой получен снимок, позволяющая группировать историю по страницам.
Почему обычный запрос не работает на Wayfair
Если запросить URL листинга Wayfair с помощью простого HTTP-клиента, скорее всего, вы не получите сетку товаров. Здесь есть два препятствия. Во-первых, Wayfair рендерит карточки листинга в браузере с помощью JavaScript, поэтому исходный HTML представляет собой почти пустую оболочку до выполнения скриптов страницы, а нужные цены находятся внутри этой отрендеренной разметки. Во-вторых, Wayfair выявляет автоматический трафик: запросы с IP-адресов дата-центров и паттерны, не характерные для реального браузера, блокируются или ограничиваются по скорости ещё до того, как достигают отрендеренных данных о товарах.
Значит, рабочий трекер Wayfair требует двух вещей в одном запросе: браузера, который реально рендерит страницу, и IP-адреса, который платформа воспринимает как обычного посетителя. Можно собрать это самостоятельно с помощью headless-браузера и пула ротирующихся резидентных прокси, но настройка и поддержка этой связки занимает большую часть времени и плохо подходит для того, что должно работать без присмотра по таймеру. Crawling API совмещает оба компонента в одном вызове: вы передаёте ему URL, он рендерит страницу через доверенный IP и возвращает готовый HTML для парсинга с помощью cheerio.
Предварительные требования
Перед написанием кода нужно подготовить несколько вещей. На это уйдёт немного времени.
Базовые знания JavaScript и Node.js. Вы должны уметь писать и запускать Node-скрипты, устанавливать пакеты через npm, работать с переменными, функциями и циклами. Если Node для вас нов, официальная документация и любой вводный курс выведут вас на нужный уровень.
Node.js версии 16 и выше. Проверьте версию командой node --version. Node запускает JavaScript локально, что и делает возможным плановый, автоматический трекинг. Если Node не установлен, скачайте его с официального сайта или через менеджер версий, например nvm.
Аккаунт и токен Crawlbase. Зарегистрируйтесь, откройте личный кабинет и скопируйте токен со страницы документации аккаунта. Бесплатный тариф даёт 1000 запросов без привязки карты, этого вполне достаточно для разработки и тестирования трекера. Обращайтесь с токеном как с паролем: он аутентифицирует ваши запросы, поэтому не храните его в системе контроля версий.
Настройка проекта
Создайте папку проекта, инициализируйте её и установите библиотеки, необходимые трекеру.
node --version mkdir wayfair-price-tracker && cd wayfair-price-tracker npm init -y npm install crawlbase cheerio
Два зависимости выполняют всю работу: crawlbase, официальный Node-клиент для Crawling API, а cheerio разбирает возвращённый HTML с помощью jQuery-подобного API, позволяя извлекать отдельные поля по CSS-селектору. Встроенный модуль Node fs обеспечивает запись файла истории, так что дополнительных установок не требуется. Создайте файл tracker.js в этой папке и добавьте код из следующих шагов.
Шаг 1: получение отрендеренной страницы Wayfair
Начните с получения готовой страницы. Импортируйте класс CrawlingAPI, инициализируйте его с токеном и запросите URL листинга. В качестве примера используем страницу категории диванов. Проверка кода статуса перед парсингом позволяет явно выявлять ошибки вместо того, чтобы они оставались незамеченными.
const { CrawlingAPI } = require('crawlbase'); const api = new CrawlingAPI({ token: 'YOUR_CRAWLBASE_TOKEN' }); const wayfairPageURL = 'https://www.wayfair.com/furniture/sb0/sofas-c413892.html'; api .get(wayfairPageURL) .then((response) => { if (response.statusCode === 200) { console.log(response.body.slice(0, 500)); } }) .catch((error) => console.error('API request error:', error));
Запустите скрипт командой node tracker.js, вы должны увидеть реальную разметку листинга Wayfair в начале тела ответа, а не пустую оболочку. Это подтверждает, что рендеринг работает, прежде чем вы напишете хоть один селектор. Страницы мебели Wayfair активно используют JavaScript-рендеринг, поэтому именно этот шаг обеспечивает появление цен в возвращаемом HTML.
Первый запрос вернул полностью отрендеренную страницу Wayfair без headless-браузера и прокси на вашей стороне. Crawling API запускает страницу в реальном браузере, ротирует резидентные IP на стороне сервера и обрабатывает вызовы, которые Wayfair направляет скрейперам, возвращая готовый HTML за один вызов, именно то, что нужно плановому трекеру для автономной работы. Начните с бесплатного тарифа на странице диванов.
Шаг 2: извлечение названия и цены с помощью cheerio
Имея отрендеренный HTML, загрузите его в cheerio и пройдитесь по карточкам товаров. Wayfair размещает каждый товар в повторяющемся контейнере с атрибутом data-hb-id="Card", поэтому выберите все карточки, а затем считайте название и цену изнутри. Чтобы найти эти элементы самостоятельно на живой странице, нажмите правую кнопку мыши на товаре, выберите «Просмотр кода», найдите элемент карточки и атрибуты, обозначающие название и цену. Следующие поля, те, что Wayfair предоставляет через атрибуты data-test-id.
const cheerio = require('cheerio'); function parseProducts(html) { const $ = cheerio.load(html); const products = []; // Each listing sits in a card container $('div[data-hb-id="Card"]').each((index, element) => { const card = $(element); let productName = card .find('p[data-test-id="ListingCard-ListingCardName-Text"]') .text() .trim(); const productPrice = card .find('span[data-test-id="PriceDisplay"]') .text() .trim(); // Fall back to a label when the name is missing if (productName === '') { productName = 'Name is not available'; } if (productPrice) { products.push({ title: productName, price: productPrice }); } }); return products; }
Несколько деталей обеспечивают точность парсинга. Название товара берётся из элемента p[data-test-id="ListingCard-ListingCardName-Text"], а цена, из span[data-test-id="PriceDisplay"]. Метод .text() извлекает внутренний текст, а .trim() убирает окружающие пробелы, давая чистое значение вроде "$689.99". Если у карточки нет читаемого названия, парсер подставляет "Name is not available" вместо того, чтобы пропускать строку, так Wayfair иногда рендерит рекламные или спонсорские слоты без стандартного заголовка.
Атрибутные хуки Wayfair (data-hb-id, значения data-test-id) привязаны к сборке фронтенда и могут измениться без предупреждения. Рассматривайте приведённые выше селекторы как начальный шаблон, а не как контракт. Если поле возвращается пустым, заново осмотрите живую страницу в инструментах разработчика браузера и обновите селектор. Периодическое обновление селекторов, норма для любого промышленного трекера, а не признак поломки.
Шаг 3: ведение журнала цен в JSON с течением времени
Именно этот шаг превращает разовый скрейпинг в трекер. Вместо перезаписи файла при каждом запуске вы читаете существующую историю, добавляете новый снимок с меткой времени и записываете объединённую запись обратно. Каждый запуск добавляет одну партию строк с меткой момента выполнения, поэтому файл накапливает историю цен, которую затем можно строить на графике или сравнивать.
const fs = require('fs'); const HISTORY_FILE = 'price-history.json'; function loadHistory() { if (!fs.existsSync(HISTORY_FILE)) return []; return JSON.parse(fs.readFileSync(HISTORY_FILE, 'utf-8')); } function appendSnapshot(products, pageUrl) { const timestamp = new Date().toISOString(); const rows = products.map((p) => ({ timestamp, title: p.title, price: p.price, url: pageUrl, })); const history = loadHistory(); history.push(...rows); fs.writeFileSync(HISTORY_FILE, JSON.stringify(history, null, 2)); console.log(`Logged ${rows.length} prices at ${timestamp}`); }
Вспомогательная функция loadHistory при первом запуске, когда файл ещё не существует, возвращает пустой массив, поэтому никакой специальной обработки в других местах не требуется. appendSnapshot присваивает всем строкам одного запуска одинаковую метку ISO-времени, это позволяет легко группировать запуск или сравнивать цену товара между двумя метками. Поскольку каждая строка содержит свой источник url, можно отслеживать несколько страниц Wayfair в одном файле, сохраняя возможность их разделить при анализе.
Шаг 4: сборка полного трекера
Теперь соедините получение, парсинг и логирование в один исполняемый скрипт. Это полный трекер: запустите его, и он записывает один снимок.
const fs = require('fs'); const { CrawlingAPI } = require('crawlbase'); const cheerio = require('cheerio'); const api = new CrawlingAPI({ token: 'YOUR_CRAWLBASE_TOKEN' }); const HISTORY_FILE = 'price-history.json'; const wayfairPageURL = 'https://www.wayfair.com/furniture/sb0/sofas-c413892.html'; async function crawl(pageUrl) { const response = await api.get(pageUrl); if (response.statusCode === 200) return response.body; console.error(`Request failed: ${response.statusCode}`); return null; } function parseProducts(html) { const $ = cheerio.load(html); const products = []; $('div[data-hb-id="Card"]').each((index, element) => { const card = $(element); let productName = card .find('p[data-test-id="ListingCard-ListingCardName-Text"]') .text() .trim(); const productPrice = card .find('span[data-test-id="PriceDisplay"]') .text() .trim(); if (productName === '') productName = 'Name is not available'; if (productPrice) products.push({ title: productName, price: productPrice }); }); return products; } function loadHistory() { if (!fs.existsSync(HISTORY_FILE)) return []; return JSON.parse(fs.readFileSync(HISTORY_FILE, 'utf-8')); } function appendSnapshot(products, pageUrl) { const timestamp = new Date().toISOString(); const rows = products.map((p) => ({ timestamp, title: p.title, price: p.price, url: pageUrl })); const history = loadHistory(); history.push(...rows); fs.writeFileSync(HISTORY_FILE, JSON.stringify(history, null, 2)); console.log(`Logged ${rows.length} prices at ${timestamp}`); } async function track() { const html = await crawl(wayfairPageURL); if (!html) return; const products = parseProducts(html); appendSnapshot(products, wayfairPageURL); } track();
Запустите командой node tracker.js. При первом запуске создаётся файл price-history.json и записываются текущие цены на диваны; при каждом последующем запуске к тому же файлу добавляется новая партия с меткой времени. Этапы получения, парсинга и логирования небольшие и независимые, что упрощает расширение скрипта, например, отслеживание другой категории достигается изменением одного URL.
Как выглядит результат
Файл истории содержит по одному объекту на каждый товар за каждый запуск, причём каждый объект содержит метку времени, название, цену и исходный URL. После двух запусков с разницей в несколько часов один и тот же товар появляется дважды с одинаковым названием и, возможно, разной ценой, именно тот сигнал, ради которого и создаётся трекер.
[ { "timestamp": "2024-03-18T09:00:00.000Z", "title": "Mahwah 98'' Chenille Square Arm Sofa", "price": "$689.99", "url": "https://www.wayfair.com/furniture/sb0/sofas-c413892.html" }, { "timestamp": "2024-03-18T09:00:00.000Z", "title": "Adelmina 88.6'' Upholstered Sofa", "price": "$444.99", "url": "https://www.wayfair.com/furniture/sb0/sofas-c413892.html" }, { "timestamp": "2024-03-18T21:00:00.000Z", "title": "Mahwah 98'' Chenille Square Arm Sofa", "price": "$649.99", "url": "https://www.wayfair.com/furniture/sb0/sofas-c413892.html" } ]
Экспорт истории в CSV
JSON удобен для ведения текущего журнала, но CSV открывается прямо в Excel или Google Sheets, где построить ценовой график за пару кликов. Эта вспомогательная функция читает JSON-историю и записывает плоский файл price-history.csv с одной строкой на снимок. Она повторяет традиционный подход с колонками для названия и цены, добавляя метку времени для возможности построения графика.
const fs = require('fs'); function exportCsv() { const history = JSON.parse(fs.readFileSync('price-history.json', 'utf-8')); const headers = ['timestamp', 'title', 'price', 'url']; const escape = (value) => `"${String(value).replace(/"/g, '""')}"`; const lines = [headers.join(',')]; for (const row of history) { lines.push(headers.map((h) => escape(row[h])).join(',')); } fs.writeFileSync('price-history.csv', lines.join('\n')); console.log(`Exported ${history.length} rows to price-history.csv`); } exportCsv();
Вспомогательная функция escape оборачивает каждое поле в кавычки и дублирует встроенные кавычки, это важно, поскольку названия товаров Wayfair длинные и часто содержат запятые, знаки дюйма и другую пунктуацию. В результате получается чистый временной ряд: один столбец для времени, один для товара, один для цены, готово для сводных таблиц и графиков по товарам. Это тот же паттерн, который применяется в любом рабочем процессе ценовой аналитики.
Запуск по расписанию
Трекер полезен только тогда, когда работает самостоятельно. Есть два простых варианта. Самый простой, планировщик операционной системы: на macOS или Linux запись в cron запускает скрипт с фиксированным интервалом, добавляя при каждом запуске один снимок.
# Open your crontab crontab -e # Run the tracker every day at 9am and 9pm 0 9,21 * * * cd /path/to/wayfair-price-tracker && node tracker.js
Если вы предпочитаете держать расписание внутри Node, чтобы оно хранилось вместе с проектом и работало везде, где есть Node, установите node-cron и оберните существующую функцию track в расписание.
// npm install node-cron const cron = require('node-cron'); // At minute 0 of hours 9 and 21, every day cron.schedule('0 9,21 * * *', () => { console.log('Running scheduled price check...'); track(); });
Соблюдайте разумный интервал опроса. Двух запусков в день достаточно для отслеживания большинства ценовых движений Wayfair без лишней нагрузки на сайт, а спокойная частота запросов, главный фактор сохранения доступа. За несколько недель работы файл истории становится реальной записью того, как менялась цена каждого дивана, что и является целью трекинга в отличие от разовых проверок. Тот же набор данных естественным образом питает инструмент сравнения цен, если начать параллельно отслеживать конкурирующие листинги.
Как оставаться незаблокированным
Даже при обработанном рендеринге Wayfair следит за трафиком, похожим на скрейпер, а трекер, работающий бесконечно, подвергается большему риску, чем разовый запуск. Несколько привычек помогут поддерживать его работоспособность.
- Соблюдайте темп запросов. Трекеру не нужно опрашивать сайт часто. Запуск раз в несколько часов фиксирует ценовой тренд и держит вас далеко от любых ограничений скорости. Сопротивляйтесь желанию скрейпить в плотном цикле.
- Используйте ротацию. Пул резидентных IP распределяет запросы по множеству реальных пользовательских адресов, чтобы ни один из них не достиг лимита или не получил вызов. Crawling API делает это за вас; если вы строите собственный стек, именно здесь нужно добиться качества.
- Следите за кодами статуса. Плановое задание, начавшее возвращать ответы не с кодом 200, сигнализирует о том, что текущей частоты или уровня IP уже недостаточно. Записывайте эти ответы и снижайте темп, вместо того чтобы допускать, что молчаливые ошибки оставят в истории пробелы.
Более широкий набор практик по поддержанию долгосрочной задачи в рабочем состоянии описан в руководстве о том, как скрейпить сайты без блокировок. Если вы хотите применить тот же подход к другому маркетплейсу, руководство о том, как легко скрейпить цены на Walmart, описывает тот же паттерн «получить, затем разобрать» для другого магазина.
Законно ли скрейпить Wayfair?
Использование трекера цен Wayfair в целом допустимо, если он отслеживает только публично доступную информацию о листингах, однако слово «в целом» здесь несёт реальную смысловую нагрузку. Допустимость конкретного использования зависит от условий обслуживания Wayfair, вашей юрисдикции и того, что вы делаете с данными. Условия Wayfair ограничивают автоматический доступ, поэтому скрейпинг может нарушать их независимо от аккуратности ваших инструментов. Ни один из приведённых здесь кодов не меняет этого; он лишь обеспечивает техническую сторону вопроса. Прочитайте Пользовательское соглашение Wayfair и его robots.txt, рассматривая оба документа как границу того, что вы собираете.
Несколько правил, которых стоит придерживаться. Собирайте только публичные данные о товарах: название и цену, которые может видеть любой на странице листинга без учётной записи. Ограничьте использование трекера личными или исследовательскими целями, соблюдайте умеренный темп и держите объём запросов достаточно низким, чтобы не создавать нагрузки на серверы Wayfair. Избегайте персональных данных, включая всё, что связано с идентифицируемыми рецензентами, кроме публичного текста отзыва и рейтингов, отображаемых на странице. Не распространяйте защищённые авторским правом материалы Wayfair, такие как фотографии товаров, как собственные. Если вы планируете использовать данные в коммерческих целях, получите разрешение или официальное соглашение, а не предполагайте, что молчание означает согласие.
Это руководство намеренно ограничено публичными ценами листингов, поскольку именно здесь работа остаётся допустимой. Оно не охватывает ничего за логином, данные клиентов или продавцов, историю заказов и любые попытки обойти аутентификацию или проверку, которую вы не должны проходить. Если ваш проект требует большего, чем публичные листинги, правильный путь, официальное соглашение на получение данных с Wayfair, а не более агрессивный скрейпер. При сомнениях в коммерческом использовании проконсультируйтесь с юристом перед масштабированием.
Ключевые выводы
- Трекинг означает запись данных с течением времени. Динамическое ценообразование Wayfair делает текущую цену менее значимой, чем тренд, поэтому трекер добавляет снимки с метками времени вместо перезаписи одного значения.
- Wayfair рендерит цены на стороне клиента. Обычный запрос возвращает пустую оболочку, поэтому необходимо отрендерить страницу через доверенный IP, прежде чем cheerio сможет прочитать название и цену.
- Crawling API делает это за один вызов. Он рендерит страницу, ротирует резидентные IP и обрабатывает вызовы, возвращая готовый HTML, именно это позволяет плановому трекеру работать автономно.
-
cheerio извлекает поля. Выберите каждый контейнер
data-hb-id="Card", затем читайте название из элементаListingCard-ListingCardName-Textи цену изPriceDisplay, ожидая, что эти хуки со временем изменятся. - Запускайте по расписанию и оставайтесь на публичных данных. Запускайте дважды в день с помощью cron или node-cron, экспортируйте историю в CSV для построения графиков и держите трекер в рамках публичных цен листингов, соблюдающих ToS и robots.txt Wayfair.
Часто задаваемые вопросы
Что такое трекер цен на Wayfair?
Трекер цен на Wayfair, это небольшая программа, которая записывает цены на товары Wayfair по расписанию и ведёт историю. Вместо того чтобы проверять цену вручную, она получает страницу листинга, считывает название и цену каждого товара и добавляет строку с меткой времени в файл. Со временем этот файл превращается в журнал цен, который можно строить на графике: видеть колебания, замечать скидки и выбирать момент покупки при снижении цены.
Почему обычный запрос возвращает неполные данные с Wayfair?
Потому что Wayfair рендерит карточки товаров на стороне клиента с помощью JavaScript и блокирует автоматический трафик. Обычный HTTP-запрос с IP дата-центра, как правило, возвращает почти пустую оболочку или страницу блокировки вместо карточек листинга, поэтому цены отсутствуют в получаемом HTML. Чтобы получить полную страницу, её нужно отрендерить через доверенный IP, именно это Crawling API и делает за вас.
Как работает ценообразование на Wayfair?
Wayfair использует модель динамического ценообразования, зависящую от спроса, наличия товара, конкуренции и собственной алгоритмической ценообразовательной системы, которая собирает и анализирует данные в режиме реального времени. Продавцы устанавливают собственные цены, а Wayfair корректирует их для поддержания конкурентоспособности, поэтому один и тот же товар может стоить по-разному в разных локациях и даже в течение одного дня. Именно эта волатильность делает запись цен по расписанию более полезной, чем единоразовая проверка.
Как отслеживать снижение цен на Wayfair?
Запускайте трекер по расписанию, чтобы при каждом запуске добавлялся снимок с меткой времени, а затем сравнивайте цену товара между двумя метками в файле истории. Если более поздняя цена для одного и того же названия ниже более ранней, это снижение. График CSV в таблице делает падения очевидными в виде просадок ценовой линии, а поверх тех же данных можно настроить оповещение, когда история уже накоплена.
Мои селекторы возвращают пустые значения. Что изменилось?
Почти наверняка разметка Wayfair. Хуки data-hb-id и data-test-id, на которые опирается парсер, привязаны к сборке фронтенда Wayfair и могут измениться без предупреждения, поэтому селекторы, работавшие в прошлом месяце, могут перестать работать. Повторно осмотрите живую страницу листинга в инструментах разработчика браузера и обновите селекторы. Периодическое обновление селекторов, норма для любого промышленного трекера.
Как избежать блокировки при отслеживании цен Wayfair?
Соблюдайте умеренный интервал опроса, нескольких запусков в день достаточно для отслеживания ценовых трендов, и маршрутизируйте запросы через ротирующиеся резидентные IP, чтобы ни один адрес не достиг лимита или не получил вызов. Crawling API управляет ротацией, доверенным пулом IP и обработкой вызовов; если вы строите собственный стек, именно в это стоит инвестировать. Следите за кодами статуса плановых запусков и снижайте темп, когда начинают появляться ответы, отличные от 200.
Обходите любой сайт в масштабе, без борьбы с инфраструктурой.
Crawlbase берёт на себя прокси, отпечатки и CAPTCHA, чтобы ваша команда выпускала конвейеры данных вместо поддержки обвязки краулинга. 1 000 запросов бесплатно, без карты.
