验证中...
私信发送成功
qqparser.py
原始数据 复制代码
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import jieba
import numpy
import re
from PIL import Image
from wordcloud import ImageColorGenerator, WordCloud
# 系统内置的表情名称
FACES = (
'微笑', '撇嘴', '色', '发呆', '得意', '流泪', '害羞', '闭嘴', '睡', '大哭', '尴尬', '发怒',
'调皮', '呲牙', '惊讶', '难过', '酷冷', '汗', '抓狂', '吐', '偷笑', '可爱', '白眼', '傲慢',
'饥饿', '困', '惊恐', '流汗', '憨笑', '大兵', '奋斗', '咒骂', '疑问', '嘘', '晕', '折磨',
'衰', '骷髅', '敲打', '再见', '擦汗', '抠鼻', '鼓掌', '糗大了', '坏笑', '左哼哼', '右哼哼', '哈欠',
'鄙视', '委屈', '快哭了', '阴险', '亲亲', '吓', '可怜', '眨眼睛', '笑哭', 'doge', '泪奔', '无奈',
'托腮', '卖萌', '斜眼笑', '喷血', '惊喜', '骚扰', '小纠结', '我最美', '菜刀', '西瓜', '啤酒', '篮球',
'乒乓', '茶', '咖啡', '饭', '猪头', '玫瑰', '凋谢', '示爱', '爱心', '心碎', '蛋糕', '闪电',
'炸弹', '刀', '足球', '瓢虫', '便便', '月亮', '太阳', '礼物', '拥抱', '强', '弱', '握手',
'胜利', '抱拳', '勾引', '拳头', '差劲', '爱你', 'NO', 'OK', '爱情', '飞吻', '跳跳', '发抖',
'怄火', '转圈', '磕头', '回头', '跳绳', '挥手', '激动', '街舞', '献吻', '左太极', '右太极', '双喜',
'鞭炮', '灯笼', 'K歌', '喝彩', '祈祷', '爆筋', '棒棒糖', '喝奶', '飞行', '钞票', '药', '手枪',
'蛋', '红包', '河蟹', '羊驼', '菊花', '幽灵', '大笑', '不开心', '冷漠', '呃', '好棒', '拜托',
'点赞', '无聊', '托脸', '吃', '送花', '害怕', '花痴', '小样儿', '飙泪', '我不看', '潜水', '敬礼',
'石化', '安慰', '扮鬼脸', '无语', '狂汗', '叹气', '加油', '生病', '拜托', '孤寂', '惬意', '烦躁',
'牵手', '示爱', '情书', '月饼', '玉兔'
)
# 聊天记录中的图片标记
IMAGEMARK = '[图片]'
# 匹配表情的正则表达式,除上面列出的表情外,还可以匹配emoji表情
# 仅支持系统内置的表情,自定义表情会被识别为图片
FACEMARK = re.compile(r'{}|\[emoji\]'.format('|'.join(('\/' + face for face in FACES))), re.U)
# 聊天记录中的红包标记
# 由于没有找到有红包记录的群,所以此项无法测试
BAGMARK = re.compile(r'\[.*?红包\.*?]', re.U)
# 匹配@的人的正则表达式
# 如果聊天记录中出现了邮箱地址,会被误识别,由于可能用户名本身就是邮箱地址,所以此处不做优化
# @的用户名后面会紧跟一个空格,由于可能用户名本身包含空格,所以此处的匹配并不准确,只会将第一个空格作为用户名的结束
CALLEDMARK = re.compile(r'\@(.*)\s', re.U)
# 匹配分享链接的正则表达式
# 由于链接中可能包含跳转,即同一个链接中出现多个http字样,因此匹配时要求http的上一个字符不能是=
# 此处假定遇到的第一个汉字或空白符为链接的结束
LINKMARK = re.compile(r'[^=]?(https?\:\/\/[^\u4e00-\u9fa5\s]+)', re.I)
# 匹配上传的文件的正则表达式,支持手机端的聊天记录,电脑端未测试
# 由于手机端的文件上传记录为乱码,所以可能会被识别为表情或@的人
UPLOADEDMARK = re.compile(r'Bytet.{2}(.+)\.([^.]+?)t', re.M|re.S|re.U)
# 匹配用户名和QQ号的正则表达式,如果自定义了聊天记录导出文件的文件名则匹配不成功
# 如果是群聊天记录,手机端的导出文件名会包含群号,而电脑端则不包含
USERMARK = re.compile(r'^(.+)\((\d+)\).txt$', re.I|re.U)
# 匹配一条聊天记录的正则表达式
# 手机端的聊天记录在时间和用户名之间有2个空格,而电脑端只有1个
# 一条聊天记录会以空行为结尾,由于可能正文内容本身包含空行,因此空行的后面会无法识别
RECORDMARK = re.compile(r'(\d{4}\-\d{2}\-\d{2}\s\d{2}\:\d{2}\:\d{2})\s{1,2}(.*?)\((\d+?)\)\n(.*?)\n{2}', re.M|re.S|re.U)
# 词云图使用的字体路径,这里使用微软雅黑
FONTPATH = 'C:/Windows/Fonts/msyh.ttf'
# 记录所有出现过的用户名
USERS = {}
class User(object):
def __init__(self, uin, uid):
self.uin = uin
self.uid = uid
def __str__(self):
# 用户名(QQ号)
return '{uin}({uid})'.format(uin=self.uin, uid=self.uid)
class Record(object):
def __init__(self, sendtime, uin, uid, content):
self.sendtime = sendtime # 发送时间
self.user = User(uin, uid) # 发送人
self.images = content.count(IMAGEMARK) # 图片数量
self.faces = len(FACEMARK.findall(content)) # 表情数量
self.bags = len(BAGMARK.findall(content)) # 红包数量
self.called = CALLEDMARK.findall(content) # @的人列表
self.links = LINKMARK.findall(content) # 分享的链接
self.uploaded = self.get_uploaded(UPLOADEDMARK.search(content)) # 上传的文件
self.content = self.get_content(content) # 正文内容
USERS[uid] = self.user # 将该用户加入到用户组当中
def __str__(self):
return (
'发送时间:{sendtime}\n发送人:{user}\n'
'正文内容:{content}\n'
'字数:{words}\n'
'图片数量:{images}\n'
'表情数量:{faces}\n'
'红包数量:{bags}\n'
'@的人:{called}\n'
'分享的链接:{links}\n'
'上传的文件:{uploaded}'
).format(
sendtime = self.sendtime,
user = str(self.user),
content = self.content or '无',
words = self.words,
images = self.images,
faces = self.faces,
bags = self.bags,
called = '、'.join(self.called) if self.called else '无',
links = '、'.join(self.links) if self.links else '无',
uploaded = self.uploaded
)
@property
def words(self):
# 正文字数统计
return len(self.content)
def get_uploaded(self, matched):
return '.'.join(matched.groups()) if matched else '无'
def get_content(self, content):
if self.uploaded == '无':
content = content.replace(IMAGEMARK, '')
content = FACEMARK.sub('', content)
content = BAGMARK.sub('', content)
content = CALLEDMARK.sub('', content)
content = LINKMARK.sub('', content)
return content.strip()
else:
return ''
class Chat(object):
def __init__(self, exported):
try:
self.user = User(*USERMARK.search(exported).groups())
except:
self.user = User(exported.replace('.txt', ''), '未知的QQ号')
self.records = self.get_records(exported)
def __len__(self):
return len(self.records)
@property
def bags(self):
# 统计红包数量
return sum((record.bags for record in self.records))
@property
def begin_time(self):
# 第一条记录的发送时间
return self.records[0].sendtime
@property
def contents(self):
# 所有正文内容
return ''.join((record.content for record in self.records))
@property
def end_time(self):
# 最后一条记录的发送时间
return self.records[-1].sendtime
@property
def faces(self):
# 统计表情数量
return sum((record.faces for record in self.records))
@property
def images(self):
# 统计图片数量
return sum((record.images for record in self.records))
@property
def isgroup(self):
# 判断聊天是否为群聊,根据导出文件匹配的QQ号是否出现在了参与用户当中
return self.user.uid in USERS
@property
def links(self):
# 统计链接数量
return sum((len(record.links) for record in self.records))
@property
def uploads(self):
# 统计文件数量
return len([record for record in self.records if record.uploaded != '无'])
@property
def users(self):
# 所有聊天参与者
return USERS.values()
def get_records(self, exported):
with open(exported, encoding='utf-8') as f:
return [Record(sendtime, uin, uid, content) for sendtime, uin, uid, content in RECORDMARK.findall(f.read())]
def to_cloud(self, filename=None, background=None):
if background:
img = Image.open(background)
mask = numpy.array(img)
img.close()
else:
mask = None
filename = filename or '与{}的聊天记录词云图.jpg'.format(self.user)
content = ' '.join(jieba.cut(self.contents, cut_all=True))
cloud = WordCloud(background_color='white', font_path=FONTPATH, mask=mask).generate(content or '无记录')
if mask is not None: cloud.recolor(color_func=ImageColorGenerator(mask))
cloud.to_file(filename)
def to_file(self, filename=None):
title = '与{}的聊天记录'.format(self.user)
desc = (
'统计时间:{begin_time}至{end_time}\n'
'参与的人({users}人):{partner}\n'
'记录数量:{records}\n'
'图片数量:{images}\n'
'表情数量:{faces}\n'
'红包数量:{bags}\n'
'链接数量:{links}\n'
'文件数量:{uploads}'
).format(
begin_time = self.begin_time,
end_time = self.end_time,
users = len(self.users),
partner = '、'.join((str(user) for user in self.users)),
records = len(self),
images = self.images,
faces = self.faces,
bags = self.bags,
links = self.links,
uploads = self.uploads
)
filename = filename or '.'.join((title, 'txt'))
with open(filename, 'w', encoding='utf-8') as f:
f.write('\n\n'.join((title, desc, '\n\n'.join((str(record) for record in self.records)))))

评论列表( 0 )

你可以在登录后,对此项目发表评论