Retour au blog
Guides
Raluca Penciuc7 avril 202310 min de lecture

Le web scraping dans l'immobilier : comment extraire des données de Realtor.com comme un pro

Le web scraping dans l'immobilier : comment extraire des données de Realtor.com comme un pro

Configuration de l'environnement

Avant de pouvoir commencer le scraping, vous devez installer Node.js sur votre ordinateur. Vous pouvez télécharger la dernière version depuis le site officiel et suivre les instructions correspondant à votre système d'exploitation.

Créez ensuite un nouveau répertoire pour votre projet et accédez-y depuis votre terminal ou votre invite de commande. Exécutez la commande suivante pour initialiser un nouveau projet Node.js :

npm init -y

Cela créera un fichier package.json dans le répertoire de votre projet, qui stockera des informations sur votre projet et ses dépendances.

Pour installer TypeScript, exécutez la commande suivante :

npm install typescript -save-dev

TypeScript est un sur-ensemble de JavaScript qui ajoute un typage statique facultatif et d'autres fonctionnalités. Il est utile pour les projets de grande envergure et permet de détecter plus facilement les erreurs dès le début. TypeScript utilise un fichier de configuration appelé tsconfig.json pour stocker les options du compilateur et d'autres paramètres. Pour créer ce fichier dans votre projet, exécutez la commande suivante :

npx tsc -init

Assurez-vous que la valeur de « outDir » est définie sur « dist ». De cette façon, nous séparerons les fichiers TypeScript des fichiers compilés.

Créez maintenant un répertoire « src » dans votre projet, ainsi qu'un nouveau fichier « index.ts ». C'est là que nous conserverons le code de scraping. Pour exécuter du code TypeScript, vous devez d'abord le compiler. Afin de ne pas oublier cette étape supplémentaire, nous pouvons utiliser une commande personnalisée.

Rendez-vous dans le fichier « package.json » et modifiez la section « scripts » comme suit :

"scripts": {

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

}

Ainsi, lorsque vous exécuterez le script, il vous suffira de taper « npm run test » dans votre terminal.

Enfin, exécutez la commande suivante pour ajouter Puppeteer aux dépendances de votre projet :

npm install puppeteer

Puppeteer est une bibliothèque Node.js qui fournit une API de haut niveau pour contrôler un navigateur Chrome sans interface graphique, pouvant être utilisé pour le web scraping et les tâches d'automatisation. Elle est fortement recommandée lorsque vous souhaitez garantir l'exhaustivité de vos données, car de nombreux sites web contiennent aujourd'hui du contenu généré dynamiquement.

Sélection des données

Maintenant que votre environnement est configuré, nous pouvons commencer à nous intéresser à l'extraction des données. Pour cet article, j'ai choisi de scraper la liste des studios disponibles à la location à Plano, au Texas : https://www.realtor.com/apartments/Plano_TX/beds-studio.

Nous allons extraire les données suivantes de chaque annonce sur la page :

  • l'URL ;
  • les prix ;
  • le nombre de salles de bains ;
  • les surfaces (mesurées en pieds carrés) ;
  • les adresses physiques

Vous pouvez voir toutes ces informations mises en évidence dans la capture d'écran ci-dessous :

Apartment rental search results with listing cards highlighted in red, showing photos, prices, and address details

Extraction des données

Pour extraire toutes ces données, nous devons d'abord les localiser. Cliquez avec le bouton droit de la souris sur les sections mises en évidence, puis choisissez « Inspecter » pour ouvrir les outils de développement et afficher le document HTML. En passant le curseur de la souris dessus, vous pouvez facilement voir quelle partie correspond à chaque section :

Apartment listing card next to browser inspector highlighting HTML selectors for price, baths, square feet, and address fields

Pour ce tutoriel, j'utiliserai des sélecteurs CSS, car ils constituent l'option la plus simple. Si vous découvrez cette méthode, n'hésitez pas à consulter d'abord ce guide explicatif.

Pour commencer à écrire notre script, vérifions que l'installation de Puppeteer s'est bien déroulée :

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

Ici, nous ouvrons une fenêtre de navigateur, créons une nouvelle page, accédons à notre URL cible, puis fermons le navigateur. Par souci de simplicité et pour faciliter le débogage visuel, j'ouvre le navigateur en mode non headless et en plein écran.

Comme chaque annonce a la même structure et les mêmes données, notre algorithme va extraire toutes les informations pour l'ensemble de la liste des propriétés. À la fin du script, nous parcourrons tous les résultats et les regrouperons dans une seule liste.

Vous avez peut-être remarqué que l'URL de l'annonce n'était pas visible sur la première capture d'écran, mais qu'elle était mentionnée et mise en évidence sur la deuxième. C'est parce que vous êtes redirigé vers l'URL de la propriété lorsque vous cliquez dessus.

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

Nous localisons l'URL en sélectionnant les éléments d'ancrage qui possèdent l'attribut « data-testid » avec la valeur « card-link ». Nous convertissons ensuite le résultat en un tableau JavaScript et mappons chaque élément à la valeur de l'attribut « href ».

Cependant, la liste obtenue contiendra chaque URL deux fois. En effet, chaque annonce possède le même élément d'ancrage pour deux sections : les photos du bien et les détails de la location. Nous pouvons facilement corriger cela en utilisant la structure de données Set :

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

console.log(unique_listings_location)

Pour le prix du bien, nous allons extraire les éléments « div » qui possèdent l'attribut « data-testid » avec la valeur « card-price ». Il doit également être converti en tableau, puis mappé à son contenu textuel.

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

Pour obtenir le nombre de salles de bains et la superficie du bien, nous utiliserons l’opérateur pour les éléments enfants directs. Cela signifie que l’élément parent est identifié de manière unique, tandis que l’élément enfant possède un identifiant ou un nom de classe plus générique. À part cela, la logique est la même que précédemment :

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

Enfin, pour les adresses des annonces, nous sélectionnons les éléments « div » qui possèdent l’attribut « data-testid » défini sur la valeur « 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)

Vous devriez maintenant avoir 5 listes, une pour chaque donnée que nous avons extraite. Comme je l’ai mentionné précédemment, nous devons les regrouper en une seule. De cette façon, les informations que nous avons recueillies seront beaucoup plus faciles à traiter par la suite.

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

Le résultat final devrait ressembler à ceci :

[

    {

        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'

    }

]

Évitez la détection des bots

Si le scraping de Realtor peut sembler facile au premier abord, le processus peut devenir plus complexe et difficile à mesure que vous développez votre projet. Le site web immobilier met en œuvre diverses techniques pour détecter et empêcher le trafic automatisé, de sorte que votre scraper à grande échelle commence à être bloqué.

Realtor utilise le modèle CAPTCHA « Press & Hold » proposé par PerimeterX, réputé pour être pratiquement impossible à résoudre à partir de votre code. En outre, le site web collecte également de nombreuses données de navigateur afin de générer et de vous associer à une empreinte digitale unique.

Parmi les données de navigateur collectées, on trouve :

  • les propriétés de l'objet Navigator (deviceMemory, hardwareConcurrency, languages, platform, userAgent, webdriver, etc.)
  • des vérifications de timing et de performances
  • WebGL
  • WebRTC analyse d'adresse IP
  • et bien d'autres encore

Une façon de surmonter ces défis et de continuer à effectuer du scraping à grande échelle consiste à utiliser une API de scraping. Ce type de services offre un moyen simple et fiable d'accéder aux données de sites web tels que Realtor.com, sans avoir à développer et à maintenir votre propre scraper.

WebScrapingAPI est un exemple de ce type de produit. Son mécanisme de rotation de proxys permet d'éviter complètement les CAPTCHA, et sa base de connaissances étendue permet de randomiser les données du navigateur afin qu'il ressemble à un véritable utilisateur.

La configuration est rapide et facile. Il vous suffit de créer un compte pour recevoir votre clé API. Celle-ci est accessible depuis votre tableau de bord et sert à authentifier les requêtes que vous envoyez.

WebScrapingAPI dashboard start page showing a three-step quickstart guide for API key, API playground, and documentation

Comme vous avez déjà configuré votre environnement Node.js, nous pouvons utiliser le SDK correspondant. Exécutez la commande suivante pour l'ajouter aux dépendances de votre projet :

npm install webscrapingapi

Il ne reste plus qu'à adapter les sélecteurs CSS précédents à l'API. La fonctionnalité puissante des règles d'extraction permet d'analyser les données sans modifications significatives.

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

Conclusion

Dans ce tutoriel, nous vous avons fourni un guide étape par étape sur la manière de scraper realtor.com à l'aide de Node.js et de Puppeteer. Nous avons également abordé les moyens d'améliorer la fiabilité et l'efficacité du scraper, ainsi que les raisons pour lesquelles l'utilisation d'un service de scraping professionnel peut constituer une meilleure option dans certains cas d'utilisation.

Realtor.com est une source populaire et précieuse de données immobilières, et grâce aux compétences et aux connaissances acquises dans ce tutoriel, vous devriez désormais être en mesure d'utiliser le web scraping pour extraire ces données et les exploiter dans vos propres projets.

Que vous soyez un professionnel de l'immobilier à la recherche d'un avantage concurrentiel, un investisseur en quête de nouvelles opportunités ou un acheteur à la recherche de la propriété idéale, le web scraping peut vous fournir des informations et des données précieuses provenant de realtor.com. Nous espérons que ce tutoriel vous aura été utile et que vous êtes désormais prêt à passer à la vitesse supérieure dans le domaine de l'immobilier grâce au web scraping de realtor.com.

À propos de l'auteur
Raluca Penciuc, Développeur full-stack @ WebScrapingAPI
Raluca PenciucDéveloppeur full-stack

Raluca Penciuc est développeuse Full Stack chez WebScrapingAPI ; elle conçoit des robots de collecte de données, améliore les techniques de contournement et recherche des moyens fiables de réduire le risque de détection sur les sites cibles.

Commencez à créer

Prêt à faire évoluer votre système de collecte de données ?

Rejoignez plus de 2 000 entreprises qui utilisent WebScrapingAPI pour extraire des données Web à l'échelle de l'entreprise, sans aucun coût d'infrastructure.