Pré-requisitos
Se ainda não tiver o ambiente Node.js configurado, basta aceder 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
Vamos utilizar o TypeScript para escrever o código. Este supersconjunto 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»esteja definida como«dist», uma vez que pretendemos separar o código TypeScript do código compilado.
Por fim, 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, podendo ser utilizada para extração de dados da Web e tarefas de automação
Localização dos dados
Para este tutorial, optámos por extrair os dados das propriedades disponíveis nas Ilhas da Madeira, em 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 de propriedades:
- o nome
- o URL
- o endereço físico
- o preço
- a classificação e o número de comentários
- a miniatura
Pode vê-los destacados na imagem abaixo:

Ao abrir as Ferramentas do desenvolvedor em cada um desses elementos, você poderá observar os seletores CSS que usaremos para localizar os elementos HTML. Se não sabe muito bem como funcionam os selectores CSS, pode consultar este guia para principiantes.
Analisar os dados
Como todas as listagens têm a mesma estrutura e dados, podemos extrair todas as informações de toda a lista de propriedades no nosso algoritmo. Depois de executar o script, podemos percorrer todos os resultados e compilá-los numa única lista.
Após uma primeira olhadela ao 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.

Felizmente para nós, o site não se baseia apenas nos nomes das classes, e podemos utilizar o valor de um atributo específico como critério de extração. Na captura de ecrã acima, destacámos a facilidade com que se acede à miniatura, ao nome e ao URL de um imóvel.
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, aceder ao URL de destino, extrair os dados mencionados e, por fim, fechar o navegador. Para efeitos de depuração visual, estou a utilizar o modo não headless do navegador.
Conforme explicado acima, foi possível aceder facilmente aos dados 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 três resultados na lista, todos com o mesmo tamanho, representando os nomes, os URLs e as miniaturas de todas as propriedades na página atual.

Na secção seguinte do documento HTML, destacámos a morada, a classificação e o número de comentários de um imóvel.
// 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 duas listas, tal como as anteriores.

E, por fim, 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, iremos juntar 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 deve ficar assim:
[
{
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, é importante referir as dificuldades com que normalmente nos deparamos na extração de dados da Web, especialmente quando se pretende expandir o projeto.
Atualmente, os sites implementam várias técnicas de deteção de bots e recolhem dados do navegador para poderem impedir ou bloquear o tráfego automatizado. O Booking.com não é exceção a esta regra. Através da proteção PerimeterX, o site realiza verificações do 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
- controlo 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 necessidade de criar e manter o seu próprio scraper.
A WebScrapingAPI é um produto que utiliza a rotação de proxies para contornar os CAPTCHAs e aleatoriza os dados do navegador para simular um utilizador real. Para começar, basta criar uma conta e obter a sua chave API no painel de controlo. Esta chave é utilizada para autenticar os seus pedidos.

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-lhe analisar dados com alteraçõ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 sobre como extrair dados do Booking.com utilizando o Node.js e o Puppeteer. Mostrámos-lhe como configurar o seu ambiente e extrair detalhes de anúncios relativos à Madeira, em Portugal. No entanto, estas técnicas e conceitos podem ser aplicados também a outros sites e conjuntos de dados.
A extração de dados da Web pode ser uma ferramenta extremamente útil tanto para empresas como para cientistas de dados. Ao recolher dados do Booking.com, é possível obter informações valiosas sobre o setor hoteleiro, avaliar a concorrência e muito mais. No entanto, é importante ter em conta que a extração de dados da Web pode violar os termos de utilização de alguns sites, pelo que é sempre aconselhável verificar as políticas específicas antes de prosseguir.
Embora seja possível criar o seu próprio scraper da Web, recorrer a um serviço profissional pode, muitas vezes, ser uma opção mais segura e eficiente, especialmente no caso de projetos de maior dimensão. Um scraper profissional dispõe dos conhecimentos e dos recursos necessários para lidar com quaisquer desafios que possam surgir e apresentar 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!




