Я не разработчик, но за неделю собрал поиск по миллиону Telegram-каналов. Сервер за 17 рублей в день пережил это не сразу

Всё началось с обычной задачи: мне понадобилось найти Telegram-каналы для рекламы. Не один конкретный канал по названию, а, например, живые площадки про недвижимость, нейросети или новости отдельного города.

И вот тут поиск превращается в ручную работу. Канал ещё существует? Когда там был последний пост? Сколько людей действительно смотрят публикации? Не нарисованы ли подписчики? Пока откроешь двадцать страниц и перенесёшь цифры в таблицу, уже забудешь, с чего начал.

Я решил сделать свой поиск. Это звучит самоуверенно, потому что я не программист. Код писали Claude и Codex, а я ставил задачи, проверял результат и решал, что должно быть на сайте. Иногда один ИИ проверял работу другого. Как выяснилось, это не роскошь: ошибки они тоже делают вполне человеческие.

За неделю проект вырос из небольшой базы в такой срез:

  • 1 470 638 найденных кандидатов;
  • 1 467 027 уже проверены;
  • 1 002 860 определены как живые публичные каналы;
  • у 869 387 есть данные о среднем охвате;
  • 610 804 содержательные русскоязычные карточки допущены в sitemap;
  • сама база SQLite занимает 1,3 ГБ.

Всё это сейчас работает на VPS с одним ядром, 2 ГБ памяти и диском 15 ГБ. Он стоит 17 рублей в день.

Цифры, определения и ограничения я вынес в отдельное исследование публичных Telegram-каналов, чтобы их можно было проверить и цитировать независимо от этой статьи.

Сервис я назвал PoiskTG. Расскажу, как он устроен и почему большую часть недели я не добавлял новые функции, а спасал уже добавленные. А в конце — что с ним стало через два с половиной месяца: там нашлась ошибка, из-за которой каталог молча терял по 15 тысяч каналов в день.

Я не разработчик, но за неделю собрал поиск по миллиону Telegram-каналов. Сервер за 17 рублей в день пережил это не сразу

Миллион строк — ещё не миллион каналов

Сначала в базу попадает только кандидат: публичный username подходящего формата. Их мы брали из открыто опубликованных исследовательских наборов, доступных публичных страниц, пользовательских добавлений и упоминаний в других публичных каналах.

Участников групп, номера телефонов, личные аккаунты и приватные каналы я не собираю. Для каталога это не нужно.

После импорта каждого кандидата надо проверить. Существует ли публичная страница, есть ли название и описание, сколько подписчиков, когда выходил последний пост, виден ли охват. Боты и недоступные страницы не удаляются бесследно, а получают отдельный статус и исчезают из публичной выдачи.

На срезе от 21 июля проверка дала довольно отрезвляющий результат. Из 1,47 млн записей 463 713 оказались недоступны, ещё 4 010 — ботами. До конца очереди оставалось 3 611 адресов.

Поэтому число на главной — не размер таблицы. Там показываются только проверенные каналы с нормальным заголовком и без статуса «бот» или «недоступен». Иначе можно было бы написать «1,47 млн каналов», но почти полмиллиона из них посетителю ничего не дадут.

Слово «живой» тоже не означает, что канал хорош для рекламы. Это значит только, что публичная страница доступна и прошла базовую проверку. Для выбора площадки всё равно надо смотреть охват, активность и содержание.

Первые результаты поиска были смешными

В основе поиска стоит SQLite FTS5. Для маленького сервера это отличный вариант: отдельный поисковый кластер не нужен, запросы быстрые. Но FTS понимает слова, а не намерение человека.

Я ввёл «музыка» и получил десятки каналов с названием вроде «Музыка» и аудиторией 8–40 человек. При этом Егор Крид, SHAMAN и Radio Record в нашей базе были, но по такому запросу не находились. У артиста могло не быть слова «музыка» ни в названии, ни в описании, а импортированная категория вообще называлась «Блоги».

Переписать всем основную категорию автоматически казалось простым решением. Мы попробовали правила и довольно быстро получили канал «Построим свой дом» в категории «СВО»: короткий фрагмент сво нашёлся внутри обычного слова. После такого я отказался от идеи безоговорочно «исправлять» исходные категории.

Вместо этого сделали отдельные тематические связи. Канал может оставаться в своей основной категории, но дополнительно иметь подтверждённую тему «Музыка», «Финансы» или «Авто». Сейчас таких связей 34 916 для 33 299 каналов. Они участвуют в поиске, похожих каналах и тематических страницах.

Ранжирование теперь смотрит не только на точность текста. Учитываются тема, язык, число подписчиков, охват, активность и свежесть публикаций. Это ещё не уровень большой аналитической платформы, но выдача перестала состоять из буквальных тёзок.

Я не разработчик, но за неделю собрал поиск по миллиону Telegram-каналов. Сервер за 17 рублей в день пережил это не сразу

Мы пробовали и тяжёлую многоязычную модель e5-large. На нашем железе она давала слишком большую задержку и легко съедала запас памяти. В итоге основной поиск оставили на FTS5, тематических связях и понятных правилах. Векторную часть вынесли отдельно и не сделали обязательной: если она выключена, сайт всё равно должен нормально искать.

Как два ИИ работали на одном сервере

Сначала я просто по очереди просил Claude и Codex менять проект. Очень быстро стало понятно, что так можно потерять чужие правки. Один помощник загрузит свою версию — и затрёт то, что несколько минут назад сделал другой.

Мы завели общий Git-репозиторий прямо на сервере и отдельного пользователя с доступом к папке проекта и перезапуску нужных сервисов. Перед изменениями — проверка состояния, после — отдельный коммит и тест на работающем сайте.

Это действительно помогло. Например, один ассистент добавил безопасное чтение SQLite через mode=ro. Второй заметил редкий сценарий: после чистого рестарта у WAL-базы ещё нет файла -shm, и первые запросы могут падать с unable to open database file. Исправили откатом на обычное соединение с PRAGMA query_only=ON.

В другой раз мы ускорили страницы категорий с двух секунд до нескольких сотых. Всё выглядело прекрасно, пока проверка не показала, что из «Музыки» снова исчезли Егор Крид и SHAMAN. Кеш собирался быстро, но забыл про дополнительные темы. Данные не потерялись, однако пользователю от этого было не легче. Запрос кеша пришлось переделать.

Так что схема «одна нейросеть пишет, другая проверяет» полезна, но не отменяет обычную проверку руками. Иногда именно я замечал проблему просто потому, что открывал сайт как посетитель и видел странный результат.

Потом сервер лёг

Первый VPS был с 1 ГБ оперативной памяти. На нём одновременно жили сайт, обходчик и растущая SQLite. Некоторое время всё работало, а потом страницы начали отдавать 504.

В худший момент веб-процесс разросся примерно до 725 МБ, система ушла в swap, а load average поднялся выше 200. Параллельно обходчик продолжал проверять каналы и конкурировал с сайтом за память и диск. Nginx был жив, но дождаться ответа приложения не мог.

База при этом не разрушилась. Обходчик сохранял результат частями, WAL отработал как положено, проверка целостности показывала ok. Упал именно обслуживающий слой.

Сначала мы остановили фоновые задачи и подняли сайт. Потом поставили ограничения, чтобы та же история не повторилась:

  • веб-приложение и обработка базы работают раздельно;
  • два обходчика не могут запуститься одновременно — их останавливает общий файловый lock;
  • у веб-сервиса появились лимиты памяти и автоматический перезапуск;
  • тяжёлые категории и рейтинги отдаются из заранее подготовленного кеша;
  • при серии ошибок внешний источник ставится на паузу, а курсор сохраняется;
  • nginx ограничивает слишком частые параллельные запросы с одного адреса.

После этого я всё-таки увеличил сервер до 2 ГБ. Не потому, что оптимизация стала не нужна, а потому, что держать сайт, базу на 1,3 ГБ и постоянную проверку каналов в одном гигабайте было уже упрямством.

До кеширования страницы категорий отвечали за 1,2–2,3 секунды и при параллельных запросах иногда не успевали вовсе. Сейчас свежий замер с самого сервера выглядит так:

  • рейтинг каналов — 0,058 секунды;
  • категория «Музыка» — 0,059 секунды;
  • карточка канала — 0,067 секунды.

Сам веб-процесс после оптимизаций занимает около 74 МБ. Поисковым роботам Яндекса и Google страницы открыты. Ограничение действует на частоту обращений, а не на саму возможность индексировать каталог.

Что уже можно делать на сайте

Сейчас в PoiskTG можно искать каналы по теме и username, фильтровать их по размеру аудитории, типу и активности, сравнивать средний охват и ER. Подходящие площадки отмечаются звёздочкой и выгружаются в CSV как простой медиаплан.

У каждого канала есть отдельная карточка, похожие площадки и история доступных показателей. Есть рейтинги, форма добавления канала, заявка на подбор для рекламодателя, Telegram-бот и компактный Mini App.

В sitemap я не отправляю весь миллион. Туда попадают только русскоязычные карточки без 18+, у которых есть заголовок и хотя бы одно содержательное поле: описание, охват или дата публикации. На момент среза таких страниц было 610 804. Тематических разделов — 60.

Это всё ещё молодой продукт. Категория «Разное» слишком большая, часть тем требует ручной доработки, а качество похожих каналов приходится проверять на конкретных примерах. Я не пытаюсь представить его готовой заменой TGStat или Telemetr. У них годы исторических данных и намного больше инфраструктуры.

Моя ближайшая задача скромнее: сделать удобный бесплатный подбор площадок, где человек за несколько минут получает нормальный список для рекламы, а не начинает собственное расследование по каждому username.

Через два месяца каталог начал таять

Всё выше я написал в июле и не опубликовал. В октябре открыл сайт и увидел на главной не миллион каналов, а меньше 900 тысяч. Никто ничего не удалял.

По журналу обходчика счётчик падал каждый день: 1 004 195 каналов 26 сентября, 951 414 — 30-го, 890 652 — 4 октября. Минус 12–16 тысяч в сутки.

Причину Claude нашёл, когда я попросил его проверить сайт «свежими глазами». Обходчик раз в месяц перепроверяет каждый канал по его публичной странице. Если страница пустая — канал помечается недоступным, и его карточка на сайте отдаёт 404. Логично. Но когда Telegram считает, что запросов с одного адреса слишком много, он отдаёт на живой канал точно такую же страницу, как на несуществующий: код 200, «Telegram: Contact @name» и больше ничего. По одному ответу их не различить.

Проверили это прямо на сервере. Взяли 160 заведомо живых крупных каналов и опросили их в четыре потока, как это делал обходчик. 107 из 160 «не существовали». Потом те же каналы спросили медленно, по одному — 23 из 25 оказались на месте.

Притормаживает Telegram не ровно, а эпизодами по минуте-две, и от паузы между запросами это почти не зависит. Поэтому одной «вежливой задержкой» не обойтись. За месяц обходчик успел спрятать 314 тысяч каналов с названием. На случайной выборке примерно половина из них была жива.

Исправление получилось таким. Пустой ответ теперь только подозрение. В конце прогона подозрительные каналы перепроверяются небольшими порциями, а до и после каждой порции обходчик спрашивает несколько заведомо живых каналов. Недоступным канал считается, только если он снова промолчал, а все контрольные ответили. В первом же полном прогоне из 434 пустых ответов 314 каналов ожили, а недоступными остались 17. Раньше за прогон прятались 800–900.

Скрытые каналы сейчас возвращаются отдельным проходом. Из первых 19 тысяч, начиная с самых крупных, вернулись 86–90%.

Заодно выяснилось, почему Яндекс выкидывает страницы

Яндекс дважды брал в индекс около 110 тысяч наших карточек и оба раза потом выбрасывал десятки тысяч обратно. В Вебмастере 228 645 страниц висели исключёнными как «малоценные или маловостребованные». Посещаемость ходила ровно за индексом: 117–135 визитов в день на пике и 60–80 после.

Оказалось, что на каждой карточке канала, кроме его цифр, стоял весь текст с главной: форма поиска, блок «Зачем отдельный поиск» и ответы на частые вопросы. Три четверти текста на 600 тысячах страниц были одинаковыми. Плюс ложные 404 от обходчика. После чистки карточка похудела с 81 до 16 КБ, а в карту сайта теперь попадают только каналы от 100 подписчиков — 381 745 страниц вместо полумиллиона. Сработало ли это, станет понятно недели через две, когда поисковик переобойдёт страницы.

И ещё две ошибки — уже в цифрах

Первая. В логах сервера были сотни переходов «из Google». В Search Console за три месяца — четыре клика. Это парсеры: они подставляют Google как источник перехода, чтобы пройти мою же защиту от копирования базы. Так что считать посетителей по логам сервера нельзя, только по счётчику.

Вторая — к вопросу о том, можно ли верить ИИ на слово. В тот же вечер Claude заметил, что подключения к серверу иногда обрываются, решил, что это сеть хостинга, и с моего согласия написал в поддержку. Поддержка ответила, что фильтрации нет. Тогда он записал пакеты на сервере и параллельно проверил ещё три адреса. К Яндексу и Cloudflare подключения с моего компьютера обрывались так же часто. Сбоил мой домашний интернет. В заметках проекта теперь так и записано: «прежде чем винить хостинг, сравнить с контрольными адресами».

Сколько это стоило

Считаю честно, вместе с инструментами:

  • подписка на Claude — 100 $ в месяц;
  • подписка на ChatGPT с Codex — 20 $ в месяц;
  • VPS — 17 ₽ в день, то есть около 510 ₽ в месяц;
  • домен в зоне .ru — около 300 ₽ в год.

Вся серверная часть проекта обходится дешевле чашки кофе в неделю. Основная статья расходов — подписки на ИИ, 120 $ в месяц. Правда, их я оплачивал бы и без этого проекта: это мой рабочий инструмент, а не отдельная трата под запуск.

Самая большая инвестиция — не деньги, а время. Неделя вечеров на постановку задач, проверку чужих цифр, ручные тесты и разбор того, что ИИ «уже исправил», а на самом деле нет. Код сейчас пишется быстро. Думать за продукт по-прежнему приходится самому.

Что оказалось самым полезным

За эту неделю я несколько раз хотел просто добавить серверу ресурсов и забыть о проблеме. Хорошо, что не сделал этого сразу. Дополнительная память скрыла бы часть ошибок, но не исправила параллельные обходчики, неправильный кеш или мусор в выдаче.

Ещё один вывод: ИИ очень быстро превращает идею в работающий код, особенно когда владелец проекта сам не разработчик. Но скорость создания кода легко перепутать со скоростью создания нормального продукта. Второе начинается там, где ты проверяешь цифры, воспроизводишь сбой и не веришь фразе «всё исправлено» без живого теста.

И наконец, собрать большой список оказалось легче, чем поддерживать его полезность. Импорт добавляет тысячи адресов за минуты. Проверка, очистка, категоризация и обновление занимают дни и будут идти постоянно. История с тающим каталогом — как раз про это: ничего не падало и не выдавало ошибок, просто данные каждый день становились чуть хуже, и два месяца этого никто не замечал.

Если вам приходится подбирать Telegram-каналы для рекламы, попробуйте PoiskTG на своём реальном запросе. Особенно интересно узнать не то, что работает, а где выдача ошиблась: такие примеры сейчас помогают больше всего.

PoiskTG — мой проект, поэтому ссылка ведёт на собственный сервис. Цифры первой части — по срезу базы на 21 июля 2026 года. Раздел про тающий каталог — по состоянию на 6 октября 2026 года: в этот день на главной 901 тысяча живых каналов, и число растёт по мере возврата скрытых.