Парсинг Telegram: как мы автоматизировали сбор скрытых ID чатов через Telethon и сэкономили 20 часов рутины

Клиент пришел с классической задачей: нужно в реальном времени парсить теплые лиды из комментариев под постами в 50 крупных Telegram-каналах конкурентов. Стек: Python 3.11, Telethon, n8n для маршрутизации и Google Sheets.

Проблема: чтобы парсер цеплял комментарии, ему нужен ID привязанной группы обсуждения. Но на крупных каналах админы скрывают чаты из поиска и ставят запрет на копирование ссылок. В этой статье я покажу, как мы перестали страдать фигней с ручным вытаскиванием ID, дернули метаданные напрямую через API Телеграма и сэкономили менеджерам десятки часов монотонной работы. На выходе — готовый скрипт-сканер, который делает всю грязную работу за 2 секунды.

Точка А: Просто жесть и ручной привод

Логика работы Telegram устроена так: канал — это просто витрина. Все комментарии физически лежат в отдельной привязанной группе (Linked Chat). Чтобы наш боевой парсер мог слушать новые сообщения, ему нужно скормить ID этой группы в формате -100XXXXXXXXXX.

Но вот такая вот байда: админы каналов не дураки. Они скрывают группу из публичного доступа. В официальном клиенте (UI) нет кнопки «Скопировать ID чата».

Как это обычно решают джуны или менеджеры руками?

  1. Подписываются на канал с фейка.

  2. Пишут тестовый комментарий под постом.

  3. Переходят в ветку ответов, копируют ссылку на свой коммент.

  4. Вытаскивают из ссылки ID.

Для 3-5 каналов — терпимо. Для 50-100 каналов, которые постоянно обновляются — это просто жесть и рашн бизнес. Менеджер тратит на это полдня.

Инженерное решение: API не врет

Почесал репу и вспомнил базовую архитектуру клиент-серверных приложений. Интерфейс Telegram может скрывать от пользователя всё что угодно. Но когда вы открываете канал, мобильный клиент обязан знать ID привязанного чата, чтобы при нажатии на кнопку «Прокомментировать» отправить вас по нужному адресу.

Значит, сервер отдает этот ID в метаданных. Мы не будем ломать защиту, мы просто вежливо попросим API отдать нам то, что оно и так отдает любому клиенту.

Архитектура сканера

Мы не пихаем этот код в основной боевой парсер. Мы делаем изолированный микро-скрипт (сканер), который запускается по требованию, пробегается по списку юзернеймов, выплевывает готовые ID и завершает работу.

Листинг кода

Создаем скрипт list_chats.py на сервере:

codePython

import re
import asyncio
from telethon import TelegramClient
from telethon.tl.functions.channels import GetFullChannelRequest

# 1. Безопасный импорт ключей (никогда не хардкодим в скрипте!)
# Скрипт сам заберет ключи из основного конфига парсера
with open('/root/reserve_parser/config.py', 'r') as f: 
    content = f.read()
    
api_id = re.search(r'API_IDs*=s*(d+)', content).group(1)
api_hash = re.search(r"API_HASHs*=s*['"]([^'"]+)['"]", content).group(1)
session_path = re.search(r"SESSION_PATHs*=s*['"]([^'"]+)['"]", content).group(1)

client = TelegramClient(session_path, int(api_id), api_hash)

# 2. Список каналов для сканирования (без @)
CHANNELS = ['competitor_channel_1', 'target_blog_2', 'industry_news_3']

async def main():
    await client.start()
    for channel in CHANNELS:
        try:
            # 3. Дергаем полные метаданные канала через API
            full_channel = await client(GetFullChannelRequest(channel))
            
            # 4. Проверяем, привязан ли вообще чат к каналу
            if full_channel.full_chat.linked_chat_id:
                # Форматируем ID под стандарт Telethon (добавляем -100)
                telethon_id = int(f"-100{full_channel.full_chat.linked_chat_id}")
                print(f"Канал: @{channel} ➔ ID чата: {telethon_id}")
            else:
                print(f"Канал: @{channel} ➔ Комментарии отключены")
                
        except Exception as e:
            print(f"Ошибка обработки канала @{channel}: {e}")
            
    await client.disconnect()

if __name__ == '__main__':
    asyncio.run(main())

Что делают критические строки:

  • GetFullChannelRequest(channel) — тот самый магический метод API. Он запрашивает у серверов Telegram полную карточку канала, включая скрытые системные поля.

  • full_channel.full_chat.linked_chat_id — вытаскиваем нужный нам параметр.

  • int(f»-100…») — API отдает «голый» ID (например, 1684305947). Но для работы с методами прослушивания событий Telethon требует префикс супергруппы -100. Форматируем сразу на лету, чтобы менеджер мог просто сделать Ctrl+C -> Ctrl+V в таблицу.

Разбор ошибок: Изолированная отладка

При внедрении скрипта словили пару нюансов. Давлю спокуху и фиксим:

  1. Бан основного аккаунта (FloodWait).
    Никогда не запускайте такие сканеры с вашего личного или основного боевого аккаунта парсера. Если скормить скрипту сразу 500 каналов, антиспам Телеграма выпишет вам FloodWait на сутки.
    Решение: Используем отдельную техническую сессию (резервный аккаунт). Если каналов много, добавляем await asyncio.sleep(2) в цикл for.

  2. Каналы без комментариев.
    Скрипт падал с ошибкой NoneType object has no attribute, если мы сканировали канал, где админ вообще отключил комментарии (привязанного чата физически нет).
    Решение: Обернули извлечение ID в проверку if full_channel.full_chat.linked_chat_id:.

Точка Б и профит для бизнеса

Инструкция для сотрудника сократилась до одного действия:

  1. Закинул новые юзернеймы в скрипт.

  2. Запустил python3 list_chats.py.

  3. Забрал готовые ID с минусом и вставил в колонку CHANNELS_COM в Google Таблицу.

  • Скорость: Сбор ID для 50 каналов занимает ровно 3 секунды вместо 4 часов ручной работы.

  • Надежность: Мы не зависим от UI Телеграма и запретов на копирование. API отдает данные всегда.

  • Безопасность: Боевой парсер не перегружен лишней логикой, сканер работает как независимый микросервис.


P.S. В бизнесе побеждает тот, кто быстрее добирается до данных. Если вам нужно настроить стабильный поток лидов из Telegram и связать всё это с CRM так, чтобы оно работало как часы, без костылей и ограничений — пишите мне в Telegram, обсудим задачу на языке цифр и сроков.

Автор: chernyaevi

Источник

Оставить комментарий