此文章基于Pyhon3.0以上版本对微信好友进行数据分析,这里选择的维度主要有以下几个:性别(Sex)、头像(HeadImg)、个人签名(Signature)和位置(Location),主要采用图表和词云的方式来呈现效果,其中,对于签名这块文本信息会采用jieba模块的词频分析和snownlp模块的情感分析两种方法。由于本文中用到的大多数模块都是第三方模块,故在正式开始本文章前,先简单介绍下使用到的主要的第三方模块。
第三方模块:
- itchat:微信网页版接口封装的Python版本,可用来获取微信好友信息;
- jieba:结巴分词的Python版本,可用以对文本信息进行分词处理;
- matplotlib:Python的图标绘制模块,可用以绘制柱形图、饼图等
- snownlp:Python的中文分词模块,可用以对文本信息进行情感分析
- PIL:PIL仅支持到Python2.7,适用于Python3以上的兼容版本名为PIllow,同样是Python的图像处理模块,可用以对图片进行处理
- numpy:Python的数值计算模块,可配合 wordcloud 模块使用。
- wordcloud: Python 的词云模块,可用以绘制词云图片。
安装question:
PIL目前只支持python2.x版本,不支持python3.x。
解决办法:``
Pillow是PIL的一个派生分支,但如今已经发展成为比PIL本身更具活力的图像处理库。给Python安装Pillow非常简单,使用一行代码进行pip安装即可。
pip install Pillow
安装完成后,使用from PIL import Image就可以使用Pillow模块了
如何获取微信好友数据
分析微信好友数据的前提就是获得好友信息,通过使用itchat模块,你会发现其实并不难实现。
itchat.auto_login()
friends = itchat.get_friends(update=True)
通过auto_login()命令可以在登陆的时候使用命令行显示二维码,可加参数enableCmdQR来设置二维码背景颜色,添加参数hotReload=True将自动登录,itchat.get_friends(update=True)表示获取微信好友列表的 json 数据,以字典形式返回每个好友信息,如果设置update=True将从服务器刷新列表。
分析好友性别比例
分析好友性别,首先就得获得所有好友的性别信息,需要将好友信息的Sex字段取出来,因此我们使用了lambda匿名函数器将sex选择出来,然后使用collections.Counter分别统计Male、Female和Unknow的数目,再使用item转化为可遍历的元组,之后将此三个数值组装到一个列表中,即可用matplotlib模块绘制出饼图,关键代码如下:
def analySex(friends):
total = len(friends[1:])#统计好友数量
# 将friends中的Sex信息抽取出来,map返回的是迭代器,转化为list格式
sexs=list(map(lambda x:x['Sex'],friends[1:]))
# item返回(键,值)元组,第一维为键,第二维为值,即个数
# 取性别个数变为列表
counts=list(map(lambda x:x[1],collections.Counter(sexs).items()))
lables=['Unknow','Male','Female']# 设置标签
colors=['red','yellowgreen','lightskyblue'] # 设置标签颜色
plt.figure(figsize=(8,5),dpi=80)
# 设置为仅显示一个分区,不设置的话,图可能是椭圆的
plt.axes(aspect=1)
plt.pie(counts,#性别统计结果
labels=lables,#性别展示标签
colors=colors,#饼图展示颜色
labeldistance=1.1,#标签距离圆点距离
autopct='%3.1f%%',#饼图区域文本格式
shadow=False,#饼图是否显示阴影
startangle=90,#饼图起始角度
pctdistance=0.6#饼图区域文本距离圆点距离
)
# 图例显示
plt.legend(loc='upper right',)
plt.title(u'%s的%d位微信好友性别组成' %(friends[0]['NickName'],total),fontproperties=font_set)
plt.show()
稍微补充一下,在微信性别字段中的取值分别有Unknow,Male,Female三种,其中对应的数值分别是0、1、2接着就如上面所述的一致,将数据传递给matplotlib绘制即可,这三种不同取值各自所占的比例将会由matplotlib计算得出,效果如下:
分析好友地址分布
分析好友位置,需要我们提取Province和City字段,在此之前我曾经利用pd.DataFrame(friends)查看城市的数据段,发现一共有五十来个城市,于是我估量做出来的柱形图或者饼图铁定很难看,所以我们可以选择将其存储到本地‘location.csv’文件中,再统计各地区的好友数量将其存储到’location_analysis.xls’。最后利用BDP个人版制作出可视化地图,看起来还是勉勉强强过关的,关键代码如下:
def analyloc(friends):
#用来记录对应省份,城市的人数
freqs = {}
headers = ['NickName', 'Province', 'City']
#创建csv文件
with open('location.csv', 'w', encoding='utf-8', newline='', ) as csvFile:
# 调用DictWriter方法,其中headers指定字典的key值
writer = csv.DictWriter(csvFile, headers)
writer.writeheader() # writeheader()实现添加文件头(数据名)
#循环遍历微信好友
# 设置列名NickName、Province、City并添加其中对应的信息
for friend in friends[1:]:
row = {}
row['NickName'] = friend['NickName']
row['Province'] = friend['Province']
row['City'] = friend['City']
if (friend['Province'] != None): # 统计对应城市数目
if (friend['Province'] not in freqs):
freqs[friend['Province']] = 1
else:
freqs[friend['Province']] += 1
writer.writerow(row)#写入列所对应的信息
key_list = list(freqs.keys())#城市名或者省份名
value_list = list(freqs.values()) #对应数量
book = xlwt.Workbook(encoding='utf-8')#设置编码形式为utf-8
sheet = book.add_sheet('sheet1') #添加工作表
# Province中的'0-行, 0-列'即指定表中的单元
sheet.write(0, 0, 'Province')
sheet.write(0, 1, 'Num')
for i in range(len(freqs)): #写入工作表
sheet.write(i + 1, 0, key_list[i])
sheet.write(i + 1, 1, value_list[i])
book.save('location_analysis.xls') #保存为location_analysis.xls
接下来使用BDP报表工具点击下方“数据源” ,添加数据“excel”,上传’location_analysis.xls’——新建图表——普通图表、仪表盘示例、微信运营分析——维度:province,数值:num——右侧,图表类型:地图即可。
下面就是BDP生成的微信好友位置分布图,这也反映出我的好友来自五湖四海,O(∩_∩)O哈哈~
好友个性签名文本情感及词云的制作
先获取好友列表中的个人签名信息,这里我们会对个人签名进行两种处理方式,第一种是使用结巴分词进行分词后生成词云,第二种是使用SnowNLP模块分析好友签名的感情倾向。以下是生成词云的关键代码:
def analySignature(friends):
signatures=''
emotions=[]
pattern=re.compile("1f\d.+")
for friend in friends:
signature=friend['Signature']
if(signature!=None):
# 将个性签名中可能包含的表情及符号替换
signature=signature.strip().replace('span','').replace('class','').replace('emoji','')
#将个人签名中匹配到的数字替换为空字符串
signature=re.sub(r'1f(\d.+)','',signature)
if(len(signature)>0): #进行情绪分析
nlp=SnowNLP(signature)
#添加每段个人签名经情感分析返回后的情感系数的数据
emotions.append(nlp.sentiments)
signatures+=' '.join(jieba.analyse.extract_tags(signature,5)) #关键字提取
# 通过中文分词工具 jieba 将文本分词后写入文件
with open('signatures.txt','wt',encoding='utf-8') as file:
file.write(signatures)
#生成词云图
back_coloring=np.array(Image.open('love.jpg'))
#设置词云图
wordcloud=WordCloud(
collocations=False,#是否包括两个词的搭配
font_path='simfang.ttf',#字体设置路径
background_color='white',#背景颜色
max_words=1200,# 词云显示的最大词数
mask=back_coloring,#设置词云形状
max_font_size=30,#字体最大值
random_state=30,#随机状态数
width=960,#输出宽度
height=720,#输出高度
margin=2#外边距
)
# 根据文本生成词云, 可以用generate输入全部文本(中文不好分词),
# 也可以我们计算好词频后使用generate_from_frequencies函数
wordcloud.generate(signatures)
plt.imshow(wordcloud)#绘制二维图
plt.axis("off") # 去掉坐标轴
plt.show()# 绘制词云
wordcloud.to_file('signatures.jpg')# 保存图片
这里将展示情感分析的关键代码:
#根据情绪的得分,绘制一个柱状图
count_good=len(list(filter(lambda x:x>0.66,emotions)))#只过滤出情感系数大于0.66的数字
count_normal=len(list(filter(lambda x:x>=0.33 and x<=0.66,emotions)))
count_bad=len(list(filter(lambda x:x<0.33,emotions)))
labels=[u'负面消极',u'中性',u'正面积极']
values=(count_bad,count_normal,count_good)
plt.rcParams['font.sans-serif']=['simHei']#设置字体,以免出现乱码
plt.rcParams['axes.unicode_minus']=False#用来正确显示负号
plt.xlabel(u'情感判断')#x轴出现的文本
plt.ylabel(u'频数')#y轴出现的文本
plt.xticks(range(3),labels)#为x轴刻度设置标签
plt.legend(loc='upper right',)#图例显示在右上角
plt.bar(range(3),values,color='rgb')#设置柱体的位置分布和大小,以及颜色格式。
plt.title(u'%s的微信好友签名信息情感分析'% friends[0]['NickName'],fontproperties=font_set)
plt.show()
在词云图中,我们可以发现出现频率较多的关键词有:相信、美好、快乐、奋斗等相对正面积极向上的词汇,这也说明接下来的情感分析中正面积极的将会占较大比例。
果然,我的智商一向不会让我失望。
获取好友头像并组合成自定义头像
很明显,首先我们就得获取好友的头像,然后利用Pillow模块的拼接方法进行绘图制作,这是一个相当有意思的制作过程,因为在这里我们可以用自定义图片作为主图片的最终框架,关键代码如下:
def Splicing(friends):
k = 0
for i in friends:
img = itchat.get_head_img(userName=i["UserName"]) #获取头像
#对每张头像进行略微简单的命名
fileImage = open(r'HeadImg' + "/" + str(k) + ".jpg", 'wb')
fileImage.write(img)
fileImage.close()
k += 1 #计数,方便命名,同时递增
img = Image.open('im.bmp') #头像最终组合成的图片样例
# 获取图像的灰度值范围
w = img.size[0]
h = img.size[1]
r = math.ceil(1040 / w) #最大取值
# 重新设定大小,设定ANTIALIAS,即抗锯齿
img = img.resize((int(w * r / 100) * 100, int(h * r / 100) * 100), Image.ANTIALIAS)
s = os.listdir(r'HeadImg')
c = len(s)
w = img.size[0]
h = img.size[1]
newim = Image.new('RGB', (w, h),(255,255,255)) #根据规模创建图片
k = 0
x = 0
y = 0
for i in range(0, w, 40):
for j in range(0, h, 40):
p = img.getpixel((i, j))[2] #获取像素值
if(p != 255): #如果不等于白色,进行下面操作
try:
im = Image.open(r'HeadImg' + "/" + str(k) + ".jpg")
except:
if k == c - 1:
k = 0
else:
k += 1
finally:
im = Image.open(r'HeadImg' + "/" + str(k) + ".jpg")
im = im.resize((40, 40), Image.ANTIALIAS)#设置每张头像图片尺寸为40*40px
newim.paste(im, (i, j)) #图片粘贴的位置
if k == c - 1:
k = 0
else:
k += 1
newim.save(r'HeadImg' + "/" + "all.bmp") #保存图片
itchat.send_image(r'HeadImg' + "/" + "all.bmp", 'filehelper')#向文件助手发送该图片
当当当!展示下效果,是不是很骚O(∩_∩)O
交互界面
最后,我们可以使用Python中的内置图形界面tkinter模块,简单设置下基础界面,通过确定按钮的点击事件,使得调用函数的过程更加形象,由于该窗口过于简单,我就不一一介绍其中的代码了,关键代码如下:
#搭建界面
root = Tk()#创建窗口
root.title("Wechat爬好友数据")#补充窗口标题
root.geometry("500x350")#设置窗口大小
root.resizable(False, False)#固定窗口大小
#标签控件
label=Label(root,text=" 昵称:",font=('宋体',12))
label.place(x=60,y=37)
#显示框
text1=Text(root,font=('宋体',15),height=1,width=20)
text1.place(x=150,y=33)
text1.insert(1.0,friends[0]['NickName'])
#按钮
button1=Button(root,text="性别分析",font=('微软雅黑',10),command=analySex)
button1.place(x=50,y=90)
button2=Button(root,text="地理分布",font=('微软雅黑',10),command=analyloc)
button2.place(x=150,y=90)
button3=Button(root,text="词云图",font=('微软雅黑',10),command=analySignature)
button3.place(x=240,y=90)
button4=Button(root,text="情感分析",font=('微软雅黑',10),command=analyemotion)
button4.place(x=320,y=90)
button5=Button(root,text="拼接头像",font=('微软雅黑',10),command=Splicing)
button5.place(x=420,y=90)
button1=Button(root,text=" 退出 ",font=('微软雅黑',14),command=root.quit)
button1.place(x=200,y=150)
root.mainloop()##让根窗口进入事件循环
看起来还是勉勉强强可以接受的!