Практика работы с Telegram

Экспорт комментариев Telegram в JSONL: пример на Python

Материал · Редакционная проверка текста: · Как проверяем материалы

Выгрузка доступного обсуждения одного поста через Telethon: подготовка, предел выборки, поля результата и причины неполного экспорта.

Первоначальная инструкция: сентябрь 2026. Даты снимков и отдельных проверок указаны в материале.

ЭКСПОРТ КОММЕНТАРИЕВ TELEGRAM В JSONL — тематическая обложка с персонажем Telecobra
Тематическая иллюстрация к материалу; не снимок интерфейса.

Выберите один пост и его обсуждение#

Здесь результатом будет JSONL с сообщениями из доступной ветки одного поста. Каждая строка — отдельный JSON-объект. Пример сохраняет текст и связь ответа, но не скачивает вложения и не собирает карточки авторов.

Для публичной ссылки t.me/channel_name/123 источник — канал @channel_name, а 123 — ID поста. Именно эту пару передают команде ниже. Не подставляйте вместо ID поста ID отдельного комментария.

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

Канал: пост 123
  → связанная группа: копия поста 500 — корень обсуждения
      → комментарий 501, reply_to = 500
          → ответ 502, reply_to = 501
      → сообщение 503, reply_to = 500, текст пуст

Числа в схеме вымышленные. Вложенный ответ остаётся частью той же ветки. В API Telegram есть отдельные поля для непосредственного родителя и корня ветки; этот небольшой экспорт сохраняет только непосредственный reply_to. Для поиска исходной копии API также предоставляет messages.getDiscussionMessage; описанный CLI не требует вводить её ID вручную.

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

Подготовьте Python и свой доступ#

Скачайте готовые Python-примеры с README и лицензией MIT и распакуйте архив. Для этой инструкции нужен export_telegram.py из корня распакованного архива. В архиве исходный код; сессий, ключей и готовой базы пользователей нет.

python -m venv .venv
# Активируйте окружение способом вашей ОС
python -m pip install telethon==1.45.0

Используйте Python 3.11+ и отдельное виртуальное окружение. Получите API ID и API Hash своего приложения на my.telegram.org. Задайте TELEGRAM_API_ID, TELEGRAM_API_HASH и отдельное имя файла TELEGRAM_SESSION через переменные среды; значения в статью и скриншоты не помещайте.

При первом запуске с обращением к Telegram потребуется обычный вход владельца. Метод getReplies доступен пользовательским аккаунтам: токен Bot API не заменяет этот сценарий. Тем же аккаунтом заранее откройте пост и обсуждение. Публичная ссылка на канал не доказывает доступность всей ветки.

Файл сессии хранит доступ к аккаунту. Не передавайте его другим людям и не используйте одновременно несколькими работающими клиентами. Условия библиотеки и форма входа отделены от подписки Telecobra.

Запустите просмотр, затем выгрузку#

Перейдите в корень распакованного архива, где находится export_telegram.py. Подставьте свой источник и положительный ID поста:

python export_telegram.py comments --source @your_owned_channel --post 123 --limit 100 --output comments-123.jsonl
python export_telegram.py comments --source @your_owned_channel --post 123 --limit 100 --output comments-123.jsonl --execute

Первая команда только выводит параметры и не использует сеть. --execute включает вход и чтение Telegram. --limit ограничивает число записей: диапазон 1–10000 задан самим примером, а не объявлен Telegram как квота. По умолчанию итератор Telethon идёт от новых сообщений к старым; лимит не является фильтром дат.

Существующий выходной файл не перезаписывается. Автоматического продолжения частичной выгрузки нет: для повторной попытки задайте новое имя и затем сравните записи из того же обсуждения.

Как сообщение превращается в строку JSONL

Ниже фрагмент скачиваемого export_telegram.py, а не самостоятельный скрипт. Полная версия создаёт клиент, разбирает аргументы, открывает файл и закрывает соединение. Запускайте её командами выше.

async for item in client.iter_messages(entity, reply_to=args.post, limit=args.limit):
    row = {
        "message_id": item.id,
        "date": item.date.isoformat() if item.date else None,
        "reply_to": getattr(item.reply_to, "reply_to_msg_id", None),
        "text": item.raw_text or "",
    }
    file.write(json.dumps(row, ensure_ascii=False) + "\n")
    file.flush()

Чтение страницы ответов выполняет iter_messages(..., reply_to=...). JSON-кодирование сохраняет кириллицу и записывает перевод строки внутри текста как \n; он не создаёт вторую запись файла. Пример не добавляет ID автора, username, реакции или путь к медиа.

Разберите поля и проверьте JSONL#

ПолеЗначение в этом примереГраница вывода
message_idID полученного сообщенияПри объединении разных обсуждений храните источник отдельно: одно число не является глобальным ID сообщения.
dateВремя сообщения в ISO 8601 с часовым поясом; null, если значения нетЭто время сообщения, а не дата выгрузки и не показатель последнего посещения автора.
reply_toID непосредственного родителя либо nullРодитель может быть корнем ветки, удалённым или не попавшим под лимит сообщением.
textТекст или подпись; пустая строка для сообщения без текстаПустой текст не означает удалённый аккаунт; само вложение не скачивается.

Учебный JSONL ниже соответствует схеме из первого раздела. Сохраните его как sample-comments.jsonl в UTF-8. Это вымышленные сообщения, не результат работы с Telegram:

{"message_id": 503, "date": "2026-10-09T09:03:00+00:00", "reply_to": 500, "text": ""}
{"message_id": 502, "date": "2026-10-09T09:02:00+00:00", "reply_to": 501, "text": "Уточнение:\nнужен пример файла."}
{"message_id": 501, "date": "2026-10-09T09:01:00+00:00", "reply_to": 500, "text": "Где посмотреть структуру?"}

Здесь три сообщения и один пустой текст. Ответ 502 → 501 вложен в обсуждение. Корень 500 отсутствует в файле, поскольку показаны комментарии; это само по себе не ошибка. Число авторов по этим четырём полям установить нельзя.

Воспроизведите проверку без Telegram#

Сохраните код как check_jsonl.py. Он использует только стандартную библиотеку Python, читает локальный файл и проверяет четыре обязательных поля. Дополнительные поля допустимы. Пустой файл, пустая строка, неверный JSON, неверный тип ID или время без часового пояса вызывают ValueError.

import json
import sys
from datetime import datetime
from pathlib import Path

def inspect_jsonl(text):
    rows = []
    for line_number, line in enumerate(text.splitlines(), 1):
        if not line.strip():
            raise ValueError(f"Empty line: {line_number}")
        row = json.loads(line)
        required = {"message_id", "date", "reply_to", "text"}
        if not isinstance(row, dict) or not required <= row.keys():
            raise ValueError(f"Missing fields: {line_number}")
        if type(row["message_id"]) is not int or row["message_id"] <= 0:
            raise ValueError(f"Bad message_id: {line_number}")
        parent = row["reply_to"]
        if parent is not None and (type(parent) is not int or parent <= 0):
            raise ValueError(f"Bad reply_to: {line_number}")
        if not isinstance(row["text"], str):
            raise ValueError(f"Bad text: {line_number}")
        date = row["date"]
        if date is not None:
            if not isinstance(date, str):
                raise ValueError(f"Bad date: {line_number}")
            if datetime.fromisoformat(date).utcoffset() is None:
                raise ValueError(f"Timezone missing: {line_number}")
        rows.append(row)
    if not rows:
        raise ValueError("No records")
    unique = len({row["message_id"] for row in rows})
    return {"records": len(rows), "unique_message_ids": unique,
            "duplicate_message_ids": len(rows) - unique,
            "empty_texts": sum(row["text"] == "" for row in rows)}

if __name__ == "__main__":
    result = inspect_jsonl(Path(sys.argv[1]).read_text(encoding="utf-8"))
    print(json.dumps(result, ensure_ascii=False))
python check_jsonl.py sample-comments.jsonl

Ожидаемый результат для учебного файла:

{"records": 3, "unique_message_ids": 3, "duplicate_message_ids": 0, "empty_texts": 1}

Повторный ID не удаляется: счётчик показывает записи сверх первого вхождения. Он осмыслен для файла одного обсуждения. Два одинаковых текста с разными ID остаются разными сообщениями. Проверка не обращается к Telegram, не считает людей и не подтверждает полноту архива.

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

Почему записей меньше ожидаемого#

НаблюдениеЧто проверитьКак трактовать
Число строк равно limitДостигнут ли предел, выбранный в командеЭто возможный срез новых сообщений, а не доказательство конца обсуждения.
Обсуждение не найденоПару «канал + ID поста», наличие связанной группы и доступ того же аккаунтаОбычный чат или личный диалог не подставляют вместо поддерживаемого reply_to-сценария.
Ранее доступная ветка исчезлаЕсть ли в группе исходная автоматически пересланная копия постаTelegram позволяет отключить комментарии поста удалением этой копии; пример не восстанавливает её.
FloodWait и частичный файлСообщение об остановке и требуемое время ожиданияВ примере flood_sleep_threshold=0; уже записанные строки сохраняются. Отсутствие автоматического продолжения учитывайте отдельно.
Счётчик комментариев в клиенте отличаетсяМомент измерения, лимит, доступность и изменения обсуждения во время чтенияВыгрузка не является неизменяемым снимком всей истории.
Нет отдельного ответа от самого каналаОговорку Telethon о собственном ответе broadcast-канала на своё сообщениеДокументация reply_to прямо указывает это исключение; не обещайте полный архив каждого типа ответов.

Удалённые и недоступные сообщения не восстанавливаются. Сообщение об окончании чтения означает завершение выбранного запуска, но не подтверждает полноту исторического архива.

Наблюдаемый комментарий подтверждает сообщение в конкретное время и месте. Из него не следуют частота чтения канала, точный статус посещения, интерес к покупке или согласие на личную рассылку. Отправитель в Telegram может быть представлен каналом; имя подписи не раскрывает конкретного человека за ним. Эти сведения этот JSONL не экспортирует.

Источники и проверка примера#

Редакционная проверка: . Проверены документация, опубликованный архив, синтаксис фрагментов и локальные вымышленные записи. Реальная авторизация и онлайн-выгрузка для подготовки материала не выполнялись.

Редактура структуры: . Упорядочены разделы и пояснения. Даты исходных снимков, примеров и функциональных проверок указаны отдельно.

Одна платформа · разные задачи

Подписка Telecobra

Соберите инструменты для работы с Telegram в одном кабинете. Сроки доступа, цены и условия — на странице подписки.

В этой инструкции описана только часть возможностей платформы. Полный список доступен в разделе функций.