Apartments.com est l'une des plus grandes marketplaces de location du web, et ses pages d'annonces portent exactement les données structurées qui alimentent la comparaison des loyers, l'étude de marché et la génération de leads dans l'immobilier : un nom et une adresse de bien, le loyer mensuel, le nombre de chambres, de salles de bain, la superficie, et la liste des équipements. Le problème est que ces pages sont rendues côté client et que le site se défend durement contre le trafic automatisé, de sorte qu'une simple requête HTTP vous renvoie une coquille presque vide au lieu de l'annonce que vous êtes venu chercher.

Ce guide vous montre comment scraper Apartments.com avec Python de façon fiable. Vous construisez un scraper léger et exécutable qui récupère une annonce rendue via la Crawling API, parse les champs voulus avec BeautifulSoup, et imprime une sortie structurée propre. Nous gardons l'ensemble du tutoriel cantonné aux données publiques d'annonce, et la section sur la légalité vers la fin n'est pas du remplissage, alors lisez-la avant de pointer ceci sur un volume réel.

Ce que vous allez construire

Un script Python qui prend l'URL publique d'une annonce Apartments.com, récupère le HTML rendu via la Crawling API, et en extrait un enregistrement structuré pour le bien. Nous utiliserons une seule annonce de location comme exemple fil rouge et récupérerons ces champs :

  • Nom et adresse le nom du bien et son adresse postale.
  • Loyer le loyer mensuel ou la fourchette de loyer affichée sur l'annonce.
  • Chambres le nombre de chambres.
  • Salles de bain le nombre de salles de bain.
  • Surface la superficie du logement.
  • Équipements la liste des prestations, comme la climatisation, le parking ou la buanderie dans le logement.

Pourquoi une simple récupération échoue sur Apartments.com

Si vous demandez l'URL d'une annonce Apartments.com avec un client HTTP nu, vous obtenez une réponse avec un statut 200 et presque aucune donnée d'annonce dans le corps. Deux choses jouent contre vous. D'abord, Apartments.com rend une grande partie du contenu de ses annonces dans le navigateur avec JavaScript, donc le HTML initial est une coquille mince qui ne se remplit qu'après l'exécution des scripts de la page. Ensuite, le site repère vite le trafic automatisé : les IP de datacenter et les schémas de requêtes qui ne ressemblent pas à un vrai navigateur sont défiés ou se voient servir un captcha avant même d'atteindre le contenu rendu.

Un scraper Apartments.com qui fonctionne a donc besoin de deux choses en une requête : un navigateur qui rend réellement la page, et une IP que la plateforme lit comme celle d'un vrai visiteur. Vous pouvez assembler cela vous-même avec un navigateur sans interface plus un pool de proxies résidentiels rotatifs, mais les coudre ensemble et les maintenir en bonne santé représente l'essentiel du travail. La Crawling API réunit les deux en un seul appel : vous lui envoyez l'URL avec un token JavaScript, elle rend la page derrière une IP de confiance, et elle vous renvoie un HTML fini à parser.

Pourquoi le token JS

Crawlbase propose deux types de token. Le token normal récupère du HTML statique ; le token JavaScript (JS) rend d'abord la page dans un vrai navigateur. Apartments.com remplit les champs de ses annonces côté client, donc vous avez besoin du token JS ici. Utiliser le token normal renvoie la même coquille vide qu'une simple récupération, et il n'y a rien d'utile à en extraire.

Prérequis

Il vous faut quelques éléments en place avant d'écrire la moindre ligne de code. Aucun ne prend longtemps.

Python de base. Vous devez être à l'aise pour écrire et exécuter un script Python et installer des paquets avec pip. Si vous débutez avec le langage, la documentation officielle de Python et n'importe quel cours pour débutants vous amèneront au niveau supposé par ce tutoriel.

Python 3.8 ou plus récent. Confirmez votre version avec python --version. Si vous ne l'avez pas, installez-le depuis python.org ou via une distribution comme Anaconda.

Un compte Crawlbase et un token JS. Inscrivez-vous, ouvrez votre tableau de bord, et copiez votre token JavaScript (JS) depuis la page docs de votre compte. Traitez le token comme un mot de passe : il authentifie vos requêtes, alors gardez-le hors du contrôle de version.

Configurer le projet

Créez un environnement virtuel pour que les dépendances du projet restent isolées, puis installez les deux bibliothèques dont le scraper a besoin.

bash
python --version

python -m venv apartments_env
source apartments_env/bin/activate

pip install crawlbase beautifulsoup4

Sous Windows, activez l'environnement avec apartments_env\Scripts\activate au lieu de la ligne source. Deux dépendances font le travail : crawlbase est le client officiel de la Crawling API, et beautifulsoup4 parse le HTML renvoyé pour que vous puissiez extraire chaque champ par sélecteur CSS. Si vous n'avez jamais utilisé le parseur, le guide BeautifulSoup est un bon compagnon de ce tutoriel.

Étape 1 : Récupérer l'annonce rendue

Commencez par obtenir la page finie. Importez la classe CrawlingAPI, initialisez-la avec votre token JS, et demandez l'URL de l'annonce. Vérifier le code de statut avant de parser garde les échecs bruyants plutôt que silencieux.

python
from crawlbase import CrawlingAPI

api = CrawlingAPI({"token": "YOUR_CRAWLBASE_JS_TOKEN"})

def crawl(page_url):
    options = {"ajax_wait": "true", "page_wait": 5000}
    response = api.get(page_url, options)
    if response["status_code"] == 200:
        return response["body"].decode("utf-8")
    print(f"Request failed: {response['status_code']}")
    return None

if __name__ == "__main__":
    page_url = "https://www.apartments.com/2630-n-hamlin-ave-chicago-il/kvl7tm9/"
    html = crawl(page_url)
    print(html[:500] if html else "No HTML returned")

Les deux options d'attente comptent pour une cible rendue côté client comme celle-ci. ajax_wait indique à l'API d'attendre la fin du chargement du contenu asynchrone, et page_wait maintient un nombre fixe de millisecondes après le chargement pour que les éléments à rendu tardif apparaissent avant la capture de la page. Cinq secondes est un point de départ raisonnable ; augmentez-le si les champs de l'annonce reviennent vides. Lancez le script avec python scraper.py et vous devriez voir un vrai balisage d'annonce, pas la coquille vide que renvoie une simple récupération. Cela confirme que le rendu fonctionne avant d'écrire le moindre sélecteur.

Crawlbase Crawling API

Apartments.com a besoin d'une page rendue derrière une IP de confiance, en un seul appel. La Crawling API prend un token JS, exécute la page dans un vrai navigateur, fait tourner des IP résidentielles côté serveur, et vous remet un HTML fini : vous évitez ainsi de gérer vous-même une flotte sans interface et un pool de proxies. Pointez-la d'abord sur une annonce publique avec l'offre gratuite.

Étape 2 : Parser les champs de l'annonce avec BeautifulSoup

Avec le HTML rendu en main, chargez-le dans BeautifulSoup et extrayez chaque champ par son sélecteur. Apartments.com dispose les détails clés de l'annonce dans une structure prévisible, vous pouvez donc associer le nom, le loyer, les chambres, les salles de bain, la surface et les équipements à des sélecteurs individuels. Enveloppez toute l'extraction dans des fonctions utilitaires qui renvoient None ou une liste vide quand un élément est absent, pour qu'un seul champ manquant ne fasse pas planter l'exécution.

python
from bs4 import BeautifulSoup

def text_of(soup, selector):
    el = soup.select_one(selector)
    return el.get_text(strip=True) if el else None

def detail_at(soup, index):
    rows = soup.select(".rentInfoDetail")
    return rows[index].get_text(strip=True) if index < len(rows) else None

def scrape_listing(html):
    soup = BeautifulSoup(html, "html.parser")

    address = soup.select_one(".propertyAddress")
    location = ", ".join(
        s.get_text(strip=True) for s in address.select("span")
    ) if address else None

    amenities = [
        s.get_text(strip=True)
        for s in soup.select("#amenitiesSection .specInfo span")
    ]

    return {
        "name": text_of(soup, "#propertyName"),
        "location": location,
        "rent": detail_at(soup, 0),
        "beds": detail_at(soup, 1),
        "baths": detail_at(soup, 2),
        "size": detail_at(soup, 3),
        "amenities": amenities,
    }

Les fonctions text_of et detail_at font la même chose utile sous deux formes : elles interrogent un élément et renvoient None quand il est absent au lieu de lever une erreur sur un appel sur du vide. L'adresse est construite en joignant le texte de chaque span à l'intérieur de .propertyAddress, puisque Apartments.com répartit la rue, la ville et l'État dans des éléments distincts. Les équipements reviennent sous forme de liste car une annonce peut en avoir de zéro à plusieurs dizaines. Cette structure garde l'extraction résiliente quand un champ est absent sur une annonce donnée, ce qui est courant puisque tous les biens ne listent pas une surface ou un ensemble complet d'équipements.

Les sélecteurs dérivent

Les noms de classes d'Apartments.com (les lignes rentInfoDetail, le conteneur #amenitiesSection, les spans d'adresse) changent sans préavis. Traitez les sélecteurs ci-dessus comme un modèle de départ, pas comme un contrat. Quand un champ revient à None ou à une liste vide, réinspectez l'annonce en direct dans les outils de développement de votre navigateur et mettez le sélecteur à jour. Une maintenance périodique des sélecteurs est normale pour tout scraper en production, pas le signe que quelque chose est cassé.

Étape 3 : Tout assembler

Reliez maintenant la récupération et le parsing en un seul script exécutable. Récupérez le HTML rendu, passez-le au parseur, et imprimez l'enregistrement structuré.

python
import json
from crawlbase import CrawlingAPI
from bs4 import BeautifulSoup

api = CrawlingAPI({"token": "YOUR_CRAWLBASE_JS_TOKEN"})

def crawl(page_url):
    options = {"ajax_wait": "true", "page_wait": 5000}
    response = api.get(page_url, options)
    if response["status_code"] == 200:
        return response["body"].decode("utf-8")
    print(f"Request failed: {response['status_code']}")
    return None

def text_of(soup, selector):
    el = soup.select_one(selector)
    return el.get_text(strip=True) if el else None

def detail_at(soup, index):
    rows = soup.select(".rentInfoDetail")
    return rows[index].get_text(strip=True) if index < len(rows) else None

def scrape_listing(html):
    soup = BeautifulSoup(html, "html.parser")
    address = soup.select_one(".propertyAddress")
    location = ", ".join(
        s.get_text(strip=True) for s in address.select("span")
    ) if address else None
    amenities = [
        s.get_text(strip=True)
        for s in soup.select("#amenitiesSection .specInfo span")
    ]
    return {
        "name": text_of(soup, "#propertyName"),
        "location": location,
        "rent": detail_at(soup, 0),
        "beds": detail_at(soup, 1),
        "baths": detail_at(soup, 2),
        "size": detail_at(soup, 3),
        "amenities": amenities,
    }

def main():
    page_url = "https://www.apartments.com/2630-n-hamlin-ave-chicago-il/kvl7tm9/"
    html = crawl(page_url)
    if not html:
        return
    data = scrape_listing(html)
    print(json.dumps(data, indent=2))

if __name__ == "__main__":
    main()

À quoi ressemble la sortie

Lancez le script complet avec python scraper.py et vous obtenez un enregistrement structuré propre pour l'annonce, prêt à être écrit en JSON, CSV ou dans une base de données.

json
{
  "name": "2630 N Hamlin Ave",
  "location": "2630 N Hamlin Ave, Chicago, IL, 60647",
  "rent": "$2,350",
  "beds": "2 bd",
  "baths": "1 ba",
  "size": "1,000 sq ft",
  "amenities": ["Air Conditioning", "Dishwasher", "Basement", "Laundry Facilities"]
}

Passer à l'échelle sur les annonces et la pagination

Une annonce est une démo ; un vrai travail s'exécute sur toute une recherche. Apartments.com pagine ses résultats de recherche, donc le motif comporte deux couches : crawler chaque page de résultats pour collecter les URL d'annonces, puis récupérer chaque annonce via la même fonction que vous avez déjà écrite. Comme toutes les annonces partagent la même structure, le parseur fonctionne pour toutes sans changement.

python
import time

def collect_listing_urls(search_html):
    soup = BeautifulSoup(search_html, "html.parser")
    cards = soup.select("article.placard a.property-link")
    return [a["href"] for a in cards if a.get("href")]

def scrape_search(base_url, pages):
    listings = []
    for page in range(1, pages + 1):
        search_html = crawl(f"{base_url}{page}/")
        if not search_html:
            continue
        for url in collect_listing_urls(search_html):
            html = crawl(url)
            if html:
                listings.append(scrape_listing(html))
            time.sleep(2)
    return listings

results = scrape_search("https://www.apartments.com/chicago-il/", pages=3)
with open("listings.json", "w") as f:
    json.dump(results, f, indent=2)

Apartments.com ajoute le numéro de page au chemin de recherche, donc itérer sur page parcourt l'ensemble des résultats. Le time.sleep(2) entre les récupérations d'annonces est délibéré : il cadence l'exécution pour que vous ne matraquiez pas le site, ce qui est l'habitude la plus efficace pour rester débloqué. Ajustez le nombre de pages et le slug de ville à votre cible.

Rester débloqué

Même avec le rendu pris en charge, Apartments.com guette le trafic en forme de scraper. Quelques habitudes gardent une exécution saine, et elles s'appliquent à toute cible commerciale difficile.

  • Cadencez vos requêtes. Marteler les annonces dans une boucle serrée est le moyen le plus rapide de se faire limiter ou servir un captcha. Étalez les requêtes, comme le fait le sleep ci-dessus, et variez vos cibles plutôt que de crawler un seul chemin à plein régime.
  • Appuyez-vous sur la rotation. Un pool d'IP résidentielles répartit les requêtes sur de nombreuses adresses de vrais utilisateurs pour qu'aucune seule ne déclenche une limite de débit. La Crawling API s'en charge pour vous ; si vous montez votre propre pile, c'est la partie à bien faire.
  • Lisez les codes de statut. Une exécution qui se met à renvoyer des défis ou des erreurs vous dit que le débit ou le palier d'IP actuel ne suffit plus. Traitez cela comme un signal pour lever le pied, pas comme un bruit à ignorer.

Pour le manuel plus large, voyez comment scraper des sites web sans se faire bloquer et l'analyse plus poussée sur comment contourner les captchas en web scraping. Si vous préférez router votre propre trafic via un pool rotatif plutôt que d'utiliser l'API gérée, le Smart AI Proxy (aussi appelé l'AI Proxy) vous donne la même rotation d'IP résidentielles sous la forme d'un point de terminaison proxy prêt à l'emploi.

Est-il légal de scraper Apartments.com ?

Que le scraping d'Apartments.com soit autorisé dépend des conditions d'utilisation d'Apartments.com, de votre juridiction et de ce que vous faites des données. Les conditions restreignent l'accès automatisé, donc le scraping peut aller à l'encontre de ces conditions quel que soit le soin apporté à votre outillage. Aucun code ici ne change cela ; il fait juste fonctionner la partie technique. Lisez les conditions d'utilisation d'Apartments.com et son robots.txt, et traitez les deux comme la limite de ce que vous collectez.

Quelques lignes à respecter. Ne collectez que des données publiques d'annonce : le nom et l'adresse du bien, le loyer, les chambres, les salles de bain, la superficie et la liste d'équipements affichée publiquement que tout le monde peut voir sans compte. Respectez les attentes de débit énoncées par Apartments.com et gardez votre volume de requêtes assez bas pour ne pas solliciter ses serveurs. Évitez tout ce qui est lié à des personnes identifiables, y compris les coordonnées de propriétaires, d'agents ou de gestionnaires de biens listées sur une page. Si vous prévoyez de réutiliser les données commercialement ou en masse, obtenez une autorisation ou un accord officiel plutôt que de supposer que le silence vaut consentement.

Ce guide est délibérément cantonné aux pages d'annonces publiques car c'est la ligne qui garde le travail défendable. Il ne couvre rien derrière une connexion, ni les données de recherches enregistrées ou de compte, ni les coordonnées personnelles de personnes, ni les pages protégées par connexion, ni aucune tentative de contourner l'authentification. Données publiques d'annonce uniquement. Si votre projet a besoin de plus que cela, un accord de licence ou un fournisseur de données immobilières est la voie correcte, pas un scraper plus astucieux.

Récapitulatif

Points clés

  • Apartments.com est rendu côté client. Une simple récupération renvoie une coquille vide, vous devez donc rendre la page avant de la parser.
  • Il vous faut le rendu et une IP de confiance ensemble. La Crawling API avec un token JS fait les deux en un seul appel ; ajax_wait et page_wait contrôlent combien de temps elle attend le contenu.
  • BeautifulSoup fait l'extraction. Associez le nom, l'adresse, le loyer, les chambres, les salles de bain, la surface et les équipements aux sélecteurs actuels, et attendez-vous à ce que ces sélecteurs dérivent.
  • Passez à l'échelle en paginant la recherche, puis en bouclant sur les annonces. Collectez les URL de chaque page de résultats, récupérez chaque annonce avec le même parseur, et cadencez l'exécution avec un court sleep.
  • Restez sur les données publiques. Respectez les CGU et le robots.txt d'Apartments.com, ne collectez que les champs publics d'annonce, et ne touchez jamais aux comptes, aux connexions ni aux coordonnées personnelles de personnes.

Foire aux questions

Pourquoi une simple récupération ne renvoie-t-elle aucune donnée depuis Apartments.com ?

Parce qu'Apartments.com rend le contenu de ses annonces côté client avec JavaScript. Le HTML initial est une coquille qui ne se remplit qu'après l'exécution des scripts de la page dans un navigateur, donc une requête HTTP brute renvoie un statut 200 avec les champs loyer, chambres, salles de bain et équipements vides. Pour obtenir de vraies données, vous devez d'abord rendre la page, ce dont se charge pour vous le token JS de la Crawling API.

Ai-je besoin du token normal ou du token JS pour Apartments.com ?

Le token JS. Le token normal récupère du HTML statique, qui sur Apartments.com est la même coquille vide que renvoie une simple récupération. Le token JS rend la page dans un vrai navigateur avant de renvoyer le HTML, donc les champs de l'annonce sont présents quand BeautifulSoup les parse.

Quelles données puis-je scraper depuis une annonce Apartments.com ?

Les champs publics d'annonce : le nom et l'adresse postale du bien, le loyer mensuel ou la fourchette de loyer, le nombre de chambres et de salles de bain, la superficie et la liste d'équipements. Tenez-vous-en aux données visibles par tout visiteur sans compte, et évitez les coordonnées personnelles de propriétaires, d'agents ou de gestionnaires de biens, qui sortent du périmètre des annonces publiques que couvre ce guide.

Mes sélecteurs renvoient None ou une liste vide. Qu'est-ce qui a changé ?

Presque certainement le balisage d'Apartments.com. Ses lignes rentInfoDetail, le conteneur #amenitiesSection et les spans d'adresse changent sans préavis, donc des sélecteurs qui marchaient le mois dernier peuvent casser. Réinspectez une annonce en direct dans les outils de développement de votre navigateur et mettez les sélecteurs à jour. Une maintenance périodique des sélecteurs est normale pour tout scraper en production.

Comment gérer la pagination sur les annonces d'une ville ?

Apartments.com ajoute le numéro de page au chemin de recherche, donc vous crawlez chaque page de résultats à tour de rôle, collectez les liens d'annonces depuis les cartes qui s'y trouvent, et récupérez chaque annonce avec le même parseur. Gardez un court sleep entre les requêtes et arrêtez-vous quand une page ne renvoie plus de nouvelle carte. La fonction scrape_search ci-dessus montre la boucle complète.

Comment éviter de me faire bloquer en scrapant Apartments.com ?

Gardez votre débit de requêtes par IP bas, cadencez les requêtes avec un court délai, variez vos cibles plutôt que de boucler sur un seul chemin, et routez via des IP résidentielles rotatives pour qu'aucune seule adresse ne déclenche une limite de débit. La Crawling API gère la rotation et un pool d'IP de confiance pour vous ; si vous construisez votre propre pile, c'est la partie où investir. Surveillez les codes de statut et levez le pied quand vous commencez à voir des défis.

Commencer à construire

Crawlez n'importe quel site à grande échelle, sans combattre l'infrastructure.

Crawlbase gère les proxies, les empreintes et les CAPTCHA afin que votre équipe livre des pipelines de données au lieu de maintenir la plomberie de crawl. 1 000 requêtes gratuites, sans carte requise.

En libre-service · Sans appel commercial requis · Volumes de crawl entreprise disponibles