原文地址(本人):https://blog.csdn.net/a5878989/article/details/54974249

介绍

微信自动回复其实主要就是登录,接收消息,回复消息三个功能,微信没有提供公开的API,但是可以分析网页版微信通信原理,通过模拟浏览器来实现需要的功能。

下面将给出微信网页版通信原理以及Python代码。

分析

-获取uuid:

GET https://login.wx.qq.com/jslogin?appid=wx782c26e4c19acffb&redirect_uri=https%3A%2F%2Fwx.qq.com%2Fcgi-bin%2Fmmwebwx-bin%2Fwebwxnewloginpage&fun=new&lang=zh_CN&_=1486743163000

Param     _   (13位时间戳)

Response   window.QRLogin.code = 200; window.QRLogin.uuid = "4YyQFP2Daw==";

-获取二维码:

GET  https://login.weixin.qq.com/qrcode/4YyQFP2Daw==

Param 4YyQFP2Daw==  即上面的uuid

Response  二维码图片

-监听是否扫描二维码以及是否确认登录:

GET   https://login.wx.qq.com/cgi-bin/mmwebwx-bin/login?loginicon=true&uuid=4YyQFP2Daw==

Param uuid 同上

Response

window.code=200;window.redirect_uri="https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxnewloginpage?ticket=ARxD7GSdBYtNHOxhK0BF0ek-@qrticket_0&uuid=4YyQFP2Daw==&lang=zh_CN&scan=1486743186";

code:

408 无响应

201 扫描二维码但没有登录(此时响应数据中还包含用户头像图片base64编码的字符串,UserAvatar)

200 登录

redirect_uri  为接下来需要请求的地址

-获取后续访问所需要的key等

GET https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxnewloginpage?ticket=ARxD7GSdBYtNHOxhK0BF0ek-@qrticket_0&uuid=4YyQFP2Daw==&lang=zh_CN&scan=1486743186p

Param  URL为上次返回的redirect_uri  参数已经带上了

Response

<error>
   <ret>0</ret>
   <message/>
   <skey>@crypt_828c27e0_e98d62f6954235194f2b1252943f25ad</skey>
   <wxsid>0zEvAdWKm9ZZgYVn</wxsid>
   <wxuin>1564527827</wxuin>
   <pass_ticket>OLxGHwqL%2BWNArxvXaqjDy06qzdrSojq6DJwiBF19sgw2CibZSJBv1WwOXAfKnLIg</pass_ticket>
   <isgrayscale>1</isgrayscale>
< /error>

-初始化

POST https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxinit?r=-485039295&lang=zh_CN&pass_ticket=OLxGHwqL%2BWNArxvXaqjDy06qzdrSojq6DJwiBF19sgw2CibZSJBv1WwOXAfKnLIg

Param  r ( -    +  9位随机数),pass_ticket,{"BaseRequest": {"Uin": "1564527827", "Skey": "@crypt_828c27e0_e98d62f6954235194f2b1252943f25ad", "DeviceID": "e924318232435460", "Sid": "0zEvAdWKm9ZZgYVn"}} 第三个参数其中为json数据,DeviceID为(e + 15位随机数)

Response  返回json,包含用户自己的信息,最近联系人,订阅的公众号消息等等;这里只需要关注 UserName=@821c154488cdddbfb04141aa8f681174305d21d67a24cfd6eca3e77a152e52ff  每位用户都有一个UserName,但是每次登陆UserName都是重新分配的,SyncKey 为一组key ,后面接收消息需要将其作为参数,同时每次接收接收消息时,也会返回一组SyncKey作为在下一次请求的参数,以此类推

-状态检查

这里会建立一个长连接,每次连接大约20秒左右,若新消息,手机端发出退出网页登录指令,或者状态异常会返回特定的状态码

GET https://wx.qq.com/cgi-bin/mmwebwx-bin/synccheck?r=1486743215000&skey=@crypt_828c27e0_e98d62f6954235194f2b1252943f25ad&sid=0zEvAdWKm9Z

ZgYVn&uin=1564527827&deviceid=e891796429.95749&synckey=1_660530221%7C2_660530488%7C3_660530485%7C1000_1486721341&_=1486740215000

Param

r(时间戳),skey,sid,uin,deviceid,synckey(将SyncKey中的多组key 以 key1_value1|key2_value2 的形式拼接成字符串如:3_660530485|1000_1486721341),_ (时间戳)

Response

window.synccheck={retcode:"0",selector:"2"}

retcode=0 正常 ,1101 退出登录,1102 会话异常  , selector= 0 无变化 2or6 有消息

-接收消息

若状态检查到有新消息,则请求消息

POST https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxsync?sid=0zEvAdWKm9ZZgYVn&skey=@crypt_828c27e0_e98d62f6954235194f2b1252943f25ad&lang=zh_CN&pass_ticket=OLxGHwqL%2BWNArxvXaqjDy06qzdrSojq6DJwiBF19sgw2CibZSJBv1WwOXAfKnLIg

Param

sid,skey,pass_ticket 以及 json数据 {"SyncKey": {"Count": 4, "List": [{"Key": 1, "Val": 660530221}, {"Key": 2, "Val": 660530488}, {"Key": 3, "Val": 660530485}, {"Key": 1000, "Val": 1486721341}]}, "BaseRequest": {"Sid": "0zEvAdWKm9ZZgYVn", "Skey": "@crypt_828c27e0_e98d62f6954235194f2b1252943f25ad", "DeviceID": "e141257009.76972", "Uin": "1564527827"}, "rr": "-888098293"} 其中rr (- + 9位随机数)

Response

json数据包含消息的所有信息,其中关注 FromUserName=@821c154488cdddbfb04141aa8f681174305d21d67a24cfd6eca3e77a152e52ff  消息发送者以及 Content 消息内容

-发送消息

POST https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxsendmsg?lang=zh_CN&pass_ticket=0%2BoUqOWdYEen6oDVFEIv5ncIIaJcWs1LeSi69C8tUTgcp36azGAl6a8uT02PiaHu

Param   pass_ticket, json数据{"Msg": {"FromUserName": "@9e718026650771acd6d759922e000fafceaa1a5fda83aea7b3b70bc1bd6c3774", "LocalID": "14867488199507670", "ClientMsgId": "14867488199507670", "ToUserName": "@9e718026650771acd6d759922e000fafceaa1a5fda83aea7b3b70bc1bd6c3774", "Content": "消息内容", "Type": "1"}, "BaseRequest": {"Sid": "5Qn7rswOtPRHFw92", "Skey": "@crypt_828c27e0_ad386b3d4d68a282eda03d7d5b2d3104", "DeviceID": "e397471984070243", "Uin": "1564527827"}, "Scene": "0"} 其中LocalID,ClientMsgId 为13位时间戳加上5位随机数

Response  返回响应的状态码,发送成功会返回 LocalID 和 ClientMsgID

以上就是我们需要的知道的,当然其他比如读取所有联系人等都是大同小异,这里就不多赘述了。

到这里还有关键的一步,那就是如何根据收到的消息自动回复,当然是接入其他可以聊天的程序了,这里为了方便我使用的simsimi聊天机器人的外链,它不需要登录等操作,连request  header 都不用伪造^0^,直接将接受的消息post过去,将返回的消息作为微信回复消息;当然也可以接入更智能的机器人。

-获取自动回复的消息

POST http://www.niurenqushi.com/api/simsimi/

Param   txt (发送的消息)

Response  {"code":100000,"text":"消息"}

代码

运行以下代码,会自动弹出二维码图片,手机扫码登录之后开始运行,手机端发送退出登录指令时结束。

重点在于流程和思路,代码也比较糙,注释也就不加了,多指教^0dfd^

  1. # -*- coding:utf-8 -*-
  2. #author:fengw
  3. import urllib,urllib2,cStringIO,re,sys,os,cookielib,ssl,requests,time,json,random,threading,warnings
  4. from PIL import Image
  5. from matplotlib import pyplot as plt
  6. import xml.etree.cElementTree as et
  7.  
  8. reload(sys)
  9. sys.setdefaultencoding('utf-8')
  10. warnings.filterwarnings("ignore")
  11.  
  12. def get_device_id():
  13. return 'e'+str(random.random()*10000000000)[0:10]+str(random.random()*100000)[0:5]
  14. def qrcode_img():
  15. response=urllib2.urlopen(QRCODE_KEY_URL).read()
  16. p=re.compile(r'(\d+(\.\d+)?)')
  17. code=p.findall(response)[0][0]
  18. if code =='':
  19. p=re.compile(r'\"(.*)\"')
  20. qrcode_key=p.findall(response)[0]
  21. qrcode_img_url=QRCODE_IMG_BASE_URL+qrcode_key
  22. global CHECK_LOGIN_STATUS_BASE_URL
  23. CHECK_LOGIN_STATUS_BASE_URL=CHECK_LOGIN_STATUS_BASE_URL+qrcode_key
  24. qrcode_img=Image.open(cStringIO.StringIO(urllib2.urlopen(qrcode_img_url).read()))
  25. plt.ion()
  26. plt.figure()
  27. plt.imshow(qrcode_img)
  28. plt.figure()
  29. plt.close(2)
  30. else :
  31. print 'sorry,request qrcode failed...'
  32. time.sleep(2)
  33. os._exit(0)
  34.  
  35. def listen_login():
  36. run=True
  37. times=0
  38. msg='please scan the qrcode'
  39. while run:
  40. times+=1
  41. print msg
  42. response=urllib2.urlopen(CHECK_LOGIN_STATUS_BASE_URL).read()
  43. p=re.compile(r'(\d+(\.\d+)?)')
  44. code=p.findall(response)[0][0]
  45. if code=='':
  46. msg= 'please login...'
  47. plt.close()
  48. if code=='':
  49. run=False
  50. plt.close()
  51. print 'login sucess,running....'
  52. p=re.compile(r'\"(.*)\"')
  53. redirect_url=p.findall(response)[0]
  54. response=conn.get(url=redirect_url,allow_redirects=False,verify=False)
  55. msg=response.text
  56. global ret,message,skey,wxsid,wxuin,pass_ticket,isgrayscale
  57.  
  58. xml=et.fromstring(msg)
  59. ret=xml[0].text
  60. message=xml[1].text
  61. skey=xml[2].text
  62. wxsid=xml[3].text
  63. wxuin=xml[4].text
  64. pass_ticket=xml[5].text
  65. isgrayscale=xml[6].text
  66.  
  67. if times==20:
  68. run=False
  69. def update_synckey(msg):
  70. global synckey,syncheck_key
  71. synckey=str(msg['SyncKey']).replace("u'","'")
  72. for k_v in msg['SyncKey']['List']:
  73. syncheck_key+='|'+str(k_v['Key'])+'_'+str(k_v['Val'])
  74. syncheck_key=syncheck_key[1:]
  75. def wx_init():
  76. url='https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxinit?r=-485039295&lang=zh_CN&pass_ticket='+pass_ticket
  77. data={'BaseRequest':{'DeviceID':'%s'%get_device_id(),'Sid':'%s'%wxsid,'Skey':'%s'%skey,'Uin':'%s'%wxuin}}
  78. res=conn.post(url=url,headers=headers,data=json.dumps(data),verify=False)
  79. response=res.text
  80. msg=json.loads(response)
  81. global user
  82. user=msg['User']['UserName']
  83. update_synckey(msg)
  84.  
  85. def get_contact_list():
  86. base_url='https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxgetcontact?lang=zh_CN&seq=0'
  87. base_url+='&pass_ticket='+pass_ticket+'&r='+str(int(time.time())*1000)+'&skey='+skey
  88. response=urllib2.urlopen(base_url).read()
  89. data=json.loads(response)
  90. f=open(r'd:/linklist.txt','w')
  91. for friend in data['MemberList']:
  92. msg=friend['NickName']+","+friend['RemarkName']+"\n"
  93. f.write(msg.encode('utf-8'))
  94. f.close()
  95.  
  96. def get_auto_reply(send_msg):
  97. url='http://www.niurenqushi.com/api/simsimi/'
  98. data={'txt':'%s'%send_msg}
  99. res=conn.post(url=url,data=data)
  100. res.encoding='utf-8'
  101. return json.loads(res.text,'')['text']
  102.  
  103. def reply_msg(content,touser):
  104. url='https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxsendmsg?lang=zh_CN&pass_ticket='+pass_ticket
  105. #touser='filehelper'
  106. ClientMsgId=str(int(time.time()))+str(random.random()*10000000)[0:7]
  107. print 'recive msg :',content
  108. sendmsg=get_auto_reply(content)
  109. data={'BaseRequest':{'Uin':'%s'%wxuin,'Sid':'%s'%wxsid,'Skey':'%s'%skey,'DeviceID':'%s'%get_device_id()},'Msg':{'ClientMsgId':'%s'%ClientMsgId,'Content':'%s'%sendmsg.encode('utf-8'),'FromUserName':'%s'%user,'LocalID':ClientMsgId,'ToUserName':'%s'%touser,'Type':''},'Scene':''}
  110. data=json.dumps(data,ensure_ascii=False)
  111. res=conn.post(url=url,headers=headers,data=data.encode('utf-8'),verify=False)
  112. print 'reply:',sendmsg
  113. def recive_msg():
  114. base_url='https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxsync'
  115. base_url+='?sid='+wxsid+'&skey='+skey+'&lang='+'zh_CN'+'&pass_ticket='+pass_ticket
  116. while True:
  117. try:
  118. rr='-'+str(random.random()*1000000000)[0:9]
  119. data={'BaseRequest':{'Uin':'%s'%wxuin,'Sid':'%s'%wxsid,'Skey':'%s'%skey,'DeviceID':'%s'%get_device_id()},'SyncKey':eval(synckey),'rr':'%s'%rr}
  120. res=conn.post(url=base_url,headers=headers,data=json.dumps(data),verify=False)
  121. res.encoding='utf-8'
  122. response=res.text
  123. if response==None:
  124. continue
  125. data=json.loads(response)
  126. update_synckey(data)
  127. for msg in data['AddMsgList']:
  128. content=msg['Content']
  129. fromuser=msg['FromUserName']
  130. if fromuser==user:
  131. continue
  132. if content[0:4]=='<':
  133. continue
  134. #print 'recived msg:',content.decode('unicode_escape'),'from user :',fromuser
  135. threading.Thread(target=reply_msg,args=(content,fromuser)).start()
  136. time.sleep(2)
  137. except Exception as e :
  138. pass
  139.  
  140. def sync_check():
  141. listen=True
  142. base_url='https://webpush.wx.qq.com/cgi-bin/mmwebwx-bin/synccheck'
  143. base_url+='?r='+str(int(time.time())*1000)+'&skey='+skey+'&sid='+wxsid+'&uin='+wxuin+'&deviceid='+get_device_id()+'&synckey='+syncheck_key+'&_='+str(int(time.time())*1000-3000000)
  144. request = urllib2.Request(url=base_url, headers=headers)
  145. while listen:
  146. try:
  147. res=conn.get(url=base_url,headers=headers,verify=False)
  148. response=res.text
  149. p=re.compile(r'(\d+(\.\d+)?)')
  150. retcode=p.findall(response)[0][0]
  151. if retcode=='' or retcode=='':
  152. print 'login out ...'
  153. listen=False
  154. os._exit(0)
  155. time.sleep(2)
  156. except Exception:
  157. pass
  158. if __name__ == '__main__':
  159. ssl._create_default_https_context = ssl._create_unverified_context
  160. QRCODE_KEY_URL='https://login.wx.qq.com/jslogin?appid=wx782c26e4c19acffb&redirect_uri=https%3A%2F%2Fwx.qq.com%2Fcgi-bin%2Fmmwebwx-bin%2Fwebwxnewloginpage&fun=new&lang=zh_CN&_='+str(int(time.time())*1000)
  161. QRCODE_IMG_BASE_URL='https://login.weixin.qq.com/qrcode/'
  162. CHECK_LOGIN_STATUS_BASE_URL='https://login.wx.qq.com/cgi-bin/mmwebwx-bin/login?loginicon=true&uuid='
  163. ret,message,skey,wxsid,wxuin,pass_ticket,isgrayscale='','','','','','',''
  164. synckey,user='',''
  165. syncheck_key=''
  166. cookie=cookielib.CookieJar()
  167. handler=urllib2.HTTPCookieProcessor(cookie)
  168. debug_h=urllib2.HTTPSHandler(debuglevel=0)
  169. opener=urllib2.build_opener(handler,debug_h)
  170. urllib2.install_opener(opener)
  171. conn=requests.session()
  172. headers = { 'Host': 'wx.qq.com',
  173. 'Connection': 'keep-alive',
  174. 'Accept': 'application/json, text/plain, */*',
  175. 'Origin': 'https://wx.qq.com',
  176. 'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:51.0) Gecko/20100101 Firefox/51.0',
  177. 'Content-Type': 'application/json;',
  178. 'Accept-Encoding': 'gzip, deflate, br',
  179. 'Accept-Language': 'zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3'
  180. }
  181. #获取微信二维码并显示
  182. qrcode_img()
  183. #监听用户扫描二维码和登录动作
  184. listen_login()
  185. #微信初始化
  186. wx_init()
  187. #开启子线程监听登录状态
  188. check_status_task=threading.Thread(target=sync_check)
  189. check_status_task.start()
  190. #get_contact_list()
  191. #主线程监听消息
  192. recive_msg()

python 实现微信自动回复(自动聊天)的更多相关文章

  1. 10分钟教你用Python实现微信自动回复

    01 前言&&效果展示 相信大家都有忙碌的时候,不可能一直守在微信上及时回复消息.但微信又不能像QQ一样设置自动回复.无妨,今天,我们就来用Python实现微信的自动回复功能吧,并且把 ...

  2. python实现微信自动回复机器人

    一 简单介绍 wxpy基于itchat,使用了 Web 微信的通讯协议,,通过大量接口优化提升了模块的易用性,并进行丰富的功能扩展.实现了微信登录.收发消息.搜索好友.数据统计等功能. 总而言之,可用 ...

  3. python 使微信自动回复

    https://zhuanlan.zhihu.com/p/308999073 今天是鄙人的生日,欢luo过后想写点关于itchat的文章~ (不小心暴露年龄了,是的,我已经16岁了~~) 言归正传,这 ...

  4. 用python实现的一个自动聊天的机器人

    因为之前想过 如果每天早上微信能够发送天气预报给我,给我老婆多好,然后就动手看网上的教程做了一个可以定时发送天气预报的程序, 最近又想到折腾,做了一个更加详细的版本.但是需要主动操作 具体操作看图. ...

  5. 用python玩微信(聊天机器人,好友信息统计)

    1.用 Python 实现微信好友性别及位置信息统计 这里使用的python3+wxpy库+Anaconda(Spyder)开发.如果你想对wxpy有更深的了解请查看:wxpy: 用 Python 玩 ...

  6. 微信智能机器人助手,基于hook技术,自动聊天机器人

    下载地址: 链接:https://pan.baidu.com/s/1N5uQ3gaG2IZu7f6EGUmBxA 提取码:md7z 复制这段内容后打开百度网盘手机App,操作更方便哦 微信智能助手说明 ...

  7. python实现微信接口(itchat)

    python实现微信接口(itchat) 安装 sudo pip install itchat 登录 itchat.auto_login() 这种方法将会通过微信扫描二维码登录,但是这种登录的方式确实 ...

  8. wxpy: 用 Python 玩微信【转】

    转自:https://wxpy.readthedocs.io/zh/latest/index.html 微信机器人 / 可能是最优雅的微信个人号 API wxpy 在 itchat 的基础上,通过大量 ...

  9. python实现微信接口——itchat模块

    python实现微信接口——itchat模块 安装 sudo pip install itchat 登录 itchat.auto_login()  这种方法将会通过微信扫描二维码登录,但是这种登录的方 ...

随机推荐

  1. 使用sklearn估计器构建K-Means聚类模型

    实例要求:以sklearn库自带的iris数据集为例,使用sklearn估计器构建K-Means聚类模型,并且完成预测类别功能以及聚类结果可视化. 实例代码: import pandas as pd ...

  2. CentOS7编译安装MySQL5.7.24

    目录 安装依赖 安装boost 编译安装MySQL 配置 登录MySQL,修改密码 安装依赖 (1)cmake是新版MySQL的编译工具 sudo yum install gcc gcc-c++ pc ...

  3. Kali学习笔记1:Linux基本命令及安装Java

    ls -l 详细信息ls /dev/ -ls 很详细ls -a 显示隐藏ls -lh 方便看ls -lh --sort=size 按大小排序.开头的都是隐藏 cd /media/ 进入cd .. 上一 ...

  4. Java回调机制总结

    调用和回调机制 在一个应用系统中, 无论使用何种语言开发, 必然存在模块之间的调用, 调用的方式分为几种: 1.同步调用 同步调用是最基本并且最简单的一种调用方式, 类A的方法a()调用类B的方法b( ...

  5. 【面试题】java中高以上必会技能

    java基础 1.集合相关 1.1 java中常见的集合 答:Arraylist,LinkedList,ListedList,HashMap,HashSet. 1.2 arraylist和linked ...

  6. 安卓Listview和Adapter数据设计

    ListView是一种用于垂直显示的列表控件,如果显示内容过多,则会自动出现垂直滚动条,每一行是一个View对象,在每一行上可以放置任何组件,Adapter适配器是数据和UI的桥梁,为数据显示提供了统 ...

  7. Kubernetes 笔记 01 初识 Kubernetes 新时代的领航者

    本文首发于我的公众号 Linux云计算网络(id: cloud_dev),专注于干货分享,号内有 10T 书籍和视频资源,后台回复「1024」即可领取,欢迎大家关注,二维码文末可以扫. 大明王朝时期, ...

  8. odoo开发笔记--from视图隐藏顶部&tree视图保留

    场景描述: 开发过程中,有时候我们需要去除odoo自带的一些样式, 比如,form视图,要集成自定义的界面时,就希望把顶部的服务动作 和 分页按钮 隐藏掉. 处理方式: 分两种情况: 1. 保留顶部区 ...

  9. x-pack-5.6.10激活教程

    x-pack-5.6.10激活教程 简介 X-Pack 已经作为 Elastic 公司单独的产品线,前身是 Shield, Watcher, Marvel, Graph, 和 reporting,先来 ...

  10. sql server 性能调优之 资源等待PAGELATCH

    一.概述 在前几章介绍过 sql server 性能调优资源等待之PAGEIOLATCH,PAGEIOLATCH是出现在sql server要和磁盘作交互的时候,所以加个IO两个字.这次来介绍PAGE ...