Виграй 1 ТБ резидентного трафіку, iPhone 18 Pro Max + ще 33 призи$50 на баланс = 1 квитокПриєднатись
Бізнес

Скрейпер Zillow: як парсити дані про нерухомість на Python

Скрейпер Zillow: як парсити дані про нерухомість на Python

Підсумуйте цю статтю за допомогою ШІ:

Збирати оголошення про продаж житла по всьому місту вручну довго: ціни змінюються щодня, і вчорашня таблиця в Excel швидко втрачає актуальність. Скрейпер Zillow автоматично вивантажує ціни, адреси та параметри будинків зі сторінок каталогу в готову таблицю, яку зручно фільтрувати, оновлювати й порівнювати.

Коротко про головне

  • Враховуйте правила майданчика. Правила самого сайту Zillow забороняють автоматичний збір сторінок ботами. Для комерційних проєктів використовуйте відкриті статистичні таблиці Zillow Research, офіційні партнерські програми компанії або інші каталоги нерухомості, які дозволяють автоматичне вивантаження.

  • Спершу перевіряйте скрипт на 5-10 оголошеннях. Звірте зібрані ціни, адреси та параметри кімнат із вихідними картками на сайті, перш ніж запускати великий збір.

  • Стежте за перемиканням сторінок і дублікатами. Налаштуйте перехід по сторінках пошуку, видаляйте повторювані будинки за унікальним ID об'єкта (zpid) і залишайте порожні комірки там, де дані в оголошенні не вказано.

  • Підбирайте проксі під обсяг і географію завдань. Якщо потрібно заходити з IP-адреси конкретного штату чи міста й зберігати одну адресу під час переходу з пошуку в картку будинку, обирайте ротаційні резидентські проксі. А для постійного завантаження важких сторінок без підрахунку гігабайтів підійдуть безлімітні мобільні проксі (але пам'ятайте, що проксі змінюють лише IP-адресу й не скасовують захисних лімітів самого сайту).

  • Не змішуйте різні ціни в одному стовпці. Записуйте ціну продавця з оголошення та автоматичну оцінку будинку від самого сервісу (Zestimate) в різні колонки таблиці, а також зберігайте дату й час парсингу кожної картки.

💡

Приклад стовпців підсумкового CSV-файлу
zpid,address,listing_status,asking_price,bedrooms,bathrooms,source_url,collected_at

Які дані може збирати скрейпер Zillow

Набір даних залежить від того, які саме сторінки ви відкриваєте: загальну пошукову видачу, картки конкретних будинків чи профілі рієлторів. У загальній видачі зазвичай видно лише базові параметри (ціна, адреса, кількість спалень і ванних), а всередині картки будинку можна додатково забрати текст опису, розрахункову оцінку вартості (Zestimate) та історію зміни ціни. З телефонами в оголошеннях будьте уважні: зазвичай там указано номер рієлтора або агентства, а не особистий телефон власника будинку.

Перш ніж парсити дані Zillow, визначтеся зі списком стовпців майбутньої таблиці. У нашому прикладі на Python нижче ми збираємо лише об'єкти, виставлені на продаж (зі статусом FOR_SALE):

Поле

Формат

Що означає в нашому прикладі

zpid

Обов'язковий рядок

Унікальний ID об'єкта на сайті; зберігається як текст і слугує для видалення дублікатів.

address

Обов'язковий рядок

Повна адреса об'єкта з картки пошукової видачі.

listing_status

Обов'язковий рядок

Статус FOR_SALE (оголошення з іншими статусами скрипт пропускає).

asking_price

Число або null

Ціна продажу з оголошення (у прикладі вказана в доларах США - USD).

bedrooms

Число або null

Кількість спалень (нуль зберігається як допустиме число, наприклад для квартир-студій).

bathrooms

Число або null

Кількість ванних кімнат, включно з дробовими значеннями (наприклад, 1.5 за наявності гостьового санвузла).

zestimate

Число або null

Орієнтовна вартість будинку за внутрішньою оцінкою Zillow (в USD); зберігається окремо від ціни продавця.

description

Рядок або null

Текст опису з повної картки вибраного будинку.

source_url

Обов'язковий рядок

Пряме посилання на сторінку оголошення.

search_source_url

Обов'язковий рядок

Посилання на сторінку пошукової видачі, на якій знайшли оголошення.

collected_at

Дата й час із часовим поясом

Точний час збереження сторінки пошуку.

detail_collected_at

Дата й час або null

Час збереження повної картки будинку (залишається null, якщо окрема сторінка будинку не відкривалася).

synthetic

Булеве значення

Позначка навчальних даних: дорівнює true для записів із тестових HTML-прикладів.

Якщо скрипт розбирає попередньо завантажені на комп'ютер HTML-сторінки, він бере дату їх збереження з файлу налаштувань (sample.json), а під час завантаження сторінок мережею записує поточний час відповіді сервера. У нашому тестовому прикладі нижче вказані умовні дати.

Пропущені необов'язкові поля записуються як null у JSON і залишаються порожніми комірками в CSV. Якщо ж у картці немає обов'язкового поля (наприклад, ID або адреси) або замість ціни трапився некоректний текст, скрипт одразу зупиниться й видасть помилку, щоб у таблицю не потрапили випадкові цифри.

Вартість оренди, ціну минулого продажу, площу будинку, історію цін і телефони агентів наш навчальний скрипт не збирає - під них потрібно прописати окремі поля в коді. Якщо додаватимете їх у свою таблицю, створіть під кожен вид ціни окрему колонку (щоб не плутати поточну ціну продавця з орендною ставкою чи оцінкою Zestimate).

Чим збирати дані Zillow: скрипт на Python, готові парсери чи офіційні вивантаження

Вибір способу залежить від того, як часто вам потрібні дані та чи готові ви писати код. Якщо потрібно один раз вивантажити невелику таблицю в Excel, простіше взяти готовий хмарний парсер або браузерне розширення. А якщо ви збираєте дані регулярно й за своїм списком полів, краще написати скрипт на Python або розгорнути власний браузерний скрейпер.

Інструмент або підхід

Для яких завдань підходить

Що важливо враховувати

Власний скрипт на Python

Збір будь-яких потрібних полів, перевірка даних і збереження у власну базу

Потрібно самостійно оновлювати код у разі зміни верстки сайту й стежити за правилами доступу.

Yozh Scraper

Регулярний збір динамічних сторінок через реальний браузер у Docker

Потрібно запустити контейнер у себе на ПК чи сервері й налаштувати шаблон селекторів під потрібні сторінки.

Розширення для браузера або хмарний парсер

Швидке разове вивантаження в таблицю без написання коду

Заздалегідь перевірте список доступних полів, ліміти рядків на тарифі та вартість підписки.

Сторонній сервіс Scraping API

Завантаження вебсторінок через інфраструктуру зовнішнього сервісу

Уточніть, що саме повертає сервіс: сирий HTML-код сторінки чи вже готовий JSON з полями об'єкта.

Офіційні вивантаження та партнерські програми Zillow

Офіційні інтеграції та аналітика цін по містах і штатах

Перевірте, які саме показники є в таблицях, і вивчіть умови ліцензії.

Перед купівлею підписки на будь-який готовий сервіс перевірте його на 5-10 оголошеннях. Подивіться, чи заходить парсер усередину картки будинку по докладний опис, чи бере лише короткий список із пошуку, як він заповнює порожні комірки та чи зберігає ID об'єкта (zpid) разом із датою вивантаження.

Врахуйте важливий момент: якщо сторонній сервіс називається «Zillow API», це ще не означає, що він належить самій компанії Zillow. Найчастіше це звичайний сторонній парсер, який сам завантажує сторінки сайту й віддає дані через свій сервер.

Якщо вам потрібна загальна статистика ринку нерухомості США, зазирніть на безкоштовний портал Zillow Research - там викладено готові таблиці із середніми цінами на житло, орендними ставками та кількістю оголошень по містах і штатах. Також у Zillow Group є офіційні партнерські програми для розробників, але вони видають дані лише під конкретні схвалені сервіси, а не відкривають повну базу всіх оголошень сайту.

💡

Детальніше про програми для парсингу без написання коду читайте в нашій статті про вебскрейпери без коду (No-Code). 

Як парсити Zillow на Python

Нижче ми розберемо робочий скрипт на Python, який витягує дані зі збережених HTML-сторінок пошуку та карток будинків, а потім зберігає їх у файли CSV і JSON. Ми перевірили цей код на тестових HTML-файлах без надсилання запитів до реального сайту Zillow.

Скрипт відкриває одну сторінку пошуку, відбирає до п'яти унікальних оголошень про продаж (FOR_SALE) і підтягує до одного з будинків докладний опис із файлу картки об'єкта. Ліміт у п'ять оголошень ми встановили просто для зручності прикладу.

💡

Параметри тестового середовища: Linux x86_64, Python 3.12.14, Requests 2.34.2 і Beautiful Soup 4.15.0. Список потрібних бібліотек і їхніх версій прописано у файлі requirements.txt. Код протестовано 4 листопада 2026 року й повторно перевірено 5 листопада 2026 року. Ви можете завантажити вихідний скрипт на Python, файл залежностей requirements.txt, тестовий приклад, набір автотестів, а також готові приклади вивантаження в CSV і JSON.

Підготуйте середовище Python і список полів

Розпакуйте архів із файлами прикладу, відкрийте папку в терміналі та створіть віртуальне середовище Python:

python
python3 -m venv .venv

source .venv/bin/activate

python -m pip install -r requirements.txt

До підсумкової таблиці потраплять ID об'єкта, адреса, статус оголошення, ціна продавця, кількість спалень і ванних кімнат, посилання на сторінки та час збору. Якщо для будинку обробляється детальна картка, до запису додадуться оцінка Zestimate і текст опису.

Паролі від проксі краще зберігати окремо від коду. У нашому скрипті налаштування HTTP-проксі бере адресу сервера зі змінної середовища ZILLOW_PROXY_URL (а якщо її не задано, скрипт підключається напряму). Враховуйте, що скрипт на Python працює сам по собі й не використовує проксі-розширення, встановлені у вашому браузері Chrome.

У файлі sample.json указано шляхи до двох тестових HTML-сторінок і час, коли їх було збережено. Коли скрипт розбирає ці файли, він переносить до підсумкової таблиці саме вихідний час збереження сторінки, а не поточний час запуску скрипта.

Де шукати дані в коді сторінки пошуку та картки будинку

Перед написанням парсера відкрийте потрібну сторінку в браузері й порівняйте її вихідний код (Ctrl+U / View Page Source) із вкладкою елементів у панелі розробника (F12 / DevTools). Так ви одразу побачите, чи приходять ціни й адреси в готовому HTML-коді, чи підвантажуються пізніше через скрипти.

Наш навчальний парсер шукає на сторінці вбудований блок із даними у форматі JSON усередині тега:

html
<script id="__NEXT_DATA__" type="application/json">

Потім він забирає список оголошень і параметри будинку за цими двома шляхами всередині JSON:

bash
Search records:

props.pageProps.searchPageState.cat1.searchResults.listResults

Property details:

props.pageProps.componentProps.gdpClientCache

Ці шляхи налаштовано під наші тестові файли: розробники сайтів періодично змінюють назви вкладених полів, тому перед запуском завжди перевіряйте актуальну структуру JSON на свіжій сторінці.

Якщо ж потрібні ціни чи описи з'являються на сторінці лише після виконання JavaScript, звичайна бібліотека Requests їх не побачить - вона завантажує лише вихідний HTML-код. Для таких сайтів сторінку потрібно відкривати через автоматизований браузер (наприклад, Playwright).

Завантаження HTML-сторінки та перевірка відповіді сервера

Функція fetch_html() перевіряє код відповіді сервера, стежить, щоб запит не перенаправили на іншу сторінку (редирект), і перевіряє заголовок Content-Type, перш ніж передати HTML-код у парсер:

python
def fetch_html(url):
    url = source_url(url)
    proxy = os.getenv("ZILLOW_PROXY_URL")

    if proxy and urlsplit(proxy).scheme != "http":
        raise ValueError("This example supports an HTTP proxy endpoint only")

    with requests.Session() as session:
        session.trust_env = False

        if proxy:
            session.proxies = {"https": proxy}

        with session.get(
            url,
            timeout=(5, 20),
            allow_redirects=False,
        ) as response:
            if response.status_code != 200:
                raise ValueError(
                    f"Stopped: HTTP {response.status_code}; no automatic retry"
                )

            if response.url != response.request.url:
                raise ValueError("Unexpected final URL")

            source_url(response.url)
            media_type = (
                response.headers.get("Content-Type", "")
                .split(";")[0]
                .lower()
                .strip()
            )

            if media_type not in {"text/html", "application/xhtml+xml"}:
                raise ValueError("Expected an HTML response")

            return response.text

Параметр timeout=(5, 20) задає два часи очікування: 5 секунд відводиться на підключення до сервера і 20 секунд - на отримання даних (це допустима пауза між пакетами даних, а не загальний час завантаження всієї сторінки).

Навіть якщо сервер відповів кодом HTTP 200, важливо перевірити, що саме прийшло всередині сторінки. Якщо замість списку будинків відкрилася сторінка з капчею або змінилася верстка, скрипт має одразу видати помилку, а не мовчки зберегти порожній файл із нулем оголошень. Наступна функція page_props() перевіряє, чи є на сторінці блок __NEXT_DATA__ і словник pageProps:

python
def page_props(html):
    soup = BeautifulSoup(html, "html.parser")
    node = soup.find("script", id="__NEXT_DATA__")

    if node is None:
        raise ValueError(
            "Expected JSON block absent: inspect the returned page"
        )

    try:
        props = json.loads(node.get_text())["props"]["pageProps"]
    except (KeyError, TypeError, json.JSONDecodeError) as exc:
        raise ValueError("Invalid or changed page-data structure") from exc

    if not isinstance(props, dict):
        raise ValueError("Expected a pageProps object")

    return props

Розбір карток об'єктів і перевірка числових полів

Скрипт відбирає лише оголошення про продаж (FOR_SALE), перевіряє наявність ID та адреси, а також видаляє повтори за zpid до того, як обмежити список п'ятьма будинками. Оголошення про оренду й уже продані будинки пропускаються, щоб орендна плата або стара ціна угоди не потрапили в колонку поточної ціни продавця (asking_price).

Якщо необов'язкове числове поле не заповнене, функція залишає None. А якщо замість числа в поле потрапив текстовий рядок або від'ємне значення, функція number() одразу зупиняє скрипт із помилкою:

python
def number(value, positive=False):
    if value is None:
        return None

    if (
        isinstance(value, bool)
        or not isinstance(value, (int, float))
        or not math.isfinite(value)
        or value < 0
        or (positive and value == 0)
    ):
        raise ValueError(
            "Unexpected numeric value; review the field mapping"
        )

    return value

Підвантажувати окрему сторінку самого будинку потрібно лише тоді, коли вам потрібні додаткові дані з картки (наприклад, повний опис або оцінка Zestimate). При цьому посилання та ID будинку (zpid) мають збігатися з оголошенням із пошукової видачі. Скрипт зберігає оцінку Zestimate в окрему колонку й записує час обробки детальної сторінки.

Головне правило під час збору бази нерухомості - не плутати різні види цін між собою й не заповнювати порожні поля приблизними значеннями.

Запуск скрипта на тестових файлах і перевірка таблиць

Запустіть скрипт на підготовлених тестових сторінках:

shell
python zillow_sample.py sample.json --output run-01

Укажіть у команді ім'я нової папки (наприклад, run-01) - скрипт сам створить її та збереже туди готові файли CSV і JSON. У терміналі з'явиться рядок:

bash
Saved 2 unique listings to run-01

Ось як виглядає перша збережена картка у форматі JSON (усі адреси, посилання та ціни в прикладі умовні):

json
{
  "zpid": "9000000001",
  "address": "100 Example Lane, Sample City, TX 00000",
  "listing_status": "FOR_SALE",
  "asking_price": 395000,
  "bedrooms": 3,
  "bathrooms": 2,
  "source_url": "https://www.zillow.com/homedetails/example-property/9000000001_zpid/",
  "search_source_url": "https://www.zillow.com/synthetic-test-city/",
  "collected_at": "2026-11-04T10:00:00+00:00",
  "zestimate": 400000,
  "description": "Synthetic property description for parser testing only.",
  "detail_collected_at": "2026-11-04T10:01:00+00:00",
  "synthetic": true
}

А так виглядають основні колонки у CSV-файлі - подивіться, як у другому рядку збережено порожню ціну й нуль спалень:

bash
zpid,listing_status,asking_price,bedrooms,bathrooms,zestimate
9000000001,FOR_SALE,395000,3,2,400000
9000000002,FOR_SALE,,0,1,

Звірте ID будинків, ціни, порожні комірки та кількість видалених дублікатів із вихідними файлами. У другому рядку навмисно пропущено ціну (порожнє місце між комами), але збережено нуль у колонці спалень (0).

Якщо ви запускаєте скрипт на реальних дозволених сторінках мережею, укажіть файл зі списком робочих посилань і додайте прапорець --live (з цим прапорцем скрипт не прийме тестові навчальні файли). А щоб перевірити роботу самого коду, запустіть вбудовані тести:

shell
python -m unittest -v

Ці тести перевіряють лише правильність роботи самого скрипта на вашому комп'ютері. Перш ніж запускати великий збір, завжди перевіряйте парсер на кількох свіжих сторінках.

Що саме перевіряли автотести

Ми прогнали 13 автоматичних тестів (4 і 5 листопада 2026 року) на підготовлених HTML-файлах:

Параметр перевірки

Результат

Тестові файли

Один файл пошукової видачі й один файл повної картки будинку.

Карток у вихідному файлі

Три оголошення до фільтрації дублікатів.

Збережено в підсумковий файл

Два унікальні об'єкти після видалення одного повтору за zpid.

Відкинуто через помилки

Нуль (у тестовому прикладі всі записи коректні).

Записи з порожніми полями

Одна картка з незаповненими необов'язковими полями.

Які поля пропущено

У другій картці поля asking_price, zestimate, description і detail_collected_at дорівнюють null.

Звірка вивантаження

Нові файли CSV і JSON повністю збіглися з еталонними файлами в архіві.

Порожнє значення detail_collected_at у другого будинку означає, що для нього розбиралася лише коротка картка з пошуку (це нормальна поведінка, а не збій завантаження).

Додатково тести перевіряють, чи скрипт вчасно зупиняється під час помилок: наприклад, якщо сервер повернув код помилки замість сторінки, перенаправив запит на іншу адресу, віддав пошкоджений JSON або картку без ID. Звернення до реального сайту Zillow, перемикання сторінок пошуку та робота через проксі в цих локальних тестах не використовувалися.

Збір динамічних сторінок через браузер у Yozh Scraper

Наш безкоштовний опенсорсний інструмент Yozh Scraper на GitHub запускається в Docker і відкриває сайти через справжній браузер Playwright. У ньому зручно один раз налаштувати правила збору полів (пресет) для динамічних сторінок, а перевірку та збереження готових таблиць залишити у своєму скрипті на Python.

У базовій версії Yozh Scraper немає готового шаблону під Zillow. Якщо ви налаштовуєте його як вебскрейпер Zillow або іншого дозволеного сайту нерухомості, спершу зберіть свій пресет із CSS-селекторами чи XPath і перевірте його на 2-3 сторінках:

  1. Запустіть Yozh Scraper у Docker. Склонуйте репозиторій із GitHub, скопіюйте файл налаштувань .env.example у .env і запустіть контейнер командою docker compose up --build. Потім відкрийте адресу http://localhost:8000/api/v1/health, щоб переконатися, що локальний сервер запущено.

  2. Створіть свій шаблон (пресет) селекторів. Перейдіть на сторінку документації API http://localhost:8000/docs і створіть пресет із потрібними CSS-селекторами чи XPath. Назвіть його з префіксом user_ (наприклад, user_zillow_property). Протестуйте пресет через запит POST /api/v1/presets/{name}/test на одній картці будинку, а потім перевірте на другій, щоб переконатися, що селектори підходять до всіх оголошень.

  3. Надішліть тестове завдання. Передайте посилання на одну картку об'єкта в ендпоінт POST /api/v1/scrape/preset/page, указавши ім'я вашого пресета. Сервер поставить завдання в чергу й поверне його номер - job_id.

  4. Перевірте зібрані поля. Переконайтеся, що у відповіді повернулися всі потрібні дані: ID об'єкта (zpid), адреса, статус оголошення, ціна, кількість кімнат і посилання на сторінку. Простежте, щоб числа були числами, а незаповнені поля залишалися зі значенням null.

  5. Заберіть результат у свій скрипт на Python. Налаштуйте в скрипті перевірку статусу завдання за адресою GET /api/v1/scrape/{job_id} раз на кілька секунд. Коли завдання завершиться, завантажте готовий JSON через GET /api/v1/scrape/{job_id}/results, перевірте, чи немає у відповіді помилок, і збережіть дані в таблицю.

💡

Детальну інструкцію зі встановлення та роботи з візуальним конструктором селекторів дивіться в нашій статті про скрін-скрейпинг (скрейпинг екрана) - там ми на прикладах розбираємо, чим читання даних із вікна браузера відрізняється від звичайного завантаження HTML-коду та парсингу за селекторами.

Як налаштувати перемикання сторінок (пагінацію) і не втрачати оголошення

Один пошуковий запит на сайті нерухомості рідко показує абсолютно всі будинки в місті. Список у видачі залежить від масштабу мапи, вибраних фільтрів і обмежень самого сайту на кількість сторінок - навіть якщо вгорі написано, що знайдено кілька тисяч оголошень.

Перед збором великого каталогу вирішіть, що саме у вас вважатиметься одним рядком таблиці: приватний будинок, окрема квартира, уся будівля цілком чи конкретне оголошення. Наприклад, у багатоквартирному будинку може продаватися одразу кілька квартир за однією поштовою адресою - їх не можна склеювати в один запис лише через збіг вулиці й номера будинку.

У нашому навчальному скрипті на Python розбирається тільки перша сторінка пошуку. Якщо ви дописуєте перемикання сторінок (пагінацію), стежте, щоб координати мапи, фільтри та сортування не збивалися під час переходу на сторінку 2, 3 і далі. Зберігайте стартове посилання з фільтрами в лозі, щоб завжди розуміти, за якими параметрами збиралася база.

Обов'язково задайте в коді умови зупинки: скрипт має припиняти перебір сторінок, якщо сайт почав віддавати ті самі оголошення по колу, на новій сторінці немає жодного нового zpid або досягнуто ваш ліміт за кількістю сторінок і часом роботи. Записуйте причину зупинки в лог - тоді ви одразу зрозумієте, чи дійшов парсер до кінця списку сам, чи перервався через помилку мережі.

Якщо ви об'єднуєте таблиці з кількох сусідніх районів, видаляйте дублікати строго за унікальним ID об'єкта (zpid) або номером конкретної квартири, а не за текстовою адресою.

Після кожного запуску зберігайте в лог чотири прості цифри:

  • Усього знайдено карток (Raw records): скільки всього оголошень прочитав скрипт до видалення повторів.

  • Унікальних об'єктів (Unique records): скільки карток залишилося після очищення дублікатів.

  • Відкинутих записів (Rejected records): скільки карток скрипт пропустив через помилки чи інший статус (і чому).

  • Карток із порожніми полями (Incomplete records): скільки унікальних будинків збережено з незаповненими необов'язковими полями.

Так ви завжди бачитимете точну кількість реально зібраних карток, а не орієнтуватиметеся на приблизний лічильник оголошень у шапці сайту.

Очищення даних Zillow і вивантаження в CSV або JSON

Щоб у готовій таблиці не збивалися фільтри й розрахунки, перед збереженням у файл приведіть усі поля до єдиного вигляду та перевірте кілька рядків уручну:

  • Зберігайте поштові індекси (ZIP) як звичайний текст. У ряді штатів США поштові індекси починаються з нуля (наприклад, 07030 у Нью-Джерсі). Якщо відкрити CSV-файл в Excel як числове поле, програма прибере перший нуль, тому стовпець індексу потрібно імпортувати саме як текст.

  • Тримайте різні види цін в окремих колонках. Не змішуйте в одному стовпці ціну продавця з оголошення (asking_price), суму минулого продажу будинку (last_sale_price), місячну оренду та розрахункову оцінку Zestimate. Для кожного типу ціни й валюти створіть своє поле.

  • Указуйте одиниці вимірювання площі. Записуйте площу будинку чи ділянки разом з одиницею вимірювання (наприклад, квадратні фути sqft або акри), щоб випадково не порівнювати площу будинку у футах із площею ділянки в акрах.

  • Не плутайте порожнє поле з числовим нулем. Якщо інформацію в картці не вказано, ставте null у JSON або залишайте порожню комірку в CSV. Але якщо в оголошенні вказано 0 спалень (bedrooms: 0) - це квартира-студія, і такий нуль потрібно зберігати як число.

  • Додавайте посилання на оголошення й час парсингу. У кожному рядку таблиці мають бути URL картки (source_url) і точний час збору (collected_at), щоб дані завжди можна було відкрити й перевірити.

Таблиці CSV зручні, коли одному об'єкту відповідає один рядок із простими полями. А якщо ви збираєте історію зміни ціни за датами або список податкових платежів за кілька років, зберігайте дані у форматі JSON або виносьте історію цін в окрему таблицю CSV, прив'язану до будинку за його zpid.

Якщо ви відстежуєте ціни щотижня, не затирайте старі рядки новими - додавайте свіжі записи з новою датою парсингу. Враховуйте: якщо оголошення зникло з пошуку, це ще не означає, що будинок купили: продавець міг тимчасово зняти картку з публікації або об'єкт перестав потрапляти під ваші фільтри.

Як виглядає запис із порожніми полями в JSON

Ось друга картка з нашого тестового прикладу на Python (адреса й посилання в ній також умовні). Ціну в оголошенні не вказано, тому в полі asking_price стоїть null, а для квартири-студії без окремої спальні збережено число 0:

json
{
  "zpid": "9000000002",
  "address": "200 Example Lane, Sample City, TX 00000",
  "listing_status": "FOR_SALE",
  "asking_price": null,
  "bedrooms": 0,
  "bathrooms": 1,
  "source_url": "https://www.zillow.com/homedetails/second-example/9000000002_zpid/",
  "search_source_url": "https://www.zillow.com/synthetic-test-city/",
  "collected_at": "2026-11-04T10:00:00+00:00",
  "zestimate": null,
  "description": null,
  "detail_collected_at": null,
  "synthetic": true
}

Такий самий принцип перевірки полів використовуйте, якщо вирішите додати в скрипт нові стовпці.

Які проксі обрати для парсингу каталогів нерухомості

Через проксі ви задаєте потрібну країну, штат чи місто, з IP-адреси якого ваш скрипт відкриває сторінки сайту. Вибір тарифу залежить від того, скільки гігабайтів іде на завантаження сторінок і чи потрібно зберігати один незмінний IP під час переходу по каталогу.

Тип проксі

Для яких завдань підходить

Вартість та умови

Ротаційні резидентські

Вибір потрібної країни, штату й міста, зміна IP на кожен запит або утримання однієї адреси до 24 годин через sticky-сесії; підходять для парсингу каталогів, збору даних для ШІ, SEO та аналізу ринків.

Базові пакети від $4 за ГБ (до $2.50/ГБ на обсязі, спецтарифи в каталозі від $2 за ГБ) із безкоштовним вибором локації та перенесенням залишку трафіку

Мобільні

Операторські IP-адреси (CGNAT) із безлімітним трафіком дають змогу не рахувати гігабайти, коли браузер постійно підвантажує важкі скрипти й фотографії будинків.

LTE/5G від $1.70 на добу з підтримкою протоколів HTTP, SOCKS5 і UDP

Статичні резидентські ISP

Одна постійна домашня IP-адреса на весь місяць із безлімітним трафіком; зручна для щоденного моніторингу тих самих регіональних вітрин із незмінної адреси.

Від $5.29 на місяць з аптаймом 99.9%

Серверні (датацентр)

Швидкі серверні проксі IPv4/IPv6 із безлімітним трафіком на виділених тарифах; підходять для роботи з відкритими API, завантаження простих сайтів і технічного моніторингу.

Від $1.77 на місяць (спільні) і від $2.18 на місяць (виділені) з підтримкою HTTP і SOCKS5

Якщо ви збираєте сторінки через браузер на Linux-сервері (наприклад, на Ubuntu 24.04 з Playwright Chromium), найпростіше підключити HTTP-проксі з авторизацією за логіном і паролем. Пропишіть адресу сервера (server), логін (username) і пароль (password) у налаштуваннях запуску browser.new_context(proxy=...), підтягуючи самі доступи зі змінних середовища. Пам'ятайте про особливість браузерів на рушії Chromium: вони приймають логін і пароль напряму лише для HTTP/HTTPS-проксі (для SOCKS5 у Chromium потрібне або прив'язування за IP без пароля, або сторонній проксифікатор).

Перед запуском основного парсера відкрийте через цей самий браузер будь-який сайт перевірки IP і переконайтеся, що визначаються потрібний штат і місто. Потім протестуйте збір на 2-3 картках і перевірте, чи всі поля потрапили в таблицю.

🔥

Перевіряйте чистоту адрес перед запуском через чекер IP-адрес у CyberYozh App: швидка перевірка репутації IP і Fraud Score за антифрод-базами.

Як збирати номери телефонів з оголошень і не плутати контакти

Перш ніж додавати збір телефонів у свій скрипт, подивіться, які саме контакти відкриті в картці оголошення та як вони підписані на сторінці.

Головне правило: телефон рієлтора чи агентства нерухомості - це не особистий номер власника будинку. Не записуйте перший-ліпший телефон зі сторінки оголошення в колонку «Телефон власника» (owner_phone). Завжди забирайте зі сторінки ім'я контактної особи та її статус (агент продавця, брокерське агентство, керуюча компанія), щоб точно знати, чий це номер.

Для кожного контакту в картці зберігайте чотири поля:

  • Номер телефону: зберігайте як текст (разом із додатковим номером ext., якщо він є), щоб Excel не спотворив формат запису.

  • Ім'я та роль: точно так, як указано в оголошенні (наприклад, Listing Agent або Property Manager).

  • Посилання на оголошення (Source URL): сторінка, з якої взято цей номер.

  • Дата й час збору (Timestamp): коли саме збережено картку.

Якщо в оголошенні вказано кілька номерів (наприклад, особистий мобільний агента й загальний телефон офісу агентства), записуйте їх у різні колонки, щоб не переплутати, який номер кому належить. Якщо телефону в картці немає, залишайте null у JSON або порожню комірку в CSV.

І не забувайте про правила: навіть якщо телефон рієлтора відкрито опубліковано на сайті, його не можна без згоди використовувати для спам-розсилок чи холодних дзвінків.

🔥

Для тестування форм зворотного зв'язку та реєстрації в онлайн-сервісах ви можете орендувати віртуальний мобільний номер або резидентський номер в особистому кабінеті CyberYozh App.

Часті помилки під час парсингу Zillow і способи їх вирішення

Якщо скрипт перестав збирати оголошення, спершу подивіться, де саме стався збій: не підключається сам проксі-сервер, сайт обмежив часті запити (видав капчу чи код 429) або змінилася верстка сторінки й парсер не знаходить потрібні поля.

Проблема або симптом

Імовірна причина

Що перевірити та як виправити

Код HTTP 403 або сторінка з капчею

Захисна система сайту обмежила автоматичний запит

Подивіться збережену відповідь сторінки, зупиніть часті запити, перевірте репутацію IP у Fraud Score і переконайтеся, що вибране джерело дозволяє збір.

Помилка HTTP 429

Надто висока частота запитів (rate limit)

Перевірте заголовок Retry-After у відповіді сервера, зменште кількість паралельних потоків і збільште паузи між запитами.

Код HTTP 200, але список записів порожній

Сайт повернув сторінку-заглушку або оновив структуру JSON

Перевірте підсумковий URL (чи не було редиректу), заголовок Content-Type і збережений HTML-файл. Переконайтеся, що потрібні поля є у вихідному коді сторінки.

У вивантаження потрапила лише частина оголошень

Спрацювали обмеження пагінації, фільтри або частина сторінок не завантажилася

Перевірте координати пошуку й активні фільтри, подивіться кількість пройдених сторінок, список відкинутих карток і причину зупинки скрипта.

У таблиці дублюються одні й ті самі будинки

Перетнулися сусідні пошукові запити або повторилася сторінка видачі

Налаштуйте видалення дублікатів за унікальним ID об'єкта (zpid), зберігаючи при цьому окремі квартири всередині будівлі й історію цін за різними датами.

Не збираються телефонні номери

Номер не вказано в картці або змінився селектор блоку контактів

Відкрийте збережену сторінку й перевірте блок контактів уручну. Якщо номера на сторінці немає, залишайте поле порожнім (null).

У стовпець ціни потрапили неправильні цифри

Переплутано поля різних видів цін або збився формат числа

Простежте, щоб ціна продавця, орендна ставка, ціна минулої угоди й оцінка Zestimate записувалися строго у свої окремі стовпці.

Парсер зупинився після оновлення сайту

Розробники сайту перейменували класи в HTML або ключі у вбудованому JSON

Порівняйте нову сторінку зі старим тестовим файлом, оновіть шляхи до ключів JSON або CSS-селектори й знову перевірте скрипт на 2-3 картках.

Якщо ж скрипт видає помилку з'єднання з самого старту, перевірте правильність IP-адреси, порту, логіна й пароля від проксі у змінній середовища. Після будь-яких виправлень у коді спершу запустіть перевірку на 1-2 сторінках і лише потім вмикайте повний збір.

Короткий чек-лист перед запуском парсера

Перед запуском регулярного збору перевірте свій парсер на 5-10 тестових картках: переконайтеся, що ціни продавців, оцінки Zestimate й порожні комірки записуються в правильні колонки без спотворень. Для простих HTML-сторінок достатньо зв'язки Requests і Beautiful Soup на Python, а якщо картки підвантажуються через JavaScript, підключіть браузерний рендеринг через безкоштовний інструмент Yozh Scraper.

🦔

Якщо для роботи скрипта вам потрібна IP-адреса конкретного штату чи міста США або проксі з безлімітним трафіком, оберіть відповідний тариф у нашому каталозі проксі і протестуйте його на своєму завданні.

 

Корисно?

Поділитися статтею

Поширені запитання про парсер Zillow