微信聊天记录解密与多格式导出:HTML、Word、Excel及年度报告
发布时间:2026/9/15 14:40:20 作者:尧图编辑部 阅读量:1,286

简介面向长期保存微信聊天记录、并希望从中挖掘价值的个人用户与开发者这份资料围绕“提取—导出—分析—训练”全流程展开。资源共268个文件压缩包25.05MB包含108个py脚本、18个html模板、58个png与44个svg图表素材以及json、md、yml等配置文件py脚本用于解析聊天数据库、清洗文本、统计聊天频率和高峰时段html模板用于呈现年度报告和词云图片与样式文件则让页面更直观md使用说明和json配置能帮助快速部署。配套的ffmpeg.exe和示例模板如home、charts、wordcloud等可直接替换数据后生成完整页面整个链路清晰从原始数据到可视化报告一气呵成。项目还给出了问答对数据集和模型训练脚本框架帮助用户把聊天记录加工成个人AI聊天助手的训练语料。目前已有945人学习下载适合具备Python基础、想做数据可视化或尝试微调模型的爱好者按需选用。1. 为什么先解密本地数据库再谈导出 HTML、Word 和 Excel微信自带的“导出聊天记录”只能生成一个排版好的文本缺少原始时间戳和消息类型拿去做年度统计基本是重写一遍格式更麻烦的是微信数据目录下往往还留着以前版本生成的MSG数据库那才是完整的聊天历史所在。把本地 SQLite 数据库解密后再按 HTML、Word、Excel 三种格式导出无论是永久保存、年度报告还是用聊天语料微调一个 LoRA 小模型都能走同一条数据链路。这条路径对个人知识备份和客服话术分析同样适用下面从数据库解密开始逐步复现。2. 微信本地聊天记录解密从 data 目录到可读的 SQLite2.1 先识别数据目录下有哪些可用版本微信 3.6 和 3.9 的历史聊天记录集中在C:\Users\用户名\Documents\WeChat Files\wxid\MSG目录下4.0 开始改用xwechat_files目录同一个微信账号升级后还会保留旧的WeChat Files不走而文件名的数字后缀也不同。这种情况下数据目录下可能同时存在多个MSG.db文件需要先按修改时间排序确定哪一版是当前正在使用的哪些是“以前版本聊天记录”的残留。常见做法是用find先看一遍find /c/Users/$USER/Documents/WeChat Files -iname MSG*.db -printf %T %p\n 2/dev/null | sort -n这条命令会同时输出时间戳和完整路径sort -n后最靠前的是最早的历史库最后一个才是当前最新库。注意 4.0 的库可能在xwechat_files/appid/msg/db下所以find的起点要修改。如果发现MSG0.db、MSG1.db这种带数字后缀的说明微信在按序号拆库它们通常大小不同、时间段不同抽取时要合并处理。2.2 用 SQLCipher 解密出明文 SQLite微信 3.x 的消息数据库使用 SQLCipher 加密不是普通 SQLite 文件。直接打开会报“file is not a database”。解密的本质是拿到密钥再调用sqlcipher解锁常见的方式是借助开源工具wechat-dump-rs或留痕-DumpKey在微信运行期间从内存里取 key再手动执行解密。这里不展开 dump 细节假设已经拿到 key用命令行解密sqlcipher MSG.db SQLCipher PRAGMA key x0123456789ABCDEF; -- 实际 key 是 32 字节 hex SQLCipher PRAGMA cipher_migrate; SQLCipher .output plain.sqlite SQLCipher .dump SQLCipher .output stdout提示cipher_migrate只在旧版本数据库中才需要如果解密时报unsupported cipher先确认 OpenSSL 版本。这段命令中PRAGMA key用的是 SQLCipher 的 raw key 语法x...内是十六进制串.output会把后续.dump内容写进新的明文文件。解密后验证一下用sqlite3 plain.sqlite .tables能看到MSG、Contact、ChatRoom等表说明成功。如果表不完整可能是新旧版本加密参数不同需要换PRAGMA cipher_page_size。2.3 从 MSG 表抽出带联系人的消息流明文库中最重要的表是MSG字段较多常用的列见下表字段含义使用注意localId自增 ID可作为增量导出的游标CreateTimeUnix 时间戳需要转成北京时间isSender1自己发送0对方发送训练问答对时按它切分strContent消息文本或 XML 占位符包含msg时是引用消息talkerId会话 IDjoin Contact 表得到昵称用 Python 一次抽出import sqlite3 import pandas as pd conn sqlite3.connect(plain.sqlite) df pd.read_sql_query( SELECT m.localId, m.CreateTime, m.isSender, m.strContent, c.NickName FROM MSG m LEFT JOIN Contact c ON m.talkerId c.UserName WHERE m.CreateTime 0 ORDER BY m.CreateTime , conn) df[date] pd.to_datetime(df[CreateTime], units, utcTrue) df[date] df[date].dt.tz_convert(Asia/Shanghai) print(df.head())这里CreateTime是 Unix 秒units加上UTC再转上海避免出现 8 小时偏移。LEFT JOIN能保留会话 ID 但昵称为空的情况那些通常是群聊后期用ChatRoom表再补名字。拿到 DataFrame 后导出三条线都在同一份数据上做。3. 导出三件套HTML 永久存档、Word 排版、Excel 统计3.1 用 Jinja2 渲染 HTML 存档页面HTML 输出最大价值是永久可读不依赖 Office 环境。项目文件里已有的home.html、charts.html、wordcloud.html正好对应“按会话浏览、趋势图表、词云”三块内容。用 Jinja2 渲染时不要手动拼字符串否则消息内容里的引号和换行会被打乱。from pathlib import Path from jinja2 import Environment, FileSystemLoader records df.to_dict(records) stats {total: len(df), days: df[date].dt.date.nunique()} env Environment(loaderFileSystemLoader(templates)) html env.get_template(home.html).render( messagesrecords, statsstats) Path(chat_export.html).write_text(html, encodingutf-8)home.html里的{% for msg in messages %}可以直接访问msg.strContent、msg.NickName。注意渲染前对strContent做一次防止 XSS 的转义Jinja2 默认会转义、、所以这里不用额外处理。生成后用浏览器打开chat_export.html按 F12 看控制台有没有资源加载错误。样式上我习惯把charts.html用 iframe 嵌进主页面这样数据更新时图表单独刷新不同步全量重绘。这个拆分对后续做年度报告很有用。3.2 python-docx 生成 Word避开粘贴卡顿如果直接在 Excel 或 Word 里粘贴复制聊天记录很容易触发 Office 剪贴板格式冲突表现为“Excel 无法复制粘贴”或“Word 关闭时卡顿”。这不是电脑性能问题而是剪贴板里同时有 HTML、RTF、纯文本三种格式。绕过 Office 剪贴板用 python-docx 在内存中构建文档是更稳的做法。from docx import Document from docx.shared import Pt doc Document() doc.styles[Normal].font.size Pt(10) for day, items in df.groupby(df[date].dt.date): doc.add_heading(str(day), level2) for _, m in items.iterrows(): p doc.add_paragraph() run p.add_run(f{m[NickName]}{m[strContent]}) run.bold bool(m[isSender]) doc.save(聊天记录.docx)doc.add_heading会使用默认标题样式如果 Word 打开后卡顿先从样式入手删掉不必要的主题字体另一个排查点是文档里的内嵌图片。聊天记录中的图片消息在strContent里是[图片]或img标签生成 Word 前应先剥离避免 python-docx 尝试下载未知资源。要做到这点只需要在写入前对每一行做正则删除import re m[strContent] re.sub(rmsg.*?/msg, , m[strContent])3.3 pandas 写入 Excel保留筛选和冻结窗格Excel 版面向数据处理人员他们要的是“能筛选、能透视”不是多漂亮的排版。用to_excel写 xlsx随后设置冻结首行和自动筛选。from openpyxl import load_workbook df.to_excel(聊天记录.xlsx, indexFalse, sheet_name全部消息) wb load_workbook(聊天记录.xlsx) ws wb[全部消息] ws.freeze_panes A2 ws.auto_filter.ref ws.dimensions wb.save(聊天记录.xlsx)这里先to_excel写入数据再load_workbook打开设置是因为 pandas 的逻辑比较简单openpyxl 可以进一步控制列宽和格式。列宽建议按字段宽度缩放ws.column_dimensions[C].width 60太长会出现 Excel 无法完整显示的问题但数据不会丢。如果之前复制粘贴时报“Excel 无法粘贴数据”大概率是不连续区域格式冲突改用这种直接写入方式就不会再遇到。三种格式选型时有一个简单判断表目标工具关注点永久存档Jinja2 HTML转义、离线可开打印/交付python-docx图片压缩、字体收敛数据分析pandas openpyxl冻结、筛选、列宽4. 年度聊天报告从时间戳到可演讲的图表4.1 清洗与统计口径年度报告的第一件事不是画图而是确定“哪些消息算有效”。微信消息里大量[图片]、[语音]、[视频]和引用通知直接统计会虚高。撤回消息在部分版本中保留在库里也会干扰数字。我一般用两步清洗先删掉msg开头的引用块再按白名单过滤表情占位符。import re pat re.compile(r\[图片\]|\[语音\]|\[视频\]|\[红包\]|\[表情\]) df_clean df.copy() df_clean df_clean[~df_clean[strContent].str.startswith(msg)] df_clean[content] df_clean[strContent].str.replace(pat, , regexTrue) df_clean[len] df_clean[content].str.len() df_clean df_clean[df_clean[len] 0]过滤后df_clean才是报告的数据底座。这里故意保留len字段后续计算“最长的单条消息”和“平均消息长度”都用它。很多网上的教程漏掉这一步导致年度总结里出现几百条[图片]报告可信度立刻下降。4.2 按天与按小时聚合找出真实活跃节奏年度聊天报告通常要回答三个问题聊了多少条、跟谁聊最多、什么时间段最活跃。用 pandas 的groupby或resample都能完成区别是要先知道时间字段已经转成了时区正确的datetime64。daily df_clean.groupby(df_clean[date].dt.date).size() hourly df_clean.groupby(df_clean[date].dt.hour).size() top_contacts ( df_clean.groupby(NickName).size().sort_values(ascendingFalse).head(10) )小时统计用dt.hour会返回 0-23 的整数画柱状图时 x 轴很直观。分钟级统计不推荐细粒度对报告没有意义。Top 联系人如果出现nan说明群聊消息对应用户在 Contact 表里没有昵称可以回查ChatRoomInfo表。把这份数据转成 JSON 塞进charts.html用 ECharts 折线图展示全年消息量每年 1 月或 12 月出现单日暴增往往是春节或年终总结聊天值得单独标出。下面的维度表可以直接映射到报告章节指标数据来源可视化年度消息总数len(df_clean)卡片日均消息数daily.mean()折线最活跃小时hourly.idxmax()柱状Top 会话top_contacts条形图4.3 词云和年度关键词词云是聊天报告里最出效果的部分。中文先分词再去停用词然后丢给 wordcloud 生成图片。字体路径在 Windows 上必须指定中文字体否则最后画出来全是方框。import jieba from wordcloud import WordCloud text .join(df_clean[content].tolist()) words .join(w for w in jieba.cut(text) if len(w) 1) wc WordCloud( font_pathC:/Windows/Fonts/simhei.ttf, width1200, height800, max_words200, background_colorwhite, ).generate(words) wc.to_file(wordcloud.png)max_words200控制词云中出现的词数len(w) 1可以过滤掉单个字的噪声比如“嗯”“哦”。如果聊天内容包含大量英文术语建议在切分前加一个英文分词器例如把GPT和LoRA当作整体。生成完图片后把它嵌入wordcloud.html报告就同时具备数字、趋势和视觉三种形态了。5. 训练个人 AI 助手从聊天语料到 LoRA 微调5.1 把聊天记录构造成问答对聊天记录本身不是训练集它只是两个人之间的真实对话缺少指令结构。个人助手通常要学的是“我习惯怎么回复”所以把对方消息视作 user自己的消息视作 assistant是最直接的策略。每次取两个人相邻的对话要求 user 的长度不少于 2 个字assistant 的回复非空组成一行 JSONL。rows [] prev None for _, m in df_clean.iterrows(): if prev is None: prev m continue if m[isSender] ! prev[isSender]: q, a (prev, m) if m[isSender] else (m, prev) if len(q[content]) 2 and len(a[content]) 0: rows.append({ instruction: 请根据我的朋友的话用你自己的风格回复。, input: q[content], output: a[content], }) prev m这里用isSender切换来捕捉连续消息的最后一条但实际上多轮上下文会丢失。更好的做法是把前 4 条消息一起拼进input让模型感知对话语境例如input设置成“用户……\n你……\n用户当前问题”。构造好后保存成chat_export.jsonl再做 9:1 切分。5.2 用 LlamaFactory 做 LoRA 微调训练环境方面我不建议直接重头训练单卡消费级显存跑 7B 模型时用 LoRA 是性价比最高的选择。llama factory一站式大模型高效微调平台对应的一站式微调工具就是 LlamaFactory它把数据转换、训练、推理串成一条命令。安装后先修改数据集配置文件dataset_info.json注册刚生成的chat_export.jsonl然后执行llamafactory-cli train \ --model_name_or_path Qwen/Qwen2.5-7B-Instruct \ --stage sft \ --template qwen \ --dataset chat_export \ --finetuning_type lora \ --lora_rank 8 \ --learning_rate 1e-4 \ --num_train_epochs 3 \ --max_seq_length 2048 \ --per_device_train_batch_size 2 \ --output_dir ./output_qwen_lorastage sft代表有监督微调template qwen必须和基座模型一致否则输入格式不对。关键参数列在下面参数推荐值影响lora_rank8-16越大模型学得越快但过拟合风险高learning_rate1e-4 到 3e-4学习率过大容易震荡num_train_epochs3-5聊天语料量小epoch 太多会记住原话max_seq_length2048超过后丢弃微信长消息要截断训练结束后检查output_dir里是否有adapter_model.safetensors有说明 LoRA 权重已保存。5.3 验证生成质量与过拟合检查用没有参与训练的 10% 消息做验证逐条让模型生成回复。如果模型输出的内容几乎等同于原聊天记录中的话说明过拟合了典型的补救是调低lora_rank到 4 或把num_train_epochs降到 2。另一种问题是“太客套”模型学会了通用寒暄但没有个人特征这时可以削弱instruction直接让模型做续写而不是回答问题。部署到本地时用vLLM加载 LoRA只需两步pip install vllm python -m vllm.entrypoints.openai.api_server \ --model ./output_qwen_lora \ --served-model-name chat_buddy验证时重点关注 50 字以上回复的长度和句式。个人聊天数据训练出来的模型质量上限取决于语料多样性如果只跟一个人聊天模型会很快把对方的声音“焊死”在你自己的表达里。6. 导出排错与增量同步三个易踩的坑6.1 不同版本的数据库混在一起密钥可能对不上数据目录下有以前版本聊天记录是常见情况升级微信后旧库不会自动删除。解密时如果先处理的是最新库回头再用同样的 key 打开旧MSG0.dbSQLCipher 会报HMAC check failed。处理方法是在 2.1 按时间排序后先读取配置文件定位当前版本再分别尝试PRAGMA cipher_migrate。我一般会写一个循环去试把成功的库记录到.gitignore文件里避免临时明文 db 进版本库。6.2 Word 和 Excel 的卡顿源头可能是 Office 剪贴板遇到“Word 关闭时卡顿”或“Excel 无法粘贴数据”先别怀疑 python 导出失败。这两个问题的共同点是 Office 在退出时尝试清理剪贴板里的富文本数据。解决方向是绕开剪贴板Word 用 python-docx 直接构造段落Excel 用 openpyxl 直接写单元格。如果必须从已有 HTML 转 Word可以把 HTML 导出为 PDF 再转那个过程不经过剪贴板。6.3 增量导出只处理新消息聊天数据量大时每次全量 dump 很慢。利用localId自增特性在上次导出位置后取增量last load_last_id() delta df[df[localId] last] if not delta.empty: export_html(delta) export_excel(delta) save_last_id(int(delta[localId].max()))把 last id 存成单独小文件下次直接读取。这同样适用于年度报告只需要在新数据上重算不用每次重读两个 GB 的明文库。以后扩展成自动任务时建议同时按date做分区避免单文件无限膨胀。本文还有配套的精品资源点击获取