微信聊天记录导出为电脑文件实操教程(附代码)

news/2024/9/25 17:14:56/

写在前面

最近,微信中加的群有点多,信息根本看不过来。如果不看,怕遗漏了有价值的信息;如果一条条向上翻阅,实在是太麻烦。

有没有办法一键导出所有聊天记录

一来翻阅更方便一点,二来还可以让 AI 帮我总结一下,避免遗漏有价值的内容。

网上翻阅了很多资料,完全有效的不多,而且很多工具都需要收费。

最终找到一个开源项目(传送门),本文将参考这个项目,分享给大家:导出微信聊天记录的几个关键步骤

    1. 手机微信数据库导入电脑端
    1. 破解数据库密码
    1. 导出数据库
    1. 提取联系人信息和聊天记录

希望给有类似需求的小伙伴带来帮助。

话不多说,直接上实操!

关键步骤拆解

1. 手机微信数据库导入电脑端

对于很少用电脑端微信的小伙伴,首先需要先把手机微信的数据迁移到电脑端:

在手机端微信,依次点击**「我-设置-聊天-聊天记录迁移与备份-迁移」**,选择迁移到电脑微信

在这里插入图片描述

继续选择部分 或者 全部聊天记录,如果聊天数据较多,可能需要稍等一段时间~

2. 破解数据库密码

电脑端自己的微信数据存放在哪?

在电脑端微信,左下角依次点击「设置-文件管理」,找到自己的微信数据存放位置,然后打开对应的文件夹。
在这里插入图片描述
文件最后一级目录就是自己的微信号,如果登录过多个微信账号的需要注意切换,比如下面这张就是我的微信数据存放位置,其中的 Msg 文件夹中存放的就是微信中所有的联系人和聊天信息。
在这里插入图片描述
打开 Msg 文件夹,会发现这里有很多 .db 结尾的,就是微信数据存放的数据库文件。如果你用任何数据库软件打开,这时是打不开的。

因为还需要数据库密码。

怎么破解数据库密码?

在这里插入图片描述

参考这个项目,我把其中破解数据库密码部分的代码提取出来了,方便大家直接使用:

def get_key(db_path, addr_len):def read_key_bytes(h_process, address, address_len=8):array = ctypes.create_string_buffer(address_len)if ReadProcessMemory(h_process, void_p(address), array, address_len, 0) == 0: return "None"address = int.from_bytes(array, byteorder='little')  # 逆序转换为int地址(key地址)key = ctypes.create_string_buffer(32)if ReadProcessMemory(h_process, void_p(address), key, 32, 0) == 0: return "None"key_bytes = bytes(key)return key_bytesdef verify_key(key, wx_db_path):if not wx_db_path or wx_db_path.lower() == "none":return TrueKEY_SIZE = 32DEFAULT_PAGESIZE = 4096DEFAULT_ITER = 64000with open(wx_db_path, "rb") as file:blist = file.read(5000)salt = blist[:16]byteKey = hashlib.pbkdf2_hmac("sha1", key, salt, DEFAULT_ITER, KEY_SIZE)first = blist[16:DEFAULT_PAGESIZE]mac_salt = bytes([(salt[i] ^ 58) for i in range(16)])mac_key = hashlib.pbkdf2_hmac("sha1", byteKey, mac_salt, 2, KEY_SIZE)hash_mac = hmac.new(mac_key, first[:-32], hashlib.sha1)hash_mac.update(b'\x01\x00\x00\x00')if hash_mac.digest() != first[-32:-12]:return Falsereturn Truephone_type1 = "iphone\x00"phone_type2 = "android\x00"phone_type3 = "ipad\x00"pm = pymem.Pymem("WeChat.exe")module_name = "WeChatWin.dll"MicroMsg_path = os.path.join(db_path, "MSG", "MicroMsg.db")type1_addrs = pm.pattern_scan_module(phone_type1.encode(), module_name, return_multiple=True)type2_addrs = pm.pattern_scan_module(phone_type2.encode(), module_name, return_multiple=True)type3_addrs = pm.pattern_scan_module(phone_type3.encode(), module_name, return_multiple=True)type_addrs = type1_addrs if len(type1_addrs) >= 2 else type2_addrs if len(type2_addrs) >= 2 else type3_addrs if len(type3_addrs) >= 2 else "None"# print(type_addrs)if type_addrs == "None":return "None"for i in type_addrs[::-1]:for j in range(i, i - 2000, -addr_len):key_bytes = read_key_bytes(pm.process_handle, j, addr_len)if key_bytes == "None":continueif db_path != "None" and verify_key(key_bytes, MicroMsg_path):return key_bytes.hex()return "None"

3. 导出数据库

上一步中,得到数据库的密码后,就可以将源文件中的数据库导出来,核心代码如下:

# 通过密钥解密数据库
def decrypt(key: str, db_path, out_path):"""通过密钥解密数据库:param key: 密钥 64位16进制字符串:param db_path:  待解密的数据库路径(必须是文件):param out_path:  解密后的数据库输出路径(必须是文件):return:"""if not os.path.exists(db_path) or not os.path.isfile(db_path):return False, f"[-] db_path:'{db_path}' File not found!"if not os.path.exists(os.path.dirname(out_path)):return False, f"[-] out_path:'{out_path}' File not found!"if len(key) != 64:return False, f"[-] key:'{key}' Len Error!"password = bytes.fromhex(key.strip())with open(db_path, "rb") as file:blist = file.read()salt = blist[:16]byteKey = hashlib.pbkdf2_hmac("sha1", password, salt, DEFAULT_ITER, KEY_SIZE)first = blist[16:DEFAULT_PAGESIZE]if len(salt) != 16:return False, f"[-] db_path:'{db_path}' File Error!"mac_salt = bytes([(salt[i] ^ 58) for i in range(16)])mac_key = hashlib.pbkdf2_hmac("sha1", byteKey, mac_salt, 2, KEY_SIZE)hash_mac = hmac.new(mac_key, first[:-32], hashlib.sha1)hash_mac.update(b'\x01\x00\x00\x00')if hash_mac.digest() != first[-32:-12]:return False, f"[-] Key Error! (key:'{key}'; db_path:'{db_path}'; out_path:'{out_path}' )"newblist = [blist[i:i + DEFAULT_PAGESIZE] for i in range(DEFAULT_PAGESIZE, len(blist), DEFAULT_PAGESIZE)]with open(out_path, "wb") as deFile:deFile.write(SQLITE_FILE_HEADER.encode())t = AES.new(byteKey, AES.MODE_CBC, first[-48:-32])decrypted = t.decrypt(first[:-48])deFile.write(decrypted)deFile.write(first[-48:])for i in newblist:t = AES.new(byteKey, AES.MODE_CBC, i[-48:-32])decrypted = t.decrypt(i[:-48])deFile.write(decrypted)deFile.write(i[-48:])return True, [db_path, out_path, key]def parse_db(key, db_path, output_dir):close_db()os.makedirs(output_dir, exist_ok=True)tasks = []for root, dirs, files in os.walk(db_path):for file in files:if '.db' == file[-3:]:if 'xInfo.db' == file:continueinpath = os.path.join(root, file)output_path = os.path.join(output_dir, file)tasks.append([key, inpath, output_path])else:try:name, suffix = file.split('.')if suffix.startswith('db_SQLITE'):inpath = os.path.join(root, file)# print(inpath)output_path = os.path.join(output_dir, name + '.db')tasks.append([key, inpath, output_path])except:continuefor i, task in enumerate(tasks):flag, msg = decrypt(*task)print(f"[{i+1}/{len(tasks)}] {flag} {msg}")print('开始数据库合并...')# 目标数据库文件target_database = os.path.join(output_dir, 'MSG.db')# 源数据库文件列表source_databases = [os.path.join(output_dir, f"MSG{i}.db") for i in range(1, 50)]if os.path.exists(target_database):os.remove(target_database)shutil.copy2(os.path.join(output_dir, 'MSG0.db'), target_database)  # 使用一个数据库文件作为模板merge_databases(source_databases, target_database)

此时,会在当前目录下生成全新的数据库文件,用任何一种数据库软件都可以打开查看详细信息。比如我这里采用的是 VS Code 中的 SQLite Viewer 插件,以下图为例,在 Misc.db 中,存放的是所有联系人的信息,包括:用户名、头像和创建时间等。
在这里插入图片描述
上图中,CreateTime 就是加为好友的时间,其含义是从1970年1月1日00:00:00 UTC到当前时间的秒数,可以通过如下代码转换成字符串类型的时间,方便查看。

from datetime import datetime
def covert_time2num(datetime_str, datetime_format="%Y-%m-%d %H:%M:%S"):dt_obj = datetime.strptime(datetime_str, datetime_format)timestamp = int(dt_obj.timestamp())return timestampdef covert_time2str(timestamp, datetime_format="%Y-%m-%d %H:%M:%S"):dt_obj = datetime.fromtimestamp(timestamp)return dt_obj.strftime(datetime_format)

4. 提取联系人信息和聊天记录

有了数据库之后,就可以着手提取其中的信息了。

想看看自己都加了哪些好友?他们都来自哪里?

看这里:所有联系人的信息存放在 Misc.db 中。下面这段代码展示了数据库中都存了哪些字段:

# 获取所有联系人(包括群聊)信息
contact_info_lists = micro_msg_db.get_contact() 
contact_infos = []
for contact_info_list in contact_info_lists:detail = decodeExtraBuf(contact_info_list[9])contact_info = {'UserName': contact_info_list[0], # 微信id'Alias': contact_info_list[1], # 微信'Type': contact_info_list[2], # 看不出来啥类型'Remark': contact_info_list[3], # 备注名'NickName': contact_info_list[4], # 昵称'smallHeadImgUrl': contact_info_list[7], # 头像url"detail": detail, # 包括地区,个性签名,电话,性别"label_name": contact_info_list[10] # 标签名,用于给好友分组的标签,大部分人都没用过这个功能,所以通常没有}contact_infos.append(contact_info)

其中, ‘UserName’ 这个字段中如果包含 ‘@chatroom’ 就代表是群聊。下面我们看一条 联系人信息 的示例:

# 微信
{'UserName': 'xxx@chatroom', 'Alias': '', 'Type': 2, 'Remark': '', 'NickName': 'xxx车主群', 'smallHeadImgUrl': 'https://wx.qlogo.cn/mmcrhead/xxx/0', 'label_name': 'None'}
# 微信好友
{'UserName': 'wxid_xxx22', 'Alias': 'Quiet_xx', 'Type': 8388611, 'Remark': '备注名', 'NickName': 'xx', 'smallHeadImgUrl': 'https://wx.qlogo.cn/mmhead/xxx/132', 'detail': {'region': ('CN', 'Beijing', 'Daxing'), 'signature': '德不孤 必有邻', 'telephone': '', 'gender': 2}, 'label_name': 'None'}

想一键获取和某个好友的聊天记录?

看这里:MicroMsg.db 中存放了所有用户的聊天记录,有很多张表。可以通过 ‘UserName’ 这个字段从数据库中检索,也可以指定信息类型和时间段,示例代码如下:

def get_chat_info(self, nickname='', remark='', alias='', time_range=None, output_type='txt', type_=None, out_path='output'):"""time_range: (start_time, end_time) ('2021-08-01 12:00:00', '2021-08-02 12:00:00')"""ret_info = self.get_contact_info(nickname, remark, alias)self.is_chatroom = ret_info['UserName'].__contains__('@chatroom')if type_ is None:messages = msg_db.get_messages(ret_info['UserName'], time_range=time_range)else:messages = msg_db.get_messages_by_type(ret_info['UserName'], type_=type_, time_range=time_range)

每条 message 的类型是不一样的,微信中所有的信息类型列举如下:

types = {'文本': 1,'图片': 3,'语音': 34,'视频': 43,'表情包': 47,'音乐与音频': 4903,'文件': 4906,'分享卡片': 4905,'转账': 492000,'音视频通话': 50,'拍一拍等系统消息': 10000,
}

善用自己的数据

看到这里的你,一定会有一个疑问:我拿到这些数据都有什么用?

这里猴哥列举自己目前最常用的需求:

1.总结提炼群聊信息

一开始做这件事情,最主要的目的就是这个,因为群聊信息实在太多了,根本看不完。

而把上面的工作流搭建好,把所有信息提取出来就是一行脚本的事。

下面拿猴哥最近加入的一个群来举例。

把该群的所有聊天信息提取出来,保存为一个 txt 文件,左下角显示总共有1万多条聊天记录,这得看到猴年马月去?
在这里插入图片描述

接下来,我们把这份 txt 文件,发给 Kimi,让它帮忙总结一下,下面左图就是 Kimi 给到的分析。

为了验证它没有在胡说八道,我们还可以把所有聊天记录,做一个词云(右图)。

怎么样?Kimi 总结的还是相当到位的,根据它的总结内容,我就可以决定是否需要继续去看群聊信息。

当然,我们还可以优化一下提示词,让它根据时间段来进行总结,便于我们定位到关键信息对应的时间段。
在这里插入图片描述

写在最后

把聊天记录和当前的 AI 大语言模型,结合在一起,一定还可以衍生出很多需求和有意思的应用场景,欢迎评论区给出你的想法和创意~

再次感谢 WeChatMsg 项目团队的开源精神 !

由于篇幅限制,本文用到的源码没有全部展示,需要源码的小伙伴,也可以在公众号【猴哥的AI知识库】后台私信我~

如果本文对你有帮助,欢迎点赞收藏备用!


http://www.ppmy.cn/news/1470343.html

相关文章

国际期货行情相关术语

1)合约:期货行情表提供了期货交易的相关信息 ,行情表中每一个期货合约都有合约代码(由期货合约交易代码和合约到期月份组成)来标识。 (2)开盘价:当日某一期货合约交易开始前五分钟集…

QT——事件

一、什么是事件 在QT中,事件(Event)是指由特定对象发生的动作或状态变化,通常用于响应用户的操作。事件可以是鼠标点击、键盘输入、窗口移动等用户操作,也可以是系统发出的信号,比如定时器超时、网络数据到达等。在QT中,可以通过连接信号与槽(Signals and Slots)的方…

服务器被墙是什么原因,怎么解决服务器被墙

服务器被墙通常是由于以下几个原因: 网络监管:某些国家或地区会对网络进行严格的监管,包括对特定网站、应用程序或服务进行屏蔽或封锁。这种情况下,服务器可能会被封锁,导致无法访问。 安全问题:服务器被发…

CDAM|数据资产管理:解锁企业价值的金钥匙

随着信息技术的飞速发展,数据已经成为企业最重要的资产之一。有效地管理数据资产,不仅有助于提升企业的运营效率,更能为企业的战略决策提供有力支持。本文将深入探讨数据资产管理的重要性、挑战以及实施策略,以期为企业打造一套高…

Linux - 进程

一、什么是进程 首先,Linux是一个多用户多进程的操作系统,系统上可以同时运行多个进程。 进程的产生:①是在执行程序或者命令时产生的;②定时任务进程 进程的类型:前台进程/后台进程 前台进程:一个终端…

AI 音乐大模型:创新的曙光还是创意产业的阴影?

💝💝💝欢迎来到我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

分享一下,如何搭建个人网站的步骤

在这段充满探索与创造的奇妙旅途中,我就像一位耐心的建筑师,在数字世界的荒原上精心雕琢,两周的时光缓缓流淌。每天,我与代码共舞,手执HTML、CSS与JavaScript这三大构建魔杖,一砖一瓦地筑起了梦想中的网络城…

docker重要操作与直连方法

文章目录 前言一、nvidia-docker安装方法1、nvidia-docker安装2、重启动ssh 二、构建镜像1、构建镜像docker拉取构建本地镜像加载构建 2、容器转镜像3、镜像打包4、删除镜像 三、构建容器1、容器构建2、启动镜像3、删除容器 四、docker直连(ssh -p)1、docker更改密码2、物理机操…