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()

作為一個碼農、程序猿,還能有這么多女性好友實屬不易啊。敏感的我,看了這個比例深深地感覺到了不安,( 此圖女朋友不可見 )另外,怎么還有一些未知生物的存在…
友情提醒: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"
)
密集恐懼癥患者請忽略!!!

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'
)??
#?保存圖片


通過柱形圖展示,可以清晰看到我的好友主要分布在河南和上海,借此不難推測出我的工作地址以及戶籍所在地。
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')

最初,只想做一個簡單的詞云圖,但是看到這個詞云圖中 夢想、努力、專注、尊重、希望 這個幾個詞以后,感覺到我的好友生活態度還是蠻積極向上的,就想不如再做一個簡單的情感分析,說干就干。
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創建微信機器人
用Python機器人監聽微信群聊
用Python獲取攝像頭并實時控制人臉
開源項目 | 用Python美化LeetCode倉庫
推薦Python中文社區旗下的幾個服務類公眾號
征稿啟事 | Python中文社區有獎征文
▼ 點擊成為 社區注冊會員? ? ? ? ?? 「在看」 一下,一起PY!
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
