Matrix 首页推荐 

Matrix 是少数派的写作社区,我们主张分享真实的产品体验,有实用价值的经验与思考。我们会不定期挑选 Matrix 最优质的文章,展示来自用户的最真实的体验和观点。 
文章代表作者个人观点,少数派仅对标题和排版略作修改。


前言

微信,承载了多数人生活的方方面面。从亲人好友之间的沟通,到公司事情的交代。对于大多数人来说,已经活在了微信的世界里了。那么如果把微信的聊天记录导出,做一个聊天记录分析,用全局视角来分析分析聊天记录,会有怎样一种有趣的收获呢?这篇文章将会以导出微信聊天记录为切入点,结合数据分析学科的知识,对微信进行聊天记录进行分析。本文分为十一个小节,读者可以根据自己的需求跳转感兴趣的章节阅读。

数据分析效果预览

为了能让读者对本文实现的效果有最直观的感受,本文中有四个实践案例,最后生成的图片如下:

分析群聊/私聊生成高频词的词云
词云添加指定形状的遮罩,并自定义任意背景颜色
分析群聊/私聊,统计发送和接收到的所有表情或者单词数量,生成二维柱状图
分析每天聊天记录数量,生成不同配色方案的聊天频率热力图

文章阅读提示

本文大体可以分为两个部分:微信聊天记录导出和微信聊天记录分析。获取微信微信聊天数据的加密密钥,对于 arm CPU 构架的 MacBook 用户来说比较简单,Intel CPU 构架的用户需要多一些耐心,根据文章教程逐步操作,相信最后也一定能获取到对应的密钥。

微信聊天记录分析部分,使用 Python 代码来做数据清洗和图片生成,需要读者稍微懂一些 Python 方面的知识。

文章中所有的代码不一定需要复制下来运行,文章中出现的代码或者截图,只是方便读者配合文字阅读理解。在 JamesHopbourn/WeChatDataAnalysis 代码仓库中提供了本文需要的全部代码和素材。

获取微信聊天记录数据库加密密钥

前置知识:微信使用 SQLite 数据库在本地保存聊天记录,为了保证聊天记录不被轻易窃取,微信基于 SQLCipher 封装了腾讯自己的 WCDB 数据库框架。为了能够导出微信聊天记录,就需要通过技术手段获取数据库的加密密钥,这就是本小结需要达成的目标。

准备工作:为了全面分析聊天记录,我们先要进行数据同步。将移动设备上的聊天记录都同步到 MacBook 上。在移动设备上点击「设置 > 通用 > 聊天记录迁移与备份 > 迁移」并根据实际情况选择「迁移全部聊天记录」或者「迁移指定聊天记录」。以笔者的 52GB 聊天记录为例,花了 50 分钟左右。

目前 macOS 平台有 Intel 和 arm 两种架构,两个构架使用的获取密钥的方法不同,请根据自己的 MacBook CPU 构架,选择对应的解密方式操作。

arm CPU 架构获取密钥方法(简易方法)

通过阅读百灵鸟安全团队发布的文章「打造 macOS 下最强的微信取证工具」,我们可以知道获取微信数据库密钥的方法。文章的后半部分,作者还开发了一个可用于获得密钥的程序,下载并运行此程序便可直接得到密钥。下一步,我们将测试这个程序,看看其效果如何。

访问 PointerSearcher-X 发布的 _ptrsx-dumper_ 二进制文件,点击第一个 _ptrsx-dumper_ 下载解压即可。然后打开微信,点击左上角微信菜单里的「关于微信」,查看当前的微信版本号。此处作者使用的微信版本是 3.8.1 (26639),记住这个版本号,下一步就要使用到它了。

访问 dumpkey 项目主页,可以看到在 README.md 文件里,有不同版本微信所对应的操作命令。根据上一步查到的 3.8.1 (26639),复制下对应的命令即可,例如 3.8.1 (26639) 版本的命令如下:

ptrsx-dumper test --pid $(pgrep WeChat |head -1) --path "WeChat+0x4B0F700->0->8->8->16->32->8->8->64->8->0->0" -n 32

最后,打开终端执行下面两条命令即可获取微信数据库的密钥,首先使用 cd 命令切换到解压出来的文件夹目录里,也可以打一个 cd 空格,然后直接把对应的文件夹拖入终端即可。然后执行上面查阅到的命令,在前面加上 ./ 代表这条命令是在当前目录下的,回车执行即可获取到微信数据库的加密密钥。

加密密钥在第二行,也就是 353e 开头的那行,但是这个十六进制的密钥,所以在实际使用的时候,需要在前面加上 0x 这两个字符,下一步使用这个密钥的时候会再具体说明。

# 切换到解压文件夹路径
cd ~/Downloads/ptrsx-aarch64-apple-darwin
    
./ptrsx-dumper test --pid $(pgrep WeChat |head -1) --path "WeChat+0x4B0F700->0->8->8->16->32->8->8->64->8->0->0" -n 32
0x600001f00f60
353e9afab4b6422981d110d65e6d555557f1aca0afaa4431a5dab66fef384596

Intel CPU 架构获取密钥方法(通杀方法)

相对于 arm 构架的 CPU 来说,因为 dumpkey 项目作者没有提供 x86 的密钥导出实现,所以就需要读者自己动手,以动态调试的方式,从内存中直接读取密钥。虽然过程较为繁琐一些,但还达不到超出多数读者理解的水平。所以只要跟着步骤一步步操作,应该都是可以获取到加密密钥的。另外提一句话 dumpkey 项目目前只提供了 3.6.2 到 3.8.2 版本微信的命令,如果以后项目不再添加微信新版本的命令,也可以通过这个方法获取加密密钥。这是一个通杀的方法,任意微信版本,任意 CPU 架构都可以使用这个方法。接下来就正式开始通过动态调试获取密钥。

安装 lldb:lldb 是 Xcode 内置的开发者工具,可以用来调试 C/OC/C++/Swift 程序的调试器。为了能够使用 lldb,需要先安装一下开发者工具。在终端中执行 lldb 命令,弹出如下所示的提示框,那就是说明没有安装过 lldb,根据指示进行安装即可。如果以前执行过 xcode-select --install 或者已经安装过 Homebrew 的话,电脑上应该已经有了这个工具。

正确安装 lldb 之后可以尝试输出版本号,如果能够正确输出版本号,就代表着安装成功了。如下图所示作者使用的 lldb 版本是:lldb-1403.0.17.67

临时关闭系统的 SIP(SystemIntegrityProtection)即系统完整性保护,默认情况下 lldb 是无法调试微信,需要关闭 SIP。在 macOS 上关闭 SIP 需要进入恢复模式的终端里去执行命令。Intel MacBook 按下开机键后按住 Command-R 组合键进入恢复模式,arm MacBook 按住开机键不放直到出现选择界面,进入恢复模式进行操作。如果仍有疑问,可以参考下面这两篇文章:

进入恢复模式的终端中,执行以下两条命令,关闭 SIP 并重启电脑进入正常系统。如果操作正确 SIP 就能关闭了,如果对于这个步骤仍有疑问的读者,可以参考少数派上的文章 「macOS 开启或关闭 SIP」,有加详细的图文解释。

# 关闭 SIP 保护
csrutil disable
    
# 重启电脑进入正常系统
reboot

正常进入系统之后,就可以使用 lldb 程序调试工具调试微信了。先运行微信,但是不要登录,让它保持在登陆窗口。然后打开终端输入 lldb -p $(pgrep WeChat) 获取微信的进程 ID,指定 lldb 通过进程 ID 挂载 debug 程序。成功挂载微信程序之后,会显示出类似如下的界面:

接下来就是打断点了,在微信里的 sqlite3_key 函数上打一个断点,当程序运行到这个函数的时候就会暂停下来,等待输入调试命令。

输入命令 br set -n sqlite3_key 即可,lldb 回显:Breakpoint 1: 2 locations.,说明 lldb 已经成功在这两个地方打上了断点。

然后继续输入命令 c 回车即可,lldb 回显:Process 59241 resuming,此处的 c 即为 continue,执行完这条命令之后,程序就能正常继续向下执行,直到触发断点。

(lldb) br set -n sqlite3_key
Breakpoint 1: 2 locations.
(lldb) c
Process 58802 resuming

那么如何触发断点呢?现在微信已经恢复正常运行了,只需要点击登陆或者扫码登陆微信即可。点击完登陆或者扫码确认之后,lldb 就会拦截到这个断点,让微信再次暂停运行,这时候微信会卡住,这是正常现象。

切换回终端查看,可以看到 lldb 已经正确地把微信在 sqlite3_key 函数的入口处断点了

最后一步,输出内存里的密钥值。在日常生活中,大家可能听说过:「电脑上临时存储数据,经常会说把数据存在内存里,因为这样比较快等类似的话」。但是很少有人会直接跟内存去打交道。但是现在在 lldb 的帮助下,就可以直接读取到内存的值,此处要读取的就是数据库的密钥。

Intel CPU 构架命令 memory read --size 1 --format x --count 32 $rsi

arm CPU 架构命令 memory read --size 1 --format x --count 32 $x1

以 arm CPU 架构为例,最后得到的结果如下所示,复制四行以十六进制表示的内存数据,后续使用 Python 处理即可获取到正确的密钥。最后输入 exit 并再次输入 y 确认退出 lldb 调试,微信即可继续正常运行。

(lldb) memory read --size 1 --format x --count 32 $x1
0x60000240c760: 0x35 0x3e 0x9a 0xfa 0xb4 0xb6 0x42 0x29
0x60000240c768: 0x81 0xd1 0x10 0xd6 0x5e 0x6d 0x55 0x55
0x60000240c770: 0x57 0xf1 0xac 0xa0 0xaf 0xaa 0x44 0x31
0x60000240c778: 0xa5 0xda 0xb6 0x6f 0xef 0x38 0x45 0x96
    
(lldb) exit
Quitting lldb will detach from one or more processes. Do you really want to proceed: [Y/n] y

使用 Python 将内存中读取的密钥进行处理,去掉开头的 0x 并把四行合并为一行,即可获得可以使用的密钥,代码如下:

#!/usr/bin/env python3
    
source = '''
0x60000240c760: 0x35 0x3e 0x9a 0xfa 0xb4 0xb6 0x42 0x29
0x60000240c768: 0x81 0xd1 0x10 0xd6 0x5e 0x6d 0x55 0x55
0x60000240c770: 0x57 0xf1 0xac 0xa0 0xaf 0xaa 0x44 0x31
0x60000240c778: 0xa5 0xda 0xb6 0x6f 0xef 0x38 0x45 0x96
'''
    
key = '0x' + ''.join(i.partition(':')[2].replace('0x', '').replace(' ', '') for i in source.split('\n')[1:5])
print(key)

最后输出密钥:0x353e9afab4b6422981d110d65e6d555557f1aca0afaa4431a5dab66fef384596

对比上一个小节,使用 ptrsx-dumper 获取的密钥:353e9afab4b6422981d110d65e6d555557f1aca0afaa4431a5dab66fef384596,除了开头需要手动补全的 0x 之外完全一致。

导出聊天记录文件

为了能够打开数据库文件进行导出操作,需要下载 DB Browser for SQLite 进行操作。下载并安装完成之后,就该去寻找微信的数据库文件了。此处就不用通过 Finder 一层层地去找了,有一个简单的办法可快速找到数据库文件位置:打开任意聊天记录,选择图片或者文件,直接右键点击「在 Finder 中显示」即可。

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

为了避免在操作过程中因为错误操作损坏数据库文件,建议先退出微信。将 msg 数据库文件从 msg_0.dbmsg_9.db 十个数据库文件单独拷贝出来操作。此处使用命令实现,如果读者对 Unix 命令比较陌生,也可以按照注释操作手动创建文件夹,复制数据库文件。

# 在桌面上创建 WeChatDB 目录
mkdir ~/Desktop/WeChatDB
    
# 切换到数据库文件所在的目录,不要直接复制此处的路径,直接从 Finder 拉到终端里
cd /Users/james/Library/Containers/com.tencent.xinWeChat/Data/Library/Application\ Support/com.tencent.xinWeChat/2.0b4.0.9/5a22781f14219edfffa333cb38aa92cf/Message
    
# 把所有的 msg 数据库文件拷贝到 WeChatDB 目录
cp msg*.db ~/Desktop/WeChatDB
    
# 在 WeChatDB 目录里创建空文件夹,从 db0 到 db9
mkdir ~/Desktop/WeChatDB/db{0..9}

最终效果如下图所示,把所有的 db 数据库文件都拷贝到新建的 WeChatDB 目录下,并创建了从 db0 到 db9 十个空文件夹。

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

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

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

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

正确设置三个值以后,就可以回车解密微信聊天记录了,进入之后看到如下的界面:

切换到「浏览数据」选项,通过选择不同表就可以查看和不同好友之间的聊天记录,如下所示就是一份聊天记录。其中模糊部分就是聊天信息的具体内容。

但是这样的浏览意义有限,可以将所有的聊天记录都导出 JSON,以便接下来使用 Python 进行数据分析。前往「文件 > 导出」,点击「表到 JSON…」

在弹出的表选择框中,按下 command+A 选择全部的数据表,也就是全部的聊天记录,继续点击「保存」。

那么保存到哪里合适呢?刚才创建 WeChatDB 文件夹的最后一步,创建了 db0 到 db9 十个空文件夹。此时就可以对号入座,把对应数据库的 JSON 文件保存到对应的 db 文件夹里。为什么要这么做,而不是把所有的聊天记录都直接放在 WeChatDB 里呢?举个例子,如果隔了一段时间,又和好友聊了很多话之后,想要对这段时间的聊天记录再次做一个分析。通过关键词检索,很快就可以找到这个好友的聊天记录放在哪一个数据库文件里,而不需要再次手动将十个数据库文件都做一次导出,然后才能进行处理。

如下图所示,就是全部数据库都导出之后的效果

环境配置

接下来将会大量使用 Python 来实现绘制词云,绘制表格,生成 Excel 等功能,所以为了避免缺少各种库而导致报错,可以提前将环境都准备,使用如下命令安装所有依赖库:

pip3 install wordcloud matplotlib pandas jieba mplfonts imageio openpyxl numpy

如果读者是使用 conda 来配置虚拟环境,也可以通过 conda 来创建虚拟环境来运行脚本:

(base) conda create -n WeChatDataAnalysis python=3.9 matplotlib wordcloud pandas imageio openpyxl -y
    
(base) conda activate WeChatDataAnalysis
    
(WeChatDataAnalysis) pip3 install jieba mplfonts

关联阅读:《为非技术人群准备的 Python 安装指南》

使用完成之后如果退出删除环境,使用以下两条命令:

(WeChatDataAnalysis)  conda deactivate
    
(base) conda remove -n WeChatDataAnalysis --all

制作词云

前置知识

有了所有好友和群聊的聊天记录,就可以正式开始制作词云了。但是在此之前,需要先对微信聊天记录导出的 JSON 结构有一个基础了解。如下所示,是一个比较简单的聊天信息的 JSON 数据。

{
  "CompressContent": null,
  "ConBlob": "chN3eGlkX3k2OHh0MDk4NHFjczIxeglsaXV6b25nZGGAAQCYAQCgAQC4AQDIAQDQAQDwAQD4AQCIAgI=",
  "IntRes1": 0,
  "IntRes2": 0,
  "StrRes1": null,
  "StrRes2": null,
  "mesDes": 0,
  "mesLocalID": 8,
  "mesSvrID": 9004976427286855000,
  "messageType": 1,
  "msgContent": "投稿一日一技",
  "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 命令指定在 WeChatDB 目录下搜索这句话出现在哪个文件里即可,命令如下:

grep -r '每次放烟雾弹都有你' ~/Desktop/WeChatDB

可以看到 grep 已经输出了所对应的文件路径:/Users/james/Desktop/WeChatDB/db2/Chat_3c0825dcf3b568028bcf00ee45656d60.json

快速写一个 Python 脚本验证一下想法,但是可以看到,msgContent 除了记录消息信息之外,还在第一行标记了是谁发的这条消息,这并不是期望的结果,所以需要对程序进行调整。

将代码稍做修改,定义 content 变量用来保存消息文本。如果消息超过一行,就丢弃掉第一行的微信号,否则直接拼接到 content,最后打印出 content 即可。此处可能有读者会有疑问,为什么是超过一行,这样处理?如果是等于一行是什么情况呢?那就是自己发送的消息了,也就是对应 else 代码处理的部分。

#!/usr/bin/env python3
import os
import json
    
data = open('你的 JSON 文件路径').read()
data = json.loads(data)
    
content = ""
for item in data:
    if (item['messageType'] == 1):
        message = item['msgContent'].split('\n')
        if len(message) > 1:
            content += " ".join(message[1:])+ "\n"
        else:
            content += message[0] + "\n"
print(content)

结巴分词

聊天记录清洗出来之后,就可以开始分词了。为了剔除没有意义的字符,例如:啊、吧、呢和吗等等等,此处直接删除了所有一个字符的词,如果读者有自己的想法,可以根据实际情况修改。

# 引入结巴分词,对群聊记录进行分词
import jieba
ls = jieba.lcut(content)
    
# 如果分词出来只有一个字符剔除这个字符
ls = [i for i in ls if len(i) > 1]
    
text = ' '.join(ls)
print(text)

生成词云

最后一步,引入 wordcloud 生成词云。请注意最后一行代码保存图片文件,保存在当前目录下,所以这个目前编辑的 Python 代码一定是要已经保存为文件的状态。为了支持中文字体,可以从网上下载任意喜欢的中文字体,将它的 tff 字体文件放在和 Python 脚本同级的目录下。并在代码中指定 font_path 参数为字体名字即可。

此处给出两个可供下载的中文字体:阿里巴巴普惠体2.0SIMSUN

import wordcloud
    
wc = wordcloud.WordCloud(
    font_path="SIMSUN.ttf",
    width=1000,
    height=1000,
    background_color="skyblue",
    max_words=200)
    
wc.generate(text)
wc.to_file("result.png")

生成的词云效果图如下:

配置停止词

上图中,捂脸、偷笑、破涕为笑这几个都是表情,由于大家经常发这几个表情,所以占了比较大的面积。但是这样的词不具有生成词云的意义。所以可以引入 wordcloud 的 stopwords 停止词功能,将这些不太重要的词排除掉。

有了这个想法之后,新的问题又来了,微信的表情包有将近 50 个默认表情包,自己一个个去统计岂不是太麻烦了?简单粗暴一点的办法就是直接上,把微信默认的表情都按一遍过去。发给自己,然后拷贝出来。使用 sed 命令处理一下,就可以使用了。

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

更新停止词之后的代码如下所示:

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

最后得到的结果如下图所示,可以看到表情相关的单词都已经剔除了,生成了一张群聊高频词的词云。

词云形状

不少人会使用词云来生成和爱人的聊天记录图片,在这种场景下,可以在词云参数里配置一张爱心图片的遮罩,会更加符合使用场景需求。接下来就要开始设置爱心遮罩了。首先需要一个爱心的图片,网上的图片由于清晰度不够,生成的效果欠佳,所以可以自己绘制一个。将两个圆的半圆部分和正方形混合,再设置一下颜色,旋转 315 度即可得到一个高清自制的爱心图片。步骤截图如下:

导出爱心的时候建议把导出像素不要设置太大,否则脚本运行时间会非常长,导出像素设置为 200 或者 300 即可。下面是一张制作好的爱心图片,可直接拿来使用。另外,文章开头提供的 GitHub 仓库链接里已经放置了这张图片。

更新后的代码如下所示:

# 引入结巴分词,对群聊记录进行分词
import jieba
ls = jieba.lcut(content)
# 如果分词出来只有一个字符剔除这个字符
ls = [i for i in ls if len(i) > 1]
text = ' '.join(ls)
    
# 引入词云
import wordcloud
# 引入停止词模块
from wordcloud import WordCloud, STOPWORDS
stopwords = STOPWORDS
# 添加新的停止词
stopwords.update(['微笑','撇嘴','色','发呆','得意','流泪','害羞','闭嘴','睡','大哭','尴尬','发怒','调皮','呲牙','惊讶','难过','囧','抓狂','吐','偷笑','愉快','白眼','傲慢','困','惊恐','憨笑','悠闲','咒骂','疑问','嘘','晕','衰','骷髅','敲打','再见','擦汗','抠鼻','鼓掌','坏笑','右哼哼','鄙视','委屈','快哭了','阴险','亲亲','可怜','笑脸','生病','脸红','破涕为笑','恐惧','失望','无语','嘿哈','捂脸','奸笑','机智','皱眉','耶','吃瓜','加油','汗','天啊','Emm','社会社会','旺柴','好的','打脸','哇','翻白眼','666','让我看看','叹气','苦涩','裂开','嘴唇','爱心','心碎','拥抱','强','弱','握手','胜利','抱拳','勾引','拳头','OK','合十','啤酒','咖啡','蛋糕','玫瑰','凋谢','菜刀','炸弹','便便','月亮','太阳','庆祝','礼物','红包','發','福','烟花','爆竹','猪头','跳跳','发抖','转圈'])
    
# 引入 imageio 读取爱心图片文件
from imageio.v2 import imread
# 读取爱心图片文件并赋值给 background 变量
background = imread('heart.png')
    
wc = wordcloud.WordCloud(
    font_path="SIMSUN.ttf",
    width=1000,
    height=1000,
    # 词云背景颜色
    background_color="skyblue",
    max_words=200,
    # 配置停止词参数
    stopwords=stopwords,
    # 配置爱心图片遮罩
    mask = background)
    
wc.generate(text)
wc.to_file("resultHeart.png")

确认 heart.png 和 Python 脚本在同一目录下之后,运行 Python 脚本,即可在目录下看到生成的 resultHeart.png 爱心词云图片。

背景颜色

自定义完了形状,为了让这张图片更加贴合主题,还可以再给它换一个背景颜色。单独摘出词云背景颜色的定义参数来看,只需要传入对应颜色名字即可修改背景颜色。除此之外还可以传入六位十六进制的 RGB 值,通过 hex code 直接定义颜色,用这种方法可以定义任意一种颜色。

    # 词云背景颜色
    background_color="skyblue"

以中国传统色为例,找到一个喜欢颜色,在颜色名字下方就有对应颜色的 hex code。以下图中的「石蕊红」为例,它的 hex code 是 #f0c9cf

#f0c9cf 替换原来代码中的 skyblue,就可以替换背景颜色了,效果图如下。

表情数量统计

表情字典获取

关键词出现频率分析完了,再对刚才忽略掉的表情进行专门统计。在上一小节的操作中,已经获取到了所有的表情定义,但是还不能直接拿来使用。重新放一下刚才上面这张配图,注意看它的表情是放在方括号里的。所以为了准确统计到每个表情数量,需要对原始表情文本重新加工一下。

此处依旧使用 sed 来处理一下表情数据,把它们变成带方括号的元素形式,方便接下来在 Python 中统计。

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

将上面处理好的表情元素定义为 stickers 列表,再通过字典推导式创一个含有 stickers 中元素,但是 value 全部为 0 的 stickers_dict 字典。接下来只要遍历聊天记录,遇到一个表情就让对应表情的计数 +1 即可。由于微信默认的表情里不包含 Emoji 表情,所以在代码要使用 stickers.extend() 函数去扩展 stickers 表情列表,将聊天过程中常用的 Emoji 表情都添加进去。此处读者甚至还可以根据自己的需求,添加想要统计的单词数量进去。

此处为了输出这个表情字典,使用了 json.dumps() 方法,指定了两个参数。ensure_ascii 参数是为了能够让中文能够正常显示出来,如果不指定这个参数,看到的会是文字对应的 Unicode 码。indent 设置了四个空格长度的缩进,方便阅读查看。如果读者在读完本篇文章之后,想要自行编写其他数据分析脚本,也可以使用这个方法去查看输出是否符合预期。

import json
    
stickers = ['[微笑]','[撇嘴]','[色]','[发呆]','[得意]','[流泪]','[害羞]','[闭嘴]','[睡]','[大哭]','[尴尬]','[发怒]','[调皮]','[呲牙]','[惊讶]','[难过]','[囧]','[抓狂]','[吐]','[偷笑]','[愉快]','[白眼]','[傲慢]','[困]','[惊恐]','[憨笑]','[悠闲]','[咒骂]','[疑问]','[嘘]','[晕]','[衰]','[骷髅]','[敲打]','[再见]','[擦汗]','[抠鼻]','[鼓掌]','[坏笑]','[右哼哼]','[鄙视]','[委屈]','[快哭了]','[阴险]','[亲亲]','[可怜]','[笑脸]','[生病]','[脸红]','[破涕为笑]','[恐惧]','[失望]','[无语]','[嘿哈]','[捂脸]','[奸笑]','[机智]','[皱眉]','[耶]','[吃瓜]','[加油]','[汗]','[天啊]','[Emm]','[社会社会]','[旺柴]','[好的]','[打脸]','[哇]','[翻白眼]','[666]','[让我看看]','[叹气]','[苦涩]','[裂开]','[嘴唇]','[爱心]','[心碎]','[拥抱]','[强]','[弱]','[握手]','[胜利]','[抱拳]','[勾引]','[拳头]','[OK]','[合十]','[啤酒]','[咖啡]','[蛋糕]','[玫瑰]','[凋谢]','[菜刀]','[炸弹]','[便便]','[月亮]','[太阳]','[庆祝]','[礼物]','[红包]','[發]','[福]','[烟花]','[爆竹]','[猪头]','[跳跳]','[发抖]','[转圈]']
# 除了微信自带的表情,添加一些常用的 Emoji 表情,除此之外还可以添加一些想要统计的单词
stickers.extend(['😂','🤣','🥰','😅','🥹','😘','🤩','🥺','😓','🙄','🤡', '哈哈']) 
stickers_dict = {stickers: 0 for stickers in stickers}
    
print(json.dumps(stickers_dict, ensure_ascii=False, indent=4))

遍历 JSON 统计表情出现次数

读取数据,for 循环遍历数据,和上一个小节几乎一样的操作。唯一值得一提的就是 count() 方法。count() 方法可以统计某个字符串里包含了多少个指定字符。举个例子来说 print("11112345".count("1")) 的结果就是 4,因为 1 在字符串里出现了四次。

data = open('/Users/james/Desktop/WeChatDB/db8/Chat_c361fce587751cedffb3e34d40fddb6b.json')
data = data.read()
data = json.loads(data)
    
# 循环所有的消息记录
for item in data:
    # 循环表情字典里的所有键
    for word in stickers_dict.keys():
        # 使用文本的 count 函数,计算里面包含了多少个 word 变量,然后加回去
        stickers_dict[word] += item['msgContent'].count(word)
print(json.dumps(stickers_dict, ensure_ascii=False, indent=4))

使用这两层 for 循环处理完之后,就可以得到每个表情都发了多少次了。

但是同时也发现了一些不足之处,有一部分表情从来都没发过,它们的计数是 0,没有统计意义需要直接剔除掉。只需要一行代码即可实现:

# 剔除计数为 0 的表情
data = {key: value for key, value in stickers_dict.items() if value != 0}
    
print(json.dumps(data, ensure_ascii=False, indent=4))

引入 pandas 生成 Excel

经过上面的表情数量统计和数据清洗,现在已经得到了每个表情包都发了多少次的统计数量。现在只需要引入 pandas 将清洗干净的数据生成 Excel,代码层面的工作就大功告成了。

接下来这部分 pandas 的代码都是 ChatGPT 实现的,如果读者有自己的个性化筛选或者排序需求,也可以试试让 ChatGPT 实现。对于 pandas 这种有固定语法且资料丰富的开源库来说,使用 ChatGPT 来生成对应代码的效率和准确性还是比较高的。

import pandas as pd
# 使用 DataFrame.from_dict() 将字典转换为 DataFrame
df = pd.DataFrame.from_dict(data, orient='index', columns=['数量'])
    
# 添加表情列
df['表情'] = df.index
    
# 按数量降序排序
df = df.sort_values(by='数量', ascending=False)
    
# 重置索引
df = df[['表情', '数量']]
df = df.reset_index(drop=True)
    
# 保存 DataFrame 到 Excel 文件
df.to_excel('~/Desktop/emoji_data.xlsx', index=False)
    
print("数据已保存到 ~/Desktop/emoji_data.xlsx 文件中。")

Number 生成横向柱状图

打开桌面上的 emoji_data.xlsx 选中所有表情数据,切换到菜单栏上的插入菜单,插入二位横向条形图即可。但是 Excel 出来的图片效果达不到预期,笔者把数据复制了一份粘贴到了 Numbers 去生成横向条形图。如果读者是 Excel 高手,也可以在 Excel 里根据自己的喜欢进行处理。

将数据复制到 Numbers 里,生成二维条形图,根据自己的喜欢修改一下颜色,添加必要的文字说明。此处为了做出的图表便于展示,只选择了前 30 条数据制作图表。

选中二维条形图,右键点击「拷贝」将二维条形图拷贝到系统剪切板。然后就可以将它粘贴到任意位置,例如粘贴到微信聊天发送框,或者打开预览 App 按下 command+N 从剪切板读取图片,然后将这张图片保存下来。

刚在上面的步骤中,为了演示添加指定单词统计的功能,将「哈哈」这个单词添加到了统计数据里。如下图所示,「哈哈」这个单词加起来的数量,已经比前三个表情的总和还多了,说明这个群聊气氛还是比较欢乐的。

再看到旺柴表情包,在表情的统计数量里也是一骑绝尘,断崖式地领先于其他各个表情包。特地算了一下,旺柴表情包在所有表情包里发送占比 33% 😂

排版设计优化

在研究微信聊天记录数据分析的过程中,在少数派上看到 无需编程:制作 iMessage 年度数据报告 这篇文章,其中的几张图效果做得很不错。如果读有余力,可以参考这样的排版设计来设计最终的效果图。

时间分段聊天频率分析

前置知识

关键词出现频率分析完了,接下来就可以对单位时间段内聊天频率进行分析了。在前置知识中已经介绍过了,消息的发送时间对应的是 msgCreateTime,它使用 Unix Time 表示。对于部分不太了解计算机知识的读者来说,此处有必要简单介绍一下什么是 Unix Time,简单说它就是代表了从 1970–01–01 08:00:00 到目前为止经过的秒数。

如果想要更直观地体会一下如何从 Unix Time 转换到方便阅读的日期文本,可用使用命令 brew install coreutils 安装 GNU 核心工具包,然后就可以使用 gdate 命令来转换 Unix Time 了。

如下图所示 @ 后面跟上秒数即可看到转换之后的结果。使用gdate +%s 也可以获取当前的 Unix Time,然后再使用 gdate -d ``@1692789201 同样可以将时间转换回来。

统计时间块消息数量

有了刚才制作词云和表情数量统计的经验,就可以照葫芦画瓢,写出获取所有消息发送时间的代码了。读取 JSON 文件,使用 json.loads() 加载为 Python 的 JSON 对象。然后遍历每一条记录的 msgCreateTime 打印出来即可。

#!/usr/bin/env python3
import os
import json
import datetime
    
data = open('/Users/james/Desktop/WeChatDB/db2/Chat_3c0825dcf3b568028bcf00ee45656d60.json').read()
data = json.loads(data)
    
dict = {}
for item in data:
    # 获取消息发送时间
    unixtime = (item['msgCreateTime'])
    print(unixtime)

获取到时间之后,还需要对它进行格式化处理,Python 内置了日期时间处理模块 datetime,可以直接调用来解析 Unix Time,可以看到现在打印出来的 value 变量已经是可阅读的「小时:分钟」格式了。

接下来就是最重要的一个步骤:时刻点分区,把某些时刻分类到一个区间里。例如把 08:0008:29 这个时间段里发送的消息都算到 08:00 里面,把 08:3008:59 都算到 08:30 里面。

可以看到时间分区之后,每个时间段里一共发送了多少条消息。

但是新的问题又来了,时间是分区好了,但是目前的分区是乱序的,需要再次处理一下,把它按照顺序整理成从 0000 到 2330 的格式。使用 sorted() 方法对 dict.items() 进行排序,使用 lambda 表达式指定使用第一个元素进行排序。这样处理之后在右侧就可以看到顺序的「时刻-消息数量」关系。

导入 maplotlib 生成图片

经过上面的数据清洗、时刻分区和时刻排序之后,现在已经得到了「时刻-消息数量」关系字典,对于这样的 x-y 数据,使用 matplotlib 来生成二维柱状图是不错的选择。但是在此之前,还是和 wordcloud 一样的操作,需要配置一下中文字体,使得图片上能够正常显示中文。

此处配置中文字体的方式与 wordcloud 有所不同,matplotlib 有专门用于管理字体的程序。在「环境配置」小节中已经安装过了,只要初始化一下就可以使用,命令如下:

# 初始化 mplfonts
mplfonts init

如果稍后发现依旧无法正常显示中文,还有另一种解决方式,通过 mplfonts 导入本地的其他中文字体。例如在项目目录下有一个 SIMSUN.ttf 字体文件,使用如下命令也可以添加这个字体。

# 导入本地字体
mplfonts install --update SIMSUN.ttf

只需要以上简单的一条命令,即可完成中文字体的配置。maplotlib 这一部分的代码比较简单,直接上代码来看:

import matplotlib.pyplot as plt
    
# 导入字体管理模块
from mplfonts import use_font
#指定中文字体(这里的中文字体根据实际情况修改)
#use_font('SIMSUN')
use_font('Noto Serif CJK SC')
    
axis_x=[i[:2]+'\n点\n'+i[2:] for i in result]
axis_y=[result[i] for i in result]
    
plt.bar(axis_x,axis_y)
plt.xlabel("时间段")
plt.ylabel("消息数量")
plt.title("蓝翼运动大众群")
plt.show()

运行 Python 程序即可生成时间段-消息数量的柱状图,点击下方最后一个保存按钮,即可将生成的图片保存到磁盘。

通过这张图可以看出,在中午饭点和晚饭之后这两个时间段里,大家的聊天活跃度是最高的。其次就是 22:0022:30 的时间段。

聊天频率热图

前置知识

有了上面日期解析和字体设置的知识储备之后,就可以试着来挑战一下热力图了。对于数据分析不太了解的读者需要先直观地认识一下什么是热力图,无需编程:制作 iMessage 年度数据报告 这篇文章里有如下这样一张配图,数字越大颜色越深。

热力图 - Heatmap 这篇文章里给热力图做了一个比较好的定义:热力图,是一种通过对色块着色来显示数据的统计图表。 绘图时,需指定颜色映射的规则。 例如,较大的值由较深的颜色表示,较小的值由较浅的颜色表示;较大的值由偏暖的颜色表示,较小的值由较冷的颜色表示。

快速上手体验

这一小节的内容就是带领读者,通过 Python 处理微信聊天记录,生成好友或者群聊的热力图。为了让读者有一个直观的代码体验,下面是一个简单的生成热力的代码,如果上面的环境配置正确,运行下面这段代码就可以看到对应的热力图效果。

#!/usr/bin/env python3
import numpy as np
import matplotlib.pyplot as plt
# 导入字体管理模块
from mplfonts import use_font
#指定中文字体(这里的中文字体根据实际情况修改)
use_font('SIMSUN')
    
x_labels = ["周一", "周二", "周三", "周四", "周五", "周六", "周日"]
y_labels = [202327, 202328, 202329, 202330]
values = np.array([
        [0, 0, 0, 0, 0, 38, 0],
        [7, 3, 0, 0, 28, 0, 31],
        [20, 0, 0, 16, 0, 0, 0],
        [0, 0, 78, 10, 7, 0, 0]
])
    
    
fig, axe = plt.subplots(figsize=(8, 3))
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.Greens)
axe.figure.colorbar(im, ax=axe)
plt.show()

通过运行上面这个快速上手的例子不难看出,只要传入对应的 y_labelsvalues 就可以绘制出自己的热力图。那么现在的问题就转变成了如何处理聊天记录数据,将数据转为换为下面这种形式:

  • Y 轴:哪一年的哪一周,即年和周数
  • Value 数据:星期几发了多少条消息

使用 Python 处理数据

直接上代码,读取文件,读取 msgCreateTime 的值,使用 datetime 库去解析 Unix Time,这是上一个小节的内容,此处不再赘述。但是值得注意的是,此处作者将 Unix Time 解析成了 %Y%W 的格式。%Y 是用四位数字表示的年份,%W 是用两位数字表示的周数,例如当前的写稿日期 2023 年 8 月 23 日,是今年的第 35 周,那就标记为 202335

week 变量的后面加上了一个数字 1,这是因为 datetime 库里第一周是用 00 表示,此处加一让它变为 01 更加符合大众的计数习惯。

#!/usr/bin/env python3
import os
import json
import datetime
    
data = open('/Users/james/Desktop/WeChatDB/db2/Chat_55ce213c2ad742d8ea91bf2c5ee282fb.json').read()
data = json.loads(data)
    
statistics_dict = {}
for item in data:
        unixtime = item.get('msgCreateTime')
        if isinstance(unixtime, int):
                # key 格式:年份 周数
                week = int(datetime.datetime.fromtimestamp(unixtime).strftime('%Y%W')) + 1
                print(week)

现在 Y 轴年份周数数据有了,接下来就是把星期几发了多少条聊天记录统计进去,此处可以用数组来存储消息计数。看到上面代码的第 9 行,定义了一个 statistics_dict 字典。稍后就是要把计数以 key-value 的形式存储进去,只是此处的 value 是一个数组。

下面就是一个数据结果的例子。拿 202328 这条数据来看,后面跟着 [0, 0, 0, 0, 0, 38, 0]这样一个数组,代表周一发了 7 条消息,周二 3 条,周三周四 0 条,周五 28 条,周六 0 条,周末 31 条。

{
    202328: [7, 3, 0, 0, 28, 0, 31],
    202329: [20, 0, 0, 16, 0, 0, 0],
    202330: [0, 0, 78, 10, 7, 0, 0]
}

接下来就是代码实现,因为上面定义的 statistics_dict 里没有任何 key,如何直接去 set 这个 value 会报错找不到这个 key。所以要先判断一下当前循环的「年份周数」key 是否存在,如何不存在就先进行初始化,然后再赋值。下面这段代码中从 6 到 9 行就是「年份周数」key 的初始化。初始化完成之后,将对应天数的索引位置数值 +1。在 datetime 库中使用 %w 来表示解析的日期是星期几,从 0–6 分别代表从周一到周末。

最后打印一下 statistics_dict 字典,可以看到已经正确收集到了所有的统计数据。

for item in data:
        unixtime = item.get('msgCreateTime')
        if isinstance(unixtime, int):
                # key 格式:年份.周
                week = int(datetime.datetime.fromtimestamp(unixtime).strftime('%Y%W')) + 1
                # 判断「年份周数」key 是否存在
                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
print(statistics_dict)

排序整理

但是心细的读者可能也注意到了,此处输出字典依旧是乱序的。需要像「时间分段聊天频率分析」小节里一样,对数据进行排序处理。可以看到在排序之后,数据变成了元组 (tuple) 的列表形式。

sort = sorted(statistics_dict.items(), key=lambda x:x[0])

回想一下在「快速上手体验」小节的分析,只要整理出了 Y 轴 和 value 的数据,就可以生成所需要的热力图。那就趁热打铁,直接循环这个列表,把里面的所有数据都追加到 y_labelsvalues 里即可,成功就在眼前!

直接上代码,将元组里第 0 个元素追加到 y_labels,第 1 个元素追加到 values,可以看到最终的结果符合预期。

整合代码 matplotlib 代码出效果图

有了上面处理好的数据,再整合 matplotlib 就可以绘图了。此处有一点需要注意,figsize = (15, 15) 是设置画布的大小。如果读者的聊天记录时间跨度比较大,比如说一年两年甚至三年,就需要把后面的数字适当地增加,扩大画布的高度让图片能够正常显示。代码最后一行调用 plt.savefig('history.png') 把图片保存到当前目录下,名为 history.png 的图片,所以这段代码一定要先保存再运行。

# 导入 matplotlib 绘图
import numpy as np
import matplotlib.pyplot as plt
from mplfonts import use_font
use_font('SimHei')
    
x_labels = ["周一", "周二", "周三", "周四", "周五", "周六", "周日"]
    
fig, axe = plt.subplots(figsize = (15, 15))
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.Greens)
# 是否开启参考刻度,如果不需要开启,注释下面这行代码
axe.figure.colorbar(im, ax=axe)
plt.savefig('history.png')

自定义颜色

觉得绿色这样的配置不好看,想要自定义格子颜色要如何操作呢?看到 matplotlib 部分的下面这行代码,cmap 就指定了图片使用什么样的配色方案。例如作者比较喜欢的另一个配色方案是 Wistia_r,那么只要把 Greens 这个单词换成 Wistia_r 即可,前面的 cmap=plt.cm. 不需要去修改,下面几张效果图是作者比较喜欢的配色方案的展示。

im = axe.imshow(values, cmap=plt.cm.Greens)

那么如何获取所有的配置方案呢?直接代码循环遍历即可。如果觉得这样的配色方案不够直观,还需要一个个去尝试,还可以参考下面这幅配色方案总览图,快速找到心仪的配色方案。

import json
import matplotlib.pyplot as plt
    
print(json.dumps(plt.colormaps(), indent=4))
# ['magma', 'inferno', 'plasma', 'viridis', 'cividis', 'twilight', 'twilight_shifted', 'turbo', 'Blues', 'BrBG', 'BuGn', 'BuPu', 'CMRmap', 'GnBu', 'Greens', 'Greys', 'OrRd', 'Oranges', 'PRGn', 'PiYG', 'PuBu', 'PuBuGn', 'PuOr', 'PuRd', 'Purples', 'RdBu', 'RdGy', 'RdPu', 'RdYlBu', 'RdYlGn', 'Reds', 'Spectral', 'Wistia', 'YlGn', 'YlGnBu', 'YlOrBr', 'YlOrRd', 'afmhot', 'autumn', 'binary', 'bone', 'brg', 'bwr', 'cool', 'coolwarm', 'copper', 'cubehelix', 'flag', 'gist_earth', 'gist_gray', 'gist_heat', 'gist_ncar', 'gist_rainbow', 'gist_stern', 'gist_yarg', 'gnuplot', 'gnuplot2', 'gray', 'hot', 'hsv', 'jet', 'nipy_spectral', 'ocean', 'pink', 'prism', 'rainbow', 'seismic', 'spring', 'summer', 'terrain', 'winter', 'Accent', 'Dark2', 'Paired', 'Pastel1', 'Pastel2', 'Set1', 'Set2', 'Set3', 'tab10', 'tab20', 'tab20b', 'tab20c', 'magma_r', 'inferno_r', 'plasma_r', 'viridis_r', 'cividis_r', 'twilight_r', 'twilight_shifted_r', 'turbo_r', 'Blues_r', 'BrBG_r', 'BuGn_r', 'BuPu_r', 'CMRmap_r', 'GnBu_r', 'Greens_r', 'Greys_r', 'OrRd_r', 'Oranges_r', 'PRGn_r', 'PiYG_r', 'PuBu_r', 'PuBuGn_r', 'PuOr_r', 'PuRd_r', 'Purples_r', 'RdBu_r', 'RdGy_r', 'RdPu_r', 'RdYlBu_r', 'RdYlGn_r', 'Reds_r', 'Spectral_r', 'Wistia_r', 'YlGn_r', 'YlGnBu_r', 'YlOrBr_r', 'YlOrRd_r', 'afmhot_r', 'autumn_r', 'binary_r', 'bone_r', 'brg_r', 'bwr_r', 'cool_r', 'coolwarm_r', 'copper_r', 'cubehelix_r', 'flag_r', 'gist_earth_r', 'gist_gray_r', 'gist_heat_r', 'gist_ncar_r', 'gist_rainbow_r', 'gist_stern_r', 'gist_yarg_r', 'gnuplot_r', 'gnuplot2_r', 'gray_r', 'hot_r', 'hsv_r', 'jet_r', 'nipy_spectral_r', 'ocean_r', 'pink_r', 'prism_r', 'rainbow_r', 'seismic_r', 'spring_r', 'summer_r', 'terrain_r', 'winter_r', 'Accent_r', 'Dark2_r', 'Paired_r', 'Pastel1_r', 'Pastel2_r', 'Set1_r', 'Set2_r', 'Set3_r', 'tab10_r', 'tab20_r', 'tab20b_r', 'tab20c_r']

总结

本文以破解微信聊天数据库为出发点,导出聊天记录之后,使用 Python 结合 jieba、numpy、matplotlib 等工具对聊天记录进行数据分析。得到了一些比较有趣的数据,或者制作出了一些有意义的图片。

本文一共有 4 个例子,如果读者能够将这几个例子都理解透了,也可以根据自己的想法,实现一些更有意思的数据分析工作。除了数据分析之外,如果读者在 AIGC 方面有所涉略,还可以参考文章 用 10 万条微信聊天记录和 280 篇博客文章,我克隆了一个数字版自己,将自己的好友聊天记录数据清洗出来,然后用同样的方法训练一个类似自己思想的聊天机器人。

本文抛砖引玉,希望有更多读者,以不同的视角来解析微信聊天记录。

关联阅读:

> 下载少数派 客户端 、关注 少数派公众号 ,了解更妙的数字生活 🍃

> 想申请成为少数派作者?冲!