Voltar ao blogue
Guias
Raluca Penciuc7 de abril de 20238 minutos de leitura

Descubra o poder dos dados: como extrair informações valiosas do Booking.com

Descubra o poder dos dados: como extrair informações valiosas do Booking.com

Pré-requisitos

Se ainda não tiver o seu ambiente Node.js configurado, basta ir ao site oficial para descarregar a versão mais recente para o seu sistema operativo. Em seguida, crie um novo diretório e execute o seguinte comando para inicializar o seu projeto:

npm init -y

Iremos utilizar TypeScript para escrever o código. Este superset do JavaScript adiciona tipagem estática opcional e outras funcionalidades. É útil para projetos de maior dimensão e pode facilitar a deteção precoce de erros. É necessário adicioná-lo às dependências de desenvolvimento do projeto e inicializar o seu ficheiro de configuração:

npm install typescript -save-dev                                                                                                                                                      npx tsc -init

Certifique-se apenas de que, no ficheiro tsconfig.json recém-gerado, a propriedade «outDir» está definida como «dist», uma vez que pretendemos separar o código TypeScript do código compilado.

Por último, o comando seguinte irá adicionar o Puppeteer às dependências do nosso projeto:

npm install puppeteer

O Puppeteer é uma biblioteca Node.js que fornece uma API de alto nível para controlar um navegador Chrome sem interface gráfica, que pode ser usado para web scraping e tarefas de automação

Localização dos dados

Para este tutorial, optámos por extrair as propriedades disponíveis nas Ilhas da Madeira, Portugal: https://www.booking.com/searchresults.en-us.html?ss=Madeira+Islands&checkin=2023-01-13&checkout=2023-01-15. É importante adicionar as datas de check-in e check-out ao URL para que todas as informações sobre as propriedades fiquem disponíveis.

Este guia aborda a extração dos seguintes dados das propriedades:

  • o nome
  • o URL
  • o endereço físico
  • o preço
  • a classificação e o número de avaliações
  • a miniatura

Pode vê-los destacados na captura de ecrã abaixo:

Booking.com hotel search result card with highlighted property name, review score, and nightly price

Ao abrir as Ferramentas do Desenvolvedor em cada um destes elementos, poderá observar os seletores CSS que iremos utilizar para localizar os elementos HTML. Se ainda não estiver familiarizado com o funcionamento dos seletores CSS, consulte este guia para principiantes.

Análise dos dados

Uma vez que todos os anúncios têm a mesma estrutura e dados, podemos extrair toda a informação da lista completa de propriedades no nosso algoritmo. Após executar o script, podemos percorrer todos os resultados e compilá-los numa única lista.

Após uma primeira olhadela no documento HTML, deve ter reparado que o site da Booking é bastante complexo e que os nomes das classes são, na sua maioria, gerados aleatoriamente.

Booking.com hotel listing page with browser devtools highlighting HTML for the property title link and image

Felizmente para nós, o site não depende exclusivamente de nomes de classes, e podemos usar o valor de um atributo específico como critério de extração. Na captura de ecrã acima, destacámos como a miniatura, o nome e o URL de um imóvel são acessíveis.

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")

Utilizámos o Puppeteer para abrir uma instância do navegador, criar uma nova página, navegar até ao nosso URL de destino, extrair os dados mencionados e, em seguida, fechar o navegador. Para fins de depuração visual, estou a utilizar o modo não headless do navegador.

Conforme explicado acima, os dados eram facilmente acessíveis graças ao atributo “data-testid”, que atribuiu um valor único ao elemento HTML. Execute o seguinte comando para executar o script:

npx tsc && node dist/index.js

O seu terminal deverá apresentar 3 resultados de lista do mesmo tamanho, representando os nomes, as URLs e as miniaturas de todas as propriedades na página atual.

Booking.com hotel listing page with browser devtools highlighting HTML for the property title link and image

Para a secção seguinte do documento HTML, destacámos o endereço, a classificação e o número de avaliações de uma propriedade.

// 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)

Tal como antes, utilizámos o atributo “data-testid”. Ao executar o script novamente, deverão aparecer mais 2 listas, tal como as anteriores.

Booking.com hotel listing page with browser devtools highlighting HTML for the price element

E, finalmente, na última secção, extraímos o preço do imóvel. O código não será diferente do que fizemos anteriormente:

// 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)

Para facilitar o processamento posterior dos dados extraídos, vamos combinar as listas resultantes numa única lista.

// 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)

O resultado final deverá agora ter este aspeto:

[

  {

    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'

  }

]

Alternativas

Embora o tutorial até este ponto tenha parecido simples, devemos mencionar as ressalvas normalmente encontradas na extração de dados da web, especialmente no caso de querer expandir o seu projeto.

Atualmente, os sites implementam várias técnicas de deteção de bots e recolhem dados do navegador para que possam impedir ou bloquear o tráfego automatizado. O Booking.com não é exceção a esta regra. Utilizando a proteção PerimeterX, o site realiza verificações no seu IP e recolhe várias informações:

  • propriedades do objeto Navigator (deviceMemory, languages, platform, userAgent, webdriver, etc.)
  • enumeração de tipos de letra e plugins
  • verificações das dimensões do ecrã
  • e muito mais.

Uma solução para estes desafios é utilizar uma API de scraping, que oferece uma forma simples e fiável de aceder a dados de sites como o Booking.com sem a necessidade de criar e manter o seu próprio scraper.

A WebScrapingAPI é um produto desse tipo, que utiliza rotação de proxies para contornar CAPTCHAs e randomiza os dados do navegador para imitar um utilizador real. Para começar, basta registar-se para criar uma conta e obter a sua chave API no painel de controlo. Esta chave é utilizada para autenticar os seus pedidos.

Dashboard quickstart guide showing three steps: API access key, API Playground, and integration into your application

Para testar rapidamente a API com o projeto Node.js já existente, podemos utilizar o SDK correspondente. Basta executar o seguinte comando:

npm install webscrapingapi

Agora, basta ajustar os seletores CSS anteriores à API. A funcionalidade de regras de extração permite analisar dados com modificações mínimas, tornando-a uma ferramenta poderosa no seu conjunto de ferramentas de web scraping.

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();

Conclusão

Neste tutorial, abordámos os conceitos básicos de como fazer web scraping no Booking.com utilizando Node.js e Puppeteer. Mostrámos-lhe como configurar o seu ambiente e extrair detalhes de anúncios para a Madeira, Portugal. No entanto, estas técnicas e conceitos podem ser aplicados a outros sites e pontos de dados também.

O web scraping pode ser uma ferramenta incrivelmente útil tanto para empresas como para cientistas de dados. Ao recolher dados do Booking.com, pode obter informações valiosas sobre o setor hoteleiro, avaliar a concorrência e muito mais. No entanto, é importante ter em mente que o web scraping pode violar os termos de utilização de alguns sites, e é sempre uma boa ideia verificar as políticas específicas antes de prosseguir.

Embora seja possível criar o seu próprio web scraper, recorrer a um serviço profissional pode muitas vezes ser uma opção mais segura e eficiente, especialmente para projetos de maior dimensão. Um scraper profissional terá os conhecimentos e os recursos necessários para lidar com quaisquer desafios que possam surgir e fornecer resultados de alta qualidade.

Esperamos que tenha gostado deste tutorial e que agora se sinta preparado para recolher dados valiosos da Booking.com utilizando um ambiente Node.js. Obrigado pela leitura!

Sobre o autor
Raluca Penciuc, Desenvolvedor Full-Stack @ WebScrapingAPI
Raluca PenciucDesenvolvedor Full-Stack

Raluca Penciuc é programadora Full Stack na WebScrapingAPI, onde desenvolve scrapers, aperfeiçoa estratégias de evasão e procura formas fiáveis de reduzir a deteção nos sites-alvo.

Comece a construir

Pronto para expandir a sua recolha de dados?

Junte-se a mais de 2.000 empresas que utilizam a WebScrapingAPI para extrair dados da Web à escala empresarial, sem quaisquer custos de infraestrutura.