新闻详情

新闻详情

首页 / 资讯中心 / 详情

微信聊天记录导出与本地永久备份:SQLCipher解密、Python实现与年度报告生成

发布时间:2026/10/2 5:31:27来源:尧图网络
微信聊天记录导出与本地永久备份:SQLCipher解密、Python实现与年度报告生成
简介提取微信聊天记录并转存为HTML、Word、CSV再生成年度聊天报告的全套工具与模板适用于有Python基础的个人用户、数据分析爱好者及微信开发者。资源基于WeChatMsg解析思路覆盖备份数据解析、格式转换、关键字频率与活跃时段统计、可视化报告输出等环节帮助读者将零散聊天内容变成可长期保存和复盘的结构化文档。压缩包共238个文件大小约25MB。其中94个Python脚本负责核心解析、转换与统计逻辑17个HTML模板用于报告页面展示61个PNG与36个SVG提供图表和视觉素材另含JSON、Markdown、YAML等配置说明文件目录结构清晰便于按模块调用或二次开发。目前已有646人学习下载。通过该资源可完整跑通“备份→解析→导出→分析→报告”流程产出支持在线浏览的HTML报告、词云图表以及便于编辑的Word、CSV文件开发者还能参考proto、qrc等文件理解微信备份格式扩展个性化分析和自动化应用。1. 微信聊天记录导出本地数据再生永久保存与年度报告换过几次手机的人都有这种体会微信里存了好几年、舍不得删的聊天记录换机后要么被仅迁移最近聊天截断要么清理缓存时误删等到想翻某段对话时只能翻截图。这套资源的思路是把 PC 端微信的本地数据库读出来解密后按三种格式导出——HTML 方便浏览器翻看和留档Word 适合打印和送审CSV 适合喂给 Excel 做二次统计最后再基于消息时间、联系人、关键词生成一份年度聊天报告。适合想给家庭聊天记录做永久备份的人也适合需要用真实聊天数据做轻量分析的从业者前提是你有对应账号在 PC 端登录过且能拿到本地库文件。2. 解密微信本地数据库密钥、库结构与依赖准备2.1 密钥获取从内存中找回 SQLCipher 的口令PC 端微信的消息库是 SQLCipher 加密的 SQLite不是普通 sqlite3 能直接打开的。SQLCipher 使用 AES-256 加密页面连接时必须提供口令passphrase或 raw key。微信客户端启动后会把解密后的数据库和口令都留在进程内存里所以社区里最常见的做法是先定位微信进程枚举可读写的内存区域再从中搜出 key。我习惯分三步走dump 或实时读取内存 → 用特征匹配候选 key → 用候选 key 尝试打开数据库验证。以下是用 pymem 枚举内存区域的代码from pymem import Pymem def list_rw_regions(process_name: str WeChat.exe): pm Pymem(process_name) regions [] addr 0 max_addr pm.process_handle.max_addr while addr max_addr: try: mbi pm.virtual_query(addr) # 0x1000 表示已提交0x04/0x40 是 PAGE_READWRITE / PAGE_EXECUTE_READWRITE if mbi.State 0x1000 and mbi.Protect in (0x04, 0x40): regions.append({ base: mbi.BaseAddress, size: mbi.RegionSize }) addr mbi.BaseAddress mbi.RegionSize except Exception: break return regions这段代码的作用是扫描进程的所有虚拟内存页过滤出可读写的提交区域。为什么只挑 PAGE_READWRITE因为微信存放 SQLCipher 口令的堆内存通常属于这类属性只读的代码段没必要扫。拿到区域列表后需要对每个区域做分块读取然后在块里搜索 32 字节的 key 候选这一步比较费时间建议每块 4MB 左右。找到候选 key 后最有效的验证方式是直接用 sqlcipher3 尝试连接数据库并跑 integrity_checkimport sqlcipher3 def verify_key(db_path: str, key: bytes) - bool: try: conn sqlcipher3.connect(db_path) conn.execute(fPRAGMA key \x{key.hex()}\) conn.execute(PRAGMA cipher_memory_security OFF) row conn.execute(PRAGMA integrity_check).fetchone() conn.close() return row is not None and row[0] ok except Exception: return False这里的关键点是PRAGMA key的写法用十六进制字符串包一层x...表示 raw key。验证不通过就继续换下一个候选不用黑盒猜SQLCipher 会直接告诉你页是否被正确解密。实际项目中我还会加一个约束候选 key 是 32 字节且整段字节不全为零能过滤掉大量噪声。2.2 认识三张核心表message、contact、chat拿到密钥后别急着导数据先摸清库结构。不同版本微信的表结构略有差异但核心字段基本稳定最关键的是消息表MSG、联系人表Contact、会话表ChatRoom。这里以消息表为例说明字段含义字段说明导出时用途MsgSvrID服务端消息 ID唯一去重、断点续导StrTalker会话标识单个联系人是对方 wxid群聊是群 id归类会话StrContent消息内容正文导出Type消息类型决定走文本、图片还是文件分支CreateTime秒级时间戳排序和年度报告Status送达状态过滤撤回消息关于 Type 字段导出前必须建一张映射表否则导出结果里全是数字Type含义导出策略1文本直接写入内容3图片从 FileStorage 复制原图HTML/Word 引用本地路径34语音原文件是 silk 格式需要转码默认只留文件名47表情多数是 GIF按 Content 里的 md5 去资源目录找49文件/链接/引用需要按子类型再拆链接取标题文件取文件名建议连库后先跑一句 GROUP BY 统计 Type 分布确认你手上的版本有没有新增类型。常见做法是用SELECT Type, COUNT(*) FROM MSG GROUP BY Type看到陌生类型再去单条抽样不要硬编码只处理上面五种。2.3 环境准备依赖清单和连接代码这个资源涉及 Python 生态依赖尽量收敛避免在客户机上装一堆东西。我一般固定用这四个sqlcipher3连加密库、python-docxWord、jinja2HTML 模板、jieba年度报告分词。装依赖时注意 sqlcipher3 在 Windows 上要先装 Visual C 构建工具否则会当场翻车。连接库的标准姿势import sqlcipher3 def open_db(db_path: str, raw_key: bytes): conn sqlcipher3.connect(db_path) conn.execute(fPRAGMA key \x{raw_key.hex()}\) # 兼容旧库时可能需要显式设置加密参数 # conn.execute(PRAGMA cipher_page_size 1024) # conn.execute(PRAGMA kdf_iter 4000) return conncipher_page_size和kdf_iter是历史版本的兼容项只要打开时报 file is not a database 且确认 key 无误优先怀疑这两个参数和你的库不匹配。新库默认 4096 页大小旧库有的版本是 1024参数不对时 select 会直接报错。我把这个也看成是资源里最容易踩的坑之一后面避坑章会再展开。3. 三种导出格式的实现HTML、Word、CSV3.1 HTML 导出模板渲染保留表情与图片HTML 是最适合长期保存的格式不依赖 Office浏览器双击就能看图片引用本地相对路径即可。计划是把同一个会话按天切分成多个 html 文件主目录下放聊天记录主页方便导航这样单个文件不会因为动图太多变成几十 MB。核心模板用 Jinja2片段如下div classmsg-row {% if msg.is_self %}self{% else %}peer{% endif %} div classmsg-meta span classsender{{ msg.sender }}/span span classtime{{ msg.time }}/span /div div classmsg-body {% if msg.type text %} p{{ msg.content | replace(\n, br) | safe }}/p {% elif msg.type image %} img src{{ msg.local_path }} loadinglazy / {% elif msg.type emoji %} img src{{ msg.local_path }} classemoji / {% endif %} /div /div渲染脚本里要做两件事一是把 MSG.StrContent 里的换行符转成br二是把图片消息的local_path从微信的 FileStorage 原目录复制到导出目录的images子目录。复制时我用的是shutil.copy2保留原始修改时间这样导出目录里能看到图片的原始时间信息。参数loadinglazy对几百条图片消息的页面很管用等浏览器滚动到对应位置才加载图片。HTML 导出还有个隐藏收益它天然支持增量同步。只要把每个会话的最近导出时间记下来下次只渲染该时间之后的消息然后刷新一下主目录的导航页就行不需要重新生成整个目录。很多聊天记录工具导出一次就废了因为没法继续追加这套按天切分的方式给续导留了口子。3.2 Word 导出docx 结构化排版Word 导出的目标场景是可打印的对话纪要排版不需要花哨但要稳定。python-docx 操作很简单核心逻辑是把每条消息拆成一个段落消息时间和发送人作为小字号灰字正文作为正常字号图片单独成段。from docx import Document from docx.shared import Pt, RGBColor doc Document() style doc.styles[Normal] style.font.name 微软雅黑 style.font.size Pt(10.5) for msg in messages: p doc.add_paragraph() meta p.add_run(f{msg[time]} {msg[sender]}) meta.font.size Pt(8) meta.font.color.rgb RGBColor(0x80, 0x80, 0x80) body p.add_run(\n msg[content]) if msg[type] image: run p.add_run(\n) run.add_picture(msg[local_path], widthPt(320))参数说明widthPt(320)是把图片宽度限制在文档内避免原图太大把 docx 撑到 100MB 以上。Word 里图片即使缩到 320pt文件体积依然保留原始像素数据所以如果图片太多我建议在导出前用 Pillow 压缩到宽度 1200px体积能降一个数量级打印效果也足够。分段逻辑上有个细节撤回的消息在 MSG 表里 Status 字段有标记直接用 SQL 过滤会漏掉python-docx 端做二次过滤更稳。这里我参照了先 SQL 粗筛、再程序细筛的处理方式理由是撤回标记在不同版本里字段值不完全一致程序里用一个集合定义已知值既透明又容易改。3.3 CSV 导出UTF-8 BOM 与字段设计CSV 是三种格式里最容易被轻视但最容易出问题的。如果只是给 Excel 用编码必须是utf-8-sig否则中文乱码是一定的。字段设计上我会把原始内容和解析后内容分成两列原始列留着 Type49 的整段 XML解析列放提取出的链接标题或文件名。这样既不丢失细节又方便统计。import csv import time def export_csv(messages, out_path: str, contact_map: dict): with open(out_path, w, newline, encodingutf-8-sig) as f: writer csv.writer(f) writer.writerow([时间, 会话, 发送人, 消息类型, 内容, 本地文件]) for msg in messages: writer.writerow([ time.strftime(%Y-%m-%d %H:%M:%S, time.localtime(msg[ts])), contact_map.get(msg[talker], msg[talker]), msg[sender], msg[type_name], msg[content], msg[local_path], ])contact_map是从 Contact 表构建的wxid → 备注名映射导出前先一次性加载成 dict。这么做的主要原因是 MSG 表里只有 wxid 没有备注名如果不映射导出的会话列全是 wxid非技术用户根本看不懂是谁。另外注意newline不写的话在 Windows 上每行后面会多一个空行这是 Python 写 CSV 最常见的玄学问题之一。CSV 的统计价值在于可以用 pandas 直接做后续分析或者导入数据库做长期归档。我一般建议把 CSV 当作原始数据备份层HTML 和 Word 当作展示层年度聊天报告则单独从 CSV 里读取再生成三层各司其职。4. 生成年度聊天报告统计口径与中间格式4.1 统计维度怎么定年度聊天报告不是把消息总数列出来就完了我的经验是先定五个维度总量、联系人热度、时间分布、词频、情感词。总量包括年度消息条数、活跃天数、日均条数联系人热度是按人聚合的消息数和字数排行时间分布拆成月度走势和 24 小时时段热力词频用 jieba 分词后做词频 TOP20情感词简单分积极/消极两档。确定维度后再统一口径最容易翻车的点是算不算群聊、算不算图片语音。我的建议是默认只统计文本和表情图片语音单独一个计数项避免词频被图片数量稀释。过滤条件统一放 SQL 里不要在多个脚本里各写一遍否则报告数据自相矛盾。import json import time from collections import Counter, defaultdict import jieba STOPWORDS set(嗯 啊 了 的 是 就 在 和 都 也.split()) def build_report(rows, year: int 2024): stats { total: 0, active_days: 0, by_month: defaultdict(int), by_hour: defaultdict(int), by_talker: Counter(), words: Counter(), } day_set set() for r in rows: ts r[ts] if time.localtime(ts).tm_year ! year: continue stats[total] 1 day_set.add(time.strftime(%Y-%m-%d, time.localtime(ts))) stats[by_month][time.localtime(ts).tm_mon] 1 stats[by_hour][time.localtime(ts).tm_hour] 1 stats[by_talker][r[talker]] 1 if r[type_name] 文本: for w in jieba.cut(r[content]): if len(w) 2 and w not in STOPWORDS: stats[words][w] 1 stats[active_days] len(day_set) return stats这段代码里的year参数是硬边界报告只统计目标年份的数据。STOPWORDS是内置停用词实际做的时候我会从外部文件加载一份更大的停用词表否则微信消息这类词会因为高频且无意义把词频榜直接霸屏。len(w) 2是过滤单字单字在分词里多是语气词统计意义不大。4.2 报告生成JSON 中间层 Jinja2 模板统计脚本输出什么格式直接决定报告页好不好写。我的做法是统计脚本只输出 JSONHTML 报告用 Jinja2 渲染二者之间不共享任何代码改报告样式不需要重新跑统计。JSON 结构分三块header 里的汇总数字、by_month 和 by_hour 两个数组、by_talker 和 words 两个排行列表。报告页的模板结构比较固定顶部是年度总览卡片中间是月度柱状图和时段热力布局底部是联系人 TOP10 和关键词 TOP20。这里不强求图表库纯 CSS 柱状图对永久保存这个目标更友好——不依赖 CDN离线也能看。用 div 高度模拟柱子的做法代码量最少且导出后单文件可移动。生成 JSON 并渲染 HTML 的主流程import json from jinja2 import Environment, FileSystemLoader def render_report(stats, talker_names: dict, out_html: str): payload { total: stats[total], active_days: stats[active_days], month: [stats[by_month].get(m, 0) for m in range(1, 13)], hour: [stats[by_hour].get(h, 0) for h in range(24)], talkers: [ {name: talker_names.get(k, k), count: v} for k, v in stats[by_talker].most_common(10) ], words: [{name: k, count: v} for k, v in stats[words].most_common(20)], } env Environment(loaderFileSystemLoader(templates)) tpl env.get_template(report.html) html tpl.render(datapayload) with open(out_html, w, encodingutf-8) as f: f.write(html)参数说明talker_names与 CSV 导出用的是同一个 contact_map保证统计里的微信名和导出文档里的会话名一致。by_month和by_hour用 range 补齐了 12 个月和 24 小时的空位否则一月份只有 30 天数据时报告页柱子会缺一段观感像数据丢了。言语之外年度报告最有存档感的是把当年聊天里第一次出现和最后一次出现的时间也放进去。代码上只需要在 build_report 里维护min_ts和max_ts两个变量渲染时转成字符串放进 header整体并不复杂但对报告的情感价值提升非常明显那段时间跨度就是这一年我们聊了这些的直观表达。5. 避坑指南五条血泪记录5.1 现象打开数据库报 file is not a database原因微信升级后消息库路径变了旧的 MicroMsg.db 只剩空壳或者数据库被微信迁移到了新目录。现在 3.9 版本之后消息库被拆成多个 msg_0.db、msg_1.db路径通常在文档\xwechat_files\wxid\db_storage\message\下。解决不要硬编码一个路径用glob扫整个 wxid 目录下的*.db再用 verify_key 逐个试。多花两分钟扫描比报错后再找半天路径强。5.2 现象CSV 用 Excel 打开中文全部乱码原因Python 默认用utf-8写入Excel 并不认 utf-8 无 BOM 文件它默认用本地 ANSI 编码解析中文字段在 GBK 里全是问号。解决打开文件时写encodingutf-8-sig它会自动在文件头加 BOMExcel 就能正确识别。这是所有导出流程里最便宜的修复却也是最常见的翻车点。5.3 现象Word 导出后文件 60MB打开卡死原因图片消息的原图被原封不动插进 docxpython-docx 的 add_picture 即使设置显示宽度文件内部依然保存原始像素数据。一次聊天里传十几张高清照片文件体积就会失控。解决在插入前用 Pillow 压缩到长边 1200px、JPEG 质量 80压缩这块放在导出脚本里提前处理word 端只收路径。从那以后我导出前都会先跑一遍图片压缩函数再检查输出文件大小是不是合理的。5.4 现象导出消息顺序错乱比实际时间晚 8 小时原因MSG 表的 CreateTime 是秒级时间戳微信服务器存的是 UTC本地显示时再转东八区。如果脚本跑在纯 Python 环境且系统时区不是中国时区time.localtime(ts)会按系统时区转直接差出 8 小时。解决统一用time.localtime(ts, timezone)或在脚本开头os.environ[TZ] Asia/Shanghai。排序上按 CreateTime 再按 MsgSvrID 双字段排序避免同一秒内多条消息顺序错乱。5.5 现象撤回的消息还在导出文档里原因MSG 表里撤回消息有两种状态一种是 Status 被标记一种是内容被替换成你撤回了一条消息。前者可以被 SQL 过滤后者半像普通消息程序很难判断。解决我的习惯是导出时把 Status 不在正常集合的跳过再把 Content 里包含撤回了一条消息关键词的文本过滤掉。宁可少几条不要出现被撤回的敏感内容。6. 进阶增量同步与一键备份导出一旦做了第一次后续就是持续更新的需求。增量导出的思路是给每个会话保存一个 checkpoint 文件记录上次导出的最大 MsgSvrID。下次导出时用WHERE MsgSvrID ?取增量再按 MsgSvrID 去重就永远不用全量重跑。import json import time CHECKPOINT checkpoint.json def load_checkpoint(talker: str) - int: try: with open(CHECKPOINT, r, encodingutf-8) as f: return json.load(f).get(talker, 0) except (FileNotFoundError, json.JSONDecodeError): return 0 def save_checkpoint(talker: str, max_id: int): cp {} try: with open(CHECKPOINT, r, encodingutf-8) as f: cp json.load(f) except (FileNotFoundError, json.JSONDecodeError): pass cp[talker] max_id with open(CHECKPOINT, w, encodingutf-8) as f: json.dump(cp, f, indent2, ensure_asciiFalse)配套的全量备份脚本我通常会做成三步先检查微信进程是否在运行并占用数据库再复制一份 db 文件到临时目录最后对副本做导出和报告生成。数据库处于被占状态时直接连库读容易读到未刷盘的数据复制出来的文件是最稳妥的工作副本Windows 下直接用shutil.copy2即可。照这样整理完整套流程就沉淀成三个入口export_all.py负责三种格式导出build_report.py负责年度报告update.sh / update.bat负责增量同步。每次微信升级后跑一遍全量校验确认密钥和路径没有变化再切回增量模式。希望帮到你让你那些被锁在微信里的对话也能变成自己能永久掌控的本地资料。本文还有配套的精品资源点击获取
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

四个指标公式原码图无未来下周大盘密钥分析 2026/10/2 7:07:28

四个指标公式原码图无未来下周大盘密钥分析

VAR3:(2*CLOSEHIGHLOW)/4; VAR4:LLV(LOW,34); VAR5:HHV(HIGH,34); QYYJ:EMA((VAR3-VAR4)/(VAR5-VAR4)*100,13); RQQ:EMA(0.667*REF(QYYJ,1)0.333*QYYJ,2); DRAWTEXT(CROSS(QYYJ,RQQ) AND QYYJ<10,L-0.2,低吸),COLORCYAN;AR26R:(CLOSE-LLV(LOW,27))/(HHV(HIGH,27)-LLV(LOW,27…

阅读更多 →
亲测12款论文降AI率工具,效果最稳的竟然是它! 2026/10/2 7:07:28

亲测12款论文降AI率工具,效果最稳的竟然是它!

最近真的有太多人问我&#xff1a;"论文 AI 率太高怎么办&#xff1f;学校现在查 AI 检测比查重还严&#xff0c;连人工改的都过不了&#xff01;" 我特别理解这种焦虑&#xff0c;因为我自己前段时间也踩过坑。各种号称降低 AI 率的工具试了一圈&#xff0c;有的乱扣…

阅读更多 →
一文读懂嵌入式知识系列:从C语言到可执行文件 2026/10/2 7:07:28

一文读懂嵌入式知识系列:从C语言到可执行文件

前言很多嵌入式开发者写了多年C语言&#xff0c;熟练实现串口、定时器、中断等功能&#xff0c;却始终搞不懂一个核心问题&#xff1a;我们写的C代码&#xff0c;到底是怎么变成单片机、ARM板子能识别、能运行的可执行程序的&#xff1f;平时IDE一键编译、下载程序的操作&#…

阅读更多 →
数据合规的同意记录怎么留存? 2026/10/2 7:07:28

数据合规的同意记录怎么留存?

如果你正在过 App 合规检查、整理同意日志&#xff0c;这篇可以直接当清单&#xff0c;对照自己的弹窗版本和日志字段查漏补缺。结论先说&#xff1a;同意记录不是一张弹窗截图&#xff0c;而是一条"谁、在什么时间、看到哪个版本的文案、点了什么、后来有没有撤回"的…

阅读更多 →
6款论文AI智能降重工具亲测:AI率直降安全线,学生党必入平价款 2026/10/2 7:07:28

6款论文AI智能降重工具亲测:AI率直降安全线,学生党必入平价款

2026年毕业季临近&#xff0c;知网、维普两大国内核心学术平台已完成AIGC检测算法的全面迭代升级&#xff1a;知网将AI检测模型更新至3.0版本&#xff0c;实现句子级精准识别&#xff0c;对AI生成内容的识别能力提升15-18个百分点&#xff1b;维普则重构检测逻辑&#xff0c;新…

阅读更多 →
一文读懂OSI与TCP/IP:TCP/UDP原理、可靠传输与iperf3验证实验 2026/10/2 7:07:22

一文读懂OSI与TCP/IP:TCP/UDP原理、可靠传输与iperf3验证实验

写这篇东西的起因很直接&#xff1a;不管是准备计算机网络考试、复习 408&#xff0c;还是被面试官追问“打开一个网页背后发生了什么”&#xff0c;你迟早都得和 OSI 七层模型、TCP/IP 协议栈、TCP 三次握手、UDP 这些词正面相遇。我在这个方向待了很多年&#xff0c;也给不少…

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉