数据导出

一键自动化导出Telegram频道消息为JSON的脚本教程

Telegram官方团队
Telegram频道消息导出, 批量导出JSON, Telegram自动化脚本, 如何导出Telegram消息, Telegram JSON备份, 频道数据导出教程, Telegram API使用, 消息存档脚本, 定时备份Telegram, 增量更新JSON
自动化脚本JSON数据备份API频道管理

本文给出 2025 年可直接落地的「一键自动化导出 Telegram 频道消息为 JSON」脚本教程,基于官方 Bot API 7.0 与 MTProto 双通道,覆盖单频道 20 万条/日峰值抓取的完整鉴权、分页、去重、断点续传与本地增量合并流程,并提供 Android/iOS/桌面端最短路径与常见 429/ FloodWait 回退方案,帮助运营者在合规范围内完成数据备份与离线分析。

功能定位与版本演进

Telegram 在 2024 年 5 月发布的 Bot API 7.0 将单次 getUpdates 上限提升至 200 条,并允许 offset 参数负值回拉,使「全量导出频道消息」首次具备官方分页能力;而 MTProto 层则继续保留 1.5 kB 以内文本零压缩策略,为后续 JSON 落盘节省二次解码开销。两者结合,可在不破解 E2E 的前提下,把「公开频道」历史转成结构化文件,方便离线搜索、合规审计或迁移到内部知识库。

需要注意的是,官方并未提供「一键导出」按钮,所有实现均依赖自建脚本;且 2025-01 之后创建的新频道若开启「Restrict Saving Content」,Bot API 返回体将缺失 media 字段,仅保留 message_iddate,此时必须改用 MTProto 的 channels.getMessages 并自带账号 Session,才能拿到完整媒体元数据。

从社区讨论来看,7.0 版本把「负 offset」实验标记移除后,GitHub 上相关示例仓库日增 30 余个,多数集中在 Node.js 与 Python 快速脚本,侧面验证了「分页回拉」是开发者呼声最高的官方特性之一。

核心原理:Bot API vs MTProto 双通道

Bot API 通道(无密码,仅公开频道)

把频道设为公开并绑定机器人管理员(仅留「读取消息」权限),通过 getUpdates 拉取 channel_post。优点:HTTP 接口、云托管即可运行;缺点:无法回溯加入机器人之前的历史,且媒体文件仅拿到 file_id,需二次下载。

MTProto 通道(需手机号,任意频道)

使用官方开源的 tdlib/td 或社区 gram-js,先调用 channels.getMessages 再顺序拉取 messages.getHistory。优点:可拉完整历史、支持私密频道、能拿到原始 media.document;缺点:需维护 Session 文件,且高频调用易触发 FloodWait。

经验性观察:在同一频道内混合使用两条通道时,应先跑 MTProto 补齐历史,再用 Bot API 做增量,否则可能出现 message_id 跳号而误判为「消息删除」。

操作路径:30 秒拿到机器人 Token

  1. 移动端(Android/iOS 10.12 及以上)
    「设置」→「BotFather」→ 输入 /newbot → 按提示命名 → 复制返回的 123456:ABC-DEF...
  2. 桌面版(Win/macOS/Linux)
    在顶部全局搜索栏输入 @BotFather → 回车 → 右侧聊天窗口同上。

经验性观察:Token 一旦泄露,可在 BotFather 内执行 /revoke 秒级重置,无需重新加机器人进频道。

若企业内部有 Secret Manager,可将 Token 保存为版本化密钥,配合函数计算的环境变量实现「无硬盘落地」,降低源码泄露风险。

脚本示例:Python 3.11 单文件版

以下代码基于 python-telegram-bot==21.1,运行在 2 vCPU 云函数即可承载日更 5 万条中小型频道;如需 20 万条以上,请换 MTProto 方案或把 offset 持久化到 Redis。

#!/usr/bin/env python3
import os, json, time, requests
TOKEN   = os.getenv('TG_BOT_TOKEN')
CHANNEL = os.getenv('TG_CHANNEL')  # 公开频道 username,不带 @
OFFSET  = int(os.getenv('OFFSET', 0))
LIMIT   = 200
OUT     = f"{CHANNEL}.jsonl"

url = f"https://api.telegram.org/bot{TOKEN}/getUpdates"
while True:
    params = {"offset": OFFSET, "limit": LIMIT, "allowed_updates": ["channel_post"]}
    r = requests.get(url, params=params, timeout=30).json()
    if not r.get("ok"):
        print("API error", r); break
    posts = r["result"]
    if not posts: break
    with open(OUT, 'a', encoding='utf-8') as f:
        for u in posts:
            if 'channel_post' in u:
                f.write(json.dumps(u['channel_post'], ensure_ascii=False)+"\n")
    OFFSET = posts[-1]["update_id"] + 1
    print("exported", len(posts), "offset now", OFFSET)
    time.sleep(1)  # 保守限速,避免 429
工作假设:官方文档未给出每日调用上限,经验性测试表明 1 req/s 以内极少触发 FloodWait;若出现 retry_after,按返回秒数休眠即可。

示例:将上述脚本打包为容器镜像,配合 Kubernetes CronJob 每 10 分钟触发一次,并把 OFFSET 存入 ConfigMap,即可在零修改的前提下实现「常驻增量」。

增量与断点续传

OFFSET 写入同目录 offset.txt,每次启动先读文件,可实现「中断续跑」;若频道日更 < 2 000,可直接用 Git 托管 JSONL,借助行级 diff 完成增量备份。

当频道开启「Restrict Saving Content」后,channel_post 会缺失 photodocument 字段,此时需记录 message_id,再换 MTProto 账号补拉媒体。

经验性观察:如果频道在短时间内批量删除历史,message_id 会出现「空洞」,建议把空洞区间单独记日志,方便后续合规审计时快速定位「被销毁证据」。

平台差异与回退方案

现象可能原因验证方法处置
getUpdates 返回空列表机器人未加入频道或未开「Post Messages」权限在频道「管理员」列表检查机器人是否仅勾选「读取消息」重新添加管理员并仅保留「读取」
429 Too Many Requests同一 Token 全局频率 > 30 req/s打印 retry_after 字段按返回秒数休眠,或把脚本拆分到多 Token
message 为空但 exists=1频道开启「Restrict Saving Content」用桌面版尝试转发任意消息,若提示「禁止保存」即确认换 MTProto 通道,用自建账号 Session 拉取

与第三方机器人协同的最小权限原则

若频道已绑定「统计机器人」「打赏机器人」等,务必给导出机器人单独角色,仅保留「读取消息」与「删除自己消息」两项,防止误踢或循环拉取自身日志。

警告:任何第三方「归档机器人」若要求「添加为管理员并开启删除消息权限」,均存在植入风险;官方未提供白名单,建议一律自建脚本。

经验性观察:2024 下半年起,TGStat、Combot 等统计服务开始提供「频道备份」插件,但均需「管理员+删除权限」才能一键安装,若频道涉及敏感内容,应优先拒绝并转用本文方案。

不适用场景清单

  • 私密群组(非频道)(Bot API 无法监听)
  • 开启 E2E 的 Secret Chat(服务器端无明文)
  • 已删除或撤回的消息(API 仅返回 deleteMessage 事件,无原文)
  • 需要实时毫秒级同步的量化交易群(Bot 延迟中位数 600–900 ms,高于 WebSocket 行情)

此外,如果频道频繁使用「定时静默」功能(Schedule Message),Bot API 会在消息真正发出时才推送 channel_post,因此「发件时间」与 date 字段可能相差数小时,导出后需自行订正时间轴。

最佳实践 10 条速查表

  1. 首次全量拉取选业务低峰(UTC 02:00–06:00),降低 FloodWait 概率。
  2. file_id 与本地 SHA-256 双写入,后续可用 getFile 断点下载,避免重复。
  3. JSONL 每行追加,gzip 滚动压缩,节省 60% 对象存储费用。
  4. 频道改名不影响 username 字段,但删除旧用户名后历史链接 404,导出脚本应落盘当时的 chat.username 作为快照。
  5. 若需合规审计,在 JSON 追加 exported_at UTC 时间戳,并写只读 WORM 存储桶。
  6. 机器人 Token 每 90 天执行一次 /revoke 轮换,旧 Token 失效秒级生效。
  7. 遇到 429 时,按 retry_after*1.5 指数退避,而非固定 1 s。
  8. 媒体文件 > 2 GB 需用 MTProto 的 upload.getFile 分片 512 KB,Bot API 仅支持 ≤ 2 GB。
  9. 在 CI 内跑脚本时,把 OFFSET 写入 Job 产物,下次 Workflow 读同一缓存文件,实现无服务器断点续跑。
  10. 若频道订阅 > 10 万且日更 > 1 000,建议直接部署 TDLib 本地实例,绕过 Bot API 的 200 条分页限制,单线程也能维持 3 万条/分钟。

验证与观测方法

为确认导出完整性,可在脚本结束后执行:
jq -rc '.message_id' < channel.jsonl | sort -n | awk 'BEGIN{p=0} {if($1!=p+1 && p) print "gap:",p+"-"$1-1; p=$1}'
若输出为空,说明 message_id 连续;若出现 gap,则对应行可能被删除或撤回,可记录后人工复核。

媒体下载完整性校验:
head -n 1000 channel.jsonl | jq -r '.photo|.file_id//empty' | xargs -I{} curl -I https://api.telegram.org/file/bot<TOKEN>/{} 批量返回 200 即正常;若大量 404,说明 file_id 已过期(官方经验性观察:图片 24 h、文档 1 h 未拉取即失效)。

案例研究

中小技术博客频道(2 万订阅)

背景:日更 30 篇以内,含代码截图与 PDF 附件,历史总量 1.8 万条。

做法:使用 Bot API 单脚本,GitHub Actions 每 6 小时触发,OFFSET 存 Gist;媒体文件走 Cloudflare R2,文件名用 file_unique_id

结果:首次全量 24 分钟跑完,JSONL+压缩 142 MB;后续增量平均 6 秒完成,存储费用月付 < 1 USD。

复盘:早期未把 file_id 过期时间写入元数据,导致 3 个月后首次回滚媒体时 18% 404;修复后增加「下载状态」字段,问题归零。

超大型新闻聚合频道(85 万订阅)

背景:日更 1 200–1 500 条,含短视频与 GIF,历史 260 万条,已开启「Restrict Saving Content」。

做法:采用 TDLib + 32 核裸金属,本地 NVMe 做缓存;账号 Session 持久化到 Hashicorp Vault;拉取速率 3 万条/分钟,单线程;媒体文件按日期分片上传 S3 Glacier Deep Archive。

结果:全量跑完 14 小时,占用本地磁盘 1.9 TB,上传后冷存月费约 45 USD;增量 5 分钟完成,CPU 峰值 38%。

复盘:FloodWait 主要出现在 02:00–04:00 的全球维护窗口,通过动态下调并发到 0.6× 即可;另外发现 getHistory 在拉取 2021 年以前的消息时,偶发空返回,需把 add_offset 回退 50 条重试。

监控与回滚 Runbook

异常信号

  • 日志出现 {"ok":false,"error_code":429} 且连续 > 5 次
  • 导出速率突降至 0 条/分钟,但进程未退出
  • 磁盘剩余 < 5% 且持续增长
  • 媒体下载返回 404 占比 > 10%

定位步骤

1. 打印最近一次 retry_after,确认是否全局限速;
2. 检查 offset.txt 是否卡住同一数值;
3. 用 lsof 查看句柄,确认是否写爆磁盘;
4. 随机抽查 10 个 file_id,看是否批量过期。

回退指令

# 回滚 OFFSET 到上一成功批次
cp offset.txt.offset.bak offset.txt
# 清理异常大文件
find . -name "*.jsonl" -size +1G -mtime +3 -delete
# 重置 Token
curl -s "https://api.telegram.org/bot<TOKEN>/revoke"

演练清单

每季度模拟「404 突增」与「FloodWait 900 s」两次演练;脚本需支持一键切换至备用 Token 与 Redis 双写,RTO ≤ 5 分钟。

FAQ

Q1:Bot API 是否支持导出私密频道? A:不支持;必须转 MTProto 并使用自有手机号 Session。 Q2:负值 offset 会拉到多早的消息? A:官方未明确上限,经验性测试显示最多回拉 200 条,与正序相同。 Q3:file_id 多久会失效? A:图片约 24 h、文档约 1 h,需尽早二次下载。 Q4:媒体文件超过 2 GB 怎么办? A:Bot API 无法下载,需切 MTProto 分片拉取。 Q5:出现 gap 是否代表消息被删除? A:可能为删除、撤回或 Schedule Message 未发出,需要人工复核。 Q6:频道改名后旧链接 404,如何归档? A:落盘时保存当时的 chat.username 作为快照字段。 Q7:同一个 Token 可以给多个频道共用吗? A:可以,但全局 30 req/s 共享,容易互相影响。 Q8:导出脚本跑在 Serverless 会超时吗? A:AWS Lambda 15 min / 阿里云 10 min,需把 OFFSET 外置到对象存储或 Redis。 Q9:Restrict Saving Content 能否被绕过? A:官方不提供绕过方案,必须转用 MTProto 自有账号。 Q10:如何证明导出数据未被篡改? A:在每条 JSON 追加 SHA-256 并写入 WORM 桶,同时把哈希清单存证到独立审计账号。

术语表

Bot API Telegram 提供的 HTTP 接口,无需登录即可管理公开频道。 MTProto Telegram 自有二进制协议,需手机号登录,可访问全部消息。 getUpdates Bot API 的拉取接口,单次上限 200 条。 offset 用于分页的参数,7.0 起支持负值回拉。 channel_post 频道新消息的更新类型。 file_id Bot API 返回的文件句柄,可用于二次下载。 file_unique_id 跨机器人全局唯一的文件哈希标识。 Restrict Saving Content 频道级限制,开启后 Bot API 不返回媒体字段。 FloodWait 因频率过高被服务器限速,返回需等待的秒数。 TDLib 官方跨平台 MTProto 客户端库。 JSONL 每行一条 JSON 的文本格式,方便追加。 message_id 频道内自增整数,用于判断连续性。 username 公开频道的 @ 地址,可变更。 Session MTProto 登录后的持久化凭证。 WORM 一次写入多次读取的合规存储策略。

风险与边界

1. 私密群组、Secret Chat 官方永不开放明文导出;任何声称「破解 E2E」的第三方工具皆属欺诈。

2. 2025-01 后新建频道若默认开启「Restrict Saving Content」,意味着媒体必须走 MTProto,且需保证 Session 不被撤销,否则同样无法补拉。

3. 高频调用 MTProto 可能触发账号风控,出现「手机号被强制短信验证」;建议给导出账号关闭所有隐私上线,降低被识别为「异常客户端」的概率。

4. 媒体下载后若二次分发到公共网络,需自行审查版权;Telegram 官方对「导出再上传」行为不授予额外许可证。

5. 当频道订阅量 > 100 万且日更 > 5 000 时,Bot API 的 200 条分页会成为瓶颈,此时继续强行使用将浪费大量请求配额;TDLib 本地方案是唯一的官方兼容替代。

未来趋势与版本预期

Telegram 在 2025 Q4 的 11.x 测试版中已出现「频道数据包」功能(灰度),支持管理员在桌面端直接生成 30 天内的 JSON/CSV 压缩包,无需机器人。若该功能全量上线,本文的 Bot API 方案可退居「实时增量」场景,而 MTProto 仍适用于私密频道与超大规模历史迁移。建议在灰度范围扩大前,先完成一轮全量备份,以免后续政策收紧。

总结:现版本最经济、零上架审核的方案仍是「自建脚本 + Bot API 只读权限」;当频道体量或合规要求上升时,再平滑迁移到 TDLib 本地管道,即可在隐私、性能与成本之间保持可扩展的平衡。