如何抓取Zillow房地产数据:搜索JSON、Zestimate、PerimeterX

Zillow将其列表数据作为JSON隐藏在页面中——你不需要解析任何一个div。以下是如何驱动地图边界搜索API、突破分页限制并应对PerimeterX的方法。

Zillow拥有超过1.1亿个房产,每月吸引大约2.45亿访客,这使其成为美国房地产数据的默认来源——价格、地址、卧室、浴室、zestimates和租金估算。它也位于PerimeterX之后并在客户端渲染,因此简单的抓取器会得到403和一个空白页面。好消息是:如果你知道在哪里找,Zillow会以JSON的形式提供其数据,并且其搜索是由你可以直接调用的地图坐标驱动的。以下是整个方法。

不要解析HTML——Zillow提供JSON

在Zillow的房产页面上不需要与CSS选择器斗争。完整的房产记录作为隐藏的网页数据嵌入在script标签中。两个位置几乎涵盖了每个页面:<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、地块和居住面积,以及精确的纬度和经度。这些信息足以满足比较模型、潜在客户评分管道或市场仪表板的需求,每个房产以一个干净的JSON对象提供,而不是一打容易在下次重新设计时失效的CSS选择器。

搜索API运行在地图坐标上

Zillow的搜索不是以关键词为主,而是以地图为主。在后台,提交搜索会向zillow.com/async-create-search-page-state发送一个请求,其中包含一个核心字段为mapBoundssearchQueryState对象:四个数字(北、南、东、西)在地图上画出一个矩形。给它一个边界框,它会返回其中的每个列表,作为结构化的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的流程图:定义一个地图边界框,调用搜索API获取JSON,通过住宅代理抓取,并分割象限以突破结果限制
通过地图坐标驱动Zillow:边界框是查询,住宅IP保持PerimeterX安静。

突破820结果上限

Zillow仅分页20页,每页约41项,因此单个搜索最多约820个结果,无论该地区有多少房屋。密集市场隐藏了更多的房源。解决方法是递归象限分割:如果一个边界框返回最大页数,将其分割为四个更小的框并分别搜索;继续细分,直到每个框返回的结果少于上限。这种放大策略可以从一个大都市中挖掘出成千上万的列表,而平面搜索永远不会显示——而且它只在密度需要时细分,因此你不会在空旷的乡村浪费请求。

这种彻底性的代价是:每次细分都会增加请求,因此一个密集的大都市可以将一次搜索变成几十次。为此做好预算——限制递归深度,并仅在结果数量证明其合理时放大,算法通过仅在一个框仍达到最大页数时细分来为你做到这一点。对于一个城市的每晚更新,大多数请求会落在少数密集的市中心框上,而郊区则在一次通过中解决,因此你只在库存所在的地方花费深度,而不是其他地方。

PerimeterX是简单抓取器失败的原因

Zillow的阻止主要不是基于速率;而是PerimeterX(现为HUMAN)对请求进行分析。数据中心IP、缺少浏览器头和不人性化的时间安排都会触发403或挑战——这就是assert status == 200真正检查的内容。解决方法是一个一致、可信的请求:美国住宅代理使出口IP看起来像购房者,现实的浏览器头,以及在任何403时旋转到一个新的IP而不是重试已被封锁的IP的指数退避。我们对PerimeterX如何检测自动化的分解深入探讨了这些信号;同样的姿态适用于使用相同供应商的Walmart

当PerimeterX收紧或你不想维护一个浏览器群时,Scraper API为你渲染页面,旋转住宅IP并清除挑战,返回相同的隐藏JSON而无需操作开销。

通过住宅IP在PerimeterX后抓取Zillow

统计面板显示Zillow的1.1亿个房产、2.45亿月访客、820结果搜索上限,以及通过象限分割获得的60万结果
规模巨大,但一次搜索限制在820——围绕这一上限进行工程才是真正的工作。

何时使用县记录

Zillow在列表、照片和估算方面表现出色,但它是一个次要来源。对于权威的所有权、销售历史和评估价值,县评估员和记录员办公室发布公共记录——通常通过他们自己的门户或批量数据导出。如果你的用例是产权、比较或税务数据而不是实时列表,县级记录更清晰、收集更宽松,并且完全没有PerimeterX的限制。使用Zillow获取市场信号,使用县记录获取真实数据。

抓取Zillow是否合法?

这是一般信息,而非法律建议。公开可用的列表数据——价格、地址、房产属性——通常被认为可以以尊重的速度收集。然而,Zillow的结果可能包含个人数据:代理姓名、电话号码和经纪人详细信息。这些信息受GDPR和类似法律保护,因此在没有合法依据的情况下,避免收集和存储这些信息。收集房产事实,而不是个人信息。

常见问题解答

我可以抓取Zillow数据吗?

可以——公开可见的列表数据可以被收集。Zillow将每个房产记录嵌入在一个script标签(__NEXT_DATA__或Apollo缓存)中,并且其搜索运行在一个你可以直接调用的地图边界API上。障碍不是访问而是PerimeterX,因此你需要住宅IP和一致的头以避免403。

Zillow有API吗?

Zillow提供大约20个官方API,包括房产详情和社区数据,但它们需要密钥、有使用限制,并且不涵盖每个字段或批量使用案例。许多团队选择抓取公共页面,正是因为隐藏的JSON已经包含了他们需要的价格、卧室、浴室、面积、zestimate和租金估算。

为什么我的Zillow抓取器会得到403?

Zillow上的403几乎总是PerimeterX,而不是速率限制。数据中心IP、缺失或不一致的浏览器头以及机器人的请求时间都会触发它。切换到美国住宅代理,发送真实的头,添加类似人类的间隔,并在每次403时使用指数退避旋转到一个新IP,而不是反复尝试被封锁的IP。

如何从一个地区抓取超过820个Zillow列表?

单个搜索限制在约820个结果(20页41项)。要深入挖掘,将地图边界框分为四个象限并分别搜索;递归细分任何仍达到上限的象限。这种放大方法仅细分密集区域,因此它捕获一个大都市的完整库存,而不会在稀疏地区浪费请求。

跳过HTML并读取嵌入的JSON,通过地图边界驱动搜索,分割象限以突破上限,并通过干净的住宅IP路由所有内容,以保持领先于PerimeterX。做到这一点,Zillow就成为一个结构化的房地产信息源,而不是403的墙。

在不与PerimeterX斗争的情况下获取Zillow JSON