Zillow 부동산 데이터 스크래핑 방법: Search JSON, Zestimate, PerimeterX

Zillow는 페이지에 숨겨진 JSON으로 목록 데이터를 전송합니다 — 단 하나의 div도 파싱할 필요가 없습니다. 여기서는 지도 경계 검색 API를 구동하고, 페이지네이션 한계를 극복하며, PerimeterX를 피하는 방법을 설명합니다.

Zillow는 1억 1천만 개 이상의 부동산을 보유하고 있으며 매달 약 2억 4천 5백만 명의 방문자를 끌어들입니다. 이는 미국 부동산 데이터의 기본 소스가 됩니다 — 가격, 주소, 침실, 욕실, zestimates 및 임대 추정치. 또한 PerimeterX 뒤에 위치하고 클라이언트 측에서 렌더링되므로 순진한 스크래퍼는 403 오류와 빈 페이지를 받습니다. 좋은 소식은 Zillow가 데이터를 JSON으로 제공한다는 것입니다. 어디를 찾아야 하는지만 알면 됩니다. 검색은 직접 호출할 수 있는 지도 좌표로 구동됩니다. 여기 전체 접근 방법이 있습니다.

HTML을 파싱하지 마세요 — Zillow는 JSON을 전송합니다

Zillow 부동산 페이지에서 CSS 선택자와 싸울 필요가 없습니다. 전체 부동산 기록은 스크립트 태그에 숨겨진 웹 데이터로 포함되어 있습니다. 거의 모든 페이지를 다루는 두 위치가 있습니다: <script id="__NEXT_DATA__">는 Next.js 캐시를 보유하고 있으며, 일부 페이지는 대신 <script id="hdpApolloPreloadedData"> (Apollo GraphQL 캐시)를 사용합니다. 둘 중 하나를 가져와 파싱하면 zpid, price, livingArea, bedrooms, bathrooms, latLong, homeStatus, rentZestimate 등을 포함한 깨끗한 객체를 얻을 수 있습니다.

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-statesearchQueryState 객체를 포함한 요청이 발생합니다. 이 객체의 핵심 필드는 mapBounds입니다: 지도의 사각형을 그리는 네 개의 숫자 (북, 남, 동, 서)입니다. 경계 상자를 제공하면 그 안의 모든 목록을 구조화된 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개 결과로 제한됩니다. 밀집된 시장은 수천 개 이상의 항목을 숨깁니다. 해결책은 재귀적 사분면 분할입니다: 경계 상자가 최대 페이지를 반환하면 네 개의 작은 상자로 나누고 각각을 검색합니다; 모든 상자가 한계보다 적은 결과를 반환할 때까지 계속 나누세요. 이 줌인 전략은 평면 검색으로는 절대 표시되지 않을 수 있는 한 도시에서 수십만 개의 목록을 드러낼 수 있습니다 — 그리고 밀도가 높은 곳에서만 세분화하므로 빈 시골에 요청을 낭비하지 않습니다.

그 철저함에는 비용이 있습니다: 각 세분화는 요청을 곱하므로 밀집된 도시는 하나의 검색을 수십 개로 바꿀 수 있습니다. 이를 예산에 포함하세요 — 재귀 깊이를 제한하고 결과 수가 이를 정당화할 때만 줌인하세요. 알고리즘은 상자가 여전히 최대 페이지에 도달할 때만 세분화하여 이를 수행합니다. 도시 전체를 야간에 새로 고칠 때 대부분의 요청은 밀집된 도심 상자에 도달하고 교외는 한 번에 해결되므로 재고가 있는 곳에만 깊이에 비용을 지출하고 다른 곳에는 지출하지 않습니다.

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억 4천 5백만 명의 월 방문자, 820개 결과 검색 한계, 사분면 분할을 통한 60만 개 결과를 보여주는 통계 패널
규모는 엄청나지만 하나의 검색은 820개로 제한됩니다 — 그 한계를 극복하는 것이 진정한 작업입니다.

대신 카운티 기록을 사용할 때

Zillow는 목록, 사진 및 추정치에 탁월하지만, 2차 소스입니다. 권위 있는 소유권, 판매 이력 및 평가된 가치를 위해 카운티 평가사 및 기록 사무소는 종종 자체 포털이나 대량 데이터 내보내기를 통해 공공 기록을 게시합니다. 사용 사례가 실시간 목록이 아닌 소유권, 비교 또는 세금 데이터라면 카운티 수준의 기록이 더 깨끗하고 수집하기 더 허용적이며 PerimeterX가 전혀 없습니다. 시장 신호를 위해 Zillow를 사용하고, 지상 진실을 위해 카운티 기록을 사용하세요.

Zillow 스크래핑은 합법인가요?

이것은 일반 정보이며 법률 자문이 아닙니다. 공개적으로 이용 가능한 목록 데이터 — 가격, 주소, 부동산 속성 — 는 일반적으로 존중하는 속도로 수집하는 것이 공정하다고 간주됩니다. 그러나 Zillow 결과에는 개인 데이터가 포함될 수 있습니다: 에이전트 이름, 전화번호 및 중개인 세부 정보. 이는 GDPR 및 유사한 법률에 따라 보호되므로 합법적인 근거 없이 이를 수집하고 저장하는 것을 피하세요. 부동산 사실을 수집하고 사람들을 수집하지 마세요.

자주 묻는 질문

Zillow 데이터를 스크래핑할 수 있나요?

예 — 공개적으로 보이는 목록 데이터는 수집할 수 있습니다. Zillow는 각 부동산 기록을 스크립트 태그 (__NEXT_DATA__ 또는 Apollo 캐시)로 JSON으로 포함하며, 검색은 직접 호출할 수 있는 지도 경계 API에서 실행됩니다. 장애물은 접근이 아니라 PerimeterX이므로, 403을 피하기 위해 주거용 IP와 일관된 헤더가 필요합니다.

Zillow에 API가 있나요?

Zillow는 약 20개의 공식 API를 제공하며, 여기에는 Property Details 및 Neighborhood Data가 포함됩니다. 그러나 이들은 키가 필요하고 사용 제한이 있으며 모든 필드나 대량 사용 사례를 다루지 않습니다. 많은 팀이 숨겨진 JSON이 이미 필요한 가격, 침실, 욕실, 면적, zestimate 및 임대 추정치를 포함하고 있기 때문에 대신 공개 페이지를 스크래핑합니다.

왜 내 Zillow 스크래퍼가 403을 받나요?

Zillow에서의 403은 거의 항상 PerimeterX 때문이지, 속도 제한 때문이 아닙니다. 데이터 센터 IP, 누락되거나 일관되지 않은 브라우저 헤더, 로봇 같은 요청 타이밍이 모두 이를 유발합니다. 미국 주거용 프록시로 전환하고, 현실적인 헤더를 보내고, 인간 같은 간격을 추가하며, 차단된 IP를 계속 두드리는 대신 403이 발생할 때마다 새로운 IP로 회전하는 지수적 백오프를 추가하세요.

한 지역에서 820개 이상의 Zillow 목록을 어떻게 스크래핑하나요?

단일 검색은 약 820개 결과 (41개의 20페이지)로 제한됩니다. 더 깊이 들어가려면 지도 경계 상자를 네 개의 사분면으로 나누고 각각을 검색하세요; 여전히 한계에 도달하는 사분면을 재귀적으로 세분화하세요. 이 줌인 접근법은 밀집된 지역만 세분화하므로, 희박한 지역에 요청을 낭비하지 않고 대도시의 전체 인벤토리를 포착합니다.

HTML을 건너뛰고 포함된 JSON을 읽고, 지도 경계로 검색을 구동하고, 사분면을 나누어 한계를 극복하며, 모든 것을 깨끗한 주거용 IP를 통해 라우팅하여 PerimeterX를 앞서가세요. 그렇게 하면 Zillow는 403의 벽 대신 구조화된 부동산 피드가 됩니다.

PerimeterX와 싸우지 않고 Zillow JSON 얻기