Парсер постов Телеграм каналов и групп

Мы — команда Datacol, занимаемся созданием парсеров уже более 15 лет. Уже неоднократно создавали для наших пользователей парсера Телеграм каналов. Понимаем, что возможно вы захотите сами создать такой парсер, используя инструменты для вайб-кодинга. Эта статья поможет вам создать такой парсер самостоятельно. Если вы не хотите сами создавать парсер, обратитесь к нам, чтобы мы разработали пасрер Телеграм под ваши требования или отправили вам готовый парсер для тестирования.

Telegram стал одной из главных площадок для торговли, новостей, маркетинга и сообществ. Тысячи каналов публикуют товары, акции, объявления — и всё это ценная структурированная (или не очень) информация. Парсер постов Telegram позволяет автоматически извлекать эти данные, обрабатывать их и сохранять в удобный формат. После сбора контент можно обрабатывать (уникализировать, создать summary) и опубликовать на ваш сайт (Worpdress, Joomla, Tilda, другие CMS), сохранить в файл (JSON, CSV, XML) или базу данных.

Как написать парсер постов Telegram на C#: от настройки до анализа данных с OpenAI

В этой части статьи разберём, как написать собственный парсер Телеграм каналов на C# с нуля: настройку на стороне Telegram, выбор подхода, пошаговую реализацию и использование OpenAI для извлечения структурированных данных из неформатированного текста. Прежде чем писать код, нужно выбрать технологическую основу. У Telegram есть два принципиально разных способа взаимодействия с данными: Bot API vs MTProto Client API.

Bot API — простой, но ограниченный

Bot API — официальный HTTP-интерфейс для ботов. Доступен через обычные GET/POST запросы, не требует специальных библиотек.

Ограничения:

  • Бот должен быть администратором канала, чтобы читать его посты.
  • Нет метода для получения истории сообщений — только real-time через webhook/polling.
  • Нельзя парсить произвольные публичные каналы.
  • Ограниченный доступ к метаданным (просмотры, реакции).

Когда использовать: когда вы управляете каналом сами и хотите собирать новые сообщения в режиме реального времени.

Пример: polling новых сообщений через Bot API

var url = $"getUpdates?offset={offset}&timeout=30&allowed_updates=["channel_post"]";
var response = await _http.GetStringAsync(url);
var result = JsonSerializer.Deserialize<TgResponse<List<TgUpdate>>>(response);

foreach (var update in result.Result)
{
    if (update.ChannelPost != null)
    {
        Console.WriteLine($"Новое сообщение: {update.ChannelPost.Text}");
        offset = update.UpdateId + 1;
    }
}

MTProto Client API (WTelegramClient) — мощный и гибкий

MTProto — нативный протокол Telegram, тот самый, который использует официальное приложение. Библиотека WTelegramClient реализует его для .NET.

Возможности:

  • Полная история сообщений любого публичного канала, чата или группы.
  • Доступ к реакциям, просмотрам, пересылкам, медиафайлам.
  • Работа без добавления бота в канал.
  • Постраничная загрузка сотен тысяч сообщений.

Используем именно этот подход — он даёт максимум возможностей для парсера телеграм групп и каналов.

Настройка на стороне Telegram

Шаг 1: Получение API-ключей (для MTProto)

    1. Перейдите на [my.telegram.org/apps](https://my.telegram.org/apps)
    2. Войдите под своим номером телефона
    3. Создайте приложение — укажите название и платформу (Desktop)
    4. Получите api_id (число) и api_hash (строка)

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

Шаг 2: Создание бота (для Bot API — опционально)

Если планируете использовать Bot API параллельно:

    1. Найдите в Telegram @BotFather.
    2. Отправьте команду /newbot.
    3. Укажите имя и username бота.
    4. Получите токен вида 1234567890:AAF…
    5. Добавьте бота администратором в нужный канал.

Шаг 3: Сессионный файл

WTelegramClient сохраняет авторизацию в файл сессии telegram_session.dat. При первом запуске потребуется ввести код подтверждения из SMS/Telegram и пароль двухфакторной аутентификации (если включён). После этого повторная авторизация не нужна.

Реализация: пишем парсер постов Telegram на C#

Зависимости (NuGet)

<PackageReference Include="WTelegramClient" Version="3.*" />
<PackageReference Include="OpenAI" Version="2.*" />

Шаг 1: Конфигурация и подключение

using WTelegram;
using TL;

private static string? Config(string what) => what switch
{
    "api_id"           => "ВАШ_API_ID",
    "api_hash"         => "ВАШ_API_HASH",
    "phone_number"     => "+7XXXXXXXXXX",
    "verification_code"=> ReadInput("Код из Telegram: "),
    "password"         => ReadInput("Пароль 2FA: "),
    "session_pathname" => "telegram_session.dat",
    _                  => null
};

using var client = new Client(Config);
var user = await client.LoginUserIfNeeded();
Console.WriteLine($"Вошли как: {user.first_name}");

При первом запуске WTelegramClient интерактивно запросит код верификации. Сессия сохраняется и используется повторно.

Шаг 2: Получение канала по username

var resolved = await client.Contacts_ResolveUsername("channel_username");

if (resolved.Chat is not Channel channel)
{
    Console.WriteLine("Это не канал");
    return;
}

Console.WriteLine($"Канал: {channel.title} (ID: {channel.id})");
var inputPeer = channel.ToInputPeer();

Метод Contacts_ResolveUsername работает для публичных каналов и групп. Для приватных чатов нужен invite-link или прямой ID.

Шаг 3: Загрузка истории сообщений с пагинацией

Telegram API отдаёт не более 100 сообщений за запрос. Для получения большего количества используем пагинацию по offset_id:

var allMessages = new List<MessageBase>();
int offsetId = 0;
int targetCount = 500; // сколько сообщений хотим получить

while (allMessages.Count < targetCount)
{
    var batchSize = Math.Min(100, targetCount - allMessages.Count);

    var history = await client.Messages_GetHistory(
        peer: inputPeer,
        offset_id: offsetId,   // ID последнего полученного сообщения
        offset_date: default,
        add_offset: 0,
        limit: batchSize,
        max_id: 0,
        min_id: 0,
        hash: 0
    );

    if (history is not Messages_ChannelMessages channelMessages) break;
    var batch = channelMessages.Messages;
    if (batch.Length == 0) break;

    allMessages.AddRange(batch);
    offsetId = batch[^1].ID; // следующий batch начнётся с этого ID

    await Task.Delay(300); // пауза, чтобы не получить flood wait
}

Console.WriteLine($"Загружено сообщений: {allMessages.Count}");

Шаг 4: Извлечение данных из сообщений

Каждое сообщение — это объект типа MessageBase. Текстовые посты — тип Message. Из них можно получить:

foreach (var msgBase in allMessages)
{
    if (msgBase is not Message msg) continue;

    var text     = msg.message;           // текст поста
    var date     = msg.Date;              // дата (UTC)
    var views    = msg.views;             // просмотры
    var forwards = msg.forwards;          // репосты
    var url      = $"https://t.me/{username}/{msg.ID}";

    // Ссылки в тексте
    var links = msg.entities?
        .OfType<MessageEntityTextUrl>()
        .Select(e => e.url)
        .ToList();

    // Хэштеги
    var hashtags = msg.entities?
        .OfType<MessageEntityHashtag>()
        .Select(e => text.Substring(e.offset, e.length))
        .ToList();

    // Реакции
    var reactions = msg.reactions?.results?
        .Select(r => new {
            emoji = (r.reaction as ReactionEmoji)?.emoticon,
            count = r.count
        }).ToList();
}

Шаг 5: Скачивание медиафайлов (опционально)

Парсер сообщений Telegram с медиаконтентом WTelegramClient позволяет загружать файлы напрямую:

if (msg.media is MessageMediaPhoto { photo: Photo photo })
{
    await using var fs = File.Create($"media/{msg.ID}.jpg");
    var type = await client.DownloadFileAsync(photo, fs);
    // type содержит Storage_FileType (jpeg, png, gif и т.д.)
}

OpenAI для извлечения структурированных данных

Это самая интересная часть. Посты в Telegram каналах-магазинах пишутся вручную и не имеют единого формата. Вот типичный пример:

    🔥 Новинка!
    Платье «Весенний бриз»
    Размеры: S, M, L, XL
    Цвет: белый, розовый, мята
    Цена: 1 890 грн
    Замер на модели 42 размер, рост 170 см
    Доступно к заказу ✅

Вручную парсить такой текст регулярками — задача с сотнями edge cases. GPT справляется с этим гораздо лучше.

Архитектура решения

Пост из Telegram

GPT-4o-mini
(system prompt с инструкцией)

JSON или «NOT_PRODUCT»

ProductInfo объект

CSV / база данных

Реализация

using OpenAI.Chat;

var _openAiClient = new ChatClient("gpt-4o-mini",
    Environment.GetEnvironmentVariable("OPENAI_API_KEY"));

private static async Task<ProductInfo?> ExtractProductAsync(string messageText)
{
    var systemPrompt =
        "You analyze Telegram channel messages. " +
        "If the message describes a product for sale, extract the data " +
        "and respond with a JSON object: " +
        "{"name":"...","price":"...","sizes":[...],"colors":[...]}. " +
        "Omit 'sizes' and 'colors' keys if not present. " +
        "If the message is NOT a product listing, respond with exactly: NOT_PRODUCT";

    var completion = await _openAiClient.CompleteChatAsync(
    [
        new SystemChatMessage(systemPrompt),
        new UserChatMessage(messageText)
    ]);

    var response = completion.Content[0].Text.Trim();

    if (response == "NOT_PRODUCT") return null;

    return JsonSerializer.Deserialize<ProductInfo>(response,
        new JsonSerializerOptions { PropertyNameCaseInsensitive = true });
}

public class ProductInfo
{
    public string Name   { get; set; } = "";
    public string Price  { get; set; } = "";
    public List<string>? Sizes  { get; set; }
    public List<string>? Colors { get; set; }
}

Почему GPT-4o-mini, а не GPT-4o?

  • Скорость: mini в 5–10 раз быстрее.
  • Стоимость: в 15–30 раз дешевле.
  • Достаточность: для извлечения структуры из коротких текстов mini справляется не хуже полной модели.

При обработке 1000 постов стоимость с GPT-4o-mini составит несколько центов против нескольких долларов с GPT-4o.

Советы по системному промпту

Хороший системный промпт для парсера — это половина успеха:

    «You analyze Telegram channel messages.
    If the message describes a product for sale:
    — Extract name, price, available sizes, available colors
    — Respond ONLY with valid JSON: {«name»:»…»,»price»:»…»,»sizes»:[…],»colors»:[…]}
    — Omit ‘sizes’ and ‘colors’ if not mentioned
    — Price should include currency symbol if present
    — Do not add explanation or markdown

    If the message is NOT a product listing (greetings, news, questions, etc.),
    respond with exactly one word: NOT_PRODUCT»

Ключевые принципы:

    1. Чёткий формат ответа — только JSON или специальная метка NOT_PRODUCT.
    2. Инструкция для негативного случая что делать, если пост не является товаром.
    3. Без markdown — никаких json обёрток, иначе десериализация сломается.
    4. На английском — промпты на английском работают стабильнее для структурированного вывода.

Сохранение результатов в CSV

private static void SaveToCsv(
    List<(string Channel, string Date, ProductInfo Product)> products,
    string path)
{
    using var writer = new StreamWriter(path, false,
        new System.Text.UTF8Encoding(encoderShouldEmitUTF8Identifier: true));

    writer.WriteLine("Channel,Date,Name,Price,Sizes,Colors");

    foreach (var (channel, date, p) in products)
    {
        writer.WriteLine(string.Join(",",
            CsvEscape(channel),
            CsvEscape(date),
            CsvEscape(p.Name),
            CsvEscape(p.Price),
            CsvEscape(p.Sizes is { Count: > 0 } ? string.Join("; ", p.Sizes) : ""),
            CsvEscape(p.Colors is { Count: > 0 } ? string.Join("; ", p.Colors) : "")
        ));
    }
}

BOM (encoderShouldEmitUTF8Identifier: true) нужен для корректного отображения кириллицы в Excel.

Ограничения и важные моменты

Flood Wait

Telegram блокирует слишком частые запросы. WTelegramClient автоматически обрабатывает FloodWaitException, но лучше добавить задержки вручную:

await Task.Delay(300); // 300ms между батчами сообщений

Terms of Service

Парсинг данных Telegram регулируется Terms of Service. Основные правила:

  • Нельзя парсить данные пользователей (личные переписки) без согласия.
  • Нельзя использовать данные для спама.
  • Публичные каналы и группы — в серой зоне; коммерческое использование требует осторожности.

Хранение сессии

Файл telegram_session.dat — это фактически токен авторизации. Храните его так же, как пароли: не в репозитории, не в облаке без шифрования.

Итоговая архитектура парсера

Program.cs (точка входа)
— Инициализация WTelegramClient
— Инициализация OpenAI ChatClient
— Перебор списка каналов

ParseChannel
— Резолв username → Channel
— Пагинация: Messages_GetHistory
— Батчи по 100 сообщений

    ⭣ MessageBase

ExtractFromMessageAsync
— Фильтрация: только Message с текстом
— Вызов ExtractProductAsync

    ⭣ текст поста

— System prompt: инструкция + формат JSON
— User prompt: текст поста
— Ответ: JSON или «NOT_PRODUCT»

    ⭣ ProductInfo

SaveToCsv
— Channel, Date, Name, Price, Sizes, Colors

Парсер телеграм каналов на C# с использованием WTelegramClient и OpenAI — мощный инструмент для автоматизации сбора данных. Парсер постов Telegram можно адаптировать под любую предметную область: товары, вакансии, объявления о недвижимости, новости — GPT легко переключается между схемами извлечения данных простой заменой системного промпта.