简介这套微信聊天记录导出与分析工具面向需要系统整理微信通讯记录的个人用户、数据分析学习者及课程设计实践者可将对话批量转换为HTML、Word、CSV等规范格式确保重要信息长期存档。工具内置深度解析模块能从时间分布、高频词汇、互动频率等维度生成年度对话分析报告量化沟通趋势与话题演变轨迹。压缩包共309个文件以103个py核心脚本、18个html页面模板、svg图标及ffmpeg辅助程序为主整体约24.99MB目录覆盖导出、解析、图表展示等模块另包含json配置、md说明与可直接运行的exe工具。已有69人学习下载适合用于个人数据管理也可作为微信数据可视化与文本分析课程的作业参考对希望二次开发或复现解析流程的读者附带的脚本注释和模板结构能降低上手门槛。1. 微信聊天记录导出与分析系统先解决“数据从哪来、能导出什么”微信至今没有官方“全量导出聊天记录”的按钮。想把自己一年的对话生成HTML发给团队复盘、把家庭聊天整理成Word留档、或者把社群活跃度导成CSV做分析都得从本地数据库动手。这套系统的工作流很直白先解开微信的SQLite数据库把message表抽成统一结构再分别渲染成HTML、Word、CSV三种产物最后用年度报告模块把统计口径固定下来。适合三类人想做个人聊天备份的普通用户、要分析群活跃度的运营、以及想研究IM数据结构的开发新人。2. 解析微信SQLite数据库破解读取、字段映射和第一段可跑代码想要导出聊天记录第一步就是“微信数据库解密”。微信没有提供开放的数据接口聊天记录在Android端落在本地SQLite库里而且是加密的。这一章先把数据库的文件结构、密钥来源和最小读取脚本讲清楚后面所有导出逻辑都建立在这一层之上。2.1 微信本地数据库的文件结构、加密方式和密钥来源先走最容易跑通的Android路线。微信的聊天记录主体存在EnMicroMsg.db路径在/data/data/com.tencent.mm/MicroMsg/{32位hash目录}/。这个库不是普通SQLite默认带了SQLCipher加密直接拿sqlite3打开会报“file is not a database”。要解开它需要一个密钥微信在客户端本地派生取IMEI和uin微信账号的内部ID能在同一目录的CompatibleInfo.cfg里找到拼接后做MD5取前7位作为SQLCipher的密码。这一步的常见做法是root手机或模拟器用adb pull把整个MicroMsg目录拉出来再从cfg里读uin和imei。拿到密钥后先用DB Browser for SQLite验一下能不能开库能开再往下写代码别一上来就调Python省得把问题混在一起。这里要提醒的是Android 7以上微信的加密参数和旧版不一样。连接时通常要显式设置cipher_use_hmac OFF和cipher_page_size 1024否则密钥正确也解不开报错依然是“file is not a database”很容易让人误判成密钥算错了。import sqlite3 # db_path 从 adb pull 出来的 MicroMsg 目录里找 EnMicroMsg.db def open_wechat_db(db_path, key): conn sqlite3.connect(db_path) # PRAGMA key 要在任何查询之前执行SQLCipher 才能解锁 conn.execute(fPRAGMA key{key};) conn.execute(PRAGMA cipher_use_hmac OFF;) conn.execute(PRAGMA cipher_page_size 1024;) return conn逻辑说明PRAGMA key是解锁入口必须在所有查询之前执行cipher_use_hmac和cipher_page_size这两个参数是兼容老版本微信加密库的关键。这里key是直接拼接进SQL的因为sqlite3的PRAGMA不支持参数占位符——这也意味着key必须来自可信配置不要拿用户输入直接拼。连接成功后先用一条SELECT count(*) FROM message验证数据能读再进入字段映射阶段。2.2 message表字段映射和最小可跑脚本解开库后导出只用盯住message表。核心字段如下表。字段含义导出时要做什么msgId消息唯一ID增量导出时记游标isSend0收1发决定“我/对方”方向createTime时间戳毫秒除以1000转时间talker会话ID单聊是对方wxid群聊是room idtype消息类型1文本3图片34语音43视频47表情49文件/链接10000系统消息content消息内容文本直达图片语音是XML描述完整的最小脚本就是先建连接再带条件拉数据def load_messages(conn, talkerNone, start_tsNone, end_tsNone): sql SELECT msgId, isSend, createTime, talker, type, content FROM message WHERE 11 params [] if talker: sql AND talker ? params.append(talker) if start_ts and end_ts: sql AND createTime BETWEEN ? AND ? params.extend([start_ts, end_ts]) sql ORDER BY createTime cur conn.execute(sql, params) cols [d[0] for d in cur.description] return [dict(zip(cols, row)) for row in cur.fetchall()]逻辑说明这个函数是后面所有导出的统一数据入口。用WHERE条件而不是全表扫描是想让全量导出和年度报告共用同一个查询骨架。talker为空时导出整个库这时一定要用start_ts/end_ts做时间分片否则聊天记录几年的量一次性load进内存64G的机器也可能扛不住。参数说明时间戳是毫秒级整数Python里格式化要先用int(ts) / 1000再交给datetime.datetime.fromtimestamp。type字段建议保留原始数字在显示层做中文映射别在数据库查询阶段翻译不然以后想按类型过滤还要改查询。到这里数据库层就通了。下一步是把这批结构化消息变成三种实际产物也就是HTML、Word、CSV各自的导出逻辑。提示iOS端的聊天记录存在iTunes备份的Documents目录下密钥派生方式完全不同工程量大个人项目建议先只做Android。企业微信的数据结构也和微信不一致不要拿这套代码直接打企业微信的库。3. 三种导出格式的落地HTML模板、Word拼装、CSV扁平化的边界问题数据抽出来了接下来就是“格式转换”。HTML、Word、CSV三种格式的服务场景完全不同HTML用来分享和在线浏览Word用来打印存档CSV用来做数据分析。如果只做一个统一导出最后一定有人嫌弃格式不对。这一章把三条导出链路分开写每条都给出最小实现和参数调整思路。3.1 HTML导出用Jinja2模板做时间线页面兼顾分享和打印HTML适合做“可分享、可截图”的聊天时间线。常见做法是准备一个Jinja2模板消息按时间分块每条消息按isSend区分左右气泡。最小实现长这样from jinja2 import Template import html as html_lib tpl_text !doctype html html langzh-cn head meta charsetutf-8 title{{ title }}/title style .msg { margin: 6px 12px; } .send { text-align: right; } .bubble { display: inline-block; max-width: 70%; padding: 8px 12px; border-radius: 8px; background: #f1f1f1; } .send .bubble { background: #95ec69; } .time { font-size: 12px; color: #999; } /style /head body h1{{ title }}/h1 {% for m in msgs %} div classmsg {{ send if m[isSend] else }} div classbubble div classtime{{ m[time_str] }}/div div{{ m[content] }}/div /div /div {% endfor %} /body /html def export_html(msgs, out_path, title聊天记录): tpl Template(tpl_text) # 手动转义防止聊天内容里的脚本被浏览器执行 safe_msgs [] for m in msgs: m dict(m) m[content] html_lib.escape(m[content]) safe_msgs.append(m) with open(out_path, w, encodingutf-8) as f: f.write(tpl.render(titletitle, msgssafe_msgs))逻辑说明Jinja2的Template默认不开autoescape聊天内容里的