Trulia ist einer der meistbesuchten Immobilien-Marktplätze in den Vereinigten Staaten, und seine Suchergebnisse enthalten genau die strukturierten Daten, die Preis-Tracking, Marktforschung und Investitionsanalyse vorantreiben: den Angebotspreis, Schlafzimmer, Bäder, Quadratmeter, die Adresse und einen Link zur Detailseite jeder Immobilie. Für alle, die einen lokalen Markt beobachten, sind diese Listing-Seiten das Rohmaterial. Der Haken ist, dass Trulia seine Ergebnisse clientseitig rendert und hart gegen automatisierten Datenverkehr vorgeht, sodass eine einfache HTTP-Anfrage eine nahezu leere Hülle liefert anstatt der gesuchten Listings.
Diese Anleitung zeigt Ihnen, wie Sie Trulia scrapen auf zuverlässige Weise mit Python. Sie bauen einen kleinen, lauffähigen Scraper, der eine gerenderte Suchergebnisseite über die Crawling API abruft, jedes Listing mit BeautifulSoup parst, die Paginierung handhabt und die Daten in JSON und CSV exportiert. Der gesamte Walkthrough beschränkt sich ausschließlich auf öffentliche Immobilien-Listings, und der Abschnitt zur Rechtslage am Ende ist kein Boilerplate, also lesen Sie ihn, bevor Sie das auf echtes Volumen anwenden.
Was Sie bauen werden
Ein Python-Skript, das eine öffentliche Trulia-Such-URL nimmt (zum Beispiel Immobilien zum Verkauf in Los Angeles, CA), das gerenderte HTML über die Crawling API abruft und für jedes Listing auf der Seite einen strukturierten Datensatz extrahiert. Wir holen diese Felder aus jeder Immobilienkarte:
- Preis der Angebotspreis des Listings.
- Adresse die Straßenadresse der Immobilie.
- Schlafzimmer die Anzahl der Schlafzimmer.
- Bäder die Anzahl der Badezimmer.
- Größe die Wohnfläche in Quadratfuß.
- Link die URL der Detailseite der Immobilie.
Warum eine einfache Anfrage bei Trulia scheitert
Wenn Sie eine Trulia-Such-URL mit einem einfachen HTTP-Client anfordern, erhalten Sie eine Antwort mit Status 200 und fast keine der Listing-Daten im Body. Zwei Dinge arbeiten gegen Sie. Erstens rendert Trulia einen Großteil seiner Ergebnisse im Browser mit JavaScript, sodass das initiale HTML eine dünne Hülle ist, die sich erst füllt, nachdem die Skripte der Seite ausgeführt werden. Zweitens kennzeichnet die Website automatisierten Datenverkehr schnell: Datacenter-IPs und Anfragemuster, die nicht wie ein echter Browser aussehen, werden herausgefordert, gedrosselt oder erhalten einen CAPTCHA, bevor sie jemals die gerenderten Listings erreichen.
Ein funktionierender Trulia-Scraper benötigt also zwei Dinge in einer Anfrage: einen Browser, der die Seite tatsächlich rendert, und eine IP, die die Plattform als echten Besucher liest. Sie können das selbst mit einem Headless-Browser plus einem Pool rotierender Residential-Proxys zusammenstellen, aber diese zu verknüpfen und gesund zu halten ist der Großteil der Arbeit. Die Crawling API fasst beides in einem einzigen Aufruf zusammen: Sie übergeben ihr die URL mit einem JavaScript-Token, sie rendert die Seite hinter einer vertrauenswürdigen IP und gibt fertiges HTML zum Parsen zurück.
Crawlbase bietet zwei Token-Typen an. Der normale Token ruft statisches HTML ab; der JavaScript (JS)-Token rendert die Seite zuerst in einem echten Browser. Trulia befüllt seine Listing-Karten clientseitig, also benötigen Sie hier den JS-Token. Der normale Token gibt dieselbe leere Hülle zurück wie ein einfacher Abruf, aus der nichts Nützliches zu parsen ist.
Voraussetzungen
Sie benötigen ein paar Dinge, bevor Sie Code schreiben. Keine davon braucht lange.
Python-Grundkenntnisse. Sie sollten vertraut darin sein, ein Python-Skript zu schreiben und auszuführen sowie Pakete mit pip zu installieren. Falls Sie die Sprache neu kennenlernen, deckt die Python-Web-Scraping-Anleitung das Level ab, das dieses Tutorial voraussetzt.
Python 3.8 oder höher. Bestätigen Sie Ihre Version mit python --version und prüfen Sie, ob pip vorhanden ist mit pip --version. Falls Sie Python nicht haben, installieren Sie es von python.org entsprechend Ihrem Betriebssystem.
Ein Crawlbase-Konto und JS-Token. Registrieren Sie sich, um bis zu 20.000 kostenlose Anfragen zu erhalten, öffnen Sie Ihr Dashboard und kopieren Sie Ihren JavaScript (JS)-Token von der Konto-Dokumentationsseite. Behandeln Sie den Token wie ein Passwort: Er authentifiziert Ihre Anfragen, also halten Sie ihn aus der Versionskontrolle heraus.
Das Projekt einrichten
Erstellen Sie eine virtuelle Umgebung, damit Projektabhängigkeiten isoliert bleiben, und installieren Sie dann die drei Bibliotheken, die der Scraper benötigt.
python --version python -m venv trulia_env source trulia_env/bin/activate pip install crawlbase beautifulsoup4 pandas
Unter Windows aktivieren Sie die Umgebung mit trulia_env\Scripts\activate statt der source-Zeile. Drei Abhängigkeiten erledigen die Arbeit: crawlbase ist der offizielle Client für die Crawling API, beautifulsoup4 parst das zurückgegebene HTML, sodass Sie Felder per CSS-Selektor extrahieren können, und pandas übernimmt den CSV-Export am Ende. Falls Sie den Parser noch nicht verwendet haben, ist die BeautifulSoup-Anleitung ein guter Begleiter zu diesem Tutorial.
Schritt 1: Die gerenderte Suchseite abrufen
Beginnen Sie damit, die fertige Seite zu erhalten. Importieren Sie die CrawlingAPI-Klasse, initialisieren Sie sie mit Ihrem JS-Token und fordern Sie die Such-URL an. Da Trulia seine Karten asynchron lädt, übergeben Sie ajax_wait und page_wait, damit die API wartet, bis die Listings vorhanden sind. Die Status-Prüfung vor dem Parsen lässt Fehler laut werden statt still zu bleiben.
from crawlbase import CrawlingAPI api = CrawlingAPI({"token": "YOUR_CRAWLBASE_TOKEN"}) options = {"ajax_wait": "true", "page_wait": 8000} def crawl(page_url): response = api.get(page_url, options) if response["headers"]["cb_status"] == "200": return response["body"].decode("utf-8") print(f"Request failed. cb_status: {response['headers']['cb_status']}") return None if __name__ == "__main__": search_url = "https://www.trulia.com/CA/Los_Angeles/" html = crawl(search_url) print(html[:500] if html else "No HTML returned")
Die zwei Warte-Optionen sind wichtig für ein clientseitig gerendertes Ziel wie dieses. ajax_wait weist die API an, zu warten, bis asynchrone Inhalte fertig geladen sind, und page_wait wartet eine feste Anzahl von Millisekunden nach dem Laden, damit spät rendernde Karten erscheinen, bevor die Seite erfasst wird. Acht Sekunden sind ein vernünftiger Start für Trulia; erhöhen Sie es, wenn die Listings leer zurückkommen. Die Crawling API gibt einen cb_status (legacy pc_status)-Header zurück, der das Crawl-Ergebnis widerspiegelt, also prüfen Sie ihn statt des rohen HTTP-Codes. Führen Sie das Skript mit python trulia_scraper.py aus und Sie sollten echtes Listing-Markup sehen, nicht die leere Hülle, die eine einfache Anfrage zurückgibt. Das bestätigt, dass das Rendering funktioniert, bevor Sie einen einzigen Selektor schreiben.
Trulia benötigt eine gerenderte Seite hinter einer vertrauenswürdigen IP, in einem einzigen Aufruf, und die ajax_wait- sowie page_wait-Optionen, die Sie gerade gesetzt haben, warten auf das clientseitige Laden. Die Crawling API nimmt einen JS-Token, führt die Seite in einem echten Browser aus, rotiert serverseitig durch Residential-IPs und übergibt fertiges HTML, sodass Sie kein eigenes Headless-Fleet und keinen Proxy-Pool betreiben müssen. Richten Sie es zunächst auf eine öffentliche Suchseite im kostenlosen Tarif.
Schritt 2: Die Listing-Karten sammeln
Bevor Sie einzelne Felder extrahieren, benötigen Sie die Menge der Immobilienkarten auf der Seite. Bei Trulia befindet sich jedes Listing in einem li-Element, und all diese li-Elemente liegen in einem ul mit dem Attribut data-testid="search-result-list-container". Die direkten Kinder dieses Containers auszuwählen gibt Ihnen einen Knoten pro Immobilie.
from bs4 import BeautifulSoup def get_listings(html): soup = BeautifulSoup(html, "html.parser") return soup.select('ul[data-testid="search-result-list-container"] > li')
Das gibt eine Liste von Karten-Elementen zurück. Jedes ist ein eigenständiger Scope, den Sie nach Preis, Adresse und dem Rest einer Immobilie abfragen können, was die Selektoren pro Feld einfach hält und verhindert, Daten zwischen Listings zu vermischen.
Schritt 3: Die Felder aus jeder Karte parsen
Mit einer Karte zur Hand extrahieren Sie jedes Feld über sein data-testid-Attribut. Trulia ist in diesen Attributen über Listings hinweg konsistent, was sie stabiler als visuelle Klassennamen macht. Kapseln Sie jeden Lookup, sodass ein fehlendes Element None zurückgibt statt zu werfen, da nicht jedes Listing jedes Feld enthält (ein Grundstücks-Listing beispielsweise hat möglicherweise keine Schlafzimmer- oder Badzahl).
def text_at(listing, selector): el = listing.select_one(selector) return el.get_text(strip=True) if el else None def parse_listing(listing): link_el = listing.select_one('a[data-testid="property-card-link"]') link = "https://www.trulia.com" + link_el["href"] if link_el else None return { "price": text_at(listing, 'div[data-testid="property-price"]'), "address": text_at(listing, 'div[data-testid="property-address"]'), "beds": text_at(listing, 'div[data-testid="property-beds"]'), "baths": text_at(listing, 'div[data-testid="property-baths"]'), "size": text_at(listing, 'div[data-testid="property-floorSpace"]'), "link": link, }
Der text_at-Helfer erledigt den sich wiederholenden Teil: Er fragt ein Element ab und gibt seinen gestrippten Text zurück oder None, wenn das Element fehlt, sodass ein fehlendes Feld den Lauf nie zum Absturz bringt. Der Preis befindet sich in property-price, die Straßenadresse in property-address, Schlafzimmer und Bäder in property-beds und property-baths, und die Wohnfläche in property-floorSpace. Der Detail-Seiten-Link befindet sich auf einem a mit data-testid="property-card-link", und da dieses href relativ ist, setzen Sie die Trulia-Origin als Präfix, um eine absolute URL zu erhalten.
Trulia's data-testid-Werte sind heute stabil, aber nicht garantiert. Wenn ein Feld über jede Karte hinweg als None zurückkommt, inspizieren Sie ein Live-Listing in den Dev Tools Ihres Browsers und aktualisieren Sie den Selektor. Periodische Selektor-Wartung ist bei jedem Produktions-Scraper normal, kein Zeichen dafür, dass etwas kaputt ist.
Schritt 4: Das vollständige Skript zusammenstellen
Nun verbinden Sie Abruf, Karten-Sammlung und Feld-Parsing in einem lauffähigen Skript. Rufen Sie das gerenderte HTML ab, iterieren Sie die Karten, parsen Sie jede zu einem Datensatz und geben Sie die Ergebnisse als JSON aus.
import json from crawlbase import CrawlingAPI from bs4 import BeautifulSoup api = CrawlingAPI({"token": "YOUR_CRAWLBASE_TOKEN"}) options = {"ajax_wait": "true", "page_wait": 8000} def crawl(page_url): response = api.get(page_url, options) if response["headers"]["cb_status"] == "200": return response["body"].decode("utf-8") print(f"Request failed. cb_status: {response['headers']['cb_status']}") return None def get_listings(html): soup = BeautifulSoup(html, "html.parser") return soup.select('ul[data-testid="search-result-list-container"] > li') def text_at(listing, selector): el = listing.select_one(selector) return el.get_text(strip=True) if el else None def parse_listing(listing): link_el = listing.select_one('a[data-testid="property-card-link"]') link = "https://www.trulia.com" + link_el["href"] if link_el else None return { "price": text_at(listing, 'div[data-testid="property-price"]'), "address": text_at(listing, 'div[data-testid="property-address"]'), "beds": text_at(listing, 'div[data-testid="property-beds"]'), "baths": text_at(listing, 'div[data-testid="property-baths"]'), "size": text_at(listing, 'div[data-testid="property-floorSpace"]'), "link": link, } def main(): search_url = "https://www.trulia.com/CA/Los_Angeles/" html = crawl(search_url) if not html: return listings = get_listings(html) results = [parse_listing(li) for li in listings] print(json.dumps(results, indent=2)) if __name__ == "__main__": main()
Wie die Ausgabe aussieht
Führen Sie das vollständige Skript mit python trulia_scraper.py aus und Sie erhalten eine saubere Liste strukturierter Datensätze, einen pro Listing auf der Seite, bereit zum Schreiben in JSON, CSV oder eine Datenbank.
[ { "price": "$4,750,000", "address": "9240 W National Blvd, Los Angeles, CA 90034", "beds": "9bd", "baths": "9ba", "size": "6,045 sqft", "link": "https://www.trulia.com/p/ca/los-angeles/..." }, { "price": "$1,499,999", "address": "245 Windward Ave, Venice, CA 90291", "beds": "4bd", "baths": "3ba", "size": "1,332 sqft", "link": "https://www.trulia.com/p/ca/venice/..." } ]
Listings mit fehlenden Daten kommen mit null in diesen Feldern zurück statt zu scheitern, weshalb ein reines Grundstücks- oder Vorverkaufs-Listing möglicherweise keine Schlafzimmer-, Bad- oder Größenangaben zeigt. Das ist erwartet, und nachgelagerter Code sollte jedes Feld als optional behandeln.
Paginierung und Daten-Export
Eine Seite ist eine Demo; ein echter Auftrag läuft über eine ganze Stadt. Trulia paginiert seine Suchergebnisse mit einem pfadbasierten Schema: Es fügt ein sequenzielles Seitensegment an die Such-URL an, sodass die erste Seite /1_p/, die zweite /2_p/ ist und so weiter. Das Iterieren dieser Zahl geht durch das Ergebnis-Set, und Sie verwenden dieselben crawl- und Parse-Funktionen auf jeder Seite wieder.
import json import time import pandas as pd def scrape_pages(base_url, num_pages): results = [] for page in range(1, num_pages + 1): page_url = f"{base_url}/{page}_p/" html = crawl(page_url) if not html: print(f"Skipping page {page}: no HTML.") continue listings = get_listings(html) if not listings: break results.extend(parse_listing(li) for li in listings) time.sleep(2) return results def export(results): with open("trulia_listings.json", "w") as f: json.dump(results, f, indent=2) pd.DataFrame(results).to_csv("trulia_listings.csv", index=False) print(f"Saved {len(results)} listings to JSON and CSV.") if __name__ == "__main__": base = "https://www.trulia.com/CA/Los_Angeles" data = scrape_pages(base, num_pages=3) export(data)
Das time.sleep(2) zwischen den Seiten ist bewusst: Es dosiert den Lauf, sodass Sie die Website nicht bombadieren, was die wirksamste einzelne Gewohnheit ist, um unblockiert zu bleiben. Die Schleife bricht auch früh ab, wenn eine Seite keine Karten zurückgibt, sodass Sie nie über die letzte Ergebnisseite hinaus anfordern. Die export-Funktion schreibt sowohl trulia_listings.json als auch trulia_listings.csv; pandas wandelt die Liste von Dicts in eine flache Tabelle um, in der jedes Feld eine Spalte wird. Passen Sie die Seitenanzahl und den Stadt-Slug in base an Ihren Zielmarkt an.
Unblockiert bleiben
Auch mit geregeltem Rendering beobachtet Trulia scraper-ähnlichen Datenverkehr. Einige Gewohnheiten halten einen Lauf gesund, und sie gelten für jedes schwere kommerzielle Ziel.
-
Dosieren Sie Ihre Anfragen. Seiten in einer engen Schleife zu hämmern ist der schnellste Weg zur Drosselung oder einem CAPTCHA. Verteilen Sie Anfragen, wie das obige
sleepes tut, und vermeiden Sie es, einen Pfad mit voller Geschwindigkeit zu crawlen. - Setzen Sie auf Rotation. Ein Pool von Residential-IPs verteilt Anfragen über viele echte Nutzer-Adressen, sodass keine einzelne ein Ratenlimit auslöst. Die Crawling API übernimmt das für Sie; wenn Sie Ihren eigenen Stack aufbauen, ist das der Teil, den Sie richtig machen müssen.
-
Lesen Sie die Statuscodes. Ein Lauf, der anfängt, Herausforderungen oder Nicht-200-
cb_status-Werte zurückzugeben, signalisiert Ihnen, dass die aktuelle Rate oder IP-Stufe nicht mehr ausreicht. Behandeln Sie das als Signal zum Zurückweichen, nicht als Rauschen zum Ignorieren.
Das umfassendere Playbook finden Sie unter Websites scrapen ohne blockiert zu werden. Wenn Ihre Ziel-Sites JavaScript intensiv nutzen, deckt die Anleitung zum Crawlen von JavaScript-Websites die Rendering-Seite ausführlicher ab.
Ist es legal, Trulia zu scrapen?
Ob das Scrapen von Trulia erlaubt ist, hängt von Trulia's Nutzungsbedingungen, Ihrem Gerichtsstand und dem ab, was Sie mit den Daten tun. Trulia's Bedingungen schränken den automatisierten Zugriff ein, sodass Scraping gegen diese Bedingungen verstoßen kann, unabhängig davon, wie sorgfältig Ihr Tooling ist. Kein Code hier ändert das; er macht nur den technischen Teil funktionsfähig. Lesen Sie Trulia's Nutzungsbedingungen und seine robots.txt, respektieren Sie seine Raten-Erwartungen und behandeln Sie beides als Grenze für das, was Sie sammeln.
Ein paar Linien, an denen es sich lohnt festzuhalten. Sammeln Sie nur öffentliche Immobilien-Listing-Daten: den Preis, die Adresse, Schlafzimmer, Bäder, Quadratmeter und den Listing-Link, den jeder ohne Konto sehen kann. Vermeiden Sie alles, das mit identifizierbaren Personen verknüpft ist, einschließlich der Kontaktdaten von Maklern, Brokern oder Eigentümern, die auf einer Karte angezeigt werden und außerhalb des öffentlichen Listing-Umfangs dieser Anleitung liegen. Ein Detail spezifisch für Immobilien ist erwähnenswert: Ein Großteil der zugrundeliegenden Immobiliendaten auf Websites wie Trulia stammt aus MLS-Feeds (Multiple Listing Service), die typischerweise lizenziert sind und eigene Nutzungsbeschränkungen haben. Diese Daten in großem Umfang weiterzuveröffentlichen kann gegen diese Lizenzen verstoßen, auch wenn die Seite selbst öffentlich ist.
Diese Anleitung ist bewusst auf öffentliche Such- und Listing-Seiten beschränkt, da das die Grenze ist, die die Arbeit vertretbar macht. Sie deckt nichts hinter einem Login, gespeicherte Such- oder Kontodaten, die persönlichen Kontaktdaten von Einzelpersonen oder Versuche ab, die Authentifizierung zu umgehen. Wenn Ihr Projekt mehr als öffentliche Listing-Felder benötigt, ist der richtige Weg ein lizenzierter Immobiliendaten-Feed oder eine offizielle Vereinbarung, kein cleverer Scraper. Wenn eine Website eine offizielle API oder Datenpartnerschaft anbietet, bevorzugen Sie diese; sie gibt Ihnen sauberere Daten und eine klare Lizenz gleichzeitig.
Wichtigste Erkenntnisse
- Trulia ist clientseitig gerendert. Eine einfache Anfrage gibt eine leere Hülle zurück, also müssen Sie die Seite rendern, bevor Sie sie parsen.
-
Sie benötigen Rendering und eine vertrauenswürdige IP zusammen. Die Crawling API mit einem JS-Token erledigt beides in einem Aufruf;
ajax_waitundpage_waitsteuern, wie lange sie auf das Laden der Karten wartet. -
Zielen Sie auf die stabilen Attribute. Trulia's
data-testid-Werte (property-price,property-address,property-beds,property-baths,property-floorSpace) treiben die Extraktion pro Feld an, wobei jede Karte auf einlibeschränkt ist. -
Paginieren Sie per Pfad und exportieren Sie beide Formate. Trulia verwendet
/N_p/-Seitensegmente; iterieren Sie sie, parsen Sie jede Karte und schreiben Sie das Ergebnis mit pandas in JSON und CSV. - Bleiben Sie bei öffentlichen Daten. Respektieren Sie Trulia's AGB und robots.txt, sammeln Sie nur öffentliche Listing-Felder, beachten Sie, dass MLS-Daten oft lizenziert sind, und berühren Sie niemals Konten, Logins oder die persönlichen Kontaktdaten von Einzelpersonen.
Häufig gestellte Fragen
Warum gibt eine einfache Anfrage keine Daten von Trulia zurück?
Weil Trulia seine Suchergebnisse clientseitig mit JavaScript rendert. Das initiale HTML ist eine Hülle, die sich erst füllt, nachdem die Skripte der Seite in einem Browser ausgeführt wurden, sodass eine rohe HTTP-Anfrage Status 200 mit leeren Preis-, Schlafzimmer-, Bad- und Adressfeldern zurückgibt. Um echte Daten zu erhalten, müssen Sie die Seite zuerst rendern, was der JS-Token der Crawling API für Sie übernimmt.
Benötige ich den normalen Token oder den JS-Token für Trulia?
Den JS-Token. Der normale Token ruft statisches HTML ab, was bei Trulia dieselbe leere Hülle ist wie bei einer einfachen Anfrage. Der JS-Token rendert die Seite in einem echten Browser, bevor er das HTML übergibt, sodass die Listing-Karten vorhanden sind, wenn BeautifulSoup sie parst.
Welche Daten kann ich von einem Trulia-Listing scrapen?
Öffentliche Listing-Felder: den Angebotspreis, die Straßenadresse, die Anzahl der Schlafzimmer und Bäder, die Wohnfläche in Quadratfuß und den Link zur Detailseite. Bleiben Sie bei Daten, die für jeden Besucher ohne Konto sichtbar sind, und vermeiden Sie die persönlichen Kontaktdaten von Maklern, Brokern oder Eigentümern, die außerhalb des öffentlichen Listing-Umfangs dieser Anleitung liegen.
Wie funktioniert die Paginierung bei Trulia?
Trulia verwendet ein pfadbasiertes Schema, das ein sequenzielles Seitensegment an die Such-URL anhängt: /1_p/ für die erste Seite, /2_p/ für die zweite und so weiter. Die obige scrape_pages-Funktion iteriert diese Zahl, ruft jede Seite über die Crawling API ab, parst die Karten und stoppt, wenn eine Seite keine Listings zurückgibt.
Meine Selektoren geben bei jeder Karte None zurück. Was hat sich geändert?
Höchstwahrscheinlich Trulia's Markup. Die data-testid-Werte, auf die dieser Scraper abzielt, können sich ohne Ankündigung ändern, sodass Selektoren, die letzten Monat funktioniert haben, brechen können. Inspizieren Sie ein Live-Listing in den Dev Tools Ihres Browsers und aktualisieren Sie die Selektoren. Periodische Selektor-Wartung ist bei jedem Produktions-Scraper normal.
Wie scrapt man andere Immobilien-Websites auf dieselbe Weise?
Dasselbe Muster überträgt sich: rendern Sie die Seite, sammeln Sie die Listing-Karten und ordnen Sie jedes öffentliche Feld einem Selektor zu. Die Details unterscheiden sich je Website; sehen Sie die Begleitanleitungen zum Scrapen von Zillow und Scrapen von Realtor.com, oder den mietfokussierten Apartments.com-Walkthrough, die alle dieselbe Abruf-und-Parse-Struktur wiederverwenden.
Crawlen Sie jede Website im großen Maßstab, ohne gegen die Infrastruktur zu kämpfen.
Crawlbase übernimmt Proxys, Fingerprints und CAPTCHAs, damit Ihr Team Datenpipelines ausliefert, statt Crawl-Infrastruktur zu pflegen. 1.000 Anfragen kostenlos, keine Karte erforderlich.
