返回博客
指南
拉卢卡·彭丘克2023年4月7日阅读时长:8分钟

释放数据的力量:如何从 Booking.com 搜刮有价值的信息

释放数据的力量:如何从 Booking.com 搜刮有价值的信息

先决条件

如果您尚未配置 Node.js 环境,只需访问其官方网站,下载适用于您操作系统的最新版本。然后创建一个新目录,并运行以下命令来初始化您的项目:

npm init -y

我们将使用 TypeScript 编写代码。作为 JavaScript 的超集,它增加了可选的静态类型检查及其他功能。这对于大型项目非常有用,并能帮助我们更早地发现错误。你需要将其添加到项目的开发依赖项中,并初始化其配置文件:

npm install typescript -save-dev                                                                                                                                                      npx tsc -init

请确保在新生成的tsconfig.json 文件中,将“outDir”属性设置为“dist”,因为我们打算将 TypeScript 代码与编译后的代码分开存放。

最后,以下命令将 Puppeteer 添加到项目的依赖项中:

npm install puppeteer

Puppeteer 是一个 Node.js 库,它提供了一个用于控制无头 Chrome 浏览器的高级 API,可用于网页抓取和自动化任务

数据位置

在本教程中,我们选择抓取葡萄牙马德拉群岛的可用房源:https://www.booking.com/searchresults.en-us.html?ss=Madeira+Islands&checkin=2023-01-13&checkout=2023-01-15。 在 URL 中添加入住和退房日期非常重要,这样才能获取到所有房源信息。

本指南涵盖以下属性数据的提取:

  • 名称
  • 该网址
  • 物理地址
  • 价格
  • 评分和评论数量
  • 缩略图

您可以在下面的截图中看到它们已被标出:

Booking.com 酒店搜索结果卡片,其中突出显示了酒店名称、评分和每晚价格

打开每个元素上的 "开发工具",你就能看到我们用来定位 HTML 元素的 CSS 选择器。如果你对 CSS 选择器的工作原理还不太了解,请参考这本新手指南

解析数据

由于所有房源信息都具有相同的结构和数据,我们可以在算法中提取整个房源列表的所有信息。运行脚本后,我们可以遍历所有结果,并将它们合并为一个列表。

粗略浏览一下 HTML 文档后,您可能会发现 Booking 网站相当复杂,而且类名大多是随机生成的。

Booking.com 的酒店列表页面,通过浏览器开发者工具突出显示了酒店名称链接和图片的 HTML 代码

幸运的是,该网站并非仅依赖类名,我们还可以将特定属性的值作为提取依据。在上方的截图中,我们标出了房产的缩略图、名称和网址是如何可获取的。

import puppeteer from 'puppeteer';

async function scrapeBookingData(booking_url: string): Promise<void> {

    // Launch Puppeteer

    const browser = await puppeteer.launch({

        headless: false,

    	  args: ['--start-maximized'],

    	  defaultViewport: null

    })

    const page = await browser.newPage()

    // Navigate to the channel URL

    await page.goto(booking_url)

    // Extract listings name

    const listings_name = await page.evaluate(() => {

        const names = document.querySelectorAll('div[data-testid="title"]')

    	  const names_array = Array.from(names)

    	  return names ? names_array.map(n => n.textContent) : []

    })

    console.log(listings_name)

    // Extract listings location

    const listings_location = await page.evaluate(() => {

        const locations = document.querySelectorAll('a[data-testid="title-link"]')

    	  const locations_array = Array.from(locations)

    	  return locations ? locations_array.map(l => l.getAttribute('href')) : []

    })

    console.log(listings_location)

    // Extract listings thumbnail

    const listings_thumbnail = await page.evaluate(() => {

        const thumbnails = document.querySelectorAll('[data-testid="image"]')

    	  const thumbnails_array = Array.from(thumbnails)

    	  return thumbnails ? thumbnails_array.map(t => t.getAttribute('src')) : []

    })

    console.log(listings_thumbnail)

    await browser.close()

}

scrapeBookingData("https://www.booking.com/searchresults.en-us.html?ss=Madeira+Islands&checkin=2023-01-13&checkout=2023-01-15")

我们使用 Puppeteer 打开了一个浏览器实例,创建了一个新页面,导航至目标 URL,提取了所需数据,然后关闭了浏览器。为了便于可视化调试,我使用了浏览器的非无头模式。

如上所述,由于“data-testid”属性为 HTML 元素分配了一个唯一值,因此可以轻松访问这些数据。运行以下命令以执行该脚本:

npx tsc && node dist/index.js

终端应显示 3 条大小相同的列表结果,分别代表当前页面上所有房源的名称、网址和缩略图。

Booking.com 的酒店列表页面,通过浏览器开发者工具突出显示了酒店名称链接和图片的 HTML 代码

在 HTML 文档的下一部分中,我们突出了该房产的地址、评分和评论数量。

// Extract listings address

const listings_address = await page.evaluate(() => {

    const addresses = document.querySelectorAll('[data-testid="address"]')

    const addresses_array = Array.from(addresses)

    return addresses ? addresses_array.map(a => a.textContent) : []

})

console.log(listings_address)

// Extract listings rating and review count

const listings_rating = await page.evaluate(() => {

    const ratings = document.querySelectorAll('[data-testid="review-score"]')

    const ratings_array = Array.from(ratings)

    return ratings ? ratings_array.map(r => r.textContent) : []

})

console.log(listings_rating)

和之前一样,我们使用了“data-testid”属性。再次运行脚本后,您应该会看到另外两个列表,与之前的列表一样。

Booking.com 的酒店列表页面,其中浏览器开发者工具对价格元素的 HTML 代码进行了高亮显示

最后,在上一节中,我们提取了房产的价格。代码与之前所做的并无二致:

// Extract listings price

const listings_price = await page.evaluate(() => {

    const prices = document.querySelectorAll('[data-testid="price-and-discounted-price"]')

    const prices_array = Array.from(prices)

    return prices ? prices_array.map(p => p.textContent) : []

})

console.log(listings_price)

为了便于对提取的数据进行后续处理,我们将把生成的列表合并为一个列表。

// Group the lists

const listings = []

for (let i = 0; i < listings_name.length; i++) {

    listings.push({

        name: listings_name[i],

        url: listings_location[i],

        address: listings_address[i],

        price: listings_price[i],

        ratings: listings_rating[i],

        thumbnails: listings_thumbnail[i]

    })

}

console.log(listings)

最终结果现在应如下所示:

[

  {

    name: 'Pestana Churchill Bay',

    url: 'https://www.booking.com/hotel/pt/pestana-churchill-bay.html?aid=304142&label=gen173nr-1FCAQoggJCFnNlYXJjaF9tYWRlaXJhIGlzbGFuZHNIMVgEaMABiAEBmAExuAEXyAEM2AEB6AEB-AEDiAIBqAIDuAK9luydBsACAdICJGViMWY2MmRjLWJhZmEtNGZhZC04MDAyLWQ4MmU3YjU5MTMwZtgCBeACAQ&ucfs=1&arphpl=1&checkin=2023-01-13&checkout=2023-01-15&group_adults=2&req_adults=2&no_rooms=1&group_children=0&req_children=0&hpos=1&hapos=1&sr_order=popularity&srpvid=42cc81de452009eb&srepoch=1673202494&all_sr_blocks=477957801_262227867_0_1_0&highlighted_blocks=477957801_262227867_0_1_0&matching_block_id=477957801_262227867_0_1_0&sr_pri_blocks=477957801_262227867_0_1_0__18480&tpi_r=2&from_sustainable_property_sr=1&from=searchresults#hotelTmpl',

    address: 'Câmara de Lobos',

    price: '911 lei',

    ratings: '9.0Wonderful 727 reviews',

    thumbnails: 'https://cf.bstatic.com/xdata/images/hotel/square200/202313893.webp?k=824dc3908c4bd3e80790ce011f763f10fd4064dcb5708607f020f2e7c92d130e&o=&s=1'

  },

  {

    name: 'Hotel Madeira',

    url: 'https://www.booking.com/hotel/pt/madeira-funchal.html?aid=304142&label=gen173nr-1FCAQoggJCFnNlYXJjaF9tYWRlaXJhIGlzbGFuZHNIMVgEaMABiAEBmAExuAEXyAEM2AEB6AEB-AEDiAIBqAIDuAK9luydBsACAdICJGViMWY2MmRjLWJhZmEtNGZhZC04MDAyLWQ4MmU3YjU5MTMwZtgCBeACAQ&ucfs=1&arphpl=1&checkin=2023-01-13&checkout=2023-01-15&group_adults=2&req_adults=2&no_rooms=1&group_children=0&req_children=0&hpos=2&hapos=2&sr_order=popularity&srpvid=42cc81de452009eb&srepoch=1673202494&all_sr_blocks=57095605_262941681_2_1_0&highlighted_blocks=57095605_262941681_2_1_0&matching_block_id=57095605_262941681_2_1_0&sr_pri_blocks=57095605_262941681_2_1_0__21200&tpi_r=2&from_sustainable_property_sr=1&from=searchresults#hotelTmpl',

    address: 'Se, Funchal',

    price: '1,045 lei',

    ratings: '8.3Very Good 647 reviews',

    thumbnails: 'https://cf.bstatic.com/xdata/images/hotel/square200/364430623.webp?k=8c1e510da2aad0fc9ff5731c3874e05b1c4cceec01a07ef7e9db944799771724&o=&s=1'

  },

  {

    name: 'Les Suites at The Cliff Bay - PortoBay',

    url: 'https://www.booking.com/hotel/pt/les-suites-at-the-cliff-bay.html?aid=304142&label=gen173nr-1FCAQoggJCFnNlYXJjaF9tYWRlaXJhIGlzbGFuZHNIMVgEaMABiAEBmAExuAEXyAEM2AEB6AEB-AEDiAIBqAIDuAK9luydBsACAdICJGViMWY2MmRjLWJhZmEtNGZhZC04MDAyLWQ4MmU3YjU5MTMwZtgCBeACAQ&ucfs=1&arphpl=1&checkin=2023-01-13&checkout=2023-01-15&group_adults=2&req_adults=2&no_rooms=1&group_children=0&req_children=0&hpos=3&hapos=3&sr_order=popularity&srpvid=42cc81de452009eb&srepoch=1673202494&all_sr_blocks=395012401_247460894_2_1_0&highlighted_blocks=395012401_247460894_2_1_0&matching_block_id=395012401_247460894_2_1_0&sr_pri_blocks=395012401_247460894_2_1_0__100000&tpi_r=2&from_sustainable_property_sr=1&from=searchresults#hotelTmpl',

    address: 'Sao Martinho, Funchal',

    price: '4,928 lei',

    ratings: '9.5Exceptional 119 reviews',

    thumbnails: 'https://cf.bstatic.com/xdata/images/hotel/square200/270120962.webp?k=68ded1031f5082597c48eb25c833ea7fcedc2ec2bc5d555adfcac98b232f9745&o=&s=1'

  }

]

替代方案

尽管到目前为止,本教程看起来很简单,但我们必须提一下网络爬虫通常会遇到的注意事项,尤其是在您希望扩大项目规模时。

如今,各大网站纷纷采用各种机器人检测技术并收集浏览器数据,以此来防范或拦截自动化流量。Booking.com 也不例外。该网站借助 PerimeterX 防护系统,会对您的 IP 地址进行核查,并收集多项信息:

  • Navigator 对象的属性(deviceMemory、languages、platform、userAgent、webdriver 等)
  • 字体和插件枚举
  • 屏幕尺寸检查
  • 等等。

应对这些挑战的一个解决方案是使用数据抓取 API,它提供了一种简单可靠的方式,可以从 Booking.com 等网站获取数据,而无需自行开发和维护数据抓取工具。

WebScrapingAPI 就是这样一款产品,它利用代理轮换来绕过验证码,并通过随机化浏览器数据来模拟真实用户。开始使用时,只需注册一个账户,然后从仪表盘获取您的 API 密钥。该密钥用于对您的请求进行身份验证。

仪表盘快速入门指南,包含三个步骤:API 访问密钥、API 测试平台以及集成到您的应用程序中

要快速使用现有的 Node.js 项目测试 API,我们可以利用其对应的 SDK。只需运行以下命令:

npm install webscrapingapi

现在,您只需将之前的 CSS 选择器调整为 API 格式即可。提取规则功能让您只需进行微调即可解析数据,使其成为您网络爬虫工具箱中的一款强力工具。

import webScrapingApiClient from 'webscrapingapi';

const client = new webScrapingApiClient("YOUR_API_KEY");

async function exampleUsage() {

    const api_params = {

        'render_js': 1,

    	  'proxy_type': 'datacenter',

    	  'timeout': 60000,

    	  'extract_rules': JSON.stringify({

            names: {

                selector: 'div[data-testid="title"]',

                output: 'text',

                all: '1'

        	},

        	locations: {

                selector: 'a[data-testid="title-link"]',

                output: '@href',

                all: '1'

        	},

        	addresses: {

                selector: '[data-testid="address"]',

                output: 'text',

                all: '1'

        	},

        	prices: {

                selector: '[data-testid="price-and-discounted-price"]',

                output: 'text',

                all: '1'

        	},

        	ratings: {

                selector: '[data-testid="review-score"]',

                output: 'text',

                all: '1'

        	},

        	thumbnails: {

                selector: '[data-testid="image"]',

                output: '@src',

                all: '1'

        	}

        })

    }

    const URL = "https://www.booking.com/searchresults.en-us.html?ss=Madeira+Islands&checkin=2023-01-13&checkout=2023-01-15"

    const response = await client.get(URL, api_params)

    if (response.success) {

        // Group the lists

    	  const listings = []

    	  for (let i = 0; i < response.response.data.names.length; i++) {

            listings.push({

               name: response.response.data.names[i],

               url: response.response.data.locations[i],

               address: response.response.data.addresses[i],

               price: response.response.data.prices[i],

               ratings: response.response.data.ratings[i],

               thumbnails: response.response.data.thumbnails[i]

            })

        }

        console.log(listings)

    } else {

        console.log(response.error.response.data)

    }

}

exampleUsage();

结论

在本教程中,我们介绍了如何使用 Node.js 和 Puppeteer 抓取 Booking.com 数据的基础知识。我们向您演示了如何配置环境,并提取葡萄牙马德拉岛的房源详情。不过,这些技术和概念同样适用于其他网站和数据源。

网络爬虫对于企业和数据科学家而言都是极其有用的工具。通过从Booking.com收集数据,您可以深入了解酒店业、评估竞争对手情况,并获得更多有价值的洞察。不过,需要注意的是,网络爬虫可能违反某些网站的使用条款,因此在操作前务必查阅相关政策。

虽然您可以自行开发网络爬虫,但使用专业服务通常是更安全、更高效的选择,对于大型项目而言尤其如此。专业的爬虫服务具备专业知识和资源,能够应对可能出现的任何挑战,并提供高质量的成果。

希望您喜欢这篇教程,并已掌握如何在 Node.js 环境中从 Booking.com 收集有价值的数据。感谢阅读!

关于作者
Raluca Penciuc,WebScrapingAPI 全栈开发工程师
Raluca Penciuc全栈开发工程师

Raluca Penciuc 是 WebScrapingAPI 的全栈开发工程师,主要负责开发爬虫、优化规避机制,并探索可靠的方法以降低在目标网站上的被检测概率。

开始构建

准备好扩展您的数据收集规模了吗?

加入2,000多家企业,使用WebScrapingAPI在无需任何基础设施开销的情况下,以企业级规模提取网页数据。