Сервис агрегации объявлений недвижимости — это не просто «собрать ссылки с разных сайтов в одну ленту». На практике перед нами полноценный конвейер данных, где каждый этап решает свою задачу: сбор, нормализация, дедупликация, обновление, поиск, аналитика и доставка данных в интерфейс или API. Именно от архитектуры зависит, будет ли система полезным рабочим инструментом или очередным «парсером на коленке», который перестанет работать после первого изменения вёрстки на сайте-доноре.
Когда я начинал строить агрегатор для российского рынка недвижимости, стало очевидно: главная сложность не в том, чтобы вытащить объявления, а в том, чтобы поддерживать качество данных на длинной дистанции. Источники постоянно меняются, цены прыгают, дубли плодятся, а пользователь ждёт быстрый и точный поиск. Ниже разберу, как спроектировать систему, которая устойчива к реальному хаосу данных.
Что должен уметь сервис агрегации объявлений
Если говорить практично, хороший агрегатор недвижимости закрывает сразу несколько сценариев:
- показывает объявления из разных источников в одном интерфейсе;
- обновляет цены, статус и параметры объектов;
- убирает дубли;
- позволяет быстро фильтровать по важным параметрам;
- строит аналитику по рынку;
- отдаёт данные во внешний API, CRM или другие сервисы.
В российских условиях это особенно критично: рынок фрагментирован, каждая площадка живёт по своим правилам, а одно и то же жильё часто встречается сразу на нескольких досках — иногда с разными ценами и описаниями. Поэтому ценность сервиса не в самом факте сбора объявлений, а в том, как данные приводятся к единому виду и насколько быстро становятся пригодными для сравнения и анализа. Без этого агрегатор превращается в свалку карточек, по которой невозможно принимать решения.
Из чего состоит архитектура
Удобнее всего мыслить о системе как о нескольких независимых контурах. У каждого — своя зона ответственности и свои типичные проблемы.
| Контур | Задача | Что ломается чаще всего |
|---|---|---|
| Сбор данных | Забирает объявления из источников | Блокировки, антибот-защита, изменения вёрстки |
| Нормализация | Приводит поля к единому формату | Разнобой в названиях, единицах измерения, валютах |
| Дедупликация | Находит одинаковые объекты | Дубликаты по разным агентствам и площадкам |
| Хранение | Сохраняет сырьё и нормализованные данные | Потеря истории, конфликт схем |
| Поиск и фильтры | Отдаёт быстрые результаты | Медленные запросы, плохие индексы |
| Аналитика | Строит динамику цен и спроса | Нет истории изменений, нет событийной модели |
| API и интерфейс | Показывает данные пользователю | Слишком тесная связка с внутренними сервисами |
Такая декомпозиция полезна не только для крупных продуктов. Даже небольшой проект проще поддерживать и развивать, если сбор, обработка и выдача данных разделены. На старте кажется, что можно обойтись одним скриптом, который парсит, нормализует и сразу пишет в базу. Но как только появляется второй источник или потребность в истории изменений, этот монолит начинает трещать по швам.
Типовая схема работы сервиса
1. Источники данных
Обычно данные поступают из нескольких каналов:
- открытые доски объявлений;
- сайты агентств;
- фиды и выгрузки от партнёров;
- API от застройщиков или CRM;
- собственные формы добавления объектов.
На практике лучший результат даёт гибридный подход. Одни источники удобнее подключать через API, другие — через парсинг HTML, третьи — через выгрузки CSV/XLSX. Универсального способа нет, и попытка всё загнать в один тип коннектора обычно заканчивается громоздкими адаптерами. Гораздо важнее заранее продумать интерфейс коллектора, который умеет принимать сырые данные в любом виде и отдавать их на следующий этап.
2. Планировщик задач
Сбор данных нельзя запускать «по наитию». Нужен планировщик, который определяет:
- как часто опрашивать каждый источник;
- какие регионы и фильтры обновлять;
- что делать при ошибках;
- когда запускать перепроверку старых карточек.
Для «горячих» источников полезны частые короткие проходы — например, каждые 15–30 минут для новых объявлений в крупных городах. Для менее динамичных — более редкое обновление раз в несколько часов или даже раз в сутки. Это экономит ресурсы, снижает риск блокировок и не перегружает доноров. Планировщик должен уметь приоритизировать задачи: сначала новые объекты, потом обновление цен по активным карточкам, затем проверка снятых с публикации.
3. Воркеры сбора
Воркер — это отдельный исполнитель, который берёт задачу из очереди и собирает объявления. Хорошая практика — изолировать источники друг от друга. Если один сайт меняет разметку или начинает резать трафик, это не должно валить весь пайплайн. Для каждого источника или группы похожих источников лучше делать отдельный тип воркера со своей логикой повторных попыток, таймаутами и обработкой специфичных ошибок.
Например, для ЦИАН и Авито логика работы с куками и прокси сильно отличается, а для партнёрского XML-фида вообще не нужен браузерный рендеринг. Поэтому изоляция на уровне воркеров позволяет быстро добавлять новые источники и отключать проблемные, не трогая ядро системы.
4. Очереди и асинхронность
Очередь задач нужна почти всегда. Она помогает:
- сгладить пики нагрузки;
- не терять задания при сбоях;
- масштабировать сбор по мере роста числа источников;
- отделить быстрые операции от тяжёлых.
Для агрегатора недвижимости это критично, потому что часть задач очень тяжёлая: парсинг страниц, скачивание фотографий, пересчёт дублей, генерация поисковых индексов, построение аналитических выборок. Если выполнять всё синхронно в рамках одного процесса, система захлебнётся при первом же всплеске. Очередь позволяет накапливать задачи и обрабатывать их воркерами параллельно, а при необходимости — увеличивать количество воркеров для конкретного типа задач.
5. Нормализация и очистка
Сырые данные из разных источников всегда выглядят по-разному. Один и тот же параметр может называться так:
- «общая площадь»;
- «площадь, м²»;
- «S общая»;
- «метраж».
Архитектура должна включать слой нормализации, где всё приводится к единому словарю:
- цена — в рублях;
- площадь — в квадратных метрах;
- этаж — числом;
- тип сделки — в фиксированном наборе значений;
- геолокация — в единой системе координат и адресном формате.
Без этого поиск и аналитика быстро превращаются в хаос. Например, если цена в одном источнике указана в тысячах рублей, а в другом — в долларах, сравнение становится невозможным. Поэтому нормализация должна быть строгой и идемпотентной: повторный прогон по тем же сырым данным должен давать тот же результат. Это важно для пересборки данных при изменении правил.
6. Дедупликация
Это один из самых недооценённых блоков. На рынке недвижимости один объект может быть опубликован несколькими агентами, застройщиком и владельцем одновременно. Если не делать дедупликацию, пользователь увидит «засорённую» выдачу и быстро потеряет доверие.
Дедупликация обычно строится по нескольким признакам сразу:
- адрес;
- координаты;
- площадь;
- количество комнат;
- этаж;
- цена;
- фотографии;
- текстовое описание;
- телефон или идентификатор источника.
Важно понимать: точного совпадения часто нет. Один агент может указать площадь 54.5 м², другой — 55 м², а фотографии сделать с разных ракурсов. Нужна вероятностная модель, которая умеет сопоставлять схожие карточки и учитывать погрешности. Чаще всего используют комбинацию правил и машинного обучения: например, вычисляют коэффициент схожести по нормализованному адресу и ключевым параметрам, а затем применяют порог для автоматического объединения. Всё, что ниже порога, уходит в ручную модерацию или в статус «возможный дубль».
Как хранить данные правильно
Для агрегатора полезно разделять несколько слоёв хранения. Это не роскошь, а необходимость, если вы хотите пережить изменение правил нормализации или диагностировать ошибки.
Слой 1. Сырые данные
Сюда попадает всё, что пришло из источника без изменений. Это нужно, чтобы:
- разбирать ошибки;
- восстанавливать данные после изменений;
- сравнивать, как объект выглядел в исходнике;
- пересобирать нормализацию заново.
Хранить сырьё можно в виде JSONB в PostgreSQL или в объектном хранилище, если объёмы большие. Главное — не терять исходную структуру и метаданные: источник, время сбора, версию коллектора.
Слой 2. Нормализованные сущности
Здесь лежат уже очищенные объявления и объекты. Это основной рабочий слой для поиска, фильтров и API. Обычно это реляционная схема с таблицами объектов, объявлений, цен, параметров и медиафайлов. Важно, чтобы эта схема была версионируемой: при изменении правил нормализации вы сможете пересобрать данные из сырого слоя, не затронув пользовательский интерфейс.
Слой 3. История изменений
Без истории невозможно строить аналитику цен и мониторинг изменений. Полезно хранить события:
- появление объявления;
- изменение цены;
- смена статуса;
- обновление описания;
- снятие с публикации.
Событийная модель — это не просто логи, а полноценный источник для аналитики. Например, по событию «цена изменилась» можно рассчитать динамику по району, а по «снято с публикации» — скорость продажи объектов. Поэтому события должны быть типизированы и храниться с временными метками.
Слой 4. Поисковый индекс
Для быстрых фильтров лучше использовать отдельный индекс, а не нагружать основную базу сложными запросами. Это особенно важно, если пользователь одновременно фильтрует по району, цене, метражу, типу дома и расстоянию до метро. В роли поискового индекса хорошо себя показывают Elasticsearch, Typesense или даже специализированные решения на базе колоночных СУБД.
Поисковый контур: как сделать быстрый интерфейс
Пользователь агрегатора не хочет ждать. Поэтому поиск должен быть вынесен в отдельный контур и оптимизирован под чтение. На практике это значит, что поисковый индекс обновляется асинхронно после нормализации и дедупликации, а пользовательские запросы идут только в этот индекс.
Что важно в поиске
- геофильтрация по карте;
- диапазоны цен и площадей;
- фильтры по комнатности, этажу, ремонту, типу дома;
- сортировка по дате, цене, ликвидности;
- быстрый отклик даже при большом объёме данных.
Что обычно ускоряет систему
- отдельный поисковый индекс;
- предрасчитанные поля;
- кэширование часто используемых запросов;
- денормализация данных для чтения;
- ограничение тяжёлых запросов на уровне API.
Отдельно стоит отметить предрасчёт агрегатов. Например, если в выдаче нужно показывать медианную цену по ЖК, не стоит считать её на лету при каждом запросе — лучше хранить готовое значение в индексе и обновлять по расписанию.
Как строить аналитику поверх агрегатора
Если сервис умеет не только показывать объявления, но и анализировать рынок, его ценность растёт в разы. Для этого нужно заранее заложить событийную модель данных, о которой я говорил выше. Без истории изменений аналитика превращается в статичный срез, который быстро устаревает.
Какие метрики полезны
- динамика средней цены по району;
- изменение цены конкретного объекта;
- скорость снятия объявлений;
- распределение объектов по метражу и комнатности;
- количество новых объявлений по дням;
- доля дублей;
- активность по источникам.
Почему это важно
Обычная выдача отвечает на вопрос «что есть сейчас». Аналитика отвечает на вопросы:
- как быстро объект дорожает или дешевеет;
- насколько стабилен район;
- где рынок перегрет;
- какие объявления зависают слишком долго;
- какие источники дают наиболее полные данные.
Для пользователя это уже не просто список квартир, а инструмент для оценки ликвидности и принятия решения. Например, если объект висит на площадке три месяца с постепенным снижением цены, это сигнал о завышенной начальной стоимости или проблемах с локацией. Агрегатор может подсветить такие моменты автоматически.
Безопасность и устойчивость
У агрегатора недвижимости есть две особые боли: нестабильность внешних источников и риск технических ошибок в собственной системе. Источники могут менять вёрстку, вводить капчу, банить IP, отдавать неполные данные. А внутренние баги могут привести к потере или искажению данных.
Что нужно предусмотреть
- повторные попытки при временных сбоях;
- таймауты и лимиты на запросы;
- изоляцию источников;
- мониторинг ошибок парсинга;
- алерты на резкое падение объёма данных;
- логи по каждому этапу пайплайна;
- версионирование схемы данных.
Типовая ошибка
Часто разработчики думают только о старте парсинга и забывают о жизненном цикле данных. В результате объявления успешно собираются, но потом невозможно понять:
- когда объект изменился;
- почему пропал;
- кто источник дубля;
- что было в карточке вчера.
Для рынка недвижимости это критично. Представьте, что вы анализируете динамику цен, а у вас нет истории — только текущее состояние. Вы не сможете ответить, как менялась цена за последние месяцы, и все выводы будут поверхностными. Поэтому логирование и событийная модель — это не «потом добавим», а фундамент.
Монолит или микросервисы
Для небольшого продукта часто достаточно модульного монолита. Это проще и быстрее в разработке, меньше инфраструктурных накладных расходов. Но по мере роста нагрузки и числа источников архитектура обычно эволюционирует в более разнесённую.
Когда достаточно монолита
- несколько источников;
- небольшой объём объявлений;
- один поисковый сценарий;
- один разработчик или маленькая команда.
В таком случае можно держать сбор, нормализацию и поиск в одном приложении, но с чёткими модульными границами: отдельные классы или пакеты для коллекторов, нормализаторов и поискового слоя. Это позволит безболезненно выносить компоненты в отдельные сервисы позже.
Когда стоит делить на сервисы
- много источников с разной логикой;
- отдельные команды отвечают за сбор, поиск и аналитику;
- высокая нагрузка;
- нужны независимые релизы;
- есть интеграции с внешними CRM и API клиентов.
Практичный подход — начинать с модульной архитектуры, но сразу выделить чёткие границы: сбор, нормализация, поиск, аналитика, API. Если границы определены правильно, переход к микросервисам становится вопросом инфраструктуры, а не переписывания логики.
Таблица: какие решения подходят для разных стадий проекта
| Стадия | Что важно | Архитектурный приоритет |
|---|---|---|
| MVP | Быстро собрать первые источники | Простая схема, минимальный набор сущностей |
| Ранний продукт | Стабильность и обновления | Очереди, логирование, нормализация |
| Рост | Качество выдачи и скорость | Дедупликация, поисковый индекс, аналитика |
| Платформа | Интеграции и масштабирование | Модульность, API, отдельные контуры обработки |
Типовые ошибки при проектировании
- Смешивать сбор, обработку и выдачу в одном сервисе.
- Хранить только финальный результат без сырья и истории.
- Игнорировать дедупликацию.
- Делать поиск напрямую по основной базе.
- Не закладывать повторную обработку при изменении правил нормализации.
- Недооценивать логирование и мониторинг.
- Строить архитектуру под один источник, а не под сеть источников.
Практический чек-лист для запуска
Перед запуском сервиса проверьте, что у вас есть:
- список источников с приоритетами;
- схема нормализации полей;
- очередь задач;
- механизм повторных попыток;
- хранилище сырых данных;
- слой нормализованных объявлений;
- история изменений;
- дедупликация;
- отдельный поисковый индекс;
- API для выдачи данных;
- мониторинг ошибок и объёма данных;
- регламент обновления источников.
Как понять, что архитектура работает
Хорошая архитектура сервиса агрегации объявлений недвижимости проявляется не в красивой диаграмме, а в устойчивости на реальном потоке данных.
Признаки здоровой системы:
- новые источники подключаются без переписывания ядра;
- один сломанный источник не останавливает весь сервис;
- поиск отвечает быстро даже при росте базы;
- дублей становится меньше;
- аналитика строится без ручной выгрузки;
- изменения в объявлениях отслеживаются автоматически.
Вывод
Архитектура сервиса агрегации объявлений недвижимости должна быть построена вокруг данных, а не вокруг интерфейса. Сначала нужен надёжный конвейер: сбор, нормализация, дедупликация, хранение истории и быстрый поиск. Только потом имеет смысл добавлять аналитику, интеграции и сложные пользовательские сценарии.
Если система спроектирована правильно, она не просто показывает объявления, а превращает хаос рынка в понятный набор решений: что смотреть, что сравнивать, где переплатили, а где объект действительно стоит внимания. Это и есть главная ценность для пользователя — экономия часов ручного мониторинга и уверенность в том, что решение принято на основе полных и актуальных данных.
FAQ
Чем агрегатор объявлений отличается от обычного парсера?
Парсер только собирает данные, а агрегатор дополнительно нормализует, удаляет дубли, обновляет карточки и предоставляет удобный поиск и аналитику. Парсер — это один из компонентов агрегатора, но не весь продукт.
Нужна ли отдельная база для истории изменений?
Да. Без истории невозможно отслеживать динамику цены, статус объекта и качество источников. История нужна для аналитики, выявления трендов и восстановления данных при ошибках.
Что важнее: парсинг или дедупликация?
Оба этапа важны, но дедупликация сильнее влияет на качество продукта для пользователя. Сырые данные без неё быстро теряют ценность: выдача засоряется повторами, а аналитика искажается.
Можно ли начать с монолита?
Да, если проект небольшой. Но границы между сбором, обработкой, поиском и API лучше выделить сразу — это сэкономит время при последующем масштабировании.
Почему нельзя строить поиск прямо по основной базе?
Потому что это быстро начинает тормозить при росте данных и сложности фильтров. Отдельный поисковый индекс даёт стабильную скорость и лучше подходит для геофильтров, полнотекстового поиска и агрегаций.
