Zillow ist eine der meistbesuchten Immobilienplattformen im Web. Die Angebotseiten enthalten genau die strukturierten Daten, die für Preisverfolgung, Marktforschung und Investitionsanalysen benötigt werden: Angebotspreis, Schlaf- und Badezimmeranzahl, Wohnfläche, Immobilientyp und Anschrift. Wer einen lokalen Markt untersucht, braucht diese öffentlichen Angebotsdaten als Rohmaterial. Dutzende von Immobilien manuell zu erfassen ist langsam und fehleranfällig.
Diese Anleitung zeigt, wie man Zillow mit Python zuverlässig scrapt. Sie erstellen ein kleines, lauffähiges Skript, das gerenderter Zillow-Seiten über die Crawling API abruft, Immobilien-Links von einer Suchergebnisseite sammelt, die gewünschten Felder mit BeautifulSoup analysiert, die Paginierung abwickelt und sauberes JSON und CSV exportiert. Die gesamte Anleitung beschränkt sich auf öffentliche Angebotsdaten. Der Abschnitt zur Rechtslage am Ende ist kein Boilerplate, lesen Sie ihn also, bevor Sie das Skript in großem Umfang einsetzen.
Was Sie erstellen werden
Ein Python-Skript, das eine öffentliche Zillow-Such-URL für einen Standort entgegennimmt, die Links der Immobilienseiten sammelt, jedes gerenderte Angebot über die Crawling API abruft und pro Immobilie einen strukturierten Datensatz extrahiert. Das durchgängige Beispiel sind zum Verkauf stehende Immobilien in Columbia Heights, Washington, DC. Folgende Felder werden extrahiert:
- Price der gelistete Angebotspreis der Immobilie.
- Beds die Anzahl der Schlafzimmer.
- Baths die Anzahl der Badezimmer.
- Size die Wohnfläche des Hauses in Quadratfuß.
- Address die im Angebot angezeigte Straßenadresse.
- Type der Immobilientyp, z. B. Eigentumswohnung, Reihenhaus oder Einfamilienhaus.
- Link die kanonische URL der Immobilienseite.
Warum eine einfache Anfrage bei Zillow scheitert
Rufen Sie eine Zillow-Such- oder Angebotsseite mit einem einfachen HTTP-Client ab, erhalten Sie eine Antwort mit Status 200, die jedoch nur einen Bruchteil der Daten im Body enthält. Zwei Faktoren arbeiten gegen Sie. Erstens lädt Zillow die meisten Suchergebnisse und Angebotsdetails im Browser über JavaScript und Ajax, sodass das initiale HTML eine dünne Hülle ist, die sich erst nach dem Ausführen der Seitenskripte füllt. Extrahieren Sie die Immobilien-Links aus dieser ersten Antwort, erhalten Sie eine Handvoll Karten statt des vollständigen Ergebnissatzes. Zweitens erkennt Zillow automatisierten Traffic schnell: Datacenter-IPs und Anfragemuster, die nicht wie ein echter Browser wirken, werden gedrosselt, gesperrt oder mit einer Challenge konfrontiert, bevor sie den gerenderten Inhalt überhaupt erreichen.
Ein funktionierender Zillow-Scraper benötigt daher zwei Dinge in einer einzigen Anfrage: einen Browser, der die Seite tatsächlich rendert, und eine IP, die die Plattform als echten Besucher erkennt. Man kann beides selbst zusammenstellen, mit einem Headless-Browser und einem Pool rotierender Residential-Proxys, doch diese Komponenten zu verbinden und betriebsbereit zu halten ist der Großteil der Arbeit. Die Crawling API fasst beides in einem einzigen Aufruf zusammen: Sie senden die URL mit einem JavaScript-Token, die API rendert die Seite hinter einer vertrauenswürdigen IP und gibt Ihnen fertiges HTML zum Parsen zurück.
Crawlbase bietet zwei Token-Typen an. Das normale Token ruft statisches HTML ab; das JavaScript (JS)-Token rendert die Seite zuerst in einem echten Browser. Zillow füllt seine Suchergebnisse und Angebotsfelder clientseitig, daher benötigen Sie hier das JS-Token. Das normale Token gibt dieselbe dünne Hülle zurück wie eine gewöhnliche Anfrage, aus der sich kaum etwas Nützliches parsen lässt.
Voraussetzungen
Bevor Sie mit dem Schreiben von Code beginnen, sind einige Dinge erforderlich. Keines davon nimmt viel Zeit in Anspruch.
Python-Grundkenntnisse. Sie sollten in der Lage sein, ein Python-Skript zu schreiben, auszuführen und Pakete mit pip zu installieren. Falls Sie mit dem Parsing neu sind, ist der BeautifulSoup-Leitfaden eine gute Ergänzung zu diesem Tutorial.
Python 3.8 oder neuer. Prüfen Sie Ihre Version mit python --version. Falls Sie sie nicht haben, installieren Sie sie von python.org oder über eine Distribution wie Anaconda, und stellen Sie sicher, dass Python in Ihrem PATH enthalten ist.
Ein Crawlbase-Konto und JS-Token. Registrieren Sie sich, öffnen Sie Ihr Dashboard und kopieren Sie Ihr JavaScript (JS)-Token von der Account-Dokumentationsseite. Crawlbase enthält bis zu 20.000 kostenlose Anfragen zum Einstieg, was für diese Anleitung mehr als ausreicht. Behandeln Sie das Token wie ein Passwort: Es authentifiziert Ihre Anfragen, halten Sie es daher aus der Versionskontrolle fern.
Das Projekt einrichten
Erstellen Sie eine virtuelle Umgebung, damit die Projektabhängigkeiten isoliert bleiben, und installieren Sie dann die Bibliotheken, die der Scraper benötigt.
python --version python -m venv zillow_env source zillow_env/bin/activate pip install crawlbase beautifulsoup4
Unter Windows aktivieren Sie die Umgebung mit zillow_env\Scripts\activate statt der source-Zeile. Zwei Abhängigkeiten erledigen die Arbeit: crawlbase ist der offizielle Client für die Crawling API, und beautifulsoup4 parst das zurückgegebene HTML, sodass Sie einzelne Felder per CSS-Selektor extrahieren können. Sowohl json als auch csv sind in der Standardbibliothek enthalten, sodass für den Exportschritt nichts weiter installiert werden muss.
Schritt 1: Eine gerenderte Zillow-Seite abrufen
Beginnen Sie damit, eine fertige Seite zu laden. Importieren Sie die CrawlingAPI-Klasse, initialisieren Sie sie mit Ihrem JS-Token und rufen Sie eine Zillow-Such-URL ab. Da Zillow Ergebnisse asynchron lädt, übergeben Sie ajax_wait und page_wait, um auf den dynamischen Inhalt zu warten, bevor die Seite erfasst wird. Die Überprüfung des Crawlbase-cb_status (legacy pc_status) vor dem Parsen sorgt dafür, dass Fehler sofort auffallen statt lautlos zu scheitern.
from crawlbase import CrawlingAPI api = CrawlingAPI({"token": "YOUR_CRAWLBASE_TOKEN"}) OPTIONS = { "user_agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/122.0", "ajax_wait": "true", "page_wait": 5000, } 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: {response['headers']['cb_status']}") return None if __name__ == "__main__": serp_url = "https://www.zillow.com/columbia-heights-washington-dc/sale/" html = crawl(serp_url) print(html[:500] if html else "No HTML returned")
Die beiden Warte-Optionen sind wichtig für ein clientseitig gerendertes Ziel wie Zillow. ajax_wait weist die API an, auf das Laden asynchroner Inhalte zu warten, und page_wait hält für eine feste Anzahl von Millisekunden nach dem Laden inne, damit spät gerenderte Karten erscheinen, bevor die Seite erfasst wird. Fünf Sekunden sind ein vernünftiger Ausgangswert; erhöhen Sie diesen, falls die Ergebnisse dünn zurückkommen. Führen Sie das Skript mit python zillow_scraper.py aus, und Sie sollten echtes Zillow-Suchelement-Markup sehen, nicht die Hülle einer einfachen Anfrage. Das bestätigt, dass das Rendering funktioniert, bevor Sie einen einzigen Selektor schreiben.
Zillow benötigt eine gerenderte Seite hinter einer vertrauenswürdigen IP in einem einzigen Aufruf, und genau das richten die Optionen ajax_wait und page_wait oben ein. Die Crawling API nimmt ein JS-Token, führt die Seite in einem echten Browser aus, rotiert serverseitig durch Residential-IPs und liefert Ihnen fertiges HTML, sodass Sie weder eine eigene Headless-Browser-Flotte noch einen Proxy-Pool betreiben müssen. Probieren Sie es zunächst mit einer öffentlichen Suchseite im kostenlosen Tarif aus.
Schritt 2: Immobilien-Links von der Suchergebnisseite sammeln
Eine Zillow-Suchergebnisseite ist ein Raster von Immobilienkarten, von denen jede auf ein vollständiges Angebot verlinkt. Laden Sie das gerenderte HTML in BeautifulSoup und extrahieren Sie den href aus dem Link jeder Karte. Zillow verschachtelt diese innerhalb seines Ergebnisrasters, daher navigiert der Selektor vom Raster-Container bis zum Link der Immobilienkarte.
from bs4 import BeautifulSoup CARD_SELECTOR = ( 'div[id="grid-search-results"] > ul > li[class^="ListItem-"] ' 'article[data-test="property-card"] a[data-test="property-card-link"]' ) def get_property_urls(html): soup = BeautifulSoup(html, "html.parser") return [a["href"] for a in soup.select(CARD_SELECTOR) if a.get("href")]
Der class^="ListItem-"-Matcher ist ein Präfix-Selektor: Zillow hängt an seine generierten Klassennamen einen Hash an, sodass ListItem- jedes Listenelement unabhängig vom Suffix trifft. Wenn man diesen Selektor auf das gerenderte Such-HTML anwendet, erhält man eine saubere Liste von Immobilien-URLs:
[ "https://www.zillow.com/homedetails/1429-Girard-St-NW-101-Washington-DC-20009/2053968963_zpid/", "https://www.zillow.com/homedetails/1439-Euclid-St-NW-APT-301-Washington-DC-20009/68081615_zpid/", "https://www.zillow.com/homedetails/1362-Newton-St-NW-Washington-DC-20010/472850_zpid/", "https://www.zillow.com/homedetails/1458-Columbia-Rd-NW-APT-300-Washington-DC-20009/82293130_zpid/" ]
Zillows generierte Klassennamen und data-test-Attribute ändern sich ohne Vorankündigung. Betrachten Sie die hier angegebenen Selektoren als Ausgangspunkt, nicht als Vertrag. Wenn eine Liste leer zurückkommt, prüfen Sie die Live-Seite in den Entwicklertools Ihres Browsers und aktualisieren Sie den Selektor. Regelmäßige Selektor-Wartung ist bei jedem Produktionsscraper normal und kein Zeichen dafür, dass etwas kaputt ist.
Schritt 3: Paginierung über Suchergebnisseiten hinweg
Eine Suchergebnisseite ist nur ein Ausschnitt des Ergebnissatzes. Zillow paginiert mit einem {pageNo}_p-Pfadsegment: Sie rufen die erste Seite ab, um die Gesamtanzahl der Seiten zu lesen, und durchlaufen dann jede Seite, um Links zu sammeln. Ein kleines Retry-Wrapper um den Abruf verhindert, dass eine einzelne langsame Seite den Lauf abbricht.
import time def fetch_html(page_url, max_retries=2): for attempt in range(max_retries + 1): html = crawl(page_url) if html: return html if attempt < max_retries: print(f"Retrying ({attempt + 1}/{max_retries})...") time.sleep(1) print(f"Unable to fetch {page_url}") return None def collect_all_urls(base_url, max_pages): first_html = fetch_html(f"{base_url}1_p/") if not first_html: return [] soup = BeautifulSoup(first_html, "html.parser") last = soup.select_one("div.search-pagination > nav > li:nth-last-child(3)") total_pages = int(last.text) if last else 1 pages = min(total_pages, max_pages) all_urls = get_property_urls(first_html) for page in range(2, pages + 1): html = fetch_html(f"{base_url}{page}_p/") if html: all_urls.extend(get_property_urls(html)) time.sleep(2) return all_urls
fetch_html versucht einen fehlgeschlagenen Abruf bis zu zweimal mit einer kurzen Pause und gibt bei Erfolg das HTML zurück; nach dem Aufgeben gibt es None zurück. collect_all_urls liest die höchste Seitenzahl aus der Paginierungs-Navigation (Zillow platziert sie nahe dem Ende der Liste, daher nth-last-child(3)), begrenzt den Crawl auf Ihr max_pages-Limit, damit ein großer Markt nicht außer Kontrolle gerät, und sammelt Links von jeder Seite. Das time.sleep(2) zwischen den Seiten drosselt den Lauf, damit Sie die Website nicht überlasten.
Schritt 4: Jede Immobilienseite parsen
Mit einer vollständigen Liste von Immobilien-URLs rufen Sie jedes Angebot ab und extrahieren die Felder. Zillow gruppiert die Kerninformationen im macro-data-view-Block, daher ordnen die folgenden Selektoren Preis, Schlafzimmer, Badezimmer, Fläche, Adresse und Typ einzelnen Elementen zu. Jedes Feld ist abgesichert, sodass ein fehlendes Feld None zurückgibt, anstatt den Lauf zum Absturz zu bringen.
VIEW = 'div[data-testid="macro-data-view"]' FACTS = ( f'{VIEW} > div[data-renderstrat="inline"]:nth-child(2) ' 'div[data-testid="bed-bath-sqft-facts"]' ) def text_of(soup, selector): el = soup.select_one(selector) return el.get_text(strip=True) if el else None def scrape_property(html, url): soup = BeautifulSoup(html, "html.parser") return { "link": url, "price": text_of(soup, f'{VIEW} span[data-testid="price"] > span'), "address": text_of(soup, f'{VIEW} div[class^="styles__AddressWrapper-"] > h1'), "beds": text_of(soup, f'{FACTS} > div[data-testid="bed-bath-sqft-fact-container"]:first-child > span:first-child'), "baths": text_of(soup, f'{FACTS} > button > div[data-testid="bed-bath-sqft-fact-container"] > span:first-child'), "size": text_of(soup, f'{FACTS} > div[data-testid="bed-bath-sqft-fact-container"]:last-child > span:first-child'), "type": text_of(soup, f'{VIEW} > div[data-renderstrat="inline"]:nth-child(3) div.dBmBNo:first-child > span'), }
Der text_of-Helfer fragt ein Element ab und gibt seinen bereinigten Text zurück oder None, wenn das Element fehlt, sodass ein Angebot, das ein Feld weglässt, die Schleife nicht abbricht. Die Selektoren stammen direkt aus Zillows Angebotslayout: price liest den Haupt-Preis-Span, address das H1 im Adressbereich, und Schlafzimmer, Badezimmer und Fläche befinden sich alle im gemeinsamen bed-bath-sqft-facts-Container, unterschieden durch ihre Position. Badezimmer sitzen in Zillows Markup innerhalb eines button-Elements, weshalb dieser Selektor sich leicht von den anderen beiden unterscheidet.
Schritt 5: Das vollständige Skript zusammenstellen
Verbinden Sie nun die Teile zu einem lauffähigen Skript: URLs über alle Seiten sammeln, jede Immobilie scrapen und die Datensätze als JSON und CSV exportieren.
import csv import json import time from crawlbase import CrawlingAPI from bs4 import BeautifulSoup api = CrawlingAPI({"token": "YOUR_CRAWLBASE_TOKEN"}) OPTIONS = { "user_agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/122.0", "ajax_wait": "true", "page_wait": 5000, } CARD_SELECTOR = ( 'div[id="grid-search-results"] > ul > li[class^="ListItem-"] ' 'article[data-test="property-card"] a[data-test="property-card-link"]' ) VIEW = 'div[data-testid="macro-data-view"]' FACTS = ( f'{VIEW} > div[data-renderstrat="inline"]:nth-child(2) ' 'div[data-testid="bed-bath-sqft-facts"]' ) 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: {response['headers']['cb_status']}") return None def fetch_html(page_url, max_retries=2): for attempt in range(max_retries + 1): html = crawl(page_url) if html: return html if attempt < max_retries: time.sleep(1) return None def text_of(soup, selector): el = soup.select_one(selector) return el.get_text(strip=True) if el else None def get_property_urls(html): soup = BeautifulSoup(html, "html.parser") return [a["href"] for a in soup.select(CARD_SELECTOR) if a.get("href")] def collect_all_urls(base_url, max_pages): first_html = fetch_html(f"{base_url}1_p/") if not first_html: return [] soup = BeautifulSoup(first_html, "html.parser") last = soup.select_one("div.search-pagination > nav > li:nth-last-child(3)") total_pages = int(last.text) if last else 1 pages = min(total_pages, max_pages) all_urls = get_property_urls(first_html) for page in range(2, pages + 1): html = fetch_html(f"{base_url}{page}_p/") if html: all_urls.extend(get_property_urls(html)) time.sleep(2) return all_urls def scrape_property(html, url): soup = BeautifulSoup(html, "html.parser") return { "link": url, "price": text_of(soup, f'{VIEW} span[data-testid="price"] > span'), "address": text_of(soup, f'{VIEW} div[class^="styles__AddressWrapper-"] > h1'), "beds": text_of(soup, f'{FACTS} > div[data-testid="bed-bath-sqft-fact-container"]:first-child > span:first-child'), "baths": text_of(soup, f'{FACTS} > button > div[data-testid="bed-bath-sqft-fact-container"] > span:first-child'), "size": text_of(soup, f'{FACTS} > div[data-testid="bed-bath-sqft-fact-container"]:last-child > span:first-child'), "type": text_of(soup, f'{VIEW} > div[data-renderstrat="inline"]:nth-child(3) div.dBmBNo:first-child > span'), } def save_outputs(records): with open("zillow_properties.json", "w") as f: json.dump(records, f, indent=2) if not records: return with open("zillow_properties.csv", "w", newline="") as f: writer = csv.DictWriter(f, fieldnames=records[0].keys()) writer.writeheader() writer.writerows(records) def main(): serp_url = "https://www.zillow.com/columbia-heights-washington-dc/sale/" urls = collect_all_urls(serp_url, max_pages=2) records = [] for url in urls: html = fetch_html(url) if html: records.append(scrape_property(html, url)) time.sleep(2) save_outputs(records) print(f"Saved {len(records)} properties") if __name__ == "__main__": main()
Das Skript sammelt Immobilien-Links über bis zu zwei Suchergebnisseiten, ruft jedes Angebot mit dem Retry-Wrapper ab, parst es zu einem Datensatz und verlangsamt die Schleife mit einem Zwei-Sekunden-Sleep. save_outputs schreibt sowohl eine JSON-Datei als auch eine CSV, wobei die Schlüssel des ersten Datensatzes als Header verwendet werden, sodass die Daten in der Form vorliegen, die Ihr nachgelagertes Tool benötigt. Passen Sie max_pages und die Such-URL an Ihren Zielstandort an.
Wie die Ausgabe aussieht
Führen Sie das vollständige Skript mit python zillow_scraper.py aus, und Sie erhalten pro Immobilie einen sauber strukturierten Datensatz, der für Analysen, eine Datenbank oder eine Tabelle bereit ist.
[ { "link": "https://www.zillow.com/homedetails/1008-Fairmont-St-NW-Washington-DC-20001/473889_zpid/", "price": "$850,000", "address": "1008 Fairmont St NW, Washington, DC 20001", "beds": "3", "baths": "4", "size": "1,801", "type": "Townhouse" }, { "link": "https://www.zillow.com/homedetails/1438-Meridian-Pl-NW-APT-106-Washington-DC-20010/467942_zpid/", "price": "$385,000", "address": "1438 Meridian Pl NW APT 106, Washington, DC 20010", "beds": "2", "baths": "2", "size": "634", "type": "Condominium" } ]
Die zugehörige CSV enthält dieselben Spalten, eine Zeile pro Immobilie, und lässt sich direkt in pandas oder eine beliebige Tabellenkalkulation laden, um nach Preisklasse, Schlafzimmerzahl oder Immobilientyp zu filtern.
Bei größerem Umfang nicht geblockt werden
Selbst wenn das Rendering abgedeckt ist, beobachtet Zillow scraper-ähnlichen Traffic. Einige Gewohnheiten halten längere Läufe gesund, und sie gelten für jedes schwierige kommerzielle Ziel.
- Anfragen drosseln. Anfragen in einer engen Schleife zu hämmern ist der schnellste Weg, gedrosselt oder herausgefordert zu werden. Die Zwei-Sekunden-Pausen oben sind die Untergrenze, nicht die Obergrenze; vergrößern Sie sie für größere Jobs und variieren Sie Ihre Ziele, statt einen Pfad mit voller Geschwindigkeit zu crawlen.
- Rotation nutzen. Ein Pool von Residential-IPs verteilt Anfragen auf viele echte Nutzeradressen, sodass keine einzelne ein Ratenlimit auslöst. Die Crawling API übernimmt das für Sie; wenn Sie Ihren eigenen Stack betreiben, ist das der Teil, den Sie richtig hinkriegen müssen.
-
Statuscodes beachten. Ein Lauf, der beginnt, 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ückrudern, nicht als Rauschen, das Sie ignorieren.
Für größere Crawls stellt der asynchrone Crawler Anfragen in eine Warteschlange und liefert Ergebnisse an einen Webhook, was für das Abarbeiten vieler Suchergebnisseiten ohne offene Verbindungen geeignet ist. Das allgemeine Playbook finden Sie unter wie man Websites scrapt, ohne geblockt zu werden. Und wenn Sie Marktdaten über mehrere Portale hinweg vergleichen möchten, lässt sich derselbe Ansatz auf Redfin scrapen, Realtor.com und Trulia übertragen.
Ist es legal, Zillow zu scrapen?
Ob das Scrapen von Zillow erlaubt ist, hängt von Zillows Nutzungsbedingungen, Ihrer Rechtsordnung und dem Verwendungszweck der Daten ab. Zillows Bedingungen schränken den automatisierten Zugriff und die Datenerfassung ein, sodass Scraping gegen diese Bedingungen verstoßen kann, unabhängig davon, wie sorgfältig Ihr Tooling ist. Zillow ist zudem in der Vergangenheit gegen Parteien vorgegangen, die seine Angebote in großem Maßstab abgeerntet haben, weshalb dies kein hypothetisches Risiko ist. Keiner der hier aufgeführten Codes ändert daran etwas; er macht lediglich den technischen Teil funktionsfähig. Lesen Sie Zillows Nutzungsbedingungen und seine robots.txt, und betrachten Sie beides als Grenze für das, was Sie erfassen.
Einige Grundsätze sollten eingehalten werden. Erfassen Sie nur öffentliche Angebotsdaten: Angebotspreis, Schlafzimmer, Badezimmer, Wohnfläche, Immobilientyp und Straßenadresse, die jeder ohne Konto einsehen kann. Vermeiden Sie alles, was mit identifizierbaren Personen verbunden ist, einschließlich der Kontaktdaten von Maklern, Eigentümern oder anderen auf einer Seite genannten Personen, da es sich um personenbezogene Daten handelt, die außerhalb des Bereichs öffentlicher Angebote liegen. Respektieren Sie Zillows angegebene Ratenerwartungen und halten Sie Ihr Anfragevolumen niedrig genug, um dessen Server nicht zu belasten. Beachten Sie auch, dass ein Großteil der zugrundeliegenden Immobilien- und Verkaufsdaten auf Immobilienportalen aus MLS-Feeds stammt, die in der Regel lizenziert sind und eigene Weiterverbreitungsbeschränkungen tragen, sodass das Erfassen dieser Daten Ihnen nicht das Recht gibt, sie zu republizieren.
Diese Anleitung beschränkt sich bewusst auf öffentliche Angebotsseiten, da dies die Grenze ist, die die Arbeit vertretbar macht. Sie deckt nichts hinter einem Login, gespeicherte Such- oder Kontodaten, personenbezogene oder Kontaktdaten von Einzelpersonen oder Versuche ab, Authentifizierung zu umgehen. Nur öffentliche Angebotsdaten. Wenn Ihr Projekt mehr benötigt, ist der richtige Weg eine Lizenzvereinbarung: Zillow bietet offizielle APIs und Partnerprogramme für erlaubte Anwendungsfälle an, und lizenzierte MLS- oder Immobiliendatenanbieter decken den Rest ab. Das ist der korrekte Weg für kommerzielle oder Massennutzung, nicht ein ausgefeilterer Scraper.
Wichtigste Erkenntnisse
- Zillow wird clientseitig gerendert. Eine einfache Anfrage gibt eine dünne Hülle mit nur einem Teil der Ergebnisse zurück, daher müssen Sie die Seite rendern, bevor Sie sie parsen.
-
Sie benötigen Rendering und eine vertrauenswürdige IP gemeinsam. Die Crawling API mit einem JS-Token erledigt beides in einem Aufruf;
ajax_waitundpage_waitsteuern, wie lange auf Inhalte gewartet wird. -
Arbeiten Sie in zwei Schichten. Sammeln Sie Immobilien-Links von jeder Suchergebnisseite mit dem
property-card-link-Selektor, rufen Sie dann jedes Angebot ab und parsen Sie es auf Preis, Schlafzimmer, Badezimmer, Fläche, Adresse und Typ. -
Paginieren und exportieren. Durchlaufen Sie Zillows
{pageNo}_p-Seiten bis zu einem Maximum, drosseln Sie den Lauf mit kurzen Pausen und schreiben Sie die Datensätze in JSON und CSV. - Bleiben Sie bei öffentlichen Daten. Respektieren Sie Zillows ToS und robots.txt, beachten Sie, dass es bei Scraping rechtliche Schritte unternommen hat und MLS-Daten oft lizenziert sind, und berühren Sie niemals Logins, Konten oder Personendaten.
Häufig gestellte Fragen
Warum gibt eine einfache Anfrage nur einen Teil der Zillow-Ergebnisse zurück?
Weil Zillow seine Suchergebnisse und Angebotsdetails clientseitig mit JavaScript und Ajax lädt. Das initiale HTML ist eine Hülle, die sich erst nach dem Ausführen der Seitenskripte im Browser füllt, sodass eine rohe HTTP-Anfrage den Status 200 zurückgibt, bei dem die meisten Karten und Angebotsfelder fehlen. Um den vollständigen Satz zu erhalten, müssen Sie die Seite zuerst rendern, was das JS-Token der Crawling API für Sie übernimmt.
Benötige ich das normale Token oder das JS-Token für Zillow?
Das JS-Token. Das normale Token ruft statisches HTML ab, das bei Zillow dieselbe dünne Hülle wie ein gewöhnlicher Abruf ist. Das JS-Token rendert die Seite in einem echten Browser, bevor es das HTML zurückgibt, sodass die Suchkarten und Angebotsfelder vorhanden sind, wenn BeautifulSoup sie parst.
Welche Daten kann ich von einem Zillow-Angebot scrapen?
Öffentliche Angebotsfelder: Angebotspreis, Anzahl der Schlaf- und Badezimmer, Wohnfläche, Immobilientyp, Straßenadresse und Angebots-Link. Bleiben Sie bei Daten, die für jeden Besucher ohne Konto sichtbar sind, und vermeiden Sie die persönlichen Kontaktdaten von Maklern oder Eigentümern, die außerhalb des in dieser Anleitung behandelten Bereichs öffentlicher Angebote liegen.
Meine Selektoren geben None zurück. Was hat sich geändert?
Fast sicher Zillows Markup. Die generierten Klassennamen und data-test-Attribute (das ListItem--Präfix, der macro-data-view-Block, der bed-bath-sqft-facts-Container) ändern sich ohne Vorankündigung, sodass Selektoren, die letzten Monat funktioniert haben, jetzt kaputt sein können. Prüfen Sie eine Live-Seite in den Entwicklertools Ihres Browsers und aktualisieren Sie die Selektoren. Regelmäßige Selektor-Wartung ist bei jedem Produktionsscraper normal.
Wie gehe ich mit der Paginierung über alle Angebote eines Standorts hinweg um?
Zillow hängt ein {pageNo}_p-Segment an den Suchpfad an. Rufen Sie die erste Seite ab, um die Gesamtseitenzahl aus der Paginierungs-Navigation zu lesen, begrenzen Sie den Crawl auf ein max_pages-Maximum und durchlaufen Sie dann jede Seite, um Immobilien-Links zu sammeln. Die obige collect_all_urls-Funktion zeigt die vollständige Schleife mit einem kurzen Sleep zwischen den Seiten.
Kann ich gescrapte Zillow-Daten kommerziell nutzen?
Betrachten Sie das als rechtliche, nicht als technische Frage. Ein Großteil von Zillows Immobiliendaten stammt aus lizenzierten MLS-Feeds mit eigenen Weiterverbreitungsbedingungen, und Zillows eigene Nutzungsbedingungen schränken die Wiederverwendung ein, sodass kommerzielle oder Massennutzung generell einer Genehmigung bedarf. Prüfen Sie die Bedingungen, erwägen Sie Zillows offizielles API oder Partnerprogramm und holen Sie rechtlichen Rat ein, bevor Sie ein Produkt auf Basis dieser Daten erstellen.
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.
