Python处理聊天记录

前言

终于快到999纪念日了,本来想给宝买台手机,可是宝说米有纪念意义。最近刷到一对好看的💍,但是它店铺关门大吉了,只能作罢。

戒指

有意义,有意义,什么东西才有意义呢,又是我能亲手实现的东西,博主灵机一动,聊天记录是承载两个人情感的重要桥梁,因此我只需要对聊天记录进行处理,生成专属于我们两个人的词云图不就可以了么!!(博主的扣扣聊天记录也一直保存到现在哦 hiahia)

准备工作

查了很多技术路线和相关资料,发现微信是用SQLite数据库保存聊天记录的,同时使用SQLCipher封装了自己的WCDB 数据库框架,因此只有获取数据库的密钥才能获取到聊天记录,然后才能进行数据处理。

通过阅读百灵鸟安全团队发布的文章「打造 macOS 下最强的微信取证工具」,了解到了微信密钥提取的仓库,下载最新的release,然后查看一下我们微信的版本号。

微信版本号

然后访问dumpkey首页找到我们的微信所对应的操作命令。

1
sudo ./dumper test --pid $(pgrep WeChat |head -1) --path "WeChat+81059936@8@8@16@32@8@8@64@8@0@0" -n 32

开始破解密钥

通过输入刚刚查询的代码,我们就可以解密密钥了。

啊哦,出现了错误

error: OpenProcess, (os/kern) failure. code: 5

根据提示输入

1
sudo codesign --sign - --force --deep /Applications/WeChat.app

怎么还是不行,试试看关闭SIP。

关机,然后重新启动你的Mac电脑,在开机时一直按住Command+R迸入Recovery模式。M1的电脑可以试试看长按电源键,然后点击选项,输入密码后在实用工具里面点击终端。

恢复模式

在终端上输入命令 csrutil disable然后回车,然后输入reboot重启即可。

然后再操作一遍我们就可以得到密钥

密钥

因为是16进制,因此需要在前面加上0x即

0x1B89B26FF2FD482E987ADD77DC4BC263D6FFA2781436466589AFCA80AAA8E6A3

铛铛铛~铛!!我们得到了SQLite的密钥

提取数据

然后随便在微信里面找一张图,在finder中显示,

聊天记录

在打开的 Finder 中,向外回退两层目录,所有的 msg_数字.db 里面存储的都是聊天记录。

finder目录

为了能够打开数据库文件进行导出操作,需要下载 DB Browser for SQLite 进行操作。同时,为了保证数据的一致性,我们最好关闭微信进行操作。

1
2
3
4
# 在上图文件夹中打开终端
cp msg*.db ~/Desktop/Proj/wechat
# 建立db0-db9
mkdir db{0..9}

建立文件夹

然后使用 DB Browser for SQLite 任意打开一个数据库文件,此处有三个注意点:

  1. 密码类型:选择「原始密钥」

  2. 密码,第一种使用 ptrsx-dumper 获取到的密码前面没有 0x 字符,需要自己手动加上,第二种 Python 解析出来的密码可以直接使用,例如此处我填写的密码就是:0x1B89B26FF2FD482E987ADD77DC4BC263D6FFA2781436466589AFCA80AAA8E6A3

  3. 加密设置:选择「SQLCipher 3 默认」

数据库连接

然后我们就可以进入数据库了!!

数据库展示

切换到「浏览数据」选项,通过选择不同表就可以查看和不同好友之间的聊天记录,如下所示就是一份聊天记录。

聊天记录

下一步我们就可以将数据导出了,每个db对应每个文件夹。

导出记录

导出后我们就可以进行下一步操作啦!!

数据处理

有了我们的json数据就可以制作词云了。

需要的库如下

1
pip3 install wordcloud matplotlib pandas jieba mplfonts imageio openpyxl numpy

有了所有的聊天记录,我们可以正式制作词云了,先了解一下导出Json数据的结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
{
"CompressContent": null,
"ConBlob": "chN3eGlkX3k2OHh0MDk4NHFjczIxeglsaXV6b25nZGGAAQCYAQCgAQC4AQDIAQDQAQDwAQD4AQCIAgI=",
"IntRes1": 0,
"IntRes2": 0,
"StrRes1": null,
"StrRes2": null,
"mesDes": 0,
"mesLocalID": 8,
"mesSvrID": 9004976427286855000,
"messageType": 1,
"msgContent": "hello world",
"msgCreateTime": 1611106801,
"msgImgStatus": 1,
"msgSeq": 0,
"msgSource": "",
"msgStatus": 2,
"msgVoiceText": null
}

其中比较值得关注的字段如下:

  • msgContent:消息内容
  • msgCreateTime:消息创建时间,使用 Unix Time 表示
  • messageType:消息类型
    • 文本:1
    • 图片:3
    • 语音:34
    • 视频:43
    • 表情包:47
    • 位置:48
    • 分享消息:49
    • 系统消息:10000
  • msgStatus:消息状态
    • 收到消息:4
    • 发出消息:2、3
  • msgVoiceText:微信的语音转文字识别结果

前置知识有了,就可以开始动手写代码了。制作词云图片,只需要提取出所有 messageType 等于 1 的记录,并把这些聊天记录进行分词之后,就可以制作词云了。

那么新的问题来了,如何快速找到那个好友/群聊对应的 JSON 文件?使用 grep 命令即可。找到想要生成词云的群聊,任意选择一句聊天记录,只要和别的群有一定区分度即可。例如:

聊天记录

使用grep命令查询

grep

我们可以看到文件的路径

文件路径

生成词云图

我们先写个代码处理一下词云图。

  1. 首先我们需要能够读取聊天记录里的内容,可以使用json来读取。

  2. 其次,我们需要把聊天记录里面的句子切割成各个单词,还需要剔除没有意义的字符,例如:啊、吧、呢和吗等等等。同时还需要屏蔽掉微信的表情包,微信的表情包有将近 50 个默认表情包,自己一个个去统计岂不是太麻烦了?简单粗暴一点的办法就是直接上,把微信默认的表情都按一遍过去。发给自己,然后拷贝出来。使用 sed 命令处理一下,就可以使用了。

    pbpaste|sed "s|\]\[|','|g ; s|\[|\['| ; s|\]|'\]|"

    也可以用正则表达式来做。

  3. 为了美观,我们还可以设置背景图片,通过mask参数设置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
import os
import json
# 引入结巴分词,对群聊记录进行分词
import jieba
# 引入词云
import wordcloud
# 引入停止词模块
from wordcloud import WordCloud, STOPWORDS
# 引入 imageio 读取爱心图片文件
from imageio.v2 import imread

data = open('/Users/disda/Desktop/Proj/wechat/db5/Chat_f611156ea3b00eb663747961911867d4.json').read()
data = json.loads(data)

content = ""
for item in data:
if (item['messageType'] == 1):
message = item['msgContent'].split('\n')
content += message[0] + "\n"

ls = jieba.lcut(content)
# 如果分词出来只有一个字符剔除这个字符
ls = [i for i in ls if len(i) > 1]
text = ' '.join(ls)

stopwords = STOPWORDS
# 添加新的停止词
stopwords.update(
['微笑', '撇嘴', '色', '发呆', '得意', '流泪', '害羞', '闭嘴', '睡', '大哭', '尴尬', '发怒', '调皮', '呲牙', '惊讶',
'难过', '囧', '抓狂', '吐', '偷笑', '愉快', '白眼', '傲慢', '困', '惊恐', '憨笑', '悠闲', '咒骂', '疑问', '嘘',
'晕', '衰', '骷髅', '敲打', '再见', '擦汗', '抠鼻', '鼓掌', '坏笑', '右哼哼', '鄙视', '委屈', '快哭了', '阴险',
'亲亲', '可怜', '笑脸', '生病', '脸红', '破涕为笑', '恐惧', '失望', '无语', '嘿哈', '捂脸', '奸笑', '机智', '皱眉',
'耶', '吃瓜', '加油', '汗', '天啊', 'Emm', '社会社会', '旺柴', '好的', '打脸', '哇', '翻白眼', '666', '让我看看',
'叹气', '苦涩', '裂开', '嘴唇', '爱心', '心碎', '拥抱', '强', '弱', '握手', '胜利', '抱拳', '勾引', '拳头', 'OK',
'合十', '啤酒', '咖啡', '蛋糕', '玫瑰', '凋谢', '菜刀', '炸弹', '便便', '月亮', '太阳', '庆祝', '礼物', '红包',
'發', '福', '烟花', '爆竹', '猪头', '跳跳', '发抖', '转圈'])

# 读取爱心图片文件并赋值给 background 变量
background = imread('heart.png')

wc = wordcloud.WordCloud(
font_path="SIMSUN.ttf",
width=1000,
height=1000,
# 替换 skyblue 为 hexcode 可以设置任意背景颜色
background_color="skyblue",
max_words=200,
# 配置停止词参数
stopwords=stopwords,
# 配置爱心图片遮罩
mask=background
)

wc.generate(text)
wc.to_file("resultHeart.png")

resultHeart

其中background_color,可以改成任意rgb,例如background_color="#f0c9cf"

resultHeart

表情包统计

本来想https://worditout.com/来生成Emoji统计图的,但是微信的Emoji都是自制的,所以没办法统计出来,Emoji好像matplotlib也显示不出来

matplotlib

被迫用Excel画了一下

excel图

时间分段聊天频率分析

我们可以通过统计每天各个时间段的聊天记录数量对聊天记录进行分析,

通过手动分箱操作,每隔半小时分一个箱。然后使用matplotlib中的📊来显示。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#!/usr/bin/env python3
import os
import json
import datetime
import matplotlib.pyplot as plt


data = open('/Users/disda/Desktop/Proj/wechat/db5/Chat_f611156ea3b00eb663747961911867d4.json').read()
data = json.loads(data)

dict = {}
for item in data:
# 获取消息发送时间
unixtime = (item['msgCreateTime'])
if isinstance(unixtime, int):
value = datetime.datetime.fromtimestamp(unixtime).strftime('%H:%M')
# 如果分钟数小于 30 则把后两位变成 00
if int(value[3:]) < 30:
value = value[:2]+'00'
# 如果分钟数大于 30 则把后两位变成 30
else:
value = value[:2]+'30'
# 如果存在这个时间点就把计数 +1
if dict.get(value) is not None:
dict[value] = dict.get(value)+1
# 如果不存在这个时间点就初始化计数 = 1
else:
dict[value] = 1

# 开始对 dict 时刻分区结果排序
sort_result = sorted(dict.items(), key=lambda x:x[0])
# 存储排序之后的结果
result = {}
for item in sort_result:
result[item[0]] = item[1]

axis_x=[i[:2]+'\n点\n'+i[2:] for i in result]
axis_y=[result[i] for i in result]

plt.figure(figsize=(16,9))
plt.bar(axis_x,axis_y)
plt.xlabel("时间段")
plt.ylabel("消息数量")
plt.title("和宝宝聊天哒时区图")
plt.show()

时区图

聊天频率热图分析

最后还可以通过热力图来分析聊天的数据,我们知道python下标从0开始,因此第一周就用0表示,不符合我们使用习惯,我们提取周数然后进行+1操作即可,同理Python中周一也用0表示,一并加一操作即可。然后循环统计每周结果,通过matplotlib画图即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
#!/usr/bin/env python3
import os
import json
import datetime
import numpy as np
import matplotlib.pyplot as plt
from mplfonts import use_font
# 导入 SimHei 字体,第一次使用请在项目目录中使用如下命令导入
# mplfonts init && mplfonts install --update SimHei.ttf
use_font('SimHei')

data = open('/Users/disda/Desktop/Proj/wechat/db5/Chat_f611156ea3b00eb663747961911867d4.json').read()
data = json.loads(data)

statistics_dict = {}
for item in data:
unixtime = item.get('msgCreateTime')
if isinstance(unixtime, int):
# key 格式:年份.周
week = str(int(datetime.datetime.fromtimestamp(unixtime).strftime('%Y%W')) + 1)
week = week[:4]+'年'+datetime.datetime.fromtimestamp(unixtime).strftime('%m')+'月第('+week[-2:]+')周'
if statistics_dict.get(week) is None:
# 初始化这周的数据为全 0
statistics_dict[week] = [0, 0, 0, 0, 0, 0, 0]
# 对应的天数 +1
day = int(datetime.datetime.fromtimestamp(unixtime).strftime('%w'))
statistics_dict[week][day] = statistics_dict[week][day] + 1

# 对数据结果进行排序
sort = sorted(statistics_dict.items(), key=lambda x:x[0])
values = []
y_labels = []
for item in sort:
values.append(item[1])
y_labels.append(item[0])
x_labels = [ "周日", "周一", "周二", "周三", "周四", "周五", "周六"]

fig, axe = plt.subplots(figsize = (15, 150))
axe.set_xticks(np.arange(len(x_labels)))
axe.set_yticks(np.arange(len(y_labels)))
axe.set_xticklabels(x_labels)
axe.set_yticklabels(y_labels)
im = axe.imshow(values, cmap=plt.cm.Wistia)

# 添加数值标签
for i in range(len(y_labels)):
for j in range(len(x_labels)):
axe.text(j, i, values[i][j], ha="center", va="center", color="black")

axe.figure.colorbar(im, ax=axe)
plt.savefig('history.png')

history

lldb手动方法(原理)

Tencent的开源项目WCDB[2]是一个高效、完整、易用的移动数据库框架,基于SQLCipher[3],支持iOS, macOS和Android。

SQLCipher[4] 中使用 sqlite3_key[5] 函数打开加密的数据库,wcdb 将其封装在setCipherKey[6]方法下:

1
int sqlite3_key(sqlite3 *db, const void *pKey, int nKey)

使用 br set -n sqlite3_key 设置其断点。再使用memory read --size 1 --format x --count 32 $rsi 获取 pKey 传参的值:

arm则是memory read --size 1 --format x --count 32 $x1

  1. 打开电脑端微信(不要登陆)

  2. 在Terminal输入命令lldb -p $(pgrep WeChat)

lldb

  1. br set -n sqlite3_key 设置断点

  2. 输入c,回车(继续运行

  3. 登陆电脑端微信

  4. 输入memory read --size 1 --format x --count 32 $x1,回车

1
2
3
4
5
6
ori_key="""0x600001ea4760: 0x1b 0x89 0xb2 0x6f 0xf2 0xfd 0x48 0x2e
0x600001ea4768: 0x98 0x7a 0xdd 0x77 0xdc 0x4b 0xc2 0x63
0x600001ea4770: 0xd6 0xff 0xa2 0x78 0x14 0x36 0x46 0x65
0x600001ea4778: 0x89 0xaf 0xca 0x80 0xaa 0xa8 0xe6 0xa3"""
key = '0x' + ''.join(i.partition(':')[2].replace('0x', '').replace(' ', '') for i in ori_key.split('\n')[1:5])
print(key)

文中有彩蛋哦前半段的密文为ZmRhbGFpYV9tZ2xt,后半段隐写在❤️里面嘻嘻

提交到Gittalk有奖励哦😄


参考🔗:

导出多年微信聊天记录,我用可视化分析了出自己的口头禅