一、说明
- 适用环境:Python2.7、Python3及以上版本
- itchat是一个开源的微信个人号接口,使用python调用微信从未如此简单
- 使用不到三十行的代码,就可以完成一个能够处理所有信息的微信机器人
- 先附上本文会用到的包,文章中不再做说明
#-*- coding: utf-8 -*-
import pandas as pd
import numpy as np
import plotly.offline as py # 保存图表,同于plotly.plotly as py,同时增加了离线功能
py.init_notebook_mode(connected=True) # 离线绘图时,需要额外进行初始化
import plotly.graph_objs as go # 创建各类图表
import plotly.figure_factory as ff # 创建table
from plotly import tools
import matplotlib.pyplot as plt
from pyecharts import options as opts # 配置echarts
from pyecharts.globals import ThemeType, SymbolType
from pyecharts.charts import Geo, Map, Page
from IPython.display import HTML, display
import itchat
import jieba # 中文分词
from googletrans import Translator # 翻译
translate = Translator().translate
from scipy.misc import imread # 获取图片,读为np.array的三维数组
from PIL import Image # 操作图片
from wordcloud import WordCloud # 词云
import os, math
二、安装
pip install itchat
三、登录
1. 登录
itchat.auto_login(hotReload=True, loginCallback=li, exitCallback=lo)
- 弹出二维码,手机端微信扫描登录;
- 参数hotReload=True,使程序关闭一定时间内,重新开启不用重新扫码,该方法会生成一个静态文件 itchat.pkl,用于存储登录的状态;
- 定义登录成功和退出后调用的方法,并分别赋值给参数loginCallback 和 exitCallback。若不设置 loginCallback值,会自动删除二维码图片并清空命令行显示;
def li(): print("Log In!") # 定义登录成功的提示语
def lo(): print("Log Out!") # 定义退出的提示语
2. 向文件助手发送消息
send_str_1 = "您好吖,猜猜我是谁?\n其实,我是一个机器人儿"
send_str_2 = "您好,机器人自动发消息\n收到了请忽略或任意回复"
for s in [send_str_1, send_str_2]:
itchat.send(msg=s, toUserName="filehelper")
- 效果示意图
3. 退出登录
if 1 == 1: itchat.logout() # 使用if条件,可以避免输出ItchatReturnValue提示
四、信息获取
- 主要信息包括:好友、群聊、公众号、消息等,都是以字典的形式存放的;
- 常见的操作函数:获取get_xxxx()、更新update_xxxx()、搜索search_xxxx()、添加add_xxxx()、删除delete_xxxx()、修改set_xxxx();
- 本文仅以获取函数get_xxxx()为例,进行讲解:
friends = itchat.get_friends(update=True) # 获取通讯录好友列表
contacts = itchat.get_contact(update=True) # 获取保存到通讯录的微信群
chatrooms = itchat.get_chatrooms(update=True) # 获取近期活跃的微信群
mps = itchat.get_mps() # 获取关注的微信公众号
- 获取的信息,返回的都是列表ContactList类,继承于Python内置的列表类型list;
- 返回的列表,其中的元素类型是不同的,但最上层父类都是dict类型,根据字典的特点,信息的索引主要靠key。
五、统计分析
1. 数据处理
# 获取微信好友信息
key_list = ["UserName", "NickName", "RemarkName", "Sex", "Province", "City", "Signature", "HeadImgUrl"] # 好友指标
df_friend = pd.DataFrame()
for friend in friends:
tmp = dict()
for i in friend:
if i not in key_list:
continue
tmp[i] = friend[i]
term = pd.DataFrame(tmp, index=[0])
df_friend = df_friend.append(term, sort=False)
df_friend = df_friend.reindex(columns=key_list)
# 性别转化为中文
df_friend['Sex'] = df_friend['Sex'].apply(lambda x: '男' if x == 1 else '女')
# 将英文的省份和城市,翻译成中文,若为空时转化为未知
df_friend["Province"] = df_friend["Province"].apply(lambda x: (x if not x.encode('UTF-8').isalpha() else translate(x, dest='zh-CN').text) if x != '' else '未知')
df_friend["City"] = df_friend["City"].apply(lambda x: (x if not x.encode('UTF-8').isalpha() else translate(x, dest='zh-CN').text) if x != '' else '未知')
2. 概括统计
display(HTML(f"<h3 style='color: green'>微信好友数量:{len(friends)}</h3"))
contacts_group_chat = [ql["NickName"] for ql in contacts]
display(HTML(f"<h3 style='color: green'>保存到通讯录的群聊数量:{len(contacts)}, 具体如下:</h3"))
for gc in contacts_group_chat: print(gc, end=' ')
chatrooms_group_chat = [ql["NickName"] for ql in chatrooms if ql["NickName"] not in contacts_group_chat]
display(HTML(f"<h3 style='color: green'>近期活跃的群聊数量:{len(chatrooms) - len(contacts)}, 具体如下:</h3"))
for gc in chatrooms_group_chat: print(gc, end=' ') # 排除已保存通讯录的群
display(HTML(f"<h3 style='color: green'>关注的微信公众号数量:{len(mps)}, 具体如下:</h3"))
for ql in mps: print(ql["NickName"], end=' ')
- 结果如下
3. 男女比例
# 好友性别统计
friend_sex = df_friend.groupby("Sex")["UserName"].count().to_frame().reset_index()
trace = go.Pie(labels=friend_sex.Sex, values=friend_sex.UserName, hole=.4)
layout = dict(width=850, height=450)
fig = dict(data=[trace], layout=layout)
py.iplot(fig)
- 使用plotly包画图
4. 地域分布
# 好友地域分布
friend_area = df_friend.groupby("Province")["UserName"].count().to_frame().reset_index().sort_values(by="UserName", ascending=False)
trace = go.Bar(x=friend_area.Province, y=friend_area.UserName)
layout = dict(width=900, height=450, xaxis = dict(tickangle = -45))
fig = dict(data=[trace], layout=layout)
py.iplot(fig)
- 使用plotly包画图
5. 地图分布
# 好友地图分布
china_province = pd.read_excel('province.xlsx')
china_province["Province"] = china_province.province.apply(lambda x: x[0:3] if len(x) in (4, 6) else x[0:2])
friend_area = friend_area[friend_area.Province.isin(china_province.Province)] # 排除省份为空或国外的地区
area = (Map(opts.InitOpts(width="900px", height="500px", theme=ThemeType.PURPLE_PASSION))
.add("", [list(z) for z in zip(list(friend_area.Province), list(friend_area.UserName))], maptype="china")
.set_global_opts(visualmap_opts=opts.VisualMapOpts(max_=int(np.max(friend_area.UserName)), range_text=["多", "少"], range_color=['#FFFF00', '#D6292B']))
.set_series_opts(label_opts=opts.LabelOpts(is_show=True)))
area.render_notebook()
- 使用pyecharts包画图
6. 签名词云
# 好友签名词云
background_image = imread("picture.jpeg") # 词云背景图,自选,本文选心形
sign_str = ''.join(df_friend.Signature.tolist()) # 签名转化为list
# 仅选取汉字、大小写英文字母,其它的一概忽略
jb_str = ''
for s in sign_str:
if s.isspace() or (s >= u'\u4e00' and s <= u'\u9fa5') or (s >= u'\u0041' and s <= u'\u005a') or (s >= u'\u0061' and s <= u'\u007a'):
jb_str += s
# 使用结巴分词,对签名文字进行分词,分词后排除掉单字,因为单字词云无意义
cut_str = ' '.join([jb for jb in jieba.cut(jb_str) if len(jb) > 1])
# 使用wordcloud包制作词云,可能需要单独安装汉字字体
wordcloud = WordCloud(font_path="/System/Library/Fonts/STHeiti Medium.ttc", scale=4, background_color="white", mask=background_image).generate(cut_str)
fig = plt.gcf()
fig.set_size_inches(18, 12)
plt.imshow(wordcloud, interpolation="lanczos")
plt.axis("off")
plt.show()
- 制作词云,自选背景图片,因包含汉字需要进行分词;
- 使用wordcloud包制作词云,可能需要单独安装汉字字体;
-
使用matplotlib包画图
7. 头像拼图
# 创建文件夹user_image,存储所有好友头像照片
try:
os.mkdir("user_image")
except FileExistsError:
pass
# 下载好友头像,并存储
image_dir = "./user_image/"
for k, friend in enumerate(friends):
image_name = str(k)+'.jpg'
user_name = friend["UserName"]
img = itchat.get_head_img(userName=user_name)
with open(image_dir + image_name, 'wb') as file:
file.write(img)
# 根据好友数量,设计头像拼图的最佳行列数
phone_width = 200
phone_height = 200
pic_list = [path for path in os.listdir(image_dir) if path.endswith(".jpg")] # 头像照片的文件名列表
pic_num = len(pic_list)
array = int(math.sqrt(pic_num))
if array != math.sqrt(pic_num):
if abs(array * array - pic_num) > abs((array + 1) * (array + 1) - pic_num):
array = array + 1
tem = dict()
for temp in zip(range(1, array + 1)[::-1], range(array + 1, array * 2 + 1)):
term = abs(temp[0] * temp[1] - pic_num)
if term in tem:
continue
else:
tem[term] = temp
row_col = tem[min(tem)]
else:
row_col = (array, array)
row_num = row_col[0]
col_num = row_col[1]
# 创建底图,宽为:列数*200px,高为:行数*200px
to_image = Image.new("RGBA", (phone_width * col_num, phone_height * row_num))
# 循环粘贴每一个头像照片
n = 0
for i in range(0, row_num):
for j in range(0, col_num):
if n == pic_num:
break
image_any = Image.open(image_dir + pic_list[n]) # 读取头像照片
image_any_size = image_any.resize((phone_width, phone_height)) # 设置照片大小(200px*200px)
loc = (j * phone_width, i * phone_height) # 计算照片粘贴的位置
to_image.paste(image_any_size, loc) # 粘贴照片
n += 1
# 展示好友头像拼图
fig = plt.gcf()
fig.set_size_inches(16, 9)
plt.imshow(to_image, interpolation="lanczos")
plt.axis("off")
plt.show()
-
头像拼图时的行列个数,设计思路:对好友总数开根号,若正好能开根号,则为行列个数,一般都开不尽,处理办法是寻找行列数都最优,保证拼图时空出来的位置最少
-
具体思路
1)好友总数M开根号取整后为n
2)确认n*n
和(n+1)*(n+1)
谁离总数更近(差值小),哪个值近则以该值为准;
3)计算一些列值:n*(n+1) - M、(n-1)*(n+2)-M、(n-2)*(n+3)-M.......
4)忽略正负号,求得差值最小时,对应的两个数,即为最优行列数
5)特别地,当差值最小时,存在多个对应关系时,取最后的一对
6)具体思路见代码,此处有疑问欢迎交流 - 使用matplotlib包画图
六、发送消息
- 推荐使用
send
方法,可以实现大多数发送需求 - 方法:
send(msg="Message", toUserName=None)
- 参数
文字消息使用字符串
文件使用关键字@fil@
图片使用关键字@img@
视频使用关键字@vid@
- demo
if 1==1: itchat.send(msg="Python_data_analysis", toUserName="filehelper") # 发送文字
if True: itchat.send(msg="@fil@%s" % "./Python.txt", toUserName="filehelper") # 发送文件
if True: itchat.send(msg="@img@%s" % "./picture.jpeg", toUserName="filehelper") # 发送图片
if True: itchat.send(msg="@vid@%s" % "./Tableau_introduction.mp4", toUserName="filehelper") # 发送视频
- 效果图
七、自动回复
- 实现原理:当收到微信好友发来的消息时,我们将这个消息传给图灵机器人API,它会根据消息做出答复,然后我们将这个消息返回给微信好友;
- 上图灵官网注册账号,获取机器人API的key值。不过,从2019-06-05开始,图灵平台对个人用户的调用,做了限制。未认证的用户不能调用了,个人认证后,调用次数为100次/天,不太好玩了;
- 实现步骤
定义一个从图灵机器人获取返回结果的函数
调用itchat封装好的装饰器
定义自动回复函数:可以指定聊天好友、设置默认回复
运行itchat.run()函数,开启自动回复 - 代码
def from_turing_reply(msg):
"""从图灵机器人获取回复内容"""
api_url = 'http://www.tuling123.com/openapi/api'
data = {
"key": "64496a7591a3450682188885461fc328",
"info": msg,
"user_id": "Robot"
}
try:
r = requests.post(api_url, data=data).json()
return r.get("text")
except:
return ''
@itchat.msg_register(itchat.content.TEXT)
def text_reply(msg):
"""自动文本回复"""
default_reply = "我陪主人去月球了,明天再陪你聊!"
if msg["FromUserName"] in ["@07c0724e5bb70a6e99651277c16daa66"]: # 指定好友
reply = from_turing_reply(msg["Text"])
if "当天请求次数已用完" in reply:
reply = default_reply
return reply or default_reply
itchat.run()
网友评论