
简介PyWxDump 是一套面向微信数据本地化处理的开源工具集适合安全研究人员、数据分析爱好者及需要备份聊天记录的用户。它可获取微信昵称、账号、手机号、邮箱及数据库密钥解密并合并多类型数据库支持通过 Web 界面查看聊天记录导出为 html、csv 等格式便于 AI 训练语料整理与自动回复场景同时兼容微信多开与多账户信息获取。资源包共 71 个文件以 49 个 Python 脚本为核心实现辅以 10 个 Markdown 文档说明数据库字段、CE 基址偏移与 MAC 解密方法另含少量图片、配置与可执行文件整体约 2.54MB结构紧凑。目前已有 1103 人学习下载。读者可借此掌握数据库解密、基址定位与记录导出流程并参考极简版 pywxdumpmini 快速获取密钥与数据库位置用于日常备份、远程查看或网络安全研究。1. 微信聊天记录本地化从数据库文件到 AI 训练语料的完整链路微信聊天记录里藏着大量真实对话语料做 AI 训练、微调、RAG 检索增强甚至只是给自己留一份可检索的存档都有实际价值。但很多人卡在第一步手机里的聊天记录到底存在哪、怎么读、怎么变成能用的 csv 或 html。常见做法是找到本地 SQLite 数据库文件解密后读取消息表再按会话维度导出。这条链路涉及数据库解密、多账户识别、消息类型解析、导出格式设计四个环节每个环节都有具体的坑。这篇笔记按「文件在哪 → 怎么解密 → 怎么读表 → 怎么导出 → 怎么避坑」的顺序拆一遍适合想自己动手做聊天记录本地化、给 AI 训练准备语料的工程师。全程只谈本地文件操作不涉及任何网络传输或第三方服务。2. 微信本地数据库的文件结构与解密前置条件2.1 数据库文件分布与多账户目录识别微信在 Android 和 iOS 上的存储路径不同但核心数据库文件命名有规律。Android 常见路径是/data/data/com.tencent.mm/MicroMsg/32位hash/EnMicroMsg.db其中32位hash是账户标识多账户登录时会有多个这样的目录。iOS 则通常在 App 沙盒的Documents/hash/DB/下。PC 端微信的数据库在WeChat Files/wxid/Msg/下文件名类似MSG0.db、MSG1.db分片存储。识别多账户的关键是那个 32 位 hash 目录名它由用户 UIN 和 IMEI 等参数拼接后 MD5 生成。实际做的时候不需要自己算直接遍历MicroMsg下所有 32 位十六进制命名的目录每个目录对应一个登录过的账户。目录里的EnMicroMsg.db就是主消息库SnsMicroMsg.db是朋友圈FTS5IndexMicroMsg.db是全文索引。做聊天记录导出只需要关注EnMicroMsg.db。提示操作前先把整个账户目录复制一份到工作目录所有读取都在副本上做避免误写原库导致微信异常。2.2 解密参数IMEI、UIN 与密钥推导EnMicroMsg.db是 SQLCipher 加密的不是普通 SQLite。解密需要密钥密钥推导方式是md5(IMEI UIN)取前 7 位。IMEI 在 Android 10 以后普通应用拿不到常见替代是用android_id或者直接留空字符串。UIN 存在com.tencent.mm/shared_prefs/system_config_prefs.xml里的default_uin字段是个带符号的整数取绝对值后参与拼接。import hashlib def derive_key(imei: str, uin: str) - str: # 微信 SQLCipher 密钥md5(imei uin) 的前 7 位小写十六进制 raw (imei uin).encode(utf-8) md5 hashlib.md5(raw).hexdigest() return md5[:7] # 常见组合imei 为空字符串uin 从 system_config_prefs.xml 读取 key derive_key(, 1234567890) print(key) # 输出 7 位密钥这段代码的逻辑是严格按微信的密钥推导规则来先拼接 IMEI 和 UIN做 MD5取前 7 位。参数说明上imei在拿不到真实值时就传空串实测大部分机型仍然能解uin必须是default_uin的绝对值带负号会导致密钥错误。如果解出来报file is not a database九成是密钥不对先检查 UIN 有没有取绝对值。拿到密钥后用 SQLCipher 打开# 用 sqlcipher 命令行打开解密后的库 sqlcipher EnMicroMsg.db sqlite PRAGMA key 你的7位密钥; sqlite PRAGMA cipher_use_hmac OFF; -- 部分老版本需要关掉 sqlite ATTACH DATABASE plain.db AS plaintext KEY ; sqlite SELECT sqlcipher_export(plaintext); sqlite DETACH DATABASE plaintext;cipher_use_hmac OFF这行是血泪经验微信用的 SQLCipher 版本较老不关 HMAC 会报HMAC mismatch。导出成plain.db后就是标准 SQLite后面所有操作都用普通 sqlite3 即可。2.3 消息表结构与关键字段含义解密后的库里表很多核心是message表。关键字段msgId消息唯一 IDtalkerId会话 ID群聊是chatroom结尾type消息类型content消息内容createTime时间戳毫秒isSend是否自己发送0 接收 1 发送status状态。type字段决定content怎么解析1 是文本3 是图片34 是语音43 是视频49 是链接/文件/引用等复合消息10000 是系统提示。rcontact表存联系人username是 wxidnickname是昵称remark是备注。导出时要把talkerId关联到rcontact拿到可读的名字。群聊的talkerId是xxxxchatroom成员信息在chatroom表里需要额外解析。3. 从 message 表到 csv/html导出脚本与格式设计3.1 按会话聚合查询与消息类型过滤导出前先确定要导哪些会话。常见做法是按talkerId分组统计每个会话的消息数和时间范围再决定导全部还是指定几个。下面这条 SQL 先摸清数据分布-- 统计每个会话的消息量、时间范围、最后一条消息时间 SELECT talkerId, COUNT(*) AS msg_count, MIN(createTime) AS first_ts, MAX(createTime) AS last_ts FROM message WHERE type IN (1, 49) -- 先只看文本和复合消息 GROUP BY talkerId ORDER BY msg_count DESC;type IN (1, 49)是过滤条件1 是纯文本49 涵盖链接、文件、引用回复等做 AI 训练语料时这两类最有价值。图片语音视频的content是 XML 或路径直接导出意义不大。createTime是毫秒时间戳导出时要除以 1000 再格式化。3.2 导出为 csv字段设计与编码处理csv 适合做后续数据处理和 AI 训练。字段建议talker会话名、sender发送者、is_send、type、content、create_time。content 里的换行、逗号、引号必须转义否则 csv 会错行。import sqlite3, csv, time, re def clean_content(raw: str) - str: # 去掉 XML 标签保留可读文本 text re.sub(r[^], , raw) # 压缩连续空白 text re.sub(r\s, , text).strip() return text conn sqlite3.connect(plain.db) cur conn.cursor() cur.execute( SELECT m.talkerId, m.isSend, m.type, m.content, m.createTime, r.nickname, r.remark FROM message m LEFT JOIN rcontact r ON m.talkerId r.username WHERE m.type IN (1, 49) ORDER BY m.createTime ASC ) with open(chat_export.csv, w, newline, encodingutf-8-sig) as f: writer csv.writer(f, quotingcsv.QUOTE_ALL) writer.writerow([talker, sender, is_send, type, content, create_time]) for talkerId, isSend, mtype, content, ts, nickname, remark in cur: talker remark or nickname or talkerId sender me if isSend else talker t time.strftime(%Y-%m-%d %H:%M:%S, time.localtime(ts / 1000)) writer.writerow([talker, sender, isSend, mtype, clean_content(content), t]) conn.close()逻辑说明LEFT JOIN rcontact是为了拿到可读的会话名优先用备注remark其次昵称最后退回 wxid。encodingutf-8-sig是为了 Excel 打开不乱码这是踩过坑的——用纯 utf-8 在 Windows Excel 里中文会变乱码。quotingcsv.QUOTE_ALL把所有字段都加引号避免 content 里的逗号导致列错位。clean_content去掉 XML 标签因为 type 49 的消息 content 是 XML 结构直接导出会带一堆标签。3.3 导出为 html可读性与检索友好html 适合人工翻阅和本地检索。设计上按会话分块每条消息带时间、发送者、内容自己发的靠右对方靠左。用纯静态 html不依赖任何外部资源方便本地打开和全文搜索。import html as html_mod def export_html(rows, out_pathchat_export.html): parts [!DOCTYPE htmlhtml langzh-CNheadmeta charsetutf-8, title聊天记录导出/title, stylebody{font-family:sans-serif;max-width:800px;margin:0 auto;padding:20px}, .msg{margin:8px 0;padding:8px;border-radius:6px;background:#f5f5f5}, .me{background:#d1e7ff;text-align:right}, .meta{font-size:12px;color:#888}/style/headbody] for talker, sender, isSend, mtype, content, t in rows: cls msg me if isSend else msg parts.append(fdiv class{cls}div classmeta{t} · {html_mod.escape(sender)}/div fdiv{html_mod.escape(content)}/div/div) parts.append(/body/html) with open(out_path, w, encodingutf-8) as f: f.write(\n.join(parts))html_mod.escape必须加否则 content 里的会破坏页面结构这是翻车过的地方——某次导出后页面直接白屏排查半天发现是消息里带了 html 标签。样式用内联 style不引外部 css保证单文件可离线打开。自己发的消息加me类靠右显示视觉上区分对话双方。3.4 多账户批量导出与目录组织多账户场景下每个账户目录独立处理输出按账户分目录避免文件名冲突。#!/bin/bash # 批量处理多个账户目录 for account_dir in /path/to/MicroMsg/*/; do hash$(basename $account_dir) if [ -f $account_dir/EnMicroMsg.db ]; then echo 处理账户: $hash mkdir -p output/$hash # 解密 sqlcipher $account_dir/EnMicroMsg.db PRAGMA key$KEY; PRAGMA cipher_use_hmacOFF; ATTACH DATABASE output/$hash/plain.db AS p KEY ; SELECT sqlcipher_export(p); DETACH DATABASE p; # 导出 python export.py --db output/$hash/plain.db --out output/$hash/ fi done逻辑是按目录遍历每个账户单独解密、单独导出输出目录用 hash 命名。参数上$KEY需要按账户分别推导因为不同账户的 UIN 不同。实际做的时候建议把密钥推导也写进脚本从每个账户的system_config_prefs.xml里读 UIN 自动算。4. 避坑与排查解密失败、乱码、消息缺失的常见原因4.1 解密报 file is not a database现象SQLCipher 打开后执行任何查询都报file is not a database。原因密钥错误或者cipher_use_hmac没关。解决先确认 UIN 取的是绝对值再确认 IMEI 拼接顺序是imei uin不是反过来。如果还不行试PRAGMA cipher_page_size 1024和PRAGMA kdf_iter 4000老版本微信用的参数和新版 SQLCipher 默认值不同。4.2 导出的 csv 在 Excel 里中文乱码现象csv 用文本编辑器看正常Excel 打开中文变问号或方块。原因Excel 默认按 GBK 解析utf-8 无 BOM 时识别不了。解决写文件时用encodingutf-8-sigBOM 头会让 Excel 正确识别 utf-8。这个坑几乎每个人都会踩一次。4.3 消息内容里带 XML 标签导致可读性差现象type 49 的消息导出来是一大段msgappmsg...。原因微信把链接、文件、引用回复都塞进 XML 结构的 content。解决用正则去标签或者按 XML 解析提取title和des字段。做 AI 训练时建议只保留纯文本部分标签对训练没价值还占 token。4.4 群聊消息发送者显示为 wxid现象群聊导出的 sender 全是chatroom或一串 wxid分不清谁说的。原因群聊消息的content里带:\n前缀标识发送者talkerId是群 ID 不是个人。解决解析 content 开头的wxid_xxx:\n部分用rcontact关联出昵称。群成员列表在chatroom表的memberlist字段里逗号分隔。4.5 导出后消息数量对不上现象微信里看到的条数和导出的条数差很多。原因过滤条件太严或者message表只存了部分分片。解决先不加 type 过滤统计总数和微信里对比。PC 端微信消息分片在MSG0.db到MSG9.db需要全部解密后 UNION 查询。Android 端单库一般完整但撤回的消息status字段会变导出时按需过滤。5. 进阶把导出语料喂给 AI 训练与自动回复的衔接技巧导出只是第一步真正做 AI 训练还要考虑语料清洗和格式转换。csv 里的对话是扁平的训练对话模型需要构造成user/assistant配对。一个实用技巧是按时间窗口切分同一个会话里自己发的消息作为 assistant前一条对方消息作为 user间隔超过 30 分钟就断开避免跨话题配对。def build_pairs(rows, gap_seconds1800): pairs, last_user [], None for talker, sender, isSend, mtype, content, t in rows: ts time.mktime(time.strptime(t, %Y-%m-%d %H:%M:%S)) if not isSend: last_user (content, ts) elif last_user and (ts - last_user[1]) gap_seconds: pairs.append({user: last_user[0], assistant: content}) last_user None return pairsgap_seconds1800是经验值半小时内的对话通常属于同一话题。这个阈值可以按自己的聊天习惯调话痨型可以放到 3600简洁型可以缩到 600。构造出的 pairs 直接可以转成 JSONL 喂给微调框架。自动回复的衔接上导出语料可以作为检索库把 pairs 存进向量库新消息来了先检索相似历史对话把最相关的几条作为 few-shot 拼进 prompt。这样回复风格更贴近本人。注意检索时要按 talker 过滤别把工作群的话术用到家人对话里。验证导出质量有个笨办法但很有效随机抽 20 条导出记录回微信里搜原文对比看时间、发送者、内容是否一致。我一般会重点检查带表情、带换行、带链接的消息这三类最容易在导出时出问题。踩过最深的坑是某次导出后没验证直接拿去训练结果模型学了一堆 XML 标签生成的全是msg开头后悔药都没得吃。所以导出后一定先抽样核对再进训练流程。希望帮到你。本文还有配套的精品资源点击获取