Как собирать данные о недвижимости с Zillow: JSON поиска, Zestimate, PerimeterX
Zillow передает данные о своих объявлениях в виде JSON, скрытого на странице — вам никогда не придется разбирать ни один div. Вот как использовать API поиска по границам карты, обойти ограничение на количество страниц и справиться с PerimeterX.
Zillow содержит более 110 миллионов объектов недвижимости и привлекает около 245 миллионов посетителей в месяц, что делает его основным источником данных о недвижимости в США — цены, адреса, количество спален и ванных комнат, оценки zestimate и арендные ставки. Он также защищен PerimeterX и рендерится на стороне клиента, поэтому наивные скрейперы получают 403 и пустую страницу. Хорошая новость: Zillow предоставляет данные в виде JSON, если вы знаете, где искать, и его поиск управляется координатами карты, которые можно вызвать напрямую. Вот весь подход.
Не разбирайте HTML — Zillow передает JSON
Нет необходимости бороться с CSS-селекторами на странице объекта Zillow. Полная запись объекта встроена как скрытые веб-данные в теге скрипта. Два местоположения охватывают почти каждую страницу: <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)
Встроенная запись гораздо богаче, чем видимая страница. Наряду с ценой и адресом вы получаете zestimate и rentZestimate — собственные оценки продажи и аренды Zillow — плюс homeStatus (на продажу, продано, в аренду), daysOnZillow, площадь участка и жилой площади, а также точные широту и долготу. Это все, что нужно модели сравнений, системе оценки лидов или рыночной панели, предоставляется в виде одного чистого JSON-объекта на объект вместо десятка хрупких CSS-селекторов, которые ломаются при следующем редизайне.
API поиска работает на координатах карты
Поиск на Zillow не ориентирован на ключевые слова — он ориентирован на карту. Внутри, отправка поиска вызывает запрос на zillow.com/async-create-search-page-state с объектом searchQueryState, основное поле которого — 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})

Обойдите потолок в 820 результатов
Zillow разбивает на страницы только 20 страниц по примерно 41 элементу, так что один поиск ограничивается примерно 820 результатами, независимо от того, сколько домов в районе. Плотные рынки скрывают тысячи других. Решение — рекурсивное разделение квадрантов: если ограничивающий прямоугольник возвращает максимальное количество страниц, разделите его на четыре меньших и ищите в каждом; продолжайте делить, пока каждый прямоугольник не вернет меньше, чем лимит. Эта стратегия увеличения масштаба может выявить сотни тысяч объявлений из одного мегаполиса, которые плоский поиск никогда бы не показал — и она делит только там, где плотность это оправдывает, так что вы не тратите запросы на пустую сельскую местность.
У этой тщательности есть своя цена: каждое подразделение умножает количество запросов, так что плотный мегаполис может превратить один поиск в десятки. Планируйте это — ограничьте глубину рекурсии и увеличивайте масштаб только там, где количество результатов это оправдывает, что алгоритм делает за вас, разделяя только тогда, когда прямоугольник все еще достигает максимального количества страниц. Для ночного обновления всего города большинство запросов затем попадает на несколько плотных центральных прямоугольников, в то время как пригороды разрешаются за один проход, так что вы тратите на глубину ровно там, где находится инвентарь, и нигде больше.
PerimeterX — причина, по которой наивные скрейперы терпят неудачу
Блокировка Zillow не основана в первую очередь на скорости; это PerimeterX (теперь HUMAN), который профилирует запрос. IP-адреса дата-центров, отсутствующие заголовки браузера и нечеловеческое время выполнения все вызывают 403 или вызов — именно это проверяет assert status == 200. Решение — это согласованный, надежный запрос: резидентные прокси США, чтобы выходной IP выглядел как покупатель дома, реалистичные заголовки браузера и экспоненциальное отклонение, которое переключается на новый IP при любом 403, а не повторяет сожженный. Наш анализ как PerimeterX обнаруживает автоматизацию охватывает сигналы в деталях; та же позиция применяется к Walmart, который использует того же поставщика.
Когда PerimeterX ужесточает или вы предпочли бы не поддерживать парк браузеров, Scraper API рендерит страницу, переключает резидентные IP и снимает вызов за вас, возвращая тот же скрытый JSON без операционных накладных расходов.
Собирайте данные с Zillow за PerimeterX с резидентными IP

Когда использовать записи округа вместо этого
Zillow отлично подходит для объявлений, фотографий и оценок, но это вторичный источник. Для авторитетной информации о собственности, истории продаж и оценочных значений, офисы оценщиков и регистраторов округа публикуют публичные записи — часто через свои порталы или массовые экспорты данных. Если ваш случай использования — это титулы, сравнения или налоговые данные, а не живые объявления, записи на уровне округа чище, более разрешены для сбора и полностью свободны от PerimeterX. Используйте Zillow для рыночного сигнала и записи округа для истины на местах.
Является ли сбор данных с Zillow законным?
Это общая информация, а не юридическая консультация. Публично доступные данные об объявлениях — цены, адреса, характеристики собственности — в целом считаются допустимыми для сбора в уважительных темпах. Однако результаты Zillow могут включать личные данные: имена агентов, номера телефонов и детали брокеров. Они защищены GDPR и аналогичными законами, поэтому избегайте их сбора и хранения без законного основания. Собирайте факты о собственности, а не о людях.
Часто задаваемые вопросы
Могу ли я собирать данные с Zillow?
Да — публично видимые данные об объявлениях можно собирать. Zillow встраивает каждую запись объекта как JSON в тег скрипта (__NEXT_DATA__ или кэш Apollo), и его поиск работает на API по границам карты, который вы можете вызвать напрямую. Препятствием является не доступ, а PerimeterX, поэтому вам нужны резидентные IP и согласованные заголовки, чтобы избежать 403.
Есть ли у Zillow API?
Zillow предлагает около 20 официальных API, включая данные о недвижимости и данные о районах, но они требуют ключей, имеют ограничения по использованию и не охватывают все поля или массовые случаи использования. Многие команды собирают публичные страницы именно потому, что скрытый JSON уже содержит цену, количество спален, ванных комнат, площадь, zestimate и оценку аренды, которые им нужны.
Почему мой скрейпер Zillow получает 403?
403 на Zillow почти всегда связано с PerimeterX, а не с ограничением скорости. IP-адреса дата-центров, отсутствующие или несогласованные заголовки браузера и роботизированное время выполнения все это вызывает. Переключитесь на резидентные прокси США, отправляйте реалистичные заголовки, добавьте человекоподобные интервалы и переключайтесь на новый IP при каждом 403 с экспоненциальным отклонением, а не продолжайте использовать заблокированный.
Как собрать более 820 объявлений с Zillow из одной области?
Один поиск ограничивается примерно 820 результатами (20 страниц по 41). Чтобы углубиться, разделите ограничивающий прямоугольник карты на четыре квадранта и ищите в каждом; рекурсивно разделяйте любой квадрант, который все еще достигает лимита. Этот подход увеличения масштаба делит только плотные области, так что он захватывает весь инвентарь мегаполиса без траты запросов на разреженные регионы.
Пропустите HTML и читайте встроенный JSON, управляйте поиском по границам карты, разделяйте квадранты, чтобы обойти лимит, и направляйте все через чистые резидентные IP, чтобы опережать PerimeterX. Делайте это, и Zillow станет структурированным источником данных о недвижимости, а не стеной из 403.