微信聊天记录提取与导出:解密SQLCipher数据库并生成CSV/HTML/Word文档
简介一款面向微信用户和数据分析爱好者的聊天记录提取与管理工具支持将聊天内容导出为HTML、Word、CSV等格式便于永久保存和二次利用内置分析模块可生成年度聊天报告适合需要备份重要对话、制作纪念内容或完成课程作业的群体。资源包共257个文件包含103个Python源码、可直接运行的exe、多种HTML结果模板、PNG/SVG图表素材以及JSON配置与Markdown说明其中HTML模板涵盖年度统计、词云展示等页面JSON和Markdown文件分别承担配置与使用说明压缩包整体24.96MB结构清晰既能开箱即用也能基于源码按需修改。目前已有9748人学习下载配套文件覆盖从数据解析、年度统计到可视化呈现的完整流程可帮助使用者快速理解微信聊天数据的导出与分析方法。1. 微信聊天记录提取为什么说“能导出”不等于“能用”微信聊天记录提取这件事表面上是个“把数据库里的消息读出来”的体力活实际上当你真正拿到那份本地数据时才会发现要对付的是加密库、版本差异、附件路径和编码问题。很多人第一次接触这类工具时都以为只要把手机或者电脑上的微信文件夹整个拷贝出来就能得到一份能永久保存的 HTML、Word、CSV 文档结果翻车的原因往往是数据库是加密的、表结构看不懂、图片语音全裂。这篇笔记就顺着“提取微信聊天记录并导出成 HTML、Word、CSV”这条链路把数据存在哪、怎么解密、字段怎么解析、三种格式各自怎么生成以及常见的坑一次讲透。适合想自己跑通备份流程的开发者也适合需要把聊天记录做成留档证据的普通用户。2. 聊天记录的地图数据存在哪、怎么加密、为什么不能直接拷文件2.1 三端存储安卓的 EnMicroMsg.db、iOS 的 MM.sqlite、PC 的 MSG.db微信的聊天记录不是“云同步一切”的客户端会在本地落一份数据库文件只是不同端的位置和格式差异很大。安卓端的位置在/data/data/com.tencent.mm/MicroMsg/32位hash/EnMicroMsg.db这是个 SQLite 数据库但同时被 SQLCipher 加密过。真正能看到的除了 EnMicroMsg.db还有同目录下的voice2text、appbrand等文件夹其中图片、语音、视频等附件不直接存在数据库里而是放在同目录的FileStorage下面。想提取完整的聊天记录不能只拿数据库文件必须把附件目录一起带走。iOS 端因为沙箱机制普通用户基本拿不到应用私有目录常见做法是先对手机做一次完整备份再从备份里提取MM.sqlite。这个库从 iOS 微信 8.0 开始也做了加密处理解析难度比安卓还要高一些。所以我一般不建议新手从 iOS 入手。PC 端是最适合做提取的入口。Windows 版微信会把聊天数据写到WeChat Files/wxid_xxx/Msg/目录下较新的版本里核心数据集中在MSG.db同时有MicroMsg.db存好友和群信息、MediaMsg.db存媒体消息索引。PC 端数据库同样是 SQLite 格式并且也会做加密但远比安卓好处理登录状态下能直接从进程内存或本地配置文件里拿到密钥。2.2 SQLCipher 加密别想跳过解密直接打开 ENMicroMsg.db很多人第一次拿到 EnMicroMsg.db 时用 Navicat 或 DB Browser 一打开直接报file is not a database这就是 SQLCipher 在起作用。SQLCipher 对数据库文件做了完整的加密文件头不再是标准 SQLite 的SQLite format 3而是变成了随机字节所以普通数据库工具根本不认。解密过程需要三样东西加密数据库文件、SQLCipher 库、密钥。密钥由微信端根据设备信息和账号信息生成早期安卓版本的算法是把IMEI UIN拼起来做 MD5取前 7 位再补一个\0得到 16 字节密钥后期版本加入了更多盐值公式变了。PC 端不靠公式而是登录后把密钥放在进程内存里因此各类提取工具普遍采用“扫描微信进程内存 → 定位密钥 → 用 SQLCipher 打开数据库”的套路。这里要明确一个操作原则永远不要在正在运行的微信上去改数据库或直接复制被占用的文件。正确流程是先把微信完全退出再整体复制Msg目录到你的工作目录然后在副本上做解密和解析。这样既避免文件句柄占用导致复制出 0 字节文件也避免后续误操作把原始数据弄坏。2.3 拿密钥的常见三种手段第一种是内存扫描。微信 PC 版运行时密钥会出现在进程内存中常见工具的做法是用 Python 调用 Windows API 读取 WeChat.exe 或 WeChatAppEx.exe 进程的内存空间通过特征字符串检索密钥。这种方式对 PC 版 3.x、4.x 都有效但每次微信重启后密钥可能变化所以必须在登录状态下手动触发提取。第二种是读取本地配置文件。微信 PC 版的config.data或SystemConfig.data里可能包含加密后的密钥材料但因为这个文件本身是自定义二进制格式解析难度不比内存扫描低而且新版微信已经逐步把敏感信息从明文配置里移走了我现在基本只用内存扫描。第三种是旧版公式计算。只适用于旧版安卓微信需要拿到设备 IMEI 和账号 UINUIN 可以从CompatibleInfo.cfg里获取。由于现在微信版本迭代频繁这条路的成功率不高作为应急方案知道即可。不管用哪种方式拿到密钥后都要做一件事先用 SQLCipher 对它执行PRAGMA key并查询sqlite_master验证密钥是否正确再开始导出流程。跳过这步直接去读 message 表报错会让人很难判断到底是密钥错了还是字段名不对。3. 拉通最小链路解密数据库、读 message 表、导出第一条 CSV3.1 准备环境Python、sqlcipher 与依赖的安装先准备好基础环境。我习惯用 Python 3.10 以上版本做解析和导出因为 sqlite3 是标准库处理 CSV 和生成 HTML 也不需要额外重量级依赖。数据库解密这块首选 SQLCipher 的命令行工具它比 Python 的 pysqlcipher3 绑定库更容易定位问题。# Ubuntu/Debian 系安装 SQLCipher 命令行工具 sudo apt-get install sqlcipher # Windows 可以下载 SQLCipher 的预编译命令行程序或者用 WSL # 验证安装成功 sqlcipher --version参数说明SQLCipher 命令行工具安装后会在系统 PATH 里提供sqlcipher命令如果你在 Windows 上没有 WSL也可以直接在 Python 里用pysqlcipher3绑定库但编译依赖 OpenSSL新手容易在环境上卡很久所以我建议先在命令行跑通解密再做后续处理。3.2 用命令行完成数据库解密与导出找一个已经退出微信的 PC 端环境把整个Msg目录复制到工作目录后先用命令行解密一次。cp -r /mnt/c/Users/你的用户名/Documents/WeChat Files/wxid_xxx/Msg ./wechat_data # 对 MSG.db 做解密 sqlcipher ./wechat_data/MSG.db进入sqlcipher交互环境后依次执行下面这几条 SQLPRAGMA key 你的密钥; PRAGMA cipher_memory_security OFF; SELECT count(*) FROM sqlite_master;第一行PRAGMA key用来设置解密密钥密钥如果是十六进制字节串通常要写成xABCD...的形式如果是可打印字符串直接加单引号。第二行PRAGMA cipher_memory_security OFF是为了兼容部分旧版本 SQLCipher 导出的库新库可以不执行。第三行只要能返回数字而不是报file is not a database就说明解密成功。这一步是整个流程的第一个检查点。很多教程让你直接去读 message 表但如果在sqlite_master就没通过后面全是白费。把这一步跑通后再在 sqlcipher 里执行ATTACH DATABASE decrypted.db AS plaintext KEY ;或者直接用SELECT writefile()导出解密副本具体导出命令取决于你的 SQLCipher 版本最省事的做法是把 sqlcipher 的交互命令改成 Python 的sqlite3打开解密后的副本继续解析。3.3 先读 schema 再做解析避免版本差异翻车拿到解密后的数据库第一步不应该是直接SELECT * FROM MSG而是先读表结构。不同版本的微信MSG 表的字段名和顺序差异不小直接按网上抄的字段名查询很容易遇到no such column: StrTalker或者查出来一堆空值。import sqlite3 conn sqlite3.connect(decrypted.db) cur conn.cursor() # 查看所有表 cur.execute(SELECT name FROM sqlite_master WHERE typetable) tables cur.fetchall() print(表清单:, tables) # 查看 MSG 表的建表 SQL确认字段名 cur.execute(SELECT sql FROM sqlite_master WHERE nameMSG) print(cur.fetchone()[0]) # 查看最近一条消息的原始内容 cur.execute(SELECT * FROM MSG ORDER BY CreateTime DESC LIMIT 5) rows cur.fetchall() for row in rows: print(row)这里的逻辑是先拿表清单再看建表 SQL最后抽样看数据。建表 SQL 能直接告诉你当前版本有哪些列抽样数据能帮你确认CreateTime是秒还是毫秒、Content里是纯文本还是带 XML 结构。这样做的意义在于后面写导出脚本时你是对着真实 schema 写而不是对着记忆写。PC 版微信 MSG 表常用的列有localId、TalkerId、MsgSvrID、Type、SubType、IsSender、CreateTime、Status、MsgSeq、Content、StrContent等。聊天内容主要看StrContent也就是纯文本内容字段Type表示消息类型文本消息通常对应 1图片对应 3语音对应 34视频对应 43类型 49 是引用、链接、文件等混合消息具体子类型要看SubType。但不要把这个对应关系当成硬编码还是要以当前库里的实际分布为准。3.4 输出第一条 CSV 记录当你能从数据库里读到一条消息后就可以做第一次导出了。先用 CSV 格式打底因为它结构最简单也方便后续调试。import csv import sqlite3 import time conn sqlite3.connect(decrypted.db) cur conn.cursor() # 查询文本消息的数据取最近 100 条 cur.execute( SELECT CreateTime, IsSender, StrContent, Type FROM MSG WHERE StrContent IS NOT NULL AND StrContent ! ORDER BY CreateTime DESC LIMIT 100 ) rows cur.fetchall() with open(chat_export.csv, w, newline, encodingutf-8-sig) as f: writer csv.writer(f) writer.writerow([时间, 发送方向, 内容, 消息类型]) for row in rows: ts row[0] # PC 版微信 CreateTime 一般是秒级时间戳安卓可能是毫秒级 if ts 10 ** 12: ts ts / 1000 readable_time time.strftime(%Y-%m-%d %H:%M:%S, time.localtime(ts)) sender 发送 if row[1] 1 else 接收 writer.writerow([readable_time, sender, row[2], row[3]]) print(已导出 chat_export.csv)这段代码有几个必须注意的地方。第一CreateTime的单位在不同端不一致PC 版通常是秒级安卓导出后可能是毫秒级所以代码里做了10 ** 12判断超过这个值就除以 1000。第二导出文件用了utf-8-sig编码也就是带 BOM 的 UTF-8否则你用 Excel 或 WPS 直接打开 CSV 时中文会乱码如果你只是给程序做后续处理可以用普通utf-8。第三IsSender1表示自己发的消息0表示对方发的这个逻辑在绝大多数版本里一致但也不能排除特例。跑通这段说明你已经完成了“提取 → 解密 → 解析 → 导出”的最小闭环之后所有的格式扩展都是在这条链路上做文章。4. 三种导出格式CSV、HTML、Word 各自的适用场景和实现4.1 CSV结构化数据的主力也是后续分析的基础CSV 永远是我最先做的一个导出格式因为它天然适合二次处理。导出的 CSV 可以被 Excel、WPS 直接打开也可以利用“导入csv文件”功能放进数据库或 GIS 工具里做分析。它的定位是“原始数据层”所以字段设计要尽量贴近数据库同时补上可读性。对于一个聊天记录的 CSV 导出字段建议是这样会话 ID、消息时间可读格式、原始时间戳、发送方向、消息类型、消息子类型、内容、附件文件名、关联的图片或语音路径。不要为了好看把多个字段拼成一个单元格这样会破坏后续分析的可用性。import csv import sqlite3 import time import os def export_csv(db_path, output_path): conn sqlite3.connect(db_path) cur conn.cursor() cur.execute( SELECT TalkerId, CreateTime, IsSender, Type, SubType, StrContent FROM MSG ORDER BY CreateTime ASC ) with open(output_path, w, newline, encodingutf-8-sig) as f: writer csv.writer(f) writer.writerow([会话ID, 可读时间, 原始时间戳, 方向, 类型, 子类型, 内容]) for row in cur.fetchall(): talker, create_time, is_sender, msg_type, sub_type, content row if create_time 10 ** 12: create_time_sec create_time / 1000 else: create_time_sec create_time readable time.strftime(%Y-%m-%d %H:%M:%S, time.localtime(create_time_sec)) direction 发送 if is_sender 1 else 接收 writer.writerow([talker, readable, int(create_time), direction, msg_type, sub_type, content]) conn.close() export_csv(decrypted.db, all_messages.csv)字段顺序和命名可以按自己的需求调整但有一件事不要省把原始时间戳单独留一列。因为可读时间经过了时区转换如果后续要计算消息间隔或做时间线对齐原始时间戳更可靠。同理内容字段如果包含 HTML 标签或 XML暂时不用去清洗保留原样清洗放在生成 HTML 或 Word 的时候做。CSV 导出的另一个用途是给 Excel 做数据透视比如统计一天发了多少条消息、哪个群最活跃。如果你后续要做这些分析强烈建议在 CSV 里单独加一个date列直接用strftime把日期提取出来方便表格工具按日期分组。4.2 HTML把聊天记录“还原现场”的展示层CSV 适合分析但不太适合阅读。HTML 导出能最大程度还原聊天现场左右气泡区分发送和接收、时间戳穿插在消息中间、图片直接用img标签引用相对路径。生成 HTML 时我一般用一个轻量模板不引入前端框架。核心思路是先构造一个消息列表再渲染到 HTML 字符串中。import sqlite3 import time import html import os def export_html(db_path, output_dir, media_dir): conn sqlite3.connect(db_path) cur conn.cursor() cur.execute( SELECT CreateTime, IsSender, Type, StrContent FROM MSG WHERE StrContent IS NOT NULL OR Type IN (3, 34) ORDER BY CreateTime ASC ) chat_items [] for row in cur.fetchall(): create_time, is_sender, msg_type, content row if create_time 10 ** 12: create_time create_time / 1000 readable time.strftime(%Y-%m-%d %H:%M:%S, time.localtime(create_time)) side_class sent if is_sender 1 else received content_text html.escape(str(content)) if content else chat_items.append(f div classmsg {side_class} span classtime{readable}/span div classbubble{content_text}/div /div ) html_template !DOCTYPE html html langzh-cn head meta charsetutf-8 title微信聊天记录/title style body {{ max-width: 800px; margin: 0 auto; font-family: sans-serif; }} .msg {{ margin: 8px 0; }} .sent {{ text-align: right; }} .received {{ text-align: left; }} .bubble {{ display: inline-block; padding: 8px 12px; border-radius: 8px; background: #f1f1f1; }} .sent .bubble {{ background: #95ec69; }} .time {{ font-size: 12px; color: #999; margin: 0 8px; }} /style /head body div classchat-container {items} /div /body /html html_path os.path.join(output_dir, chat_export.html) with open(html_path, w, encodingutf-8) as f: f.write(html_template.format(items\n.join(chat_items))) print(HTML 已生成:, html_path)这段代码有几个值得展开的点。第一html.escape是必须的否则聊天内容里的、、会破坏页面结构甚至产生 XSS 问题。第二不要用 Python 的字符串拼接来拼 HTML容易出错用带占位符的模板更稳。第三HTML 文件声明了langzh-cn和UTF-8编码这样用浏览器直接打开时中文和 emoji 都能正常显示。图片和语音的处理思路是数据库里存储的是附件路径或缩略图路径并不是二进制数据。导出 HTML 时应该先把FileStorage里的图片复制到 HTML 同级目录的media/文件夹里然后根据消息中的文件名替换成相对路径。这个步骤不复杂但需要做路径映射简单的做法是维护一个字典把数据库里的原始路径映射到media/图片名。HTML 还有一个好处是可以直接在浏览器里打印成 PDF或者用 WPS 打开后另存为其他格式。很多“html格式转换wps表格”的需求本质就是用户拿到导出的 HTML 后想转成可编辑的格式所以你在生成时可以尽量用标准的 table 或块级元素布局避免用了太特殊的前端库导致别人打不开。4.3 Word打印、留档和批注场景下的选择Word 导出在聊天记录这个场景里不是主力但却是“永久保存”这个目标里最常用的一环因为 Word 文档适合打印、方便加批注、还能进一步转成 PDF。项目标题里把 Word 列为三种导出格式之一所以这一步必须落地。用 Python 生成 Word 最省事的方案是python-docx。from docx import Document from docx.shared import Pt, Cm from docx.enum.text import WD_ALIGN_PARAGRAPH import sqlite3 import time def export_word(db_path, output_path): conn sqlite3.connect(db_path) cur conn.cursor() cur.execute( SELECT CreateTime, IsSender, StrContent FROM MSG WHERE StrContent IS NOT NULL AND StrContent ! ORDER BY CreateTime ASC ) doc Document() # 全局样式 style doc.styles[Normal] style.font.name 微软雅黑 style.font.size Pt(10.5) for row in cur.fetchall(): create_time, is_sender, content row if create_time 10 ** 12: create_time create_time / 1000 readable time.strftime(%Y-%m-%d %H:%M:%S, time.localtime(create_time)) sender_name 我 if is_sender 1 else 对方 paragraph doc.add_paragraph() paragraph.alignment WD_ALIGN_PARAGRAPH.LEFT run_time paragraph.add_run(f[{readable}] ) run_time.font.size Pt(8) run_time.font.color.rgb __import__(docx).shared.RGBColor(0x99, 0x99, 0x99) run_sender paragraph.add_run(f{sender_name}: ) run_sender.bold True paragraph.add_run(content) doc.save(output_path) print(Word 已生成:, output_path) export_word(decrypted.db, chat_export.docx)python-docx的底层模型是“段落 run”。Add paragraph 创建段落然后往段落里加 run每个 run 可以有不同的字体、颜色、加粗等样式。这样做的好处是代码结构清晰坏处是如果消息特别多比如上万条运行速度会明显变慢所以导出大范围聊天记录时我建议按月份分批生成多个 Word 文档。Word 导出里的一个常见问题是 emoji 字体显示。Windows 自带字体对部分 emoji 支持不佳Word 里可能会显示成方框。我的处理方式是在导出时检测内容里包含 emoji 的字符把它替换成“表情”或“[表情]”这样的占位文本。这样虽然丢失了原始的视觉细节但保证了文档在打印和存档时不出现乱码方框。如果你确实需要保留 emoji可以在 Word 里设置字体为“Segoe UI Emoji”但这种情况更多是演示需求不是留档需求。Word 导出的定位是“可编辑的存档”如果这个文档只是为了打印其实生成 PDF 更稳妥因为 Word 在不同的机器上打开排版会浮动。你可以用 Word 自带的另存为 PDF 功能也可以直接在导出后调用 LibreOffice 无头模式转换。我的习惯是 Word 作为中间格式保留同时用它导出一份 PDF 作为最终存档。5. 微信聊天记录导出避坑五个高频问题与处理清单5.1 现象目标数据库文件被占用复制出来是 0 字节或已损坏微信运行中所有数据库文件都有句柄占用如果你没退出微信就直接复制Windows 要么复制失败要么复制出一个大小异常的文件。这个文件后续打开时会出现file is not a database或者解密后表里能查到 sqlite_master 但数据全空。原因很简单微信把数据页缓存到了内存中还没完全落盘或者是文件正被独占打开复制过程拿到的只是部分内容。解决办法也不复杂先完全退出微信等进程彻底结束再复制。可以在任务管理器里确认 WeChat.exe 和 WeChatAppEx.exe 都不存在后再操作。复制的时候不要只复制 MSG.db把整个 Msg 目录一并拷走因为附件分散在各个子目录里。5.2 现象PRAGMA key 之后查询 sqlite_master 报错这是解密流程里最容易让人崩溃的错误。报错内容通常是file is not a database或SqliteError: file is not a database但你明明觉得密钥没问题。原因分三种密钥字符串格式错误、SQLCipher 版本与加密时使用的版本不兼容、密钥本身没取对。排查时先确认 PRAGMA key 的写法十六进制密钥要用x...普通字符串要用...然后确认 sqlcipher 的版本旧版本导出的数据库需要用PRAGMA cipher_compatibility 3或类似参数做兼容最后检查内存扫描到的密钥是不是少了字节很多工具输出密钥时会带上0x前缀或空字符需要做一次清洗。5.3 现象字段解析报错msgType 和 content 跟网上教程对不上微信不同版本的 message 表字段差异明显旧版本有Content字段新版本叫StrContent有些版本里TalkerId是int有些版本是varchar。如果你按旧教程写SELECT Content FROM MSG在新版本上会直接报错。最稳妥的办法就是先看 schema再写代码。SQLite 里用PRAGMA table_info(MSG)或者SELECT sql FROM sqlite_master WHERE nameMSG都能看到字段定义。看到真实字段之后再做一个GROUP BY Type统计确认当前数据库里有哪些消息类型然后再按需解析。这一条我反复强调因为聊天记录提取 90% 的坑都出在版本差异上而不是加密上。5.4 现象图片、语音导不出来导出的文件里全是裂图和空的语音条数据库里的消息记录和实际的图片、语音文件是分离存储的。MSG 表里会记录一个文件名或路径比如FileStorage/Image/2024-05/xxx.dat但如果你导出时没有把 FileStorage 整个目录复制到目标环境渲染 HTML 时自然找不到图片。解决方法是提取时把Msg/FileStorage完整复制并且保持目录结构不变。生成 HTML 时把 FileStorage 里的文件映射到 HTML 同级目录下的 media 文件夹然后根据数据库里记录的路径二次拼接。另外需要注意微信生成的图片文件有时没有扩展名需要根据文件头的魔数判断格式常见的是 JPG、PNG、GIF比较少见的是 WEBP。你也可以用 Python 的 imghdr 库或 file 命令来判断只在写文件名时补上扩展名不要改动文件内容。5.5 现象导出文件里的中文和 emoji 乱码CSV 用 Excel 打开中文乱码这个问题几乎每个新手都会遇到原因是文件编码不是 Excel 默认识别的 ANSI 或带 BOM 的 UTF-8。解决办法是在写 CSV 时指定encodingutf-8-sig这样文件头会自动带上 BOMExcel 和 WPS 都能正确识别。HTML 里的乱码则是另一个原因页面声明的字符集与文件实际编码不一致。生成 HTML 时确保meta charsetutf-8出现在 head 区并且写文件时也用encodingutf-8两边一致就不会乱。Word 里的乱码比较少见但 emoji 显示成方框很常见这个没法彻底解决只能做 emoji 到文本的替换或者接受这种显示效果。另外如果聊天内容本身包含换行符导入 CSV 时需要把换行替换成空格或\n的字面量否则会破坏 CSV 的行结构这也是踩坑高发区。6. 进阶把导出做成增量备份并用消息条数校验完整性做到这一步你已经能手动导出一份完整的聊天记录文档了。但真正要长期保存还要考虑两个问题微信每天都在产生新消息导出不能每次都全量跑另外文档导出后如何确认没有漏消息。这两个需求可以一起解决。增量备份的核心是定位新消息。MSG 表里有两个字段适合做游标CreateTime和MsgSvrID。MsgSvrID是服务端消息 ID理论上全局唯一作为增量游标最可靠。做法是先记录上一次导出的最大MsgSvrID下次只查询大于它的记录然后导出的同时把新的最大值记录到一个小文件里。校验导出完整性则用消息条数对比。数据库里直接SELECT COUNT(*) FROM MSG然后数一下你生成的 CSV 文件有多少行如果一致就说明没有漏导如果不一致就说明有过滤逻辑把不该滤掉的消息滤掉了。import sqlite3 import csv db_path decrypted.db last_sync_file last_msg_id.txt # 读取上次同步的位置 try: with open(last_sync_file, r) as f: last_msg_id int(f.read().strip()) except FileNotFoundError: last_msg_id 0 conn sqlite3.connect(db_path) cur conn.cursor() cur.execute(SELECT COUNT(*) FROM MSG WHERE MsgSvrID ?, (last_msg_id,)) new_count cur.fetchone()[0] # 增量查询 cur.execute( SELECT MsgSvrID, CreateTime, IsSender, StrContent FROM MSG WHERE MsgSvrID ? ORDER BY MsgSvrID ASC , (last_msg_id,)) with open(incremental_export.csv, a, newline, encodingutf-8-sig) as f: writer csv.writer(f) for row in cur.fetchall(): writer.writerow(row) last_msg_id row[0] # 更新游标 with open(last_sync_file, w) as f: f.write(str(last_msg_id)) print(f本次导出 {new_count} 条新消息)增量导出的思路不难但有三个细节必须处理好。第一用MsgSvrID做游标时不能简单地等于“上次最大值”因为可能出现消息乱序落库的情况所以游标更新要放在查询结果循环结束之后而不是在循环里实时更新。第二如果MsgSvrID在某些异常消息上是 0 或空值这条消息会永远满足 last_msg_id的条件导致每次都重复导出需要额外加一条MsgSvrID 0的过滤。第三增量模式依赖数据库一直被保留所以即便导出了 HTML 和 Word我也从不删原始加密库我自己的习惯是把原始数据库按日期压缩保存一份作为后悔药确认三个月内没有新的提取需求后再清理。最后一件事是验证。导出完成后写一个简单的计数脚本把数据库中的总消息数和 CSV 行数做对比再抽查几个已知的关键聊天时间点确认记录没有缺漏。这个步骤看起来很笨但在归档场景里是唯一可信的验收手段。走到这里你手里应该同时有一份原始加密库备份、一份解密后的 SQLite 库、一份 CSV 数据文件以及按需生成的 HTML 和 Word 文档。我从第一次做微信聊天记录提取到现在最大的感受是工具本身不难难的是对数据完整性和格式兼容性的敬畏希望这份方案能帮你在踩坑之前就把路走通。本文还有配套的精品资源点击获取

相关新闻

Spring Boot+Vue反诈视频宣传系统:从源码到部署全解析

Spring Boot+Vue反诈视频宣传系统:从源码到部署全解析

简介:面向毕业设计与课程设计场景,这份JavaSpringbootVue开发的反诈视频宣传系统源码包,围绕电信网络诈骗防范知识普及,提供完整的前后端分离实现方案,包含视频上传、播放、评论互动及知识展示等核心功能。压缩包共372…

2026/10/11 16:12:30 阅读更多 →
基于SSM+Vue的楼宇智能化管理系统毕业设计全攻略

基于SSM+Vue的楼宇智能化管理系统毕业设计全攻略

2026年的毕设选题又到集中开工期了。每年这个时候,我都能看到不少人在“SSM Vue”这个黄金组合上打转。你看到“楼宇智能节的系统”这个标题,大概率是“楼宇智能化系统”的笔误,或者是指楼宇的智能节点/智能终端。不管是哪种,核心…

2026/10/11 16:11:29 阅读更多 →
pstack实战:快速定位线上进程卡死与CPU飙升的线程堆栈技巧

pstack实战:快速定位线上进程卡死与CPU飙升的线程堆栈技巧

排查线上进程卡死、CPU 异常飙升、服务无响应这类问题的时候,pstack 是我第一批摸上服务器就要敲的命令。它只需要你传入一个进程号,几秒钟就能把所有线程当前执行到的函数调用堆栈完整抛出来,相当于把进程在“出事瞬间”的地图拍下来&#x…

2026/10/11 16:11:29 阅读更多 →

最新新闻

Agent卡壳了怎么办?Agentic Design Patterns异常处理与恢复模式实战

Agent卡壳了怎么办?Agentic Design Patterns异常处理与恢复模式实战

文档教程AI Agent人工智能 【免费下载链接】Agentic-Design-Patterns Agentic Design Patterns 项目地址: https://gitcode.com/gh_mirrors/agen/Agentic-Design-Patterns 点击查看 免费下载 AI Agent 干到一半突然卡壳——工具调用失败、API 返回 500、输出前言不…

2026/10/11 20:11:00 阅读更多 →
Space-Bunny-Alpha 视觉效果与生成能力展示

Space-Bunny-Alpha 视觉效果与生成能力展示

在数字内容创作的浪潮中,越来越多的创作者开始关注如何高效生成高质量的视觉作品。无论是游戏开发、影视预演,还是个人艺术创作,对角色细节、光影表现以及动态流畅度的要求都在不断攀升。很多时候,我们面对的不是“能不能做”&…

2026/10/11 20:11:00 阅读更多 →
基于YOLO的低视力学生AI助手:从训练到部署全流程

基于YOLO的低视力学生AI助手:从训练到部署全流程

简介:基于YOLO的低视力学生助手是一套完整的AI视觉辅助应用源码,主要面向低视力人群、无障碍产品开发者及机器学习初学者,通过实时图像识别将教材、标识、笔记、人脸等视觉信息转换为语音描述和物体分类结果,例如识别教材文字并朗…

2026/10/11 20:11:00 阅读更多 →
电磁场与微波测量:极化实验与马吕斯定律验证及布儒斯特角测量

电磁场与微波测量:极化实验与马吕斯定律验证及布儒斯特角测量

简介:这份PDF是电磁场与微波测量课程的极化实验报告,面向正在修读电磁场实验、需要撰写实验报告的高校学生,尤其适合想跳出百度文库旧报告误导、从不同角度理解实验原理的同学。报告围绕线极化波、马吕斯定律与喇叭天线极化展开,完…

2026/10/11 20:11:00 阅读更多 →
编译器与解释器的本质区别:快慢有道,性能优化全解析

编译器与解释器的本质区别:快慢有道,性能优化全解析

先把“翻译”这个过程掰开看:编译器与解释器的本质分工从“同传”与“笔译”的对照说起我习惯把编译器和解释器的关系比作两种翻译:一种是笔译,一种是同声传译。笔译手里有一整本书,可以花几天时间逐章推敲,把语法、术…

2026/10/11 20:11:00 阅读更多 →
等离子体反应工程:从DBD放电参数到工业放大实战

等离子体反应工程:从DBD放电参数到工业放大实战

很多人第一次听到“等离子体反应工程”这个词,第一反应多半是“这是物理学家和宇航员才碰的东西吧”。我当初也是这么想的,直到在化工厂里被一道难题逼到墙角:一套含氟有机废气的排放浓度严重超标,常规催化燃烧需要把气体加热到30…

2026/10/11 20:10:00 阅读更多 →

日新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/11 0:00:27 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/11 0:00:27 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/11 0:00:27 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/11 0:00:27 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/11 0:00:27 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/11 0:00:27 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/11 10:45:37 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/11 14:36:53 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/11 14:36:54 阅读更多 →