Booking.com это одна из крупнейших туристических площадок в открытом вебе, и её публичные страницы результатов поиска несут много структурированного сигнала: названия отелей и объектов размещения, ночные цены, оценки отзывов гостей, районы и ссылку на каждый листинг. Команды ценовой аналитики используют их для сравнения тарифов по городам, исследователи изучают спрос и предложение по направлениям, а планировщики поездок сравнивают объекты бок о бок. Всё это находится на публичной странице поиска в предсказуемом макете карточек.
Это руководство показывает, как парсить Booking.com на JavaScript и Node.js с помощью Cheerio. Вы создадите небольшой работающий скрапер, который получает публичную страницу результатов поиска Booking.com через Crawling API, разбирает название объекта, цену, оценку отзывов, местоположение и URL листинга для каждой карточки, затем экспортирует результат в виде JSON и CSV. Весь разбор ограничен публичными данными списка отелей, а раздел о законности ближе к концу не для галочки, так что прочитайте его, прежде чем направлять этот скрипт на любой реальный объём.
Что вы построите
Node.js-скрипт, который принимает публичный URL результатов поиска Booking.com, получает отрисованный HTML через Crawling API и извлекает структурированную запись для каждой карточки объекта на странице. Мы используем поиск отелей в Сан-Франциско в качестве сквозного примера и вытащим следующие поля по каждому листингу:
- Название название отеля или объекта, показанное на карточке, например «Hotel Zephyr San Francisco».
- Цена отображаемая ночная цена, вроде «US$592».
- Рейтинг публичная оценка отзывов гостей, когда Booking.com показывает её для этого объекта.
- Местоположение район и город, в которых указан объект.
- Ссылка URL на отдельную страницу объекта.
Почему обычный запрос не срабатывает на Booking.com
Если запросить URL поиска Booking.com голым HTTP-клиентом, вы редко получите карточки объектов обратно. Против вас работают две вещи. Во-первых, Booking.com отрисовывает список результатов в браузере на JavaScript, поэтому начальный HTML это почти пустая оболочка, пока не выполнятся скрипты страницы. Во-вторых, Booking.com отслеживает автоматизированный трафик: IP дата-центров и паттерны запросов, которые не выглядят как настоящий браузер, получают проверку CAPTCHA, ограничение частоты или блокировку ещё до того, как они когда-либо доберутся до отрисованных списков.
Так что рабочему скраперу нужны две вещи в одном запросе: браузер, который действительно отрисовывает страницу, и IP-адрес, который платформа воспринимает как реального посетителя. Вы можете собрать это самостоятельно из headless-браузера и пула ротируемых резидентных прокси, но поддерживать этот стек в рабочем состоянии и есть основная часть работы. Crawling API объединяет и то и другое в один вызов: вы отправляете ему URL, он отрисовывает страницу за доверенным IP и возвращает готовый HTML, который вы разбираете с помощью Cheerio.
Crawling API даёт вам два токена: обычный и JavaScript. Booking.com нужна страница, отрисованная в настоящем браузере, поэтому используйте свой JavaScript-токен для каждого запроса в этом руководстве. Обычный токен возвращает неотрисованную оболочку, и ваши селекторы вернутся пустыми.
Предварительные требования
Прежде чем писать код, вам нужно подготовить несколько вещей. Ни одна из них не займёт много времени.
Базовый JavaScript и Node.js. Вы должны уверенно писать и запускать Node-скрипт, устанавливать пакеты через npm и работать с асинхронным кодом. Для более полного разбора наше руководство по построению веб-скрапера на Node.js охватывает основы.
Node.js 16 или новее. Подтвердите свою версию командой node --version. Если его у вас нет, установите его с сайта Node.js или через менеджер версий вроде nvm.
Учётная запись Crawlbase и токен. Зарегистрируйтесь, откройте дашборд и скопируйте свой JavaScript-токен со страницы документации учётной записи. Бесплатный тариф даёт вам до 20 000 запросов без карты, и вы платите только за успешные запросы. Относитесь к токену как к паролю: он аутентифицирует ваши запросы, поэтому держите его вне системы контроля версий.
Настройте проект
Создайте папку проекта, инициализируйте её и установите две библиотеки, которые нужны скраперу.
node --version mkdir booking-scraper && cd booking-scraper npm init -y npm install crawlbase cheerio
Две зависимости делают всю работу: crawlbase это официальный Node-клиент для Crawling API, а cheerio разбирает возвращённый HTML с помощью API в стиле jQuery, так что вы можете вытаскивать отдельные поля по CSS-селектору. Создайте файл с именем scraper.js в этой папке и добавьте код из шагов ниже.
Шаг 1: Получите отрисованную страницу поиска
Начните с получения готовой страницы. Импортируйте класс CrawlingAPI, инициализируйте его своим JavaScript-токеном и запросите публичный URL результатов поиска Booking.com. Проверка кода статуса перед разбором делает сбои громкими, а не молчаливыми.
const { CrawlingAPI } = require('crawlbase'); const api = new CrawlingAPI({ token: 'YOUR_CRAWLBASE_TOKEN' }); const bookingPageURL = 'https://www.booking.com/searchresults.html?ss=San+Francisco&checkin=2025-12-25&checkout=2025-12-31&group_adults=2&no_rooms=1&group_children=0&selected_currency=USD'; api .get(bookingPageURL) .then((response) => { if (response.statusCode === 200) { console.log(response.body.slice(0, 500)); } }) .catch((error) => console.error('API request error:', error));
Запустите скрипт командой node scraper.js, и вы должны увидеть реальную разметку объектов Booking.com в начале тела, а не урезанную оболочку. Это подтверждает, что отрисовка работает, прежде чем вы напишете хоть один селектор. Crawling API использует JavaScript-токен, который вы предоставили, чтобы отрисовать страницу в настоящем браузере, так что карточки объектов присутствуют в HTML, который вы получаете обратно.
Этот первый запрос только что вернул полностью отрисованную страницу поиска Booking.com без headless-браузера или прокси на вашей стороне. Crawling API выполняет страницу в настоящем браузере, ротирует резидентные IP на стороне сервера и обрабатывает CAPTCHA, которые Booking.com подбрасывает скраперам, так что вы получаете готовый HTML из одного вызова. Сначала направьте его на публичный поиск отелей на бесплатном тарифе, затем добавьте свой парсер.
Шаг 2: Разберите каждую карточку объекта с помощью Cheerio
Имея на руках отрисованный HTML, загрузите его в Cheerio и пройдите по карточкам объектов. Booking.com оборачивает каждый листинг в элемент, помеченный data-testid="property-card", так что вы выбираете каждую карточку, затем читаете из неё название, местоположение, оценку отзывов, цену и ссылку на листинг, используя собственные атрибуты data-testid страницы. Чтение каждого поля защитным образом не даёт одному отсутствующему значению уронить прогон.
const cheerio = require('cheerio'); function parseDataFromHTML(html) { const $ = cheerio.load(html); const properties = []; const cardSelector = '[data-testid="property-card"]'; $(cardSelector).each((_, card) => { const currentCard = $(card); const extractText = (selector) => currentCard.find(selector).text().trim(); const name = extractText('[data-testid="title"]'); const location = extractText('[data-testid="address"]'); const rating = extractText( '[data-testid="review-score"] div.a3b8729ab1.d86cee9b25', ); const price = extractText( 'span[data-testid="price-and-discounted-price"]', ); const link = currentCard .find('[data-testid="title-link"]') .attr('href'); if (name) { properties.push({ name, price: price || 'Price not available', rating: rating || 'Rating not available', location, link: link || '', }); } }); return properties; }
Несколько деталей удерживают это в верности странице. Каждый листинг живёт в элементе [data-testid="property-card"]. Внутри карточки название приходит из [data-testid="title"], местоположение из [data-testid="address"], оценка отзывов из блока оценки внутри [data-testid="review-score"], цена из span[data-testid="price-and-discounted-price"], а URL листинга из атрибута href якоря [data-testid="title-link"]. Цепляясь сначала за устойчивые зацепки data-testid и откатываясь к сгенерированным именам классов только там, где приходится, вы делаете парсер устойчивее по мере смещения разметки.
Сгенерированные имена классов Booking.com (суффиксы в стиле a3b8729ab1 выше) меняются без предупреждения, и даже зацепки data-testid иногда переименовываются. Относитесь к селекторам как к отправному шаблону, а не как к контракту. Когда поле возвращается пустым, заново осмотрите живую страницу в инструментах разработчика браузера и обновите селектор. Периодическое обслуживание селекторов это норма для любого продакшен-скрапера, а не признак того, что что-то сломалось.
Шаг 3: Соберите полный скрипт с экспортом JSON и CSV
Теперь свяжите получение и разбор в один работающий скрипт, затем запишите записи на диск и как JSON, и как CSV. Устаревшее руководство сохраняло сырой HTML в файл между шагами, но сворачивание получения и разбора в один прогон сокращает число движущихся частей.
const fs = require('fs'); const { CrawlingAPI } = require('crawlbase'); const cheerio = require('cheerio'); const api = new CrawlingAPI({ token: 'YOUR_CRAWLBASE_TOKEN' }); 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 toCsv(rows) { const headers = ['name', 'price', 'rating', 'location', '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 url = 'https://www.booking.com/searchresults.html?ss=San+Francisco&checkin=2025-12-25&checkout=2025-12-31&group_adults=2&no_rooms=1&group_children=0&selected_currency=USD'; const html = await crawl(url); if (!html) return; const properties = parseDataFromHTML(html); fs.writeFileSync('booking.json', JSON.stringify(properties, null, 2)); fs.writeFileSync('booking.csv', toCsv(properties)); console.log(`Saved ${properties.length} properties to JSON and CSV`); } main();
Вставьте функцию parseDataFromHTML из Шага 2 в тот же файл, чтобы main могла её вызвать. Запустите его командой node scraper.js, и вы получите два файла: booking.json с полными структурированными записями и booking.csv, готовый к открытию в таблице. Помощник toCsv заключает в кавычки каждое поле и удваивает любые встроенные кавычки, что важно здесь, потому что названия объектов и метки районов часто содержат запятые.
Как выглядит вывод
Файл JSON содержит один объект на объект размещения, каждый с названием, ценой, публичной оценкой отзывов, местоположением и ссылкой на листинг. Значения ниже иллюстративны; ваш прогон отражает то, что актуально для ваших дат поиска.
[ { "name": "Hotel Zephyr San Francisco", "price": "US$592", "rating": "8.3", "location": "Fisherman's Wharf, San Francisco", "link": "https://www.booking.com/hotel/us/zephyr-san-francisco.html" }, { "name": "Club Quarters Hotel Embarcadero, San Francisco", "price": "US$554", "rating": "8.0", "location": "Financial District, San Francisco", "link": "https://www.booking.com/hotel/us/club-quarters-san-francisco.html" } ]
CSV отражает те же строки объектов с заголовочной строкой, так что он сразу ложится в Excel, Google Sheets или любой конвейер данных, читающий файлы с разделителями.
name,price,rating,location,link "Hotel Zephyr San Francisco","US$592","8.3","Fisherman's Wharf, San Francisco","https://www.booking.com/hotel/us/zephyr-san-francisco.html" "Club Quarters Hotel Embarcadero, San Francisco","US$554","8.0","Financial District, San Francisco","https://www.booking.com/hotel/us/club-quarters-san-francisco.html"
Обработайте пагинацию
Одна страница поиска это демо; реальная задача вытягивает каждую страницу результатов. Booking.com пагинирует свои URL поиска параметром offset, который пропускает фиксированное число объектов на страницу, так что вы можете пройти смещения в цикле, получить каждую страницу через Crawling API, разобрать её той же функцией и остановиться, когда страница вернёт ноль карточек. Поскольку каждая страница результатов использует одну и ту же структуру карточек, парсер, который вы уже написали, работает на всех них без изменений.
async function scrapeAllPages(baseUrl, maxPages) { const allProperties = []; const perPage = 25; for (let page = 0; page < maxPages; page++) { // Booking.com skips results with an offset parameter const pageUrl = `${baseUrl}&offset=${page * perPage}`; const html = await crawl(pageUrl); if (!html) break; const properties = parseDataFromHTML(html); if (properties.length === 0) break; // no more results allProperties.push(...properties); console.log(`Page ${page + 1}: ${properties.length} properties`); // Pace requests so you stay under the rate limit await new Promise((r) => setTimeout(r, 2000)); } return allProperties; }
Точный размер страницы и параметр могут меняться, поэтому проверьте пару реальных ссылок «следующая страница» в браузере и подгоните под паттерн. Важные привычки переносятся на любую цель: повторяйте цикл, пока результаты не закончатся, и ставьте короткую задержку между запросами, чтобы вы не долбили сайт. Подробнее об отрисованных, насыщенных JavaScript страницах вроде этой смотрите в нашем руководстве по обходу JavaScript-сайтов.
Как оставаться разблокированным
Даже когда отрисовка обработана, Booking.com отслеживает трафик в форме скрапера. Несколько привычек поддерживают прогон в здоровом состоянии, и они применимы к любой трудной коммерческой цели.
- Размеряйте свои запросы. Вводите задержку между получениями страниц, а не долбите поиск в плотном цикле. Распределение запросов это единственный самый крупный фактор для удержания под лимитами частоты Booking.com.
- Опирайтесь на ротацию. Пул резидентных IP распределяет запросы по множеству адресов реальных пользователей, так что ни один из них не упирается в лимит или CAPTCHA. Crawling API делает это за вас; если вы собираете свой собственный стек, это та часть, которую нужно сделать правильно.
- Читайте коды статусов. Прогон, который начинает возвращать проверки или ответы не-200, говорит вам, что текущего темпа или уровня IP больше недостаточно. Воспринимайте это как сигнал отступить, а не как шум, который можно игнорировать.
Более широкий план действий смотрите в том, как скрапить сайты, не попадая в блокировку. Если вы хотите похожие данные листингов с другой туристической платформы, тот же паттерн «получить, затем разобрать» прямо переносится на скрапинг цен Airbnb и на скрапинг Expedia на JavaScript. Чтобы сравнить эти тарифы по сайтам, наше руководство по веб-скрапингу для ценовой аналитики проходит по аналитической стороне.
Законно ли скрапить Booking.com?
Разрешён ли скрапинг Booking.com, зависит от условий обслуживания Booking.com, вашей юрисдикции и того, что вы делаете с данными. Условия Booking.com ограничивают автоматизированный доступ, поэтому скрапинг может вступать в противоречие с этими условиями, как бы аккуратен ни был ваш инструментарий. Ничего из кода здесь этого не меняет; он лишь заставляет техническую часть работать. Прочитайте Условия обслуживания Booking.com и его robots.txt, уважайте любые заявленные ими ожидания по частоте и относитесь к обоим как к границе того, что вы собираете.
Это руководство намеренно ограничено публичными данными списка отелей: название объекта, отображаемая цена, совокупная оценка отзывов, район и ссылка на листинг, которые любой может увидеть на странице поиска без входа в систему. Это фактическая бизнес-информация о листинге объекта, а не персональные данные. Другой контент вне области: отдельные отзывы гостей и люди, которые их написали, это персональные данные, всё, что за логином или потоком бронирования, под запретом, а фотографии и описания объектов это защищённый авторским правом материал, который вам не следует перераспределять оптом. Если в картину всё же входят какие-либо персональные данные, применяется законодательство о приватности, такое как GDPR и CCPA, поэтому держите свой сбор в рамках совокупных, неперсональных полей выше.
Если вашему проекту нужно больше, чем публичные листинги, правильный путь это санкционированный, а не более хитрый скрапер. Booking.com ведёт официальные аффилированные и партнёрские программы, включая Demand API, которые выставляют данные об объектах, наличии и ценах на чётких условиях с правилами атрибуции и определёнными коммерческими правами. Это правильные инструменты, когда вам нужны большие объёмы, гарантированная структура или право переиспользовать данные коммерчески. Когда вы не уверены, разрешено ли использование, получите разрешение или соглашение по данным, а не исходите из того, что молчание есть согласие.
Ключевые выводы
- Booking.com отрисовывает листинги на стороне клиента и жёстко блокирует. Обычный запрос возвращает пустую оболочку или CAPTCHA, поэтому вы должны отрисовать страницу за доверенным IP, используя JavaScript-токен, прежде чем её разбирать.
- Crawling API делает и то и другое в одном вызове. Он отрисовывает страницу в настоящем браузере, ротирует резидентные IP и обрабатывает CAPTCHA, возвращая готовый HTML, который вы разбираете с помощью Cheerio.
-
Cheerio извлекает поля. Выберите каждый
[data-testid="property-card"], затем прочитайте название, цену, оценку отзывов, местоположение и ссылку на листинг, и ожидайте, что сгенерированные имена классов будут дрейфовать. - Пагинируйте и экспортируйте. Пройдите параметр offset Booking.com в цикле, пока результаты не закончатся, размеряйте свои запросы и пишите структурированные записи и в JSON, и в CSV.
- Оставайтесь на публичных данных. Собирайте только публичные листинги отелей, относитесь к отдельным отзывам гостей и рецензентам как к персональным данным, уважайте ToS и robots.txt и предпочитайте официальный партнёрский API Booking.com для объёма или коммерческого использования.
Часто задаваемые вопросы
Могу ли я построить скрапер Booking.com на языке, отличном от JavaScript?
Да. Это руководство использует JavaScript с Cheerio, но тот же подход работает на любом языке. У Crawling API есть библиотеки и SDK для нескольких языков, так что вы получаете отрисованный HTML тем же способом и разбираете его тем HTML-парсером, который предпочитает ваш стек, например BeautifulSoup в Python. Селекторы и поля остаются теми же; меняется только синтаксис разбора.
Почему обычный запрос возвращает неполные данные от Booking.com?
Потому что Booking.com отрисовывает свой список объектов на стороне клиента на JavaScript и устраивает проверки автоматизированному трафику с помощью CAPTCHA. Сырой HTTP-запрос с IP дата-центра обычно возвращает пустую оболочку или страницу блокировки, а не карточки объектов. Чтобы получить полную страницу, вам нужно отрисовать её за доверенным IP, и это то, что обрабатывает за вас Crawling API, когда вы используете JavaScript-токен.
Мои селекторы возвращают пустые значения. Что изменилось?
Почти наверняка разметка Booking.com. Её сгенерированные имена классов вроде a3b8729ab1 меняются без предупреждения, и даже зацепки data-testid иногда переименовываются, поэтому селекторы, работавшие в прошлом месяце, могут сломаться. Заново осмотрите живую страницу в инструментах разработчика браузера, обновите селекторы в parseDataFromHTML, и вы снова в деле. Периодическое обслуживание селекторов это норма для любого продакшен-скрапера.
Заблокируют ли меня при скрапинге Booking.com?
Могут, если вы отправляете слишком много запросов слишком быстро с одного адреса. Crawling API снижает этот риск, ротируя резидентные IP и обрабатывая CAPTCHA за вас, но вам всё равно следует размерять свои запросы, добавлять задержки между страницами и следить за кодами статусов, чтобы вы могли отступить, когда появятся проверки. Эти привычки важны на любой трудной коммерческой цели.
Могу ли я скрапить отдельные отзывы гостей и имена рецензентов?
Это вне области этого руководства, и не зря. Отдельные отзывы и люди, которые их написали, это персональные данные, которые втягивают законодательство о приватности вроде GDPR и CCPA. Используйте совокупную оценку отзывов гостей как сигнал об объекте, не стройте профили отдельных рецензентов и не переопубликовывайте отзыв человека, привязанный к его личности. Для всего, что за пределами публичных листингов, используйте официальную партнёрскую программу Booking.com.
Есть ли у Booking.com официальный API?
Да. Booking.com ведёт официальные аффилированные и партнёрские программы, включая Demand API, которые выставляют данные об объектах, наличии и ценах на чётких условиях, с правилами атрибуции и определёнными коммерческими правами. Если вам нужны большие объёмы, гарантированная структура или право переиспользовать данные коммерчески, этот санкционированный маршрут и есть правильный. Этот скрапер публичных данных лучше всего подходит для исследований, прототипирования и анализа меньшего масштаба, где официальное соглашение не оправдано.
Обходите любой сайт в масштабе, без борьбы с инфраструктурой.
Crawlbase берёт на себя прокси, отпечатки и CAPTCHA, чтобы ваша команда выпускала конвейеры данных вместо поддержки обвязки краулинга. 1 000 запросов бесплатно, без карты.
