Voltar ao blogue
Guias
Raluca Penciuc7 de abril de 202310 min de leitura

Web Scraping para o sector imobiliário: Como extrair dados do Realtor.com como um profissional

Web Scraping para o sector imobiliário: Como extrair dados do Realtor.com como um profissional

Configuração do ambiente

Antes de começar a fazer scraping, é necessário instalar o Node.js no seu computador. Pode descarregar a versão mais recente a partir do sítio Web oficial e seguir as instruções de acordo com o seu sistema operativo.

Em seguida, crie um novo diretório para o seu projeto e navegue até ele no terminal ou no prompt de comando. Execute o seguinte comando para inicializar um novo projeto Node.js:

npm init -y

Isto criará um ficheiro package.json no seu diretório de projeto, que armazenará informações sobre o seu projeto e as suas dependências.

Para instalar o TypeScript, execute o seguinte comando:

npm install typescript -save-dev

O TypeScript é um superconjunto do JavaScript que adiciona tipagem estática opcional e outros recursos. É útil para projetos maiores e pode facilitar a deteção de erros logo no início. O TypeScript usa um arquivo de configuração chamado tsconfig.json para armazenar opções do compilador e outras configurações. Para criar esse arquivo no seu projeto, execute o seguinte comando:

npx tsc -init

Certifique-se de que o valor de "outDir" esteja definido como "dist". Dessa forma, separaremos os arquivos TypeScript dos arquivos compilados.

Agora, crie um diretório "src" no seu projeto e um novo ficheiro "index.ts". É aqui que vamos manter o código de raspagem. Para executar o código TypeScript, é necessário compilá-lo primeiro, portanto, para garantir que não nos esqueçamos dessa etapa extra, podemos usar um comando definido de forma personalizada.

Vá ao ficheiro "package. json" e edite a secção "scripts" desta forma:

"scripts": {

    "test": "npx tsc && node dist/index.js"

}

Desta forma, quando for executar o script, basta digitar "npm run test" no seu terminal.

E por último, mas não menos importante, execute o seguinte comando para adicionar o Puppeteer às dependências do seu projeto:

npm install puppeteer

O Puppeteer é uma biblioteca Node.js que fornece uma API de alto nível para controlar um navegador Chrome sem cabeça, que pode ser utilizado para tarefas de raspagem e automação da Web. É altamente recomendada quando se pretende garantir a integridade dos dados, uma vez que muitos sítios Web actuais contêm conteúdos gerados de forma dinâmica.

Seleção de dados

Agora que o ambiente está configurado, podemos começar a analisar a extração dos dados. Para este artigo, optei por extrair a lista de apartamentos estúdio disponíveis para aluguer em Plano, TX: https://www.realtor.com/apartments/Plano_TX/beds-studio.

Vamos extrair os seguintes dados de cada anúncio na página:

  • o URL;
  • os preços;
  • o número de banhos;
  • as superfícies (medidas em pés quadrados);
  • os endereços físicos

Pode ver todas estas informações destacadas na imagem de ecrã abaixo:

Resultados da pesquisa de arrendamento de apartamentos com fichas de anúncios destacadas a vermelho, apresentando fotografias, preços e detalhes da morada

Extração de dados

Para extrair todos estes dados, temos de os localizar primeiro. Clique com o botão direito do rato nas secções destacadas e, em seguida, escolha "Inspecionar" para abrir as Ferramentas de Desenvolvimento e ver o documento HTML. Movendo o cursor do rato sobre ele, pode ver facilmente que parte corresponde a cada secção:

Ficha de anúncio de apartamento ao lado do inspetor do navegador, destacando os seletores HTML para os campos de preço, casas de banho, área útil e morada

Para este tutorial, vou utilizar selectores CSS, uma vez que são a opção mais simples. Se é novo neste método, não hesite em consultar primeiro este guia auto-explicativo.

Para começar a escrever o nosso script, vamos verificar se a instalação do Puppeteer correu bem:

import puppeteer from 'puppeteer';

async function scrapeRealtorData(realtor_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(realtor_url)

    // Close the browser

    await browser.close()

}

scrapeRealtorData("https://www.realtor.com/apartments/Plano_TX/beds-studio")

Aqui, abrimos uma janela do navegador, criamos uma nova página, navegamos até o URL de destino e fechamos o navegador. Por uma questão de simplicidade e depuração visual, abro o navegador maximizado no modo sem cabeça.

Como cada listagem tem a mesma estrutura e os mesmos dados, no nosso algoritmo vamos extrair todas as informações de toda a lista de propriedades. No final do script, vamos iterar todos os resultados e centralizá-los numa única lista.

Deve ter reparado que o URL do anúncio não era visível na primeira imagem de ecrã, mas foi mencionado e destacado na segunda. Isso deve-se ao facto de ser redireccionado para o URL do imóvel quando clica nele.

// Extract listings location

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

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

    const locations_array = Array.from(locations)

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

})

console.log(localização_das_listas)

Localizamos o URL escolhendo os elementos âncora que têm o atributo "data-testid" com o valor "card-link". Em seguida, convertemos o resultado em uma matriz JavaScript e mapeamos cada elemento para o valor do atributo "href".

No entanto, a lista resultante conterá cada URL duas vezes. Isto acontece porque cada anúncio tem o mesmo elemento âncora para duas secções: as imagens da propriedade e os detalhes do aluguer. Podemos resolver isto facilmente utilizando a estrutura de dados Set:

const unique_listings_location = [...new Set(listings_location)]

console.log(unique_listings_location)

Para o preço do imóvel, extrairemos os elementos "div" que têm o atributo "data-testid" com o valor "card-price". Também precisa de ser convertido numa matriz e depois mapeado para o seu conteúdo de texto.

// Extract listings price

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

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

    const prices_array = Array.from(prices)

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

})

console.log(listings_price)

Para obter o número de banhos e a superfície da propriedade, utilizaremos o operador para elementos filhos diretos. Isto significa que o elemento pai é identificado de forma única, enquanto o elemento filho tem um id ou nome de classe mais genérico. Para além disso, a lógica é a mesma que a anterior:

// Extract listings baths

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

    const baths = document.querySelectorAll('li[data-testid="property-meta-baths"] > span[data-testid="meta-value"]')

    const baths_array = Array.from(baths)

    return baths ? baths_array.map(b => b.textContent) : []

})

console.log(listings_baths)

// Extract listings sqft

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

    const sqfts = document.querySelectorAll('li[data-testid="property-meta-sqft"] > span[data-testid="screen-reader-value"]')

    const sqfts_array = Array.from(sqfts)

    return sqfts ? sqfts_array.map(s => s.textContent) : []

})

console.log(listings_sqft)

Por fim, para os endereços das listagens, seleccionamos os elementos "div" que têm o atributo "data-testid" definido para o valor "card-address".

// Extract listings address

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

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

    const addresses_array = Array.from(addresses)

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

})

console.log(listings_address)

Agora você deve ter 5 listas, uma para cada dado que extraímos. Como mencionei anteriormente, devemos centralizá-las numa única. Desta forma, a informação que recolhemos será muito mais fácil de processar.

// Group the lists

const listings = []

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

    listings.push({

        url: unique_listings_location[i],

        price: listings_price[i],

        baths: listings_baths[i],

        sqft: listings_sqft[i],

        address: listings_address[i]

    })

}

console.log(listings)

O resultado final deve ser mais ou menos assim:

[

    {

        url: '/realestateandhomes-detail/1009-14th-St-Apt-410_Plano_TX_75074_M92713-98757',  

        price: '$1,349',

        baths: '1',

	  sqft: '602 square feet',

	  address: '1009 14th St Apt 410Plano, TX 75074'

    },

    {

	  url: '/realestateandhomes-detail/1009-14th-St-Apt-1_Plano_TX_75074_M95483-11211',    

	  price: '$1,616',

	  baths: '1',

	  sqft: '604 square feet',

	  address: '1009 14th St Apt 1Plano, TX 75074'

    },

    {

	  url: '/realestateandhomes-detail/1009-14th-St_Plano_TX_75074_M87662-45547',

	  price: '$1,605 - $2,565',

	  baths: '1 - 2',

	  sqft: '602 - 1,297 square feet',

	  address: '1009 14th StPlano, TX 75074'

    },

    {

	  url: '/realestateandhomes-detail/5765-Bozeman-Dr_Plano_TX_75024_M70427-45476',  	 

	  price: '$1,262 - $2,345',

	  baths: '1 - 2',

	  sqft: '352 - 1,588 square feet',

	  address: '5765 Bozeman DrPlano, TX 75024'

    },

    {

	  url: '/realestateandhomes-detail/1410-K-Ave-Ste-1105A_Plano_TX_75074_M97140-46163',  

	  price: '$1,250 - $1,995',

	  baths: '1 - 2',

	  sqft: '497 - 1,324 square feet',

	  address: '1410 K Ave Ste 1105APlano, TX 75074'

    }

]

Evitar a deteção de bots

Embora o scraping do Realtor possa parecer fácil no início, o processo pode tornar-se mais complexo e desafiante à medida que aumenta a escala do seu projeto. O sítio Web do sector imobiliário implementa várias técnicas para detetar e impedir o tráfego automatizado, pelo que o seu scraper em grande escala começa a ser bloqueado.

O Realtor utiliza o modelo "Press & Hold" de CAPTCHA, oferecido pela PerimeterX, que é conhecido por ser quase impossível de resolver a partir do seu código. Além disso, o sítio Web também recolhe vários dados do navegador para gerar e associá-lo a uma impressão digital única.

Entre os dados do navegador recolhidos, encontramos:

  • propriedades do objeto Navigator (deviceMemory, hardwareConcurrency, idiomas, plataforma, userAgent, webdriver, etc.)
  • controlos de prazos e de desempenho
  • WebGL
  • Deteção de IP WebRTC
  • e muitos mais

Uma maneira de superar esses desafios e continuar a raspagem em grande escala é usar uma API de raspagem. Esses tipos de serviços fornecem uma maneira simples e confiável de acessar dados de sites como o Realtor.com, sem a necessidade de criar e manter seu próprio raspador.

O WebScrapingAPI é um exemplo de um produto deste género. O seu mecanismo de rotação de proxy evita completamente os CAPTCHAs, e a sua base de conhecimentos alargada permite aleatorizar os dados do browser para que se pareça com um utilizador real.

A configuração é rápida e fácil. Basta registar uma conta para receber a sua chave de API. Esta pode ser acedida a partir do seu painel de controlo e é utilizada para autenticar os pedidos que envia.

Página inicial do painel da WebScrapingAPI, apresentando um guia de início rápido em três passos para a chave da API, o ambiente de teste da API e a documentação

Como já configurou o seu ambiente Node.js, podemos utilizar o SDK correspondente. Execute o seguinte comando para o adicionar às dependências do seu projeto:

npm install webscrapingapi

Agora só falta ajustar os selectores CSS anteriores à API. A poderosa caraterística das regras de extração torna possível analisar dados sem modificações significativas.

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({

            locations: {

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

                output: '@href',

                all: '1'

        	},

        	prices: {

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

                output: 'text',

                all: '1'

        	},

        	baths: {

                selector: 'li[data-testid="property-meta-baths"] > span[data-testid="meta-value"]',

                output: 'text',

                all: '1'

        	},

        	sqfts: {

                selector: 'li[data-testid="property-meta-sqft"] > span[data-testid="screen-reader-value"]',

                output: 'text',

                all: '1'

        	},

        	addresses: {

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

                output: 'text',

                all: '1'

        	}

        })

    }

    const URL = "https://www.realtor.com/apartments/Plano_TX/beds-studio"

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

    if (response.success) {

        const unique_listings_location = [...new Set(response.response.data.locations)]

    	  // Group the lists

    	  const listings = []

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

            listings.push({

                url: unique_listings_location[i],

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

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

                sqft: response.response.data.sqfts[i],

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

            })

    	  }

    	  console.log(listings)

    } else {

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

    }

}

exampleUsage();

Conclusão

Neste tutorial, fornecemos um guia passo a passo sobre como raspar o realtor.com usando o Node.js e o Puppeteer. Também discutimos maneiras de melhorar a confiabilidade e a eficiência do scraper e por que usar um serviço de scraper profissional pode ser uma opção melhor para alguns casos de uso.

O Realtor.com é uma fonte popular e valiosa de dados imobiliários e, com as competências e os conhecimentos que adquiriu neste tutorial, deve agora ser capaz de utilizar a raspagem da Web para extrair estes dados e utilizá-los nos seus próprios projectos.

Quer seja um profissional do sector imobiliário à procura de uma vantagem competitiva, um investidor à procura de novas oportunidades ou um comprador de casa à procura da propriedade perfeita, o web scraping pode fornecer-lhe informações e dados valiosos do realtor.com. Esperamos que este tutorial tenha sido útil e que esteja agora pronto para elevar o seu jogo imobiliário com a ajuda do web scraping do realtor.com.

Sobre o autor
Raluca Penciuc, Desenvolvedora Full-Stack na 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.