Redfin liste des maisons à vendre et à louer aux États-Unis et au Canada, et chaque page d'annonce porte exactement les données structurées qui alimentent le suivi des prix, la recherche de marché et l'analyse immobilière : le prix demandé, les chambres, les salles de bain, la superficie, l'adresse et le lien vers l'annonce. Récupérez cela sur une ville ou une région et vous obtenez un jeu de données que vous pouvez comparer, visualiser et surveiller dans le temps.
Ce guide vous montre comment scraper les données immobilières Redfin avec Python de façon fiable. Vous construisez un petit scraper fonctionnel qui récupère une annonce Redfin rendue via la Crawling API, analyse les champs souhaités avec BeautifulSoup, parcourt la pagination de recherche, et exporte le résultat en JSON et CSV. L'ensemble du tutoriel se limite aux données d'annonces publiques, et la section sur la légalité vers la fin n'est pas du texte standard, donc lisez-la avant de pointer ce script sur un volume réel.
Ce que vous allez construire
Un script Python qui prend une URL d'annonce Redfin publique, récupère le HTML rendu via la Crawling API, et extrait un enregistrement structuré pour la propriété. Nous utilisons une annonce de vente comme exemple et extrayons ces champs :
- Prix le prix de vente affiché sur la page de la propriété.
- Chambres le nombre de chambres.
- Salles de bain le nombre de salles de bain.
- Superficie la surface habitable de la maison.
- Adresse l'adresse, la ville, l'état et le code postal.
- Lien l'URL canonique de l'annonce elle-même.
Pourquoi une simple requête échoue sur Redfin
Si vous demandez une URL d'annonce Redfin avec un client HTTP basique, vous obtenez généralement l'un de ces deux résultats décevants : une coque HTML légère avec les champs prix, chambres et salles de bain encore vides, ou une page de blocage avant d'atteindre l'annonce. Deux facteurs jouent contre vous. Premièrement, Redfin rend une grande partie du détail de ses annonces dans le navigateur avec JavaScript, donc le HTML initial qu'une requête simple voit ne contient pas encore les chiffres que vous cherchez. Deuxièmement, Redfin applique des mesures anti-scraping, incluant la limitation de débit par IP, les CAPTCHA et la détection du user-agent, donc les IP de datacenter et les patterns de requête qui ne ressemblent pas à un vrai navigateur sont rapidement challengés.
Un scraper Redfin fonctionnel a donc besoin de deux choses en une seule requête : un navigateur qui rend réellement la page, et une IP que la plateforme perçoit comme un vrai visiteur. Vous pouvez assembler cela vous-même avec un navigateur sans interface graphique plus un pool de proxys résidentiels rotatifs, mais les assembler et les maintenir en bon état représente l'essentiel du travail. La Crawling API regroupe les deux en un seul appel : vous lui envoyez l'URL avec un token JavaScript, elle rend la page derrière une IP fiable, et vous renvoie du HTML finalisé à analyser.
Crawlbase propose deux types de token. Le token normal récupère du HTML statique ; le token JavaScript (JS) rend la page dans un vrai navigateur d'abord. Redfin remplit ses champs d'annonce côté client, donc vous avez besoin du token JS ici. Le token normal renvoie la même coque partielle qu'une requête simple, et il n'y a pas grand chose d'utile à analyser dedans.
Prérequis
Vous avez besoin de quelques éléments en place avant d'écrire du code. Aucun ne prend longtemps.
Python de base. Vous devez être à l'aise avec l'écriture et l'exécution d'un script Python et l'installation de paquets avec pip. Si vous êtes nouveau avec le langage, le guide de scraping web Python couvre les bases que ce tutoriel suppose acquises.
Python 3.8 ou ultérieur. 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). Vous obtenez jusqu'à 20 000 requêtes Crawling API gratuites et aucune carte n'est requise. Traitez le token comme un mot de passe : il authentifie vos requêtes, donc gardez-le hors du contrôle de version.
Configurer le projet
Créez un environnement virtuel pour isoler les dépendances du projet, puis installez les deux bibliothèques dont le scraper a besoin.
python --version python -m venv redfin_env source redfin_env/bin/activate pip install crawlbase beautifulsoup4
Sous Windows, activez l'environnement avec redfin_env\Scripts\activate au lieu de la ligne source. Deux dépendances font le travail : crawlbase est le client officiel pour la Crawling API, et beautifulsoup4 analyse le HTML retourné pour extraire les champs individuels par sélecteur CSS. Si vous n'avez pas encore utilisé le parseur, le guide BeautifulSoup est un bon complément à ce tutoriel.
Étape 1 : Récupérer l'annonce rendue
Commencez par obtenir la page complète. Importez la classe CrawlingAPI, initialisez-la avec votre token JS, et demandez l'URL de l'annonce. Vérifier le statut avant d'analyser rend les échecs visibles plutôt que silencieux.
from crawlbase import CrawlingAPI crawling_api = CrawlingAPI({"token": "YOUR_CRAWLBASE_TOKEN"}) def crawl(page_url): options = {"ajax_wait": "true", "page_wait": 5000} response = crawling_api.get(page_url, options) if response["headers"]["cb_status"] == "200": return response["body"].decode("utf-8") print(f"Failed to fetch the page. Crawlbase status: {response['headers']['cb_status']}") return None if __name__ == "__main__": listing_url = "https://www.redfin.com/CA/North-Hollywood/6225-Coldwater-Canyon-Ave-91606/unit-106/home/5104172" html = crawl(listing_url) print(html[:500] if html else "No HTML returned")
Les deux options d'attente sont importantes pour une cible rendue côté client comme celle-ci. ajax_wait indique à l'API d'attendre que le contenu asynchrone finisse de charger, et page_wait maintient l'attente pendant un nombre fixe de millisecondes après le chargement pour que les éléments tardifs à rendre apparaissent avant que la page ne soit capturée. Cinq secondes est un bon point de départ ; augmentez si les champs prix ou détails reviennent vides. La bibliothèque Crawlbase renvoie le statut sous response["headers"]["cb_status"] ; une valeur de "200" signifie que la page a été récupérée et rendue. Exécutez le script et vous devriez voir du vrai balisage d'annonce, pas une page de blocage, ce qui confirme que le rendu fonctionne avant d'écrire un seul sélecteur.
Redfin nécessite une page rendue derrière une IP fiable, en un seul appel, et il limite activement le débit et soumet des CAPTCHA à tout ce qui semble automatisé. La Crawling API prend un token JS, exécute la page dans un vrai navigateur, fait tourner les IP résidentielles côté serveur, et vous fournit du HTML finalisé, ce qui vous évite de gérer votre propre flotte sans interface graphique et un pool de proxys. Vous obtenez jusqu'à 20 000 requêtes gratuites.
Étape 2 : Analyser 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. Redfin dispose les détails principaux de l'annonce dans une structure prévisible, vous pouvez donc mapper le prix, l'adresse et les faits clés sur des sélecteurs individuels. Redfin n'a pas d'API publique pour les pages de vente, donc c'est un travail XPath et sélecteurs CSS : le prix se trouve dans un bloc data-rf-test-id="abp-price", l'adresse se divise entre .street-address et .bp-cityStateZip, et les faits principaux (chambres, salles de bain, superficie) se trouvent dans les lignes .keyDetails-value. Enveloppez l'extraction dans un helper qui retourne None quand un élément est manquant, pour qu'un champ absent ne fasse pas planter l'exécution.
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 find_detail(details, keyword): for value in details: if keyword in value.lower(): return value return None def parse_property(html, listing_url): soup = BeautifulSoup(html, "html.parser") price = text_of(soup, 'div[data-rf-test-id="abp-price"] div') street = text_of(soup, ".street-address") city_state_zip = text_of(soup, ".bp-cityStateZip") address = " ".join(filter(None, [street, city_state_zip])) details = [d.get_text(strip=True) for d in soup.select(".keyDetails-value")] return { "address": address or None, "price": price, "beds": find_detail(details, "bed"), "baths": find_detail(details, "bath"), "sqft": find_detail(details, "sq ft"), "link": listing_url, }
Le helper text_of interroge un élément et retourne None quand il est manquant au lieu de lever une exception sur rien. L'adresse est reconstruite à partir des deux parties que Redfin rend séparément, la ligne de rue et la ligne ville/état/code postal, jointes par un espace. Les faits principaux reviennent sous forme de liste plate de chaînes .keyDetails-value ; find_detail extrait les entrées chambres, salles de bain et superficie par mot-clé plutôt que par position fixe, ce qui survit aux petits réordonnancementsdans la mise en page. Cette structure rend l'extraction résistante quand un champ est absent sur une annonce donnée.
Les noms de classes et identifiants de test Redfin (le bloc abp-price, .street-address, .bp-cityStateZip, les lignes .keyDetails-value) 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 comme None, réinspectez l'annonce en direct dans les outils de développement de votre navigateur et mettez à jour le sélecteur. La maintenance périodique des sélecteurs est normale pour tout scraper en production.
Étape 3 : Tout assembler
Maintenant, reliez la récupération et l'analyse en un seul script exécutable. Récupérez le HTML rendu, passez-le au parseur, et affichez l'enregistrement structuré.
import json from crawlbase import CrawlingAPI from bs4 import BeautifulSoup crawling_api = CrawlingAPI({"token": "YOUR_CRAWLBASE_TOKEN"}) def crawl(page_url): options = {"ajax_wait": "true", "page_wait": 5000} response = crawling_api.get(page_url, options) if response["headers"]["cb_status"] == "200": return response["body"].decode("utf-8") print(f"Failed to fetch the page. Crawlbase status: {response['headers']['cb_status']}") return None def text_of(soup, selector): el = soup.select_one(selector) return el.get_text(strip=True) if el else None def find_detail(details, keyword): for value in details: if keyword in value.lower(): return value return None def parse_property(html, listing_url): soup = BeautifulSoup(html, "html.parser") price = text_of(soup, 'div[data-rf-test-id="abp-price"] div') street = text_of(soup, ".street-address") city_state_zip = text_of(soup, ".bp-cityStateZip") address = " ".join(filter(None, [street, city_state_zip])) details = [d.get_text(strip=True) for d in soup.select(".keyDetails-value")] return { "address": address or None, "price": price, "beds": find_detail(details, "bed"), "baths": find_detail(details, "bath"), "sqft": find_detail(details, "sq ft"), "link": listing_url, } def main(): listing_url = "https://www.redfin.com/CA/North-Hollywood/6225-Coldwater-Canyon-Ave-91606/unit-106/home/5104172" html = crawl(listing_url) if not html: return data = parse_property(html, listing_url) print(json.dumps(data, indent=2)) if __name__ == "__main__": main()
À quoi ressemble le résultat
Exécutez le script complet et vous obtenez un enregistrement structuré propre pour l'annonce, prêt à écrire en JSON, CSV ou dans une base de données.
{ "address": "6225 Coldwater Canyon Ave #106 Valley Glen, CA 91606", "price": "$627,000", "beds": "2 beds", "baths": "2 baths", "sqft": "1,209 sq ft", "link": "https://www.redfin.com/CA/North-Hollywood/6225-Coldwater-Canyon-Ave-91606/unit-106/home/5104172" }
Monter en charge sur les pages de recherche et la pagination
Une annonce est une démonstration ; un vrai travail couvre toute une recherche. Redfin pagine ses résultats de recherche, et chaque ville ou région a un chemin de résultats auquel vous pouvez ajouter un numéro de page, comme /page-2. Le modèle se compose de deux couches : parcourir chaque page de résultats de recherche pour collecter les URLs d'annonce, puis récupérer chaque annonce via la même fonction parse_property que vous avez déjà écrite. Les cartes d'annonce sur une page de recherche Redfin exposent leur URL via une ancre avec le wrapper .bp-Homecard__Photo--image ou l'élément lien de la carte, donc vous pouvez collecter les hrefs et les résoudre par rapport au domaine Redfin.
import time from urllib.parse import urljoin BASE = "https://www.redfin.com" def collect_listing_urls(search_html): soup = BeautifulSoup(search_html, "html.parser") cards = soup.select("a.bp-Homecard") urls = [urljoin(BASE, a["href"]) for a in cards if a.get("href")] return list(dict.fromkeys(urls)) def scrape_search(search_url, pages): listings = [] for page in range(1, pages + 1): page_url = search_url if page == 1 else f"{search_url}/page-{page}" search_html = crawl(page_url) if not search_html: continue for url in collect_listing_urls(search_html): html = crawl(url) if html: listings.append(parse_property(html, url)) time.sleep(2) print(f"Scraped {len(listings)} listings") return listings
L'étape dict.fromkeys supprime les URLs en double qui apparaissent quand une carte renvoie vers la même annonce plus d'une fois. Le time.sleep(2) entre les récupérations d'annonces est délibéré : il cadence l'exécution pour que vous ne marteliez pas Redfin, ce qui est l'habitude la plus efficace pour rester non bloqué. Ajustez le nombre de pages et le slug de recherche selon votre région cible.
Exporter en JSON et CSV
Une fois que vous avez une liste d'enregistrements, les écrire ne prend que deux courtes fonctions. JSON conserve la structure complète pour le code aval ; CSV est le format que tout tableur et outil BI lit.
import csv def save_json(records, path="redfin_listings.json"): with open(path, "w", encoding="utf-8") as f: json.dump(records, f, indent=2, ensure_ascii=False) def save_csv(records, path="redfin_listings.csv"): if not records: return fields = ["address", "price", "beds", "baths", "sqft", "link"] with open(path, "w", newline="", encoding="utf-8") as f: writer = csv.DictWriter(f, fieldnames=fields) writer.writeheader() writer.writerows(records) if __name__ == "__main__": results = scrape_search("https://www.redfin.com/city/11203/CA/Los-Angeles", pages=3) save_json(results) save_csv(results)
Le writer CSV impose un ordre de colonnes explicite pour que l'en-tête soit stable d'une exécution à l'autre, et les deux writers utilisent UTF-8 pour que les adresses avec des caractères accentués survivent au round trip. Avec JSON et CSV sur disque, vous pouvez alimenter directement un notebook, un tableau de bord ou un chargement de base de données.
Rester non bloqué
Même avec le rendu géré, Redfin surveille le trafic à l'allure d'un scraper avec la limitation de débit par IP, les CAPTCHA et les vérifications du user-agent. Quelques bonnes habitudes maintiennent une exécution en bonne santé, et elles s'appliquent à toute cible commerciale difficile.
-
Cadencez vos requêtes. Marteler les annonces en boucle serrée est le moyen le plus rapide d'être limité ou de recevoir un CAPTCHA. Étalez les requêtes, comme le fait le
sleepci-dessus, et variez vos cibles au lieu de crawler un chemin à pleine vitesse. - Appuyez-vous sur la rotation. Un pool d'IP résidentielles répartit les requêtes sur de nombreuses adresses d'utilisateurs réels pour qu'aucune ne déclenche une limite de débit. La Crawling API gère cela pour vous ; si vous construisez votre propre infrastructure, c'est la partie à bien soigner.
-
Lisez les codes de statut. Une exécution qui commence à renvoyer des défis ou des valeurs
cb_status(legacypc_status) non-200 vous indique que le débit ou le niveau d'IP actuel n'est plus suffisant. Traitez cela comme un signal pour ralentir, pas comme du bruit à ignorer.
Pour le guide complet, consultez comment scraper des sites web sans être bloqué. Si vous préférez acheminer votre propre trafic via un pool rotatif plutôt que d'utiliser l'API gérée, le Smart AI Proxy vous donne la même rotation d'IP résidentielles comme endpoint proxy drop-in. Vous travaillez ensuite sur d'autres portails immobiliers ? Le même pattern rendre-puis-analyser s'applique directement au scraping de Zillow et au scraping de Realtor.com, seuls les sélecteurs changent.
Est-il légal de scraper Redfin ?
Le fait que le scraping de Redfin soit autorisé dépend des conditions d'utilisation de Redfin, de votre juridiction et de ce que vous faites avec les données. Les conditions de Redfin restreignent l'accès automatisé, donc le scraping peut aller à l'encontre de ces conditions quel que soit le soin apporté à vos outils. Aucun code ici ne change cela ; il rend simplement la partie technique fonctionnelle. Lisez les Conditions d'utilisation de Redfin et son robots.txt, respectez les attentes de débit qu'ils impliquent, et traitez les deux comme la limite de ce que vous collectez.
Quelques lignes à respecter. Collectez uniquement les données d'annonces publiques : le prix, les chambres, les salles de bain, la superficie, l'adresse et le lien vers l'annonce que n'importe qui peut voir sans compte. Maintenez votre volume de requêtes suffisamment bas pour ne pas solliciter excessivement les serveurs de Redfin. Évitez tout ce qui est lié à des individus identifiables, y compris les noms et coordonnées des agents et propriétaires figurant sur une page ; une annonce immobilière publique n'est pas une licence pour créer des profils des personnes qui y sont associées. Une grande partie des données immobilières sous-jacentes des portails immobiliers provient de flux MLS sous licence selon des conditions spécifiques, donc les republier ou les revendre en masse peut se heurter à des restrictions de licence même quand la page elle-même est publique.
Ce guide est délibérément limité aux pages d'annonces publiques car c'est la ligne qui rend le travail défendable. Il ne couvre rien derrière une connexion, les données de recherche sauvegardée ou de compte, les données personnelles des agents ou propriétaires, ni aucune tentative de contournement de l'authentification. Données de propriété publiques uniquement. Si votre projet nécessite un flux régulier et sanctionné, Redfin et d'autres portails proposent des partenariats de données, et des flux MLS sous licence existent exactement à cet effet ; un accord de licence ou un fournisseur de données immobilières est la bonne voie pour un volume en production, pas un scraper plus sophistiqué.
Points clés
- Redfin est rendu côté client et bien défendu. Une requête simple renvoie une coque partielle ou une page de blocage, donc vous devez rendre la page derrière une IP fiable avant de l'analyser.
-
La Crawling API fait les deux en un seul appel. Un token JS rend la page et fait tourner les IP résidentielles côté serveur ;
ajax_waitetpage_waitcontrôlent le temps d'attente du contenu, etcb_statusvous indique si la récupération a fonctionné. -
BeautifulSoup fait l'extraction. Mappez le prix, l'adresse, les chambres, les salles de bain, la superficie et le lien vers l'annonce sur les sélecteurs actuels comme
abp-priceet.keyDetails-value, et attendez-vous à ce que ces sélecteurs dérivent. - Montez en charge en paginant la recherche, puis en parcourant les annonces. Collectez les URLs depuis chaque page de résultats, récupérez chaque annonce avec le même parseur, cadencez l'exécution avec un court délai, et exportez en JSON et CSV.
- Restez sur les données publiques. Respectez les CGU et le robots.txt de Redfin, collectez uniquement les champs publics de propriété, rappelez-vous que les données MLS sont souvent sous licence, et ne touchez jamais aux comptes, connexions ou données personnelles des agents et propriétaires.
Foire aux questions
Pourquoi une requête simple ne renvoie-t-elle pas de données de Redfin ?
Parce que Redfin rend une grande partie du détail de ses annonces côté client avec JavaScript, et il challenge le trafic automatisé. Une requête HTTP brute renvoie souvent une coque légère avec les champs prix, chambres et salles de bain vides, ou une page de blocage avant que l'annonce ne charge du tout. Pour obtenir des données réelles, vous devez rendre la page derrière une IP fiable, ce que le token JS de la Crawling API gère pour vous.
Ai-je besoin du token normal ou du token JS pour Redfin ?
Le token JS. Le token normal récupère du HTML statique, qui sur Redfin est la même coque partielle qu'une requête simple renvoie. Le token JS rend la page dans un vrai navigateur avant de retourner le HTML, donc les champs de l'annonce sont présents quand BeautifulSoup les analyse.
Quelles données puis-je scraper d'une annonce Redfin ?
Les champs d'annonce publics : le prix, le nombre de chambres et salles de bain, la superficie, l'adresse et le lien vers l'annonce. Restez sur les données visibles de tout visiteur sans compte, et évitez les noms et coordonnées des agents ou propriétaires, qui sortent du cadre des annonces publiques couvert par ce guide.
Mes sélecteurs retournent None. Qu'est-ce qui a changé ?
Presque certainement le balisage de Redfin. Le bloc abp-price, les éléments .street-address et .bp-cityStateZip, et les lignes .keyDetails-value changent sans préavis, donc des sélecteurs qui fonctionnaient le mois dernier peuvent se casser. Réinspectez une annonce en direct dans les outils de développement de votre navigateur et mettez à jour les sélecteurs. La maintenance périodique des sélecteurs est normale pour tout scraper en production.
Comment gérer la pagination sur une recherche Redfin ?
Redfin ajoute un segment de page comme /page-2 au chemin de recherche d'une région, donc vous crawlez chaque page de résultats à tour de rôle, collectez les liens d'annonces des cartes, et récupérez chaque annonce avec le même parseur. Gardez un court délai entre les requêtes et arrêtez quand une page ne renvoie plus de nouvelles cartes. La fonction scrape_search ci-dessus montre la boucle complète.
Puis-je scraper les pages de location et de vente Redfin avec la même approche ?
Oui. Le pattern rendre-puis-analyser est le même pour les deux ; seuls les sélecteurs diffèrent, puisque les pages de location et de vente disposent leurs champs différemment. Récupérez le HTML rendu via la Crawling API, puis pointez BeautifulSoup sur les sélecteurs correspondant au type de page que vous scrapez. Pour d'autres portails, le même flux s'applique au scraping de Trulia et d'autres sites immobiliers.
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.
