用 Python 分析所有微信好友

系统 158 0

用 Python 分析所有微信好友_第1张图片

            

Illustrations by Evgenij Kungur   文/ Python攻城狮


最近研究了一下itchat和matplotlib,目前实现了对微信好友头像、性别、区域、个性签名的采集及展示。

本文就来详细介绍一下这个库的用法和一些核心逻辑实现。

1.微信登录

              
                import itchatitchat.auto_login(hotReload=True)itchat.dump_login_status()
                

itchat.auto_login(hotReload= True )
itchat.dump_login_status()
              
                we_friend = itchat.get_friends(update=True)[:]
                
                  True
                
                )[:]
                

这里的 we_friend 是好友的信息的列表,每一个好友字典的 key 如下表

key 备注
UserName 微信系统内的用户编码标识
NickName 好友昵称
Sex 性别
Province 省份
City 城市
HeadImgUrl 微信系统内的头像URL
RemarkName 好友的备注名
Signature 个性签名

有了key对应的值,我们就好处理了。

2.好友性别

这里顺便提一下:如果sex=1则代表男性,sex=2代表女性

              
                total = len(we_friend[1:])for fri_info in we_friend[1:]:    sex = fri_info['sex']    # 如果sex=1 代表男性 sex=2代表女性    if sex == 1:        man += 1    elif sex == 2:        woman += 1    else:        other += 1
                
                  for
                
                 fri_info 
                
                  in
                
                 we_friend[1:]:
                
    sex = fri_info[ 'sex' ]
     # 如果sex=1 代表男性 sex=2代表女性
     if  sex == 1:
        man += 1
     elif  sex == 2:
        woman += 1
     else :
        other += 1

统计出男生、女生的以及总人数后,占比自然而然就出来了,为了更好的展示男女比例,我们以饼图展示。

              
                man_ratio = int(man)/total * 100woman_ratio = int(woman)/total * 100other_ratio = int(other)/total * 100plt.rcParams['font.sans-serif'] = ['SimHei']    # 用来正常显示中文标签plt.rcParams['axes.unicode_minus'] = False  # 用来正常显示负号plt.figure(figsize=(5, 5))  # 绘制的图片为正圆sex_li = ['男', '女', '其他']radius = [0.01, 0.01, 0.01]  # 设定各项距离圆心n个半径colors = ['red', 'yellowgreen', 'lightskyblue']proportion = [man_ratio, woman_ratio, other_ratio]plt.pie(proportion, explode=radius, labels=sex_li, colors=colors, autopct='%.2f%%')   # 绘制饼图# 加入图例 loc =  'upper right' 位于右上角 bbox_to_anchor=[0.5, 0.5] # 外边距 上边 右边 borderaxespad = 0.3图例的内边距plt.legend(loc="upper right", fontsize=10, bbox_to_anchor=(1.1, 1.1), borderaxespad=0.3)# 绘制标题plt.title('微信好友性别比例')    # 展示plt.show()
                
                  100
                
                
woman_ratio =  int (woman)/total *  100
other_ratio =  int (other)/total *  100

plt.rcParams[ 'font.sans-serif' ] = [ 'SimHei' ]     # 用来正常显示中文标签
plt.rcParams[ 'axes.unicode_minus' ] = False   # 用来正常显示负号
plt.figure(figsize=( 5 5 ))   # 绘制的图片为正圆
sex_li = [ '男' '女' '其他' ]
radius = [ 0.01 0.01 0.01 ]   # 设定各项距离圆心n个半径
colors = [ 'red' 'yellowgreen' 'lightskyblue' ]
proportion = [man_ratio, woman_ratio, other_ratio]

plt.pie(proportion, explode=radius, labels=sex_li, colors=colors, autopct= '%.2f%%' )    # 绘制饼图

# 加入图例 loc =  'upper right' 位于右上角 bbox_to_anchor=[0.5, 0.5] # 外边距 上边 右边 borderaxespad = 0.3图例的内边距
plt.legend(loc= "upper right" , fontsize= 10 , bbox_to_anchor=( 1.1 1.1 ), borderaxespad= 0.3 )

# 绘制标题
plt.title( '微信好友性别比例' )    

# 展示
plt.show()
用 Python 分析所有微信好友_第2张图片 微信好友性别比例

作为一个码农、程序猿,还能有这么多女性好友实属不易啊。敏感的我,看了这个比例深深地感觉到了不安,( 此图女朋友不可见 )另外,怎么还有一些未知生物的存在…


友情提醒:matplotlib中文乱码这个问题一直存在,这里记录下 如何解决matplotlib中文乱码

              
                import matplotlibprint(matplotlib.matplotlib_fname())    # 查看路径
                
print (matplotlib.matplotlib_fname())     # 查看路径
              
                font.family        : sans-seriffont.serif         : SimHei, DejaVu Serif, Bitstream Vera Serif, New Century Schoolbook, Century Schoolbook L, Utopia, ITC Bookman, Bookman, Nimbus Roman No9 L, Times New Roman, Times, Palatino, Charter, serif
                
                  .family
                
                        : 
                
                  sans-serif
                
                
font .serif          :  SimHei DejaVu   Serif Bitstream   Vera   Serif New   Century   Schoolbook Century   Schoolbook   L Utopia ITC   Bookman Bookman Nimbus   Roman   No9   L Times   New   Roman Times Palatino Charter serif
              
                在terminal中:cd ~/.cache/matplotlib把.cache下面的matplotlib文件夹删除。$ rm -rf matplotlib
                

把.cache下面的matplotlib文件夹删除。

$ rm -rf matplotlib

3.微信好友头像

这里其实看过我之前文章的应该知道,其实头像的拼接主要分为两部分

              
                import osnum = 0pwd_path = os.path.abspath(os.path.dirname(os.getcwd()))desc_photos = os.path.join(pwd_path, 'res/photos')for i in friends:    img = itchat.get_head_img(userName=i["UserName"])    file_image = open(desc_photos + "/" + str(num) + ".jpg", 'wb')    file_image.write(img)    file_image.close()    num += 1
                

num =  0
pwd_path =  os . path .abspath( os . path .dirname( os .getcwd()))
desc_photos =  os . path .join(pwd_path,  'res/photos' )
for  i  in  friends:
    img = itchat.get_head_img(userName=i[ "UserName" ])
    file_image =  open (desc_photos +  "/"  + str(num) +  ".jpg" 'wb' )
    file_image. write (img)
    file_image. close ()
    num +=  1
              
                ls = os.listdir(desc_photos)each_size = int(math.sqrt(float(640 * 640) / len(ls)))  # 算出每张图片的大小多少合适lines = int(640 / each_size)image = Image.new('RGBA', (640, 640))   # 创建640*640px的大图x = 0y = 0for i in range(0, len(ls) + 1):    try:        img = Image.open(desc_photos + "/" + str(i) + ".jpg")    except IOError:        print("Error")    else:        img = img.resize((each_size, each_size), Image.ANTIALIAS)        image.paste(img, (x * each_size, y * each_size))    # 粘贴位置        x += 1        if x == lines:  # 换行              x = 0              y += 1image.save(desc_full + "/好友头像拼接图.jpg")
                
                  # 算出每张图片的大小多少合适
                
                
lines = int(640 / each_size)
image = Image.new('RGBA', (640, 640))    # 创建640*640px的大图
x = 0
y = 0

for i in range(0, len(ls) + 1):
    try:
        img = Image.open(desc_photos +  "/"  + str(i) +  ".jpg" )
    except IOError:
        print( "Error" )
     else :
        img = img.resize((each_size, each_size), Image.ANTIALIAS)
        image.paste(img, (x * each_size, y * each_size))     # 粘贴位置
        x += 1
        if x == lines:   # 换行
              x = 0
              y += 1

image.save(desc_full +  "/好友头像拼接图.jpg" )

密集恐惧症患者请忽略!!!

用 Python 分析所有微信好友_第3张图片 好友头像拼接

4.微信好友地区分布

获取区域及城市

              
                prov_dict, city_dict = {}, {}for fri_info in we_friend[1:]:    prov = fri_info['province']    city = fri_info['city']    if prov and prov not in prov_dict.keys():        prov_dict[prov] = 1    elif prov:        prov_dict[prov] += 1    if city and city not in city_dict.keys():        city_dict[city] = 1    elif city:        city_dict[city] += 1
                
                  for
                
                 fri_info 
                
                  in
                
                 we_friend[
                
                  1
                
                :]:
                
    prov = fri_info[ 'province' ]
    city = fri_info[ 'city' ]
     if  prov  and  prov  not   in  prov_dict.keys():
        prov_dict[prov] =  1
     elif  prov:
        prov_dict[prov] +=  1
     if  city  and  city  not   in  city_dict.keys():
        city_dict[city] =  1
     elif  city:
        city_dict[city] +=  1

由于城市太多,我们取好友数量排名前十的城市及区域进行展示,感兴趣的可以稍微改下代码,就可以展示所有区域人数。

排序这里我用了Python的 sorted() 函数,列表的每个元素都为二维元组, key 参数传入了一个 lambda函数 ,其x就代表列表里的每一个元素,然后分别利用索引返回元素内的第一个和第二个元素,这就代表了 sorted() 函数利用哪一个元素进行排列。而 reverse 决定是正序还是倒序,默认为False。

              
                # 区域Top10prov_dict_top10 = sorted(prov_dict.items(), key=lambda x: x[1], reverse=True)[0:10]# 城市Top10city_dict_top10 = sorted(city_dict.items(), key=lambda y: y[1], reverse=True)[0:10]
                
prov_dict_top10  = sorted(prov_dict.items(), key=lambda x: x[ 1 ], reverse= True )[ 0 : 10 ]
# 城市Top10
city_dict_top10  = sorted(city_dict.items(), key=lambda y: y[ 1 ], reverse= True )[ 0 : 10 ]
              
                prov_nm, prov_num = [], []  # 省会名 + 数量for prov_data in prov_dict_top10:    prov_nm.append(prov_data[0])    prov_num.append(prov_data[1])pwd_path = os.path.abspath(os.path.dirname(os.getcwd()))desc_full = os.path.join(pwd_path, 'res')colors = ['#00FFFF', '#7FFFD4', '#F08080', '#90EE90', '#AFEEEE',          '#98FB98', '#B0E0E6', '#00FF7F', '#FFFF00', '#9ACD32']plt.rcParams['font.sans-serif'] = ['SimHei']  # 用来正常显示中文标签plt.rcParams['axes.unicode_minus'] = False  # 用来正常显示负号index = range(len(prov_num))plt.bar(index, prov_num, color=colors, width=0.5, align='center')plt.xticks(range(len(prov_nm)), prov_nm)  # 横坐轴标签for x, y in enumerate(prov_num):    # 在柱子上方1.2处标注值    plt.text(x, y + 1.2, '%s' % y, ha='center', fontsize=10)plt.ylabel('省会好友人数')  # 设置纵坐标标签prov_title = '微信好友区域Top10'plt.title(prov_title)    # 设置标题plt.savefig(desc_full + '/微信好友区域Top10')  # 保存图片
                
for  prov_data  in  prov_dict_top10:
    prov_nm.append(prov_data[ 0 ])
    prov_num.append(prov_data[ 1 ])

pwd_path = os.path.abspath(os.path.dirname(os.getcwd()))
desc_full = os.path.join(pwd_path,  'res' )
colors = [ '#00FFFF' '#7FFFD4' '#F08080' '#90EE90' '#AFEEEE' ,
           '#98FB98' '#B0E0E6' '#00FF7F' '#FFFF00' '#9ACD32' ]
plt.rcParams[ 'font.sans-serif' ] = [ 'SimHei' ]   # 用来正常显示中文标签
plt.rcParams[ 'axes.unicode_minus' ] =  False    # 用来正常显示负号

index = range(len(prov_num))
plt.bar(index, prov_num, color=colors, width= 0.5 , align= 'center' )

plt.xticks(range(len(prov_nm)), prov_nm)   # 横坐轴标签
for  x, y  in  enumerate(prov_num):
     # 在柱子上方1.2处标注值
    plt.text(x, y +  1.2 '%s'  % y, ha= 'center' , fontsize= 10 )
plt.ylabel( '省会好友人数' )   # 设置纵坐标标签
prov_title =  '微信好友区域Top10'
plt.title(prov_title)     # 设置标题
plt.savefig(desc_full +  '/微信好友区域Top10' )   # 保存图片
用 Python 分析所有微信好友_第4张图片 微信好友区域Top10 用 Python 分析所有微信好友_第5张图片 微信好友城市Top10

通过柱形图展示,可以清晰看到我的好友主要分布在河南和上海,借此不难推测出我的工作地址以及户籍所在地。

5.微信好友个性签名情感分析及词云图展示

这里使用了常用的中文分词库 jieba ,词云图的背景采用了萌萌哒小猪佩奇(´๑•_•๑)

              
                sign_li = []rule = re.compile("1fd+w*|[<>/=]")    # 定义正则规则for fri_info in we_friend[1:]:    signature = fri_info['signature']    if signature:        sign_deal = signature.replace('', '').replace('	', '').replace(' ', '')            .replace("span", "").replace("class", "").replace("emoji", "")        sign = rule.sub("", sign_deal)        sign_li.append(sign)
                
                  "1fd+w*|[<>/=]"
                
                )    
                
                  # 定义正则规则
                
                
for  fri_info  in  we_friend[1:]:
    signature = fri_info[ 'signature' ]
     if  signature:
        sign_deal = signature.replace( '
'
'' ).replace( ' ' '' ).replace( ' ' '' )
            .replace( "span" "" ).replace( "class" "" ).replace( "emoji" "" )
        sign = rule.sub( "" , sign_deal)
        sign_li.append(sign)
              
                pwd_path = os.path.abspath(os.path.dirname(os.getcwd()))conf_path = os.path.join(pwd_path, 'conf/')comment_txt = ''back_img = plt.imread(conf_path + '/peiqi.jpg')cloud = WordCloud(font_path=conf_path + '/simhei.ttf',  # 若是有中文的话,这句代码必须添加,不然会出现方框,不出现汉字                  background_color="white",  # 背景颜色                  max_words=5000,  # 词云显示的最大词数                  mask=back_img,  # 设置背景图片                  max_font_size=100,  # 字体最大值                  random_state=42,                  width=360, height=591, margin=2,  # 设置图片默认的大小,但是如果使用背景图片的话,保存的图片大小将会按照其大小保存,margin为词语边缘距离                  )for li in comment:    comment_txt += ' '.join(jieba.cut(li, cut_all=False))wc = cloud.generate(comment_txt)image_colors = ImageColorGenerator(back_img)plt.figure("wordc")plt.imshow(wc.recolor(color_func=image_colors))wc.to_file(res_full + '好友个性签名词云图.png')
                
comment_txt = ''
back_img = plt.imread(conf_path + '/peiqi.jpg')
cloud = WordCloud(font_path=conf_path + '/simhei.ttf',   # 若是有中文的话,这句代码必须添加,不然会出现方框,不出现汉字
                  background_color= "white" ,   # 背景颜色
                  max_words=5000,   # 词云显示的最大词数
                  mask=back_img,   # 设置背景图片
                  max_font_size=100,   # 字体最大值
                  random_state=42,
                  width=360, height=591, margin=2,   # 设置图片默认的大小,但是如果使用背景图片的话,保存的图片大小将会按照其大小保存,margin为词语边缘距离
                  )
for li in comment:
    comment_txt += ' '.join(jieba.cut(li, cut_all=False))
wc = cloud.generate(comment_txt)
image_colors = ImageColorGenerator(back_img)
plt.figure( "wordc" )
plt.imshow(wc.recolor(color_func=image_colors))
wc.to_file(res_full + '好友个性签名词云图.png')
用 Python 分析所有微信好友_第6张图片 好友个性签名词云图

最初,只想做一个简单的词云图,但是看到这个词云图中 梦想、努力、专注、尊重、希望 这个几个词以后,感觉到我的好友生活态度还是蛮积极向上的,就想不如再做一个简单的情感分析,说干就干。

              
                sentimentslist = []for li in comment:    if len(li) > 0:        s = SnowNLP(li)        print(li, s.sentiments)        sentimentslist.append(s.sentiments)fig1 = plt.figure("sentiment")plt.hist(sentimentslist, bins=np.arange(0, 1, 0.02))plt.savefig(res_full + '好友签名情感分析')plt.show()
                
                  for
                
                 li 
                
                  in
                
                 comment:
                
     if   len (li) >  0 :
        s = SnowNLP(li)
         print (li, s.sentiments)
        sentimentslist.append(s.sentiments)
fig1 = plt.figure( "sentiment" )
plt.hist(sentimentslist, bins=np.arange( 0 1 0.02 ))
plt.savefig(res_full +  '好友签名情感分析' )
plt.show()
用 Python 分析所有微信好友_第7张图片 好友签名情感分析

从图中可以看出,正向情感要远远多于负向情感的数据,积极乐观的人往往都在一个圈子,果然是物以类聚,人以群分啊。

本文完整源码

长按扫描下方二维码后回复" 微信好友 "获取

用 Python 分析所有微信好友_第8张图片

热 门 推 荐
用Python创建微信机器人

用Python机器人监听微信群聊

用Python获取摄像头并实时控制人脸

开源项目 | 用Python美化LeetCode仓库

推荐Python中文社区旗下的几个服务类公众号
征稿启事 | Python中文社区有奖征文

用 Python 分析所有微信好友_第9张图片

▼ 点击成为 社区注册会员           「在看」 一下,一起PY!


更多文章、技术交流、商务合作、联系博主

微信扫码或搜索:z360901061

微信扫一扫加我为好友

QQ号联系: 360901061

您的支持是博主写作最大的动力,如果您喜欢我的文章,感觉我的文章对您有帮助,请请扫描上面二维码支持博主1元、2元、5元等您想捐的金额吧,狠狠点击下面给点支持吧

发表我的评论
最新评论 总共0条评论