Zillow不動産データをスクレイピングする方法: Search JSON, Zestimate, PerimeterX

Zillowはリスティングデータをページ内に隠されたJSONとして提供しており、divを解析する必要はありません。ここでは、マップ境界検索APIを駆動し、ページネーションの制限を突破し、PerimeterXを乗り越える方法を紹介します。

Zillowは1億1,000万以上の物件を保有し、毎月約2億4,500万の訪問者を引き付けており、米国の不動産データのデフォルトソースとなっています。価格、住所、ベッド、バス、zestimates、賃料推定値を提供しています。また、PerimeterXの背後にあり、クライアントサイドでレンダリングされるため、単純なスクレイパーは403エラーと空白ページを受け取ります。良いニュースは、どこを探せばよいかを知っていれば、ZillowはデータをJSONとして提供しており、検索は直接呼び出せるマップ座標によって駆動されています。ここではその全体的なアプローチを紹介します。

HTMLを解析しないでください — ZillowはJSONを提供します

Zillowの物件ページでCSSセレクタと戦う必要はありません。完全な物件レコードはスクリプトタグ内の隠されたウェブデータとして埋め込まれています。ほとんどのページをカバーする2つの場所があります: <script id="__NEXT_DATA__">にはNext.jsのキャッシュが含まれ、一部のページでは代わりに<script id="hdpApolloPreloadedData">(Apollo GraphQLキャッシュ)を使用しています。どちらかを取得し、解析することで、zpidpricelivingAreabedroomsbathroomslatLonghomeStatusrentZestimateなどを含むクリーンなオブジェクトを得ることができます。

import json, re, requests

proxy = "http://USER:PASS@gate.quantumproxies.io:8000"
proxies = {"http": proxy, "https": proxy}
HEADERS = {"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
           "AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0 Safari/537.36",
           "accept-language": "en-US,en;q=0.9"}

def property_json(url):
    r = requests.get(url, headers=HEADERS, proxies=proxies, timeout=20)
    r.raise_for_status()                       # 403 => blocked, rotate IP
    m = re.search(r'id="__NEXT_DATA__"[^>]*>(.*?)</script>', r.text, re.S)
    if m:
        data = json.loads(m.group(1))
        cache = data["props"]["pageProps"]["componentProps"]["gdpClientCache"]
        return next(iter(json.loads(cache).values()))["property"]
    # fallback: Apollo cache
    m = re.search(r'id="hdpApolloPreloadedData"[^>]*>(.*?)</script>', r.text, re.S)
    cache = json.loads(json.loads(m.group(1))["apiCache"])
    return next(v["property"] for k, v in cache.items() if "ForSale" in k)

埋め込まれたレコードは、表示されているページよりもはるかに豊富です。価格と住所に加えて、zestimaterentZestimate — Zillow独自の販売および賃貸評価 —、homeStatus(販売中、売却済み、賃貸中)、daysOnZillow、土地と居住面積、正確な緯度と経度が得られます。これは、コンプモデル、リードスコアリングパイプライン、または市場ダッシュボードが必要とするすべてを提供し、次のリデザインで壊れる脆弱なCSSセレクタの代わりに、物件ごとに1つのクリーンなJSONオブジェクトとして提供されます。

検索APIはマップ座標で動作します

Zillowの検索はキーワード優先ではなく、マップ優先です。内部では、検索を送信すると、zillow.com/async-create-search-page-statesearchQueryStateオブジェクトを含むリクエストが送信され、その中心フィールドはmapBoundsです: マップ上に長方形を描く4つの数字(北、南、東、西)。境界ボックスを与えると、その中のすべてのリスティングを構造化されたJSONとして返します。

def search_area(bounds, page=1):
    url = "https://www.zillow.com/async-create-search-page-state"
    body = {
        "searchQueryState": {
            "pagination": {"currentPage": page},
            "mapBounds": bounds,   # {'north':..,'south':..,'east':..,'west':..}
            "filterState": {"sortSelection": {"value": "days"}},
        },
        "wants": {"cat1": ["listResults", "mapResults"], "cat2": ["total"]},
        "requestId": 2,
    }
    r = requests.put(url, json=body, headers=HEADERS, proxies=proxies, timeout=20)
    return r.json()["cat1"]["searchResults"]["listResults"]

# New Haven, CT bounding box
listings = search_area({"north": 41.366, "south": 41.230,
                        "east": -72.827, "west": -73.030})
Zillowを大規模にスクレイピングするフローダイアグラム: マップ境界ボックスを定義し、JSONのために検索APIを呼び出し、住宅プロキシを通じて取得し、結果制限を突破するために四分割します。
Zillowをマップ座標で駆動する: 境界ボックスがクエリであり、住宅IPがPerimeterXを静かに保ちます。

820件の結果上限を突破する

Zillowは約41件のアイテムを20ページにページネーションするだけなので、単一の検索は地域にどれだけの物件があっても約820件の結果で制限されます。密集した市場にはさらに数千件が隠れています。回避策は再帰的な四分割です: 境界ボックスが最大ページを返す場合、それを4つの小さなボックスに分割してそれぞれを検索します; 制限を超えるボックスがなくなるまで分割を続けます。このズームイン戦略により、平坦な検索では決して表示されない1つの都市圏から数十万件のリスティングを浮かび上がらせることができます — そしてそれは密度がそれを必要とする場所でのみ分割されるので、空の田舎でリクエストを浪費しません。

その徹底さにはコストがあります: 各分割がリクエストを倍増させるため、密集した都市圏では1つの検索が数十に変わる可能性があります。それに予算を割り当ててください — 再帰の深さを制限し、結果数がそれを正当化する場合にのみズームインします。アルゴリズムは、ボックスがまだ最大ページに達する場合にのみ分割することでそれを行います。都市全体を毎晩更新する場合、ほとんどのリクエストは密集したダウンタウンのボックスに着地し、郊外は1回のパスで解決されるので、在庫がある場所にのみ深さに費やし、他の場所には費やしません。

PerimeterXがあるため、単純なスクレイパーは失敗します

Zillowのブロックは主にレートベースではありません。PerimeterX(現在はHUMAN)がリクエストをプロファイリングしています。データセンターIP、欠落したブラウザヘッダー、不自然なタイミングはすべて403エラーまたはチャレンジを引き起こします — それがassert status == 200が本当にチェックしていることです。修正は一貫した信頼できるリクエストです: 米国住宅プロキシを使用して出口IPをホームバイヤーのように見せ、現実的なブラウザヘッダーを追加し、403が発生した場合は再試行せずに新しいIPに切り替える指数バックオフを使用します。PerimeterXが自動化を検出する方法の詳細な説明では、同じ姿勢が同じベンダーを使用するWalmartにも適用されます。

PerimeterXが厳しくなった場合やブラウザフリートを維持したくない場合、Scraper APIがページをレンダリングし、住宅IPを回転させ、チャレンジをクリアしてくれるので、運用上の負担なく同じ隠されたJSONを返します。

住宅IPでPerimeterXの背後からZillowをスクレイピングする

Zillowの1億1,000万の物件、2億4,500万の月間訪問者、820件の検索上限、四分割による60万件の結果を示す統計パネル
規模は膨大ですが、1つの検索は820件で制限されます — その上限を回避することが本当の作業です。

代わりに郡の記録を使用するタイミング

Zillowはリスティング、写真、推定値に優れていますが、二次的なソースです。所有権、販売履歴、評価額の権威ある情報については、郡の評価者および記録官事務所が公開記録を提供しています — 多くの場合、独自のポータルや大量データエクスポートを通じて。タイトル、コンプ、税データが目的で、ライブリスティングではない場合、郡レベルの記録はよりクリーンで、収集が許可されており、完全にPerimeterXから解放されています。市場シグナルにはZillowを使用し、地上の真実には郡の記録を使用してください。

Zillowをスクレイピングすることは合法ですか?

これは一般的な情報であり、法的アドバイスではありません。公開されているリスティングデータ — 価格、住所、物件属性 — は、敬意を持って収集することが広く認められています。ただし、Zillowの結果には個人データが含まれることがあります: エージェント名、電話番号、ブローカーの詳細。これらはGDPRや類似の法律で保護されているため、合法的な根拠なしに収集および保存しないでください。物件の事実を収集し、人々の情報は収集しないでください。

よくある質問

Zillowデータをスクレイピングできますか?

はい — 公開されているリスティングデータは収集可能です。Zillowは各物件レコードをスクリプトタグ(__NEXT_DATA__またはApolloキャッシュ)としてJSONで埋め込み、検索は直接呼び出せるマップ境界APIで動作します。障害はアクセスではなくPerimeterXであるため、403を避けるために住宅IPと一貫したヘッダーが必要です。

ZillowにはAPIがありますか?

Zillowは、物件詳細や地域データを含む約20の公式APIを提供していますが、キーが必要で、使用制限があり、すべてのフィールドや大量の使用ケースをカバーしていません。多くのチームが公開ページをスクレイピングするのは、隠されたJSONがすでに必要な価格、ベッド、バス、面積、zestimate、賃料推定値を含んでいるためです。

なぜ私のZillowスクレイパーが403を受け取るのですか?

Zillowでの403はほとんどがPerimeterXであり、レート制限ではありません。データセンターIP、欠落または一貫性のないブラウザヘッダー、ロボット的なリクエストタイミングがそれを引き起こします。米国住宅プロキシに切り替え、現実的なヘッダーを送信し、人間のような間隔を追加し、403ごとに新しいIPに切り替える指数バックオフを使用して、ブロックされたものを叩くのではなく、回避してください。

1つのエリアから820件以上のZillowリスティングをスクレイピングするにはどうすればよいですか?

単一の検索は約820件の結果(41件の20ページ)で制限されます。より深く掘り下げるには、マップ境界ボックスを4つの四分割に分割し、それぞれを検索します; 制限を超える四分割がある場合は再帰的に分割します。このズームインアプローチは密集した地域のみを分割するため、まばらな地域でリクエストを浪費することなく、都市圏の完全な在庫をキャプチャします。

HTMLをスキップして埋め込まれたJSONを読み取り、マップ境界で検索を駆動し、四分割して制限を突破し、すべてをクリーンな住宅IP経由でルーティングしてPerimeterXに先んじましょう。それを行えば、Zillowは403の壁ではなく、構造化された不動産フィードになります。

PerimeterXと戦わずにZillow JSONを取得する