Как собирать данные о недвижимости: полное руководство

Сбор данных о недвижимости с сайтов сопряжен с рядом уникальных сложностей. Сайты с объявлениями используют рендеринг JavaScript, динамическую загрузку контента и антибот-защиту, которая блокирует традиционные парсеры. Этот гайд рассматривает практические решения с использованием Python и Инструменты Bright Data.

Примечание: Я не связан с Bright Data. Это платформа, с которой я лучше всего знаком, поэтому здесь я решил использовать именно её.

Почему скрейпинг данных о недвижимости сложен

Сайты недвижимости создают три основные проблемы:

1. Страницы-списки и детальные страницы
Результаты поиска показывают лишь ограниченный набор данных (цена, адрес, миниатюра). Полные сведения об объекте, такие как площадь, история объекта, информация об агенте, требуют перехода на отдельные страницы объявлений, что многократно увеличивает число необходимых запросов.

2. Рендеринг на JavaScript
Большинство платформ недвижимости рендерят контент на стороне клиента. Обычный HTTP-запрос возвращает неполный HTML, потому что данные загружаются через JavaScript после первоначальной загрузки страницы.

3. Защита от ботов
Сайты используют фильтрацию по IP, капчи, ограничение частоты запросов и браузерный фингерпринтинг, чтобы выявлять и блокировать скраперы.

Базовый скрейпер на Requests и BeautifulSoup

Этот подход работает для простых сайтов без серьезных антибот-механизмов.

Анализ целевой страницы

Перед тем как писать код, изучите структуру страницы:

  1. Откройте результаты поиска в браузере
  2. Щёлкните правой кнопкой мыши по объявлению об объекте недвижимости и выберите «Inspect»
  3. Определите HTML-элементы, содержащие нужные вам данные (цена, адрес, ссылки)

Установка

pip install requests beautifulsoup4

Основной код скребка

импорт requests
с сайта bs4 импорт BeautifulSoup
импорт csv
url = "https://www.example.com/homes/for_sale/San-Francisco/"
заголовки = {
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, как Gecko) Chrome/120.0.0.0 Safari/537.36"
}
response = requests.get(url, headers=headers)
если response.status_code == 200:
    soup = BeautifulSoup(response.content, 'html.parser')
    результаты = []
    property_items = soup.find_all("li", {"data-testid": "property-card"})
    для пункт в property_items:
        попробуйте:
            адрес = item.find("адрес").get_text(strip=Правда)
            цена = item.find("span", {"data-test": "собственность-карточка-цена"}).get_text(strip=Правда)
            url_link = item.find("a").get("href")
            full_url = f "https://www.example.com{url_link}" если ссылка и не url_link.startswith("http") else ссылка
            если адрес или цена:
                results.append({
                    "адрес": адрес,
                    "цена": цена,
                    "ссылка": full_url
                })
        кроме Исключение в роли e:
            печать(f"Error parsing card: {e}")
    если результаты:
        с открыть("real_estate_data.csv", "w", newline="", кодировка="utf-8") в роли f:
            writer = csv.DictWriter(f, fieldnames=["адрес", "цена", "ссылка"])
            writer.writeheader()
            writer.writows(results)
        печать(f "Scraped {len(результаты)} списки")
else:
    печать(f"Failed to fetch page. Status code: {response.status_code}")

Ограничение: Этот подход не работает на сайтах с рендерингом JavaScript или антибот-защитой.

Работа с антибот-защитой с помощью Bright Data

Когда базовый скрейпинг не работает, Bright Data предлагает несколько решений в зависимости от ваших задач.

Вариант 1: Unlocker APIs (рекомендуется для статических страниц)

Unlocker APIs обрабатывают Ротация проксиРешение CAPTCHAи автоматический обход антиботов. Вы отправляете один запрос, а он возвращает чистый HTML или JSON.

Лучше всего подходит для:

  • страницы, не требующие взаимодействия с браузером
  • Скрейпинг больших объемов с предсказуемой стоимостью
  • Команд без прокси-инфраструктуры

Прямой доступ к API (рекомендуемый метод)

импорт запросов
API_KEY = "YOUR_API_KEY"
ZONE_NAME = "ИМЯ_ВАШЕЙ_ЗОНЫ"
заголовки = {
    "Авторизация": f"Bearer {API_KEY}",
    "Content-Type": "application/json"
}
полезная нагрузка = {
    "зона": ZONE_NAME,
    "url": "https://www.example.com/homes/for_sale/San-Francisco/",
    "формат": "сырой"
}
response = requests.post(
    "https://api.brightdata.com/request",
    headers=headers,
    json=payload
)
если response.status_code == 200:
    html_content = response.text
    # Разбор с помощью BeautifulSoup
    soup = BeautifulSoup(html_content, 'html.parser')
    # Извлеките данные, как показано выше

Родной доступ на основе прокси-сервера

Для рабочих процессов, уже использующих маршрутизацию через proxy:

импорт запросы
хост = 'brd.superproxy.io'
порт = 33335
имя пользователя = 'brd-customer--zone-'
пароль = ''
proxy_url = f'http://{имя пользователя}:{пароль}@{host}:{порт}'
прокси = {
    'http': proxy_url,
    'https': proxy_url
}
url = "https://www.example.com/homes/for_sale/San-Francisco/"
response = requests.get(url, proxies=proxies)

Примечание: Для нативного доступа через прокси установите SSL-сертификат Bright Data, чтобы избежать ошибок SSL, или задайте verify=False. в ваших запросах (не рекомендуется для боевого использования).

Работа с рендеринговым содержимым JavaScript

Если страницы возвращают неполные данные, используйте x-unblock-expect header для ожидания конкретных элементов:

заголовки = {
    "Авторизация": f"Bearer {API_KEY}",
    "Content-Type": "application/json"
}
полезная нагрузка = {
    "зона": ZONE_NAME,
    "url": "https://www.example.com/property/12345",
    "формат": "сырой",
    "заголовки": {
        "x-unblock-expect": '{"элемент": ".property-details"}'
    }
}

Вариант 2: Browser API (для интерактивных страниц)

Когда вам нужна полная работа с браузером — нажатие кнопок, прокрутка, обработка сценариев входа — используйте Browser API. Для других инструментов читайте моя статья о лучших браузерах для скрейпинга.

Лучше всего подходит для:

  • Сайты с большим количеством JavaScript, требующие взаимодействия
  • Многошаговые сценарии навигации
  • Сайты со сложной антибот-защитой

Пример кукловода

const puppeteer = require('puppeteer-core');
const AUTH = 'YOUR_USERNAME:YOUR_PASSWORD';
const TARGET_URL = 'https://www.example.com/homes/for_sale/San-Francisco/';
async function scrapeRealEstate() {
    const browserWSEndpoint = `wss://${@brd.superproxy.io">AUTH}@brd.superproxy.io:9222`;
    const browser = await puppeteer.connect({ browserWSEndpoint });
    
    try {
        const page = await browser.newPage();
        await page.goto(TARGET_URL, { timeout: 120000 });
        
        // Ожидание загрузки списков
        await page.waitForSelector('[data-testid="собственность-card"]');
        
        // Извлечение данных
        constings = await page.evaluate(() => {
            const cards = document.querySelectorAll('[data-testid="собственность-card"]');
            return Array.from(cards).map(card => ({
                адрес: card.querySelector('адрес')?.textContent?.trim(),
                цена: card.querySelector('[data-test="собственность-card-price"]')?.textContent?.trim(),
                link: card.querySelector('a')?.href
            }));
        });
        
        console.log(listings);
    } finally {
        await browser.close();
    }
}
scrapeRealEstate();

Пример драматурга

с сайта playwright.sync_api импорт sync_playwright
АВТ = 'YOUR_USERNAME:YOUR_PASSWORD'
def scrape_real_estate():
    с sync_playwright() в роли p:
        browser = p.chromium.connect_over_cdp(f'wss://{@brd.superproxy.io">AUTH}@brd.superproxy.io:9222')
        page = browser.new_page()
        
        page.goto('https://www.example.com/homes/for_sale/San-Francisco/', timeout=120000)
        page.wait_for_selector('[data-testid="собственность-card"]')
        
        listings = page.evaluate(''() => {
            const cards = document.querySelectorAll('[data-testid=""собственность-card"]');
            return Array.from(cards).map(card => ({
                адрес: card.querySelector('адрес')?.textContent?.trim(),
                цена: card.querySelector('[data-test="собственность-card-price"]')?.textContent?.trim(),
                ссылка: card.querySelector('a')?.href
            }));
        }''')
        
        browser.close()
        вернуть списки

Вариант 3: Web Scraper API (готовые скрейперы)

Для популярных сайтов недвижимости, таких как Zillow, Realtor.com, или Redfin, Bright Data и другие поставщики предлагают готовые скрейперы, которые сразу возвращают структурированные данные.

импорт запросы
API_KEY = "YOUR_API_KEY"
DATASET_ID = "gd_xxxxx"  # ID скребка для недвижимости
заголовки = {
    "Авторизация": f "Носитель {API_KEY}",
    "Content-Type": "application/json"
}
# Синхронное соскабливание (до 20 URL)
response = requests.post(
    f "https://api.brightdata.com/datasets/v3/scrape?dataset_id={DATASET_ID}&format=json",
    headers=headers,
    json=[{"url": "https://www.zillow.com/homedetails/123-main-st/12345_zpid/"}]
)
data = response.json()

Пагинация и работа с несколькими страницами

Объявления о недвижимости разбиты на несколько страниц. Обрабатывайте пагинацию, проходя по номерам страниц:

импорт requests
с сайта bs4 импорт BeautifulSoup
импорт время
API_KEY = "YOUR_API_KEY"
ZONE_NAME = "ИМЯ_ВАШЕЙ_ЗОНЫ"
BASE_URL = "https://www.example.com/homes/for_sale/San-Francisco/"
all_results = []
для page_num в ассортимент(1, 6):
    url = f"{BASE_URL}?page={page_num}"
    
    полезная нагрузка = {
        "зона": ZONE_NAME,
        "url": url,
        "формат": "сырой"
    }
    
    response = requests.post(
        "https://api.brightdata.com/request",
        заголовки={"Авторизация": f "Носитель {API_KEY}", "Content-Type": "application/json"},
        json=payload
    )
    
    если response.status_code == 200:
        суп = BeautifulSoup(response.text, 'html.parser')
        # Разбор и добавление результатов
        # ...
    
    time.sleep(1)  # Базовое ограничение скорости

Извлечение полной информации о недвижимости

Страницы со списком объектов содержат лишь часть данных. Для полной информации собирайте данные с отдельных страниц объектов:

def scrape_property_details(property_url, api_key, zone_name):
    полезная нагрузка = {
        "зона": имя_зоны,
        "url": property_url,
        "формат": "сырой"
    }
    
    response = requests.post(
        "https://api.brightdata.com/request",
        заголовки={"Авторизация": f "Носитель {api_key}", "Content-Type": "application/json"},
        json=payload
    )
    
    если response.status_code == 200:
        суп = BeautifulSoup(response.text, 'html.parser')
        возврат {
            "описание": soup.find("div", {"data-testid": "описание"})?.get_text(strip=Правда),
            "спальни": soup.find("span", {"data-testid": "кровати"})?.get_text(strip=Правда),
            "ванные комнаты": soup.find("span", {"data-testid": "ванны"})?.get_text(strip=Правда),
            "квадратный фут": soup.find("span", {"data-testid": "квадратный фут"})?.get_text(strip=Правда),
        }
    возврат Нет
# Обогащение листинговых данных
для листинг в результаты:
    details = scrape_property_details(listing["ссылка"], API_KEY, ИМЯ_ЗОНЫ)
    если детали:
        listing.update(details)

Выбор подходящего продукта

Итоги

  1. Начните с простого с помощью requests и BeautifulSoup для простых сайтов
  2. Используйте API разблокировщика когда вы сталкиваетесь с антибот-защитой на статических страницах
  3. Переключитесь на Browser API когда страницы требуют взаимодействия через JavaScript
  4. Рассмотрите Web Scraper API для популярных сайтов с уже готовыми скраперами
  5. Обрабатывайте пагинацию для сбора данных по нескольким страницам
  6. Собирайте данные со страниц с подробной информацией для полной информации об объекте недвижимости

Похожие записи