Voltar ao blogue
Guias
Raluca PenciucLast updated on Mar 31, 20267 min read

Web scraping para o setor imobiliário: como extrair dados do Realtor.com como um profissional

Web scraping para o setor imobiliário: como extrair dados do Realtor.com como um profissional

A recolha de dados precisos e atualizados é crucial para empresas e particulares em muitos setores, e o setor imobiliário não é exceção. O Realtor.com é um site popular para encontrar apartamentos e casas para venda ou arrendamento, uma vez que contém uma grande quantidade de informações que podem ser valiosas para profissionais do setor imobiliário, investidores e compradores de imóveis.

Neste tutorial, vou mostrar-lhe como extrair dados do Realtor.com para que tenha à sua disposição os dados necessários para dar início ao seu projeto. Vou explicar o processo de configuração de um projeto, navegação até ao Realtor.com e extração dos dados desejados.

Também discutirei formas de melhorar a fiabilidade e a eficiência do scraper e por que razão a utilização de um serviço profissional de scraping pode ser uma opção melhor para alguns casos de utilização.

No final deste tutorial, deverá ter uma boa compreensão de como extrair dados do realtor.com, independentemente da sua profissão: seja um profissional do setor imobiliário à procura de uma vantagem competitiva, um investidor em busca de novas oportunidades ou um comprador de imóveis à procura da propriedade perfeita.

Configuração do ambiente

Antes de começar a extrair dados, precisa de instalar o Node.js no seu computador. Pode descarregar a versão mais recente a partir do site 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 aceda-o no seu terminal ou linha de comandos. Execute o seguinte comando para inicializar um novo projeto Node.js:

npm init -y

Isto irá criar um ficheiro package.json no diretório do seu 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 outras funcionalidades. É útil para projetos de maior dimensão e pode facilitar a deteção precoce de erros. O TypeScript utiliza um ficheiro de configuração chamado tsconfig.json para armazenar opções do compilador e outras definições. Para criar este ficheiro no seu projeto, execute o seguinte comando:

npx tsc -init

Certifique-se de que o valor de “outDir” está definido como “dist”. Desta forma, separaremos os ficheiros TypeScript dos ficheiros compilados.

Agora, crie um diretório “src” no seu projeto e um novo ficheiro “index.ts”. É aqui que iremos manter o código de scraping. Para executar código TypeScript, é necessário compilá-lo primeiro; por isso, para garantir que não nos esquecemos deste passo adicional, podemos usar um comando definido por nós.

Vá até ao ficheiro “package.json” e edite a secção “scripts” da seguinte forma:

"scripts": {

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

}

Desta forma, quando 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 interface gráfica, que pode ser usado para tarefas de web scraping e automação. É altamente recomendado quando se pretende garantir a integridade dos seus dados, uma vez que muitos sites hoje em dia contêm conteúdo gerado dinamicamente.

Seleção de dados

Agora que já tem o seu ambiente configurado, podemos começar a ver como extrair os dados. Para este artigo, optei por extrair a lista de estúdios disponíveis para arrendamento 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 casas de banho;
  • as áreas (medidas em pés quadrados);
  • os endereços físicos

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

Extração de dados

Para extrair todos estes dados, precisamos primeiro de os localizar. Clique com o botão direito do rato nas secções destacadas e, em seguida, selecione «Inspecionar» para abrir as Ferramentas de Programador e visualizar o documento HTML. Ao passar o cursor do rato por cima, pode ver facilmente qual a parte que corresponde a cada secção:

Para este tutorial, vou utilizar seletores CSS, pois são a opção mais simples. Se não estiver familiarizado com este método, sinta-se à vontade para consultar primeiro este guia autoexplicativo.

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é ao nosso URL de destino e, em seguida, fechamos o navegador. Por uma questão de simplicidade e depuração visual, abro o navegador maximizado no modo não headless.

Como cada anúncio tem a mesma estrutura e os mesmos dados, no nosso algoritmo iremos extrair todas as informações para toda a lista de imóveis. No final do script, iremos percorrer todos os resultados e centralizá-los numa única lista.

Talvez tenha reparado que o URL do anúncio não estava visível na primeira captura de ecrã, mas foi mencionado e destacado na segunda. Isso acontece porque é redirecionado 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(listings_location)

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

No entanto, a lista resultante conterá cada URL duas vezes. Isso acontece porque cada anúncio tem o mesmo elemento âncora para duas secções: as imagens do imóvel e os detalhes do arrendamento. 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, iremos extrair os elementos “div” que possuem o atributo “data-testid” com o valor “card-price”. Este também precisa de ser convertido numa matriz e, em seguida, 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 casas de banho e a área do imóvel, 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 de antes:

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

E, finalmente, para os endereços dos anúncios, selecionamos os elementos “div” que possuem o atributo “data-testid” definido com 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 deve ter 5 listas, uma para cada dado que extraímos. Como mencionei anteriormente, devemos centralizá-las numa única lista. Desta forma, a informação que recolhemos será muito mais fácil de processar posteriormente.

// 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 deverá ficar 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'

    }

]

Evite a deteção de bots

Embora o scraping do Realtor possa parecer fácil à primeira vista, o processo pode tornar-se mais complexo e desafiante à medida que o seu projeto cresce. O site imobiliário implementa várias técnicas para detetar e impedir o tráfego automatizado, pelo que o seu scraper, à medida que cresce, 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 site 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, languages, platform, userAgent, webdriver, etc.)
  • verificações de tempo e desempenho
  • WebGL
  • WebRTC deteção de IP
  • e muito mais

Uma forma de superar estes desafios e continuar a fazer scraping em grande escala é utilizar uma API de scraping. Este tipo de serviços oferece uma forma simples e fiável de aceder a dados de sites como o Realtor.com, sem a necessidade de criar e manter o seu próprio scraper.

A WebScrapingAPI é um exemplo desse tipo de produto. O seu mecanismo de rotação de proxies evita completamente os CAPTCHAs, e a sua base de conhecimento alargada permite randomizar os dados do navegador para que pareça um utilizador real.

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

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, basta ajustar os seletores CSS anteriores à API. A poderosa funcionalidade das regras de extração permite 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 fazer scraping do realtor.com usando Node.js e Puppeteer. Também discutimos formas de melhorar a fiabilidade e a eficiência do scraper, e por que razão a utilização de um serviço profissional de scraping pode ser uma opção melhor para alguns casos de utilização.

O realtor.com é uma fonte popular e valiosa de dados imobiliários e, com as competências e os conhecimentos que adquiriu neste tutorial, deverá agora ser capaz de utilizar o web scraping para extrair estes dados e aplicá-los nos seus próprios projetos.

Quer seja um profissional do setor imobiliário à procura de uma vantagem competitiva, um investidor em busca de novas oportunidades ou um comprador à procura do imóvel perfeito, 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 nível no setor imobiliário com a ajuda do web scraping do realtor.com.

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.