Поднял сервер на старом телефоне Samsung и сделал там бота для сообщений о новых квартирах в Белграде
КодингСамый крупный сайт недвижимости в Сербии Halo Oglasi позволяет подписаться на обновления по фильтру. Но оповещения приходят раз в сутки. За это время усердные экспаты успевают выгрести все интересные квартиры, просто проверяя сайт каждые 15 минут. Я написал бота, который парсит каталог по фильтру и присылает оповещения о новых квартирах в Telegram через 3–5 минут после публикации.
План: скрипт на сервере, уведомления в мессенджер. Но Halo Oglasi блокирует сервер из Германии. Резервный сервер тоже. Ради прикола я решил запилить бота на старом смартфоне.
В итоге бот живёт на Samsung Galaxy S9+, который лежит у меня без дела.
Хронология: что я пробовал, где ошибся и почему план поменялся несколько раз. Если вы хотите сделать похожего бота для своего сайта объявлений, эта история сэкономит вам вечер.
Задача: успевать раньше других
Halo Oglasi — главный сайт объявлений в Сербии. Нужно найти квартиру в чётко определённой локации и с отфильтрованными характеристиками.
Новые варианты появляются почти каждый день, и хорошие варианты по нормальной цене уходят быстро.
У меня было три способа получать новые объявления:
- Подписка на поиск на самом сайте. Бесплатно и без кода. Но письмо приходит не чаще раза в день, есть ещё режимы «раз в три дня» и «раз в неделю». За сутки хорошую квартиру успевают забрать.
- Проверка по расписанию через внешний сервис. Не чаще раза в час. Тоже медленно.
- Свой бот. Скрипт раз в несколько минут открывает поиск, сравнивает с прошлым результатом и шлёт в Telegram только новое.
Нужна была скорость, поэтому я выбрал третий вариант.
При первом разборе страницы выяснилось, что вверху выдачи сайт показывает платные «поднятые» объявления. Они не всегда соответствуют фильтрам. Если просто собирать всё со страницы, бот будет присылать мусор. Значит, каждое объявление нужно дополнительно проверять по моим условиям.
Первый план: сервер и Python
Боту нужна машина, которая работает круглосуточно. Сначала попробовал свой сервер на Hetzner. Там работают другие мои боты.
Вот что умеет бот:
- проверяет поиск в случайный момент через 3–5 минут, чтобы запросы не шли по секундомеру;
- просматривает до 5 страниц выдачи;
- шлёт в Telegram карточку: фото, цена, цена за м², площадь, комнаты, этаж, адрес, кто продаёт — собственник или агентство;
- отдельно сообщает о снижении цены: старая цена зачёркнута, новая жирным;
- помнит, что уже прислал, и не шлёт дубли;
- при первом запуске молча запоминает все текущие объявления и пишет одно сообщение «Начинаю следить». Дальше — только новое;
- умеет дополнительные фильтры: потолок цены, потолок цены за м², минимальная площадь, «только от собственника»;
- если 6 проверок подряд не удались, присылает предупреждение, а страницу сохраняет в файл для разбора.
Настройки лежат в одном файле .env. Чтобы следить за другим поиском, достаточно настроить фильтры на сайте и вставить ссылку. Можно несколько ссылок через пробел.
Для установки написал скрипт install.sh: он ставит зависимости и создаёт системную службу, которая перезапускает бота при сбое. Весь бот в архиве весил 9,7 КБ.
Стена: Cloudflare не пускает серверы
Первый же тестовый запуск закончился ошибкой: сайт ответил кодом 403, «доступ запрещён».
Сначала я подумал, что виноват код. В заголовках бот представлялся Chrome 129, а сетевой «отпечаток» соединения был от другой версии. Защита от ботов такие несостыковки замечает.
Я проверил сайт с сервера тремя способами:
- Обычный curl — 403. В ответе заголовки server: cloudflare и cf-mitigated: challenge.
- Библиотека curl_cffi, которая подделывает отпечаток браузера: Chrome, Chrome 131, Safari, Firefox — везде 403 и страница «Just a moment...».
- Главная страница сайта — тоже 403.
Диагноз: перед сайтом стоит Cloudflare. С адресов дата-центров он требует пройти проверку браузера с JavaScript. Код ни при чём.
Следующая гипотеза: поставить на сервер настоящий браузер. Я установил Chromium через Playwright, около 150 МБ. Первый тест прошёл за 2 секунды. Слишком быстро.
Оказалось, это моя ошибка в проверке. Сайт открылся по-сербски, и вместо «Just a moment» в заголовке было «Sačekajte trenutak...». Скрипт искал английское слово, не нашёл его и решил, что проверка пройдена.
Я переписал тест: ждать до 45 секунд и смотреть, появится ли настоящий заголовок «Halo oglasi». Запустил в двух режимах — невидимый браузер и браузер на виртуальном экране. Оба 45 секунд висели на «Sačekajte trenutak...». Результат: не прошёл.
Дальше на сервере оставались только инструменты обхода: «невидимые» браузеры, сервисы решения капчи, платные чужие домашние прокси. Я от них отказался по двум причинам.
Первая: это прямое противостояние защите сайта. Сайт ясно говорит, что не хочет автоматических запросов с серверов. Вторая: такие обходы ненадёжны, Cloudflare регулярно их закрывает. Бот, который ломается раз в месяц, мне не нужен.
Получается, что бот должен ходить на сайт как обычный посетитель — с обычного домашнего интернета и не чаще человека с открытой вкладкой.
Где взять домашний интернет, который не спит
Я проверил сайт с MacBook из дома через curl_cffi. Код 200, заголовок «Prodaja stanova Banovo brdo | Halo oglasi». С домашнего адреса Cloudflare пускает.
Там же, на Mac, я впервые запустил бота на живом сайте. Он нашёл 57 объявлений со всех страниц. Цена, площадь, комнаты, этаж, адрес и дата распознались верно. Нашлись и три объявления от собственников.
Код работал. Оставалось найти домашнее устройство, которое не выключается.
MacBook. Не подходит: ноутбук засыпает, а бот должен работать 24/7.
iPhone не подходит. iOS замораживает приложение через несколько секунд или минут после того, как оно ушло с экрана. Терминалы для iPhone есть, но бот в них работает, только пока приложение открыто. Автоматизации в «Командах» нельзя запускать каждые 3–5 минут круглосуточно.
Samsung Galaxy S9+. Старый Android-телефон лежал без дела. Android позволяет приложению работать в фоне постоянно. Телефон стоит на домашнем Wi‑Fi, потребляет копейки, а нагрузка от бота мизерная. Его я и выбрал.
Телефон как сервер: Termux
На Android есть Termux — полноценный Linux-терминал. В нём работают Python, SSH и всё, что нужно боту.
Установка. Версия Termux из Google Play устарела, поэтому я скачал APK с F-Droid. Телефон ответил «Приложение не установлено». Кнопка «Всё равно установить» в окне Play Защиты не помогла. Сработало так: временно выключил Play Защиту и скачал сборку для arm64 с GitHub разработчиков Termux.
Первые команды. Вставлять текст в Termux можно долгим нажатием, но набирать и проверять длинные команды на телефоне — мучение. Поставил на телефон SSH, чтобы дальше управлять им с Mac, как обычным сервером.
На этом пути Termux сломался. Он обновился наполовину: curl новый, а библиотека шифрования старая. Ошибка CANNOT LINK EXECUTABLE "curl". Из-за этого не работал и менеджер пакетов pkg — он сам использует curl. Помогло полное обновление через apt full-upgrade напрямую. Потом ещё раз обновил список пакетов, поставил openssh, задал пароль и запустил sshd.
На самом телефоне я ввёл всего несколько команд. Дальше подключился с Mac по SSH на порт 8022 и работал уже со своей клавиатуры.
Проверка сайта с телефона. Обычный curl — снова 403. Это меня не остановило: на Mac тоже пропускал не обычный curl, а curl_cffi. Cloudflare смотрит не только на IP, но и на то, как программа устанавливает соединение. Я поставил на телефон Python 3.14 и curl_cffi. Ответ — код 200.
Запуск: бот работает
Проверка. На телефоне бот нашёл те же 57 объявлений. Все поля распознались.
Постоянная работа. На сервере за перезапуск отвечал systemd. На Android его нет, поэтому я поставил termux-services — лёгкий менеджер служб runit. Он запускает бота и поднимает его после сбоя. Логи пишутся в отдельный файл.
Чтобы Android не усыплял процессор, включил termux-wake-lock. В настройках телефона нужно дать Termux неограниченную работу от батареи и добавить его в «никогда не спящие» приложения.
Автозапуск после перезагрузки. Для этого есть приложение Termux:Boot. Оно не установилось: то же «Приложение не установлено». Я сделал запасной вариант: при открытии Termux сам запускает wake-lock и SSH, а служба бота стартует вместе с ним. Правило простое: перезагрузил телефон — один раз открыл Termux.
Стек
| Задача | Чем решил |
|---|---|
| Язык | Python (3.12 на сервере, 3.14 в Termux) |
| Загрузка страниц | curl_cffi с отпечатком Chrome |
| Разбор объявлений | BeautifulSoup, регулярные выражения |
| Память бота | SQLite: виденные объявления и их цены |
| Уведомления | Telegram Bot API: фото с подписью или текст |
| Настройки | Файл .env |
| «Сервер» | Samsung Galaxy S9+, Termux |
| Служба и перезапуск | termux-services (runit) |
| Чтобы не засыпал | termux-wake-lock |
| Управление | SSH с MacBook на порт 8022 |
| Отброшено | Oracle Cloud, Hetzner + systemd, Playwright + Chromium |