公众号接口

1. 公众号消息会话

目前公众号内主要有这样几类消息服务的类型,分别用于不同的场景。

群发消息

公众号可以以一定频次(订阅号为每天1次,服务号为每月4次),向用户群发消息,包括文字消息、图文消息、图片、视频、语音等。

被动回复消息

在用户给公众号发消息后,微信服务器会将消息发到开发者预先在开发者中心设置的服务器地址(开发者需要进行消息真实性验证),公众号可以在5秒内做出回复,可以回复一个消息,也可以回复命令告诉微信服务器这条消息暂不回复。被动回复消息可以设置加密(在公众平台官网的开发者中心处设置,设置后,按照消息加解密文档来进行处理。其他3种消息的调用因为是API调用而不是对请求的返回,所以不需要加解密)。

客服消息

在用户给公众号发消息后的48小时内,公众号可以给用户发送不限数量的消息,主要用于客服场景。用户的行为会触发事件推送,某些事件推送是支持公众号据此发送客服消息的,详见微信推送消息与事件说明文档。

模板消息

在需要对用户发送服务通知(如刷卡提醒、服务预约成功通知等)时,公众号可以用特定内容模板,主动向用户发送消息。

2. 公众号内网页

对于公众号内网页,提供以下场景接口:

网页授权获取用户基本信息

通过该接口,可以获取用户的基本信息

微信JS-SDK

是开发者在网页上通过JavaScript代码使用微信原生功能的工具包,开发者可以使用它在网页上录制和播放微信语音、监听微信分享、上传手机本地图片、拍照等许多能力。

3. 微信开发者文档

微信开发者文档网址 https://mp.weixin.qq.com/wiki/home/index.html

接入微信公众平台

接入微信公众平台开发,开发者需要按照如下步骤完成:

  1. 填写服务器配置
  2. 验证服务器地址的有效性
  3. 依据接口文档实现业务逻辑

填写服务器配置

登录微信公众平台官网后,在公众平台后台管理页面 - 开发者中心页,点击“修改配置”按钮,填写服务器地址(URL)、Token和EncodingAESKey,其中URL是开发者用来接收微信消息和事件的接口URL。Token可由开发者可以任意填写,用作生成签名(该Token会和接口URL中包含的Token进行比对,从而验证安全性)。EncodingAESKey由开发者手动填写或随机生成,将用作消息体加解密密钥。

同时,开发者可选择消息加解密方式:明文模式、兼容模式和安全模式。模式的选择与服务器配置在提交后都会立即生效,请开发者谨慎填写及选择。加解密方式的默认状态为明文模式,选择兼容模式和安全模式需要提前配置好相关加解密代码,详情请参考消息体签名及加解密部分的文档。

微信公众号接口只支持80接口。

公众平台页面

利用测试平台

测试平台登陆地址 http://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=sandbox/login

配置阿里服务器nginx

在nginx添加以下配置

vi /etc/nginx/sites-available/default

添加以下配置实现80端口的转发

        location /weixin { proxy_pass http://127.0.0.1:8000;}  

http://47.95.8.70/weixin

验证服务器地址的有效性

开发者提交信息后,微信服务器将发送GET请求到填写的服务器地址URL上,GET请求携带四个参数:

开发者通过检验signature对请求进行校验。若确认此次GET请求来自微信服务器,请原样返回echostr参数内容,则接入生效,成为开发者成功,否则接入失败。

校验流程:

  1. 将token、timestamp、nonce三个参数进行字典序排序
  2. 将三个参数字符串拼接成一个字符串进行sha1加密
  3. 开发者获得加密后的字符串可与signature对比,标识该请求来源于微信

Python代码实现(以Flask框架为例):

# -*- coding:utf-8 -*-from flask import Flask, request, make_response
import hashlib
import xmltodict
import timeapp = Flask(__name__)WECHAT_TOKEN = "zhangbiao"@app.route('/weixin', methods=['GET', 'POST'])
def wechat():args = request.argsprint argssignature = args.get('signature')timestamp = args.get('timestamp')nonce = args.get('nonce')echostr = args.get('echostr')# 1. 将token、timestamp、nonce三个参数进行字典序排序temp = [WECHAT_TOKEN, timestamp, nonce]temp.sort()# 2. 将三个参数字符串拼接成一个字符串进行sha1加密temp = "".join(temp)# sig是我们计算出来的签名结果sig = hashlib.sha1(temp).hexdigest()# 3. 开发者获得加密后的字符串可与signature对比,标识该请求来源于微信if sig == signature:# 根据请求方式.返回不同的内容 ,如果是get方式,代表是验证服务器有效性# 如果POST方式,代表是微服务器转发给我们的消息if request.method == "GET":return echostrelse:return 'errno', 403if __name__ == '__main__':app.run(host='0.0.0.0',port=8000)

运行上述的代码后,再点击提交,测试就会通过

公众号接收与发送消息

验证URL有效性成功后即接入生效,成为开发者。如果公众号类型为服务号(订阅号只能使用普通消息接口),可以在公众平台网站中申请认证,认证成功的服务号将获得众多接口权限,以满足开发者需求。

此后用户每次向公众号发送消息、或者产生自定义菜单点击事件时,开发者填写的服务器配置URL将得到微信服务器推送过来的消息和事件,然后开发者可以依据自身业务逻辑进行响应,例如回复消息等。

用户向公众号发送消息时,公众号方收到的消息发送者是一个OpenID,是使用用户微信号加密后的结果,每个用户对每个公众号有一个唯一的OpenID。

接收普通消息

当普通微信用户向公众账号发消息时,微信服务器将POST消息的XML数据包到开发者填写的URL上。

微信服务器在五秒内收不到响应会断掉连接,并且重新发起请求,总共重试三次。假如服务器无法保证在五秒内处理并回复,可以直接回复空串,微信服务器不会对此作任何处理,并且不会发起重试。

各消息类型的推送使用XML数据包结构,如:

<xml>
<ToUserName><![CDATA[gh_866835093fea]]></ToUserName>
<FromUserName><![CDATA[ogdotwSc_MmEEsJs9-ABZ1QL_4r4]]></FromUserName>
<CreateTime>1478317060</CreateTime>
<MsgType><![CDATA[text]]></MsgType>
<Content><![CDATA[你好]]></Content>
<MsgId>6349323426230210995</MsgId>
</xml>

  

注意:<![CDATA 与 ]]> 括起来的数据不会被xml解析器解析。

xmltodict 模块基本用法

xmltodict 是一个用来处理xml数据的很方便的模块。包含两个常用方法parse和unparse

1. parse

xmltodict.parse()方法可以将xml数据转为python中的dict字典数据:

>>> import xmltodict
>>> xml_str = """
... <xml>
... <ToUserName><![CDATA[gh_866835093fea]]></ToUserName>
... <FromUserName><![CDATA[ogdotwSc_MmEEsJs9-ABZ1QL_4r4]]></FromUserName>
... <CreateTime>1478317060</CreateTime>
... <MsgType><![CDATA[text]]></MsgType>
... <Content><![CDATA[你好]]></Content>
... <MsgId>6349323426230210995</MsgId>
... </xml>
... """
>>>
>>> xml_dict = xmltodict.parse(xml_str)
>>> type(xml_dict)
<class 'collections.OrderedDict'>  # 类字典型,可以按照字典方法操作
>>>
>>> xml_dict
OrderedDict([(u'xml', OrderedDict([(u'ToUserName', u'gh_866835093fea'), (u'FromUserName', u'ogdotwSc_MmEEsJs9-ABZ1QL_4r4'), (u'CreateTime', u'1478317060'), (u'MsgType', u'text'), (u'Content', u'\u4f60\u597d'), (u'MsgId', u'6349323426230210995')]))])
>>>
>>> xml_dict['xml']
OrderedDict([(u'ToUserName', u'gh_866835093fea'), (u'FromUserName', u'ogdotwSc_MmEEsJs9-ABZ1QL_4r4'), (u'CreateTime', u'1478317060'), (u'MsgType', u'text'), (u'Content', u'\u4f60\u597d'), (u'MsgId', u'6349323426230210995')])
>>>
>>> for key, val in xml_dict['xml'].items():
...     print key, "=", val
...
ToUserName = gh_866835093fea
FromUserName = ogdotwSc_MmEEsJs9-ABZ1QL_4r4
CreateTime = 1478317060
MsgType = text
Content = 你好
MsgId = 6349323426230210995
>>>

2. unparse

xmltodict.unparse()方法可以将字典转换为xml字符串:

xml_dict = {"xml": {"ToUserName" : "gh_866835093fea","FromUserName" : "ogdotwSc_MmEEsJs9-ABZ1QL_4r4","CreateTime" : "1478317060","MsgType" : "text","Content" : u"你好","MsgId" : "6349323426230210995",}
}>>> xml_str = xmltodict.unparse(xml_dict)
>>> print xml_str
<?xml version="1.0" encoding="utf-8"?>
<xml><FromUserName>ogdotwSc_MmEEsJs9-ABZ1QL_4r4</FromUserName><MsgId>6349323426230210995</MsgId><ToUserName>gh_866835093fea</ToUserName><Content>你好</Content><MsgType>text</MsgType><CreateTime>1478317060</CreateTime></xml>
>>>
>>> xml_str = xmltodict.unparse(xml_dict, pretty=True) # pretty表示友好输出
>>> print xml_str
<?xml version="1.0" encoding="utf-8"?>
<xml><FromUserName>ogdotwSc_MmEEsJs9-ABZ1QL_4r4</FromUserName><MsgId>6349323426230210995</MsgId><ToUserName>gh_866835093fea</ToUserName><Content>你好</Content><MsgType>text</MsgType><CreateTime>1478317060</CreateTime>
</xml>
>>>

  

普通消息类别

  1. 文本消息
  2. 图片消息
  3. 语音消息
  4. 视频消息
  5. 小视频消息
  6. 地理位置消息
  7. 链接消息

  

文本消息

 <xml><ToUserName><![CDATA[toUser]]></ToUserName><FromUserName><![CDATA[fromUser]]></FromUserName> <CreateTime>1348831860</CreateTime><MsgType><![CDATA[text]]></MsgType><Content><![CDATA[this is a test]]></Content><MsgId>1234567890123456</MsgId></xml>

  

被动回复消息

当用户发送消息给公众号时(或某些特定的用户操作引发的事件推送时),会产生一个POST请求,开发者可以在响应包中返回特定XML结构,来对该消息进行响应(现支持回复文本、图片、图文、语音、视频、音乐)。严格来说,发送被动响应消息其实并不是一种接口,而是对微信服务器发过来消息的一次回复。

假如服务器无法保证在五秒内处理并回复,必须做出下述回复,这样微信服务器才不会对此作任何处理,并且不会发起重试(这种情况下,可以使用客服消息接口进行异步回复),否则,将出现严重的错误提示。详见下面说明:

  1. (推荐方式)直接回复success
  2. 直接回复空串(指字节长度为0的空字符串,而不是XML结构体中content字段的内容为空)

一旦遇到以下情况,微信都会在公众号会话中,向用户下发系统提示“该公众号暂时无法提供服务,请稍后再试”:

  1. 开发者在5秒内未回复任何内容
  2. 开发者回复了异常数据,比如JSON数据等

回复的消息类型

  1. 文本消息
  2. 图片消息
  3. 语音消息
  4. 视频消息
  5. 音乐消息
  6. 图文消息

回复文本消息

<xml>
<ToUserName><![CDATA[toUser]]></ToUserName>
<FromUserName><![CDATA[fromUser]]></FromUserName>
<CreateTime>12345678</CreateTime>
<MsgType><![CDATA[text]]></MsgType>
<Content><![CDATA[你好]]></Content>
</xml>

代码实现

我们现在来实现一个针对文本消息的收发程序。实现的业务逻辑类似与“鹦鹉学舌”,粉丝发什么内容,我们就传回给粉丝什么内容。

# -*- coding:utf-8 -*-from flask import Flask, request, make_response
import hashlib
import xmltodict
import timeapp = Flask(__name__)WECHAT_TOKEN = "zhangbiao"@app.route('/weixin', methods=['GET', 'POST'])
def wechat():args = request.argsprint argssignature = args.get('signature')timestamp = args.get('timestamp')nonce = args.get('nonce')echostr = args.get('echostr')# 1. 将token、timestamp、nonce三个参数进行字典序排序temp = [WECHAT_TOKEN, timestamp, nonce]temp.sort()# 2. 将三个参数字符串拼接成一个字符串进行sha1加密temp = "".join(temp)# sig是我们计算出来的签名结果sig = hashlib.sha1(temp).hexdigest()# 3. 开发者获得加密后的字符串可与signature对比,标识该请求来源于微信if sig == signature:# 根据请求方式.返回不同的内容 ,如果是get方式,代表是验证服务器有效性# 如果POST方式,代表是微服务器转发给我们的消息if request.method == "GET":return echostrelse:resp_data = request.dataresp_dict = xmltodict.parse(resp_data).get('xml')print resp_dict# 如果是文本消息if 'text' == resp_dict.get('MsgType'):response = {"ToUserName": resp_dict.get('FromUserName'),"FromUserName": resp_dict.get('ToUserName'),"CreateTime": int(time.time()),"MsgType": "text","Content": resp_dict.get('Content'),}print resp_dict.get('Content')else:response = {"ToUserName": resp_dict.get('FromUserName'),"FromUserName": resp_dict.get('ToUserName'),"CreateTime": int(time.time()),"MsgType": "text","Content": u"哈哈哈哈",}if response:response = {"xml": response}response = xmltodict.unparse(response)else:response = ''return make_response(response)else:return 'errno', 403if __name__ == '__main__':app.run(host='0.0.0.0',port=8000)

tasks.py

有趣的表情

QQ表情

实际是字符串转义,如 /::D/::P 等,仍属于文本信息。

emoji

绘文字(日语:絵文字/えもじ emoji)是日本在无线通信中所使用的视觉情感符号,绘意指图形,文字则是图形的隐喻,可用来代表多种表情,如笑脸表示笑、蛋糕表示食物等。

在NTTDoCoMo的i-mode系统电话系统中,绘文字的尺寸是12x12 像素,在传送时,一个图形有2个字节。Unicode编码为E63E到E757,而在Shift-JIS编码则是从F89F到F9FC。基本的绘文字共有176个符号,在C-HTML4.0的编程语言中,则另增添了76个情感符号。

最早由栗田穰崇(Shigetaka Kurita)创作,并在日本网络及手机用户中流行。

自苹果公司发布的iOS 5输入法中加入了emoji后,这种表情符号开始席卷全球,目前emoji已被大多数现代计算机系统所兼容的Unicode编码采纳,普遍应用于各种手机短信和社交网络中。

本质是Unicode字符,也属于文本消息。

自定表情

微信的自定义表情不是文本,也不是图片,而是一种不支持的格式,微信未提供处理此消息的接口。

接收其他普通消息

接收图片消息

<xml>
<ToUserName><![CDATA[toUser]]></ToUserName>
<FromUserName><![CDATA[fromUser]]></FromUserName>
<CreateTime>1348831860</CreateTime>
<MsgType><![CDATA[image]]></MsgType>
<PicUrl><![CDATA[this is a url]]></PicUrl>
<MediaId><![CDATA[media_id]]></MediaId>
<MsgId>1234567890123456</MsgId>
</xml>

  

参数 描述
ToUserName 开发者微信号
FromUserName 发送方帐号(一个OpenID)
CreateTime 消息创建时间 (整型)
MsgType image
PicUrl 图片链接
MediaId 图片消息媒体id,可以调用多媒体文件下载接口拉取数据。
MsgId 消息id,64位整型

接收视频消息

<xml>
<ToUserName><![CDATA[toUser]]></ToUserName>
<FromUserName><![CDATA[fromUser]]></FromUserName>
<CreateTime>1357290913</CreateTime>
<MsgType><![CDATA[video]]></MsgType>
<MediaId><![CDATA[media_id]]></MediaId>
<ThumbMediaId><![CDATA[thumb_media_id]]></ThumbMediaId>
<MsgId>1234567890123456</MsgId>
</xml>

接收小视频消息

<xml>
<ToUserName><![CDATA[toUser]]></ToUserName>
<FromUserName><![CDATA[fromUser]]></FromUserName>
<CreateTime>1357290913</CreateTime>
<MsgType><![CDATA[shortvideo]]></MsgType>
<MediaId><![CDATA[media_id]]></MediaId>
<ThumbMediaId><![CDATA[thumb_media_id]]></ThumbMediaId>
<MsgId>1234567890123456</MsgId>
</xml>

接收语音消息

<xml>
<ToUserName><![CDATA[toUser]]></ToUserName>
<FromUserName><![CDATA[fromUser]]></FromUserName>
<CreateTime>1357290913</CreateTime>
<MsgType><![CDATA[voice]]></MsgType>
<MediaId><![CDATA[media_id]]></MediaId>
<Format><![CDATA[Format]]></Format>
<MsgId>1234567890123456</MsgId>
</xml>

请注意,开通语音识别后,用户每次发送语音给公众号时,微信会在推送的语音消息XML数据包中,增加一个Recognition字段(注:由于客户端缓存,开发者开启或者关闭语音识别功能,对新关注者立刻生效,对已关注用户需要24小时生效。开发者可以重新关注此帐号进行测试)。开启语音识别后的语音XML数据包如下:

<xml>
<ToUserName><![CDATA[toUser]]></ToUserName>
<FromUserName><![CDATA[fromUser]]></FromUserName>
<CreateTime>1357290913</CreateTime>
<MsgType><![CDATA[voice]]></MsgType>
<MediaId><![CDATA[media_id]]></MediaId>
<Format><![CDATA[Format]]></Format>
<Recognition><![CDATA[腾讯微信团队]]></Recognition>
<MsgId>1234567890123456</MsgId>
</xml>

  

多出的字段中,Format为语音格式,一般为amr,Recognition为语音识别结果(把语音转换成了文字),使用UTF8编码。  

回复其他普通消息

回复图片消息

<xml>
<ToUserName><![CDATA[toUser]]></ToUserName>
<FromUserName><![CDATA[fromUser]]></FromUserName>
<CreateTime>12345678</CreateTime>
<MsgType><![CDATA[image]]></MsgType>
<Image>
<MediaId><![CDATA[media_id]]></MediaId>
</Image>
</xml>

  

回复视频消息

<xml>
<ToUserName><![CDATA[toUser]]></ToUserName>
<FromUserName><![CDATA[fromUser]]></FromUserName>
<CreateTime>12345678</CreateTime>
<MsgType><![CDATA[video]]></MsgType>
<Video>
<MediaId><![CDATA[media_id]]></MediaId>
<Title><![CDATA[title]]></Title>
<Description><![CDATA[description]]></Description>
</Video>
</xml>

  

回复用户语音消息识别(代码实现)

把语音的消息转换成文字返回

# -*- coding:utf-8 -*-from flask import Flask, request, make_response
import hashlib
import xmltodict
import timeapp = Flask(__name__)WECHAT_TOKEN = "zhangbiao"@app.route('/weixin', methods=['GET', 'POST'])
def wechat():args = request.argsprint argssignature = args.get('signature')timestamp = args.get('timestamp')nonce = args.get('nonce')echostr = args.get('echostr')# 1. 将token、timestamp、nonce三个参数进行字典序排序temp = [WECHAT_TOKEN, timestamp, nonce]temp.sort()# 2. 将三个参数字符串拼接成一个字符串进行sha1加密temp = "".join(temp)# sig是我们计算出来的签名结果sig = hashlib.sha1(temp).hexdigest()# 3. 开发者获得加密后的字符串可与signature对比,标识该请求来源于微信if sig == signature:# 根据请求方式.返回不同的内容 ,如果是get方式,代表是验证服务器有效性# 如果POST方式,代表是微服务器转发给我们的消息if request.method == "GET":return echostrelse:resp_data = request.dataresp_dict = xmltodict.parse(resp_data).get('xml')if 'voice' == resp_dict.get('MsgType'):print resp_datares = resp_dict.get('Recognition') or u'未识别'response = {"ToUserName": resp_dict.get('FromUserName'),"FromUserName": resp_dict.get('ToUserName'),"CreateTime": int(time.time()),"MsgType": "text","Content": res}else:response = {"ToUserName": resp_dict.get('FromUserName'),"FromUserName": resp_dict.get('ToUserName'),"CreateTime": int(time.time()),"MsgType": "text","Content": u"哈哈哈哈",}if response:print 123response = {"xml": response}print 456response = xmltodict.unparse(response)print 789else:response = ''return make_response(response)else:return 'errno', 403if __name__ == '__main__':app.run(host='0.0.0.0',port=8000)voice.py

voice.py

关注/取消关注事件

用户在关注与取消关注公众号时,微信会把这个事件推送到开发者填写的URL。

微信服务器在五秒内收不到响应会断掉连接,并且重新发起请求,总共重试三次。

假如服务器无法保证在五秒内处理并回复,可以直接回复空串,微信服务器不会对此作任何处理,并且不会发起重试。

<xml>
<ToUserName><![CDATA[toUser]]></ToUserName>
<FromUserName><![CDATA[FromUser]]></FromUserName>
<CreateTime>123456789</CreateTime>
<MsgType><![CDATA[event]]></MsgType>
<Event><![CDATA[subscribe]]></Event>
</xml>

关注成功后,返回感谢关注

# -*- coding:utf-8 -*-from flask import Flask, request, make_response
import hashlib
import xmltodict
import timeapp = Flask(__name__)WECHAT_TOKEN = "zhangbiao"@app.route('/weixin', methods=['GET', 'POST'])
def wechat():args = request.argsprint argssignature = args.get('signature')timestamp = args.get('timestamp')nonce = args.get('nonce')echostr = args.get('echostr')# 1. 将token、timestamp、nonce三个参数进行字典序排序temp = [WECHAT_TOKEN, timestamp, nonce]temp.sort()# 2. 将三个参数字符串拼接成一个字符串进行sha1加密temp = "".join(temp)# sig是我们计算出来的签名结果sig = hashlib.sha1(temp).hexdigest()# 3. 开发者获得加密后的字符串可与signature对比,标识该请求来源于微信if sig == signature:# 根据请求方式.返回不同的内容 ,如果是get方式,代表是验证服务器有效性# 如果POST方式,代表是微服务器转发给我们的消息if request.method == "GET":return echostrelse:resp_data = request.dataresp_dict = xmltodict.parse(resp_data).get('xml')print resp_dict.get('MsgType')if "event" == resp_dict.get('MsgType'):if "subscribe" == resp_dict.get("Event"):response = {"ToUserName": resp_dict.get("FromUserName", ""),"FromUserName": resp_dict.get("ToUserName", ""),"CreateTime": int(time.time()),"MsgType": "text","Content": u"感谢您的关注!"}else:response = Noneelse:response = {"ToUserName": resp_dict.get('FromUserName'),"FromUserName": resp_dict.get('ToUserName'),"CreateTime": int(time.time()),"MsgType": "text","Content": u"哈哈哈哈",}if response:response = {"xml": response}response = xmltodict.unparse(response)else:response = ''return make_response(response)else:return 'errno', 403if __name__ == '__main__':app.run(host='0.0.0.0',port=8000)

focus.py  

获取接口调用凭据

access_token是公众号的全局唯一票据,公众号调用各接口时都需使用access_token。开发者需要进行妥善保存。access_token的存储至少要保留512个字符空间。access_token的有效期目前为2个小时,需定时刷新,重复获取将导致上次获取的access_token失效。

接口说明

请求方法

https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET

参数说明

错误时微信会返回JSON数据包如下:

{"errcode":40013,"errmsg":"invalid appid"
}

  

代码实现

# -*- coding:utf-8 -*-
import time
import urllib2
import jsonfrom flask import Flask, requestWECHAT_APPID = ""
WECHAT_APPSECRET = ""class AccessToken(object):"""
    获取accessToken保存accessToken判断是否过期,如果没有过期,那么直接返回一次请求的access_token"""
    access_token = {"access_token": "","update_time": time.time(),"expires_in": 7200}@classmethoddef get_access_token(cls):# 判断是否有accessToken or access_token 有没有过期# if 没有 access_tokon 或者 access_token 过期了:if not cls.access_token.get('access_token') or (time.time() - cls.access_token.get('update_time')) > cls.access_token.get('expires_in'):# 去获取accessTokenurl = 'https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s' % (WECHAT_APPID, WECHAT_APPSECRET)# 获取响应response = urllib2.urlopen(url).read()# 转成字典resp_json = json.loads(response)if 'errcode' in resp_json:raise Exception(resp_json.get('errmsg'))else:# 保存数据cls.access_token['access_token'] = resp_json.get('access_token')cls.access_token['expires_in'] = resp_json.get('expires_in')cls.access_token['update_time'] = time.time()return cls.access_token.get('access_token')else:return cls.access_token.get('access_token')if __name__ == '__main__':print AccessToken.get_access_token()

gentate_token

带参数的二维码

为了满足用户渠道推广分析和用户帐号绑定等场景的需要,公众平台提供了生成带参数二维码的接口。使用该接口可以获得多个带不同场景值的二维码,用户扫描后,公众号可以接收到事件推送。

目前有2种类型的二维码:

  1. 临时二维码,是有过期时间的,最长可以设置为在二维码生成后的30天(即2592000秒)后过期,但能够生成较多数量。临时二维码主要用于帐号绑定等不要求二维码永久保存的业务场景

  2. 永久二维码,是无过期时间的,但数量较少(目前为最多10万个)。永久二维码主要用于适用于帐号绑定、用户来源统计等场景。

获取带参数的二维码的过程包括两步,首先创建二维码ticket,然后凭借ticket到指定URL换取二维码。

创建二维码ticket

每次创建二维码ticket需要提供一个开发者自行设定的参数(scene_id),分别介绍临时二维码和永久二维码的创建二维码ticket过程。

临时二维码请求说明

http请求方式: POST
URL: https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token=TOKEN
POST数据格式:json
POST数据例子:{"expire_seconds": 604800, "action_name": "QR_SCENE", "action_info": {"scene": {"scene_id": 123}}}

永久二维码请求说明

http请求方式: POST
URL: https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token=TOKEN
POST数据格式:json
POST数据例子:{"action_name": "QR_LIMIT_SCENE", "action_info": {"scene": {"scene_id": 123}}}
或者也可以使用以下POST数据创建字符串形式的二维码参数:
{"action_name": "QR_LIMIT_STR_SCENE", "action_info": {"scene": {"scene_str": "123"}}}

  

返回说明

正确的Json返回结果:

{"ticket":"gQH47joAAAAAAAAAASxodHRwOi8vd2VpeGluLnFxLmNvbS9xL2taZ2Z3TVRtNzJXV1Brb3ZhYmJJAAIEZ23sUwMEmm3sUw==","expire_seconds":60,"url":"http:\/\/weixin.qq.com\/q\/kZgfwMTm72WWPkovabbI"}

  

错误的Json返回示例:

{"errcode":40013,"errmsg":"invalid appid"}

通过ticket换取二维码

获取二维码ticket后,开发者可用ticket换取二维码图片。请注意,本接口无须登录态即可调用。

请求说明

HTTP GET请求(请使用https协议)
https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket=TICKET

代码实例

# -*- coding:utf-8 -*-
import time
import urllib2
import jsonfrom flask import Flask, requestWECHAT_APPID = ""
WECHAT_APPSECRET = ""class AccessToken(object):"""
    获取accessToken保存accessToken判断是否过期,如果没有过期,那么直接返回一次请求的access_token"""
    access_token = {"access_token": "","update_time": time.time(),"expires_in": 7200}@classmethoddef get_access_token(cls):# 判断是否有accessToken or access_token 有没有过期# if 没有 access_tokon 或者 access_token 过期了:if not cls.access_token.get('access_token') or (time.time() - cls.access_token.get('update_time')) > cls.access_token.get('expires_in'):# 去获取accessTokenurl = 'https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s' % (WECHAT_APPID, WECHAT_APPSECRET)# 获取响应response = urllib2.urlopen(url).read()# 转成字典resp_json = json.loads(response)if 'errcode' in resp_json:raise Exception(resp_json.get('errmsg'))else:# 保存数据cls.access_token['access_token'] = resp_json.get('access_token')cls.access_token['expires_in'] = resp_json.get('expires_in')cls.access_token['update_time'] = time.time()return cls.access_token.get('access_token')else:return cls.access_token.get('access_token')app = Flask(__name__)# http://127.0.0.1/get_qrcode?id=1
@app.route('/get_qrcode')
def get_qrcode():scene_id = request.args.get('id')access_token = AccessToken.get_access_token()url = "https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token=%s" % access_tokenparams = {"expire_seconds": 604800,"action_name": "QR_SCENE","action_info": {"scene": {"scene_id": scene_id}}}response = urllib2.urlopen(url, data=json.dumps(params)).read()# 转成字典resp_json = json.loads(response)ticket = resp_json.get('ticket')if ticket:return '<img src="https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket=%s">' % ticketelse:return resp_jsonif __name__ == '__main__':app.run(host='0.0.0.0')

get_qrcode.py

扫描带参数二维码

用户扫描带场景值二维码时,可能推送以下两种事件:

  • 如果用户还未关注公众号,则用户可以关注公众号,关注后微信会将带场景值关注事件推送给开发者。

  • 如果用户已经关注公众号,则微信会将带场景值扫描事件推送给开发者。

1. 用户未关注时,进行关注后的事件推送

推送XML数据包示例:

<xml><ToUserName><![CDATA[toUser]]></ToUserName>
<FromUserName><![CDATA[FromUser]]></FromUserName>
<CreateTime>123456789</CreateTime>
<MsgType><![CDATA[event]]></MsgType>
<Event><![CDATA[subscribe]]></Event>
<EventKey><![CDATA[qrscene_123123]]></EventKey>
<Ticket><![CDATA[TICKET]]></Ticket>
</xml>

参数 描述
ToUserName 开发者微信号
FromUserName 发送方帐号(一个OpenID)
CreateTime 消息创建时间 (整型)
MsgType 消息类型,event
Event 事件类型,subscribe
EventKey 事件KEY值,qrscene_为前缀,后面为二维码的参数值
Ticket 二维码的ticket,可用来换取二维码图片

  

2. 用户已关注时的事件推送

推送XML数据包示例:

<xml>
<ToUserName><![CDATA[toUser]]></ToUserName>
<FromUserName><![CDATA[FromUser]]></FromUserName>
<CreateTime>123456789</CreateTime>
<MsgType><![CDATA[event]]></MsgType>
<Event><![CDATA[SCAN]]></Event>
<EventKey><![CDATA[SCENE_VALUE]]></EventKey>
<Ticket><![CDATA[TICKET]]></Ticket>
</xml>

  

参数 描述
ToUserName 开发者微信号
FromUserName 发送方帐号(一个OpenID)
CreateTime 消息创建时间 (整型)
MsgType 消息类型,event
Event 事件类型,SCAN
EventKey 事件KEY值,是一个32位无符号整数,即创建二维码时的二维码scene_id
Ticket 二维码的ticket,可用来换取二维码图片

  

代码

# -*- coding:utf-8 -*-from flask import Flask, request, make_response
import hashlib
import xmltodict
import time
import jsonapp = Flask(__name__)WECHAT_TOKEN = "zhangbiao"@app.route('/weixin', methods=['GET', 'POST'])
def wechat():args = request.argssignature = args.get('signature')timestamp = args.get('timestamp')nonce = args.get('nonce')echostr = args.get('echostr')# 1. 将token、timestamp、nonce三个参数进行字典序排序temp = [WECHAT_TOKEN, timestamp, nonce]temp.sort()# 2. 将三个参数字符串拼接成一个字符串进行sha1加密temp = "".join(temp)# sig是我们计算出来的签名结果sig = hashlib.sha1(temp).hexdigest()# 3. 开发者获得加密后的字符串可与signature对比,标识该请求来源于微信if sig == signature:# 根据请求方式.返回不同的内容 ,如果是get方式,代表是验证服务器有效性# 如果POST方式,代表是微服务器转发给我们的消息if request.method == "GET":return echostrelse:resp_data = request.dataresp_dict = xmltodict.parse(resp_data).get('xml')# 如果是文本消息if 'text' == resp_dict.get('MsgType'):response = {"ToUserName": resp_dict.get('FromUserName'),"FromUserName": resp_dict.get('ToUserName'),"CreateTime": int(time.time()),"MsgType": "text","Content": resp_dict.get('Content'),}print resp_dict.get('Content')elif "event" == resp_dict.get('MsgType'):if "subscribe" == resp_dict.get("Event"):response = {"ToUserName": resp_dict.get("FromUserName", ""),"FromUserName": resp_dict.get("ToUserName", ""),"CreateTime": int(time.time()),"MsgType": "text","Content": u"感谢您的关注!"}if resp_dict.get('EventKey'):response["Content"] += u"场景值是:"response["Content"] += resp_dict.get('EventKey')elif 'SCAN' == resp_dict.get('Event'):# 当用户关注过扫描的时候,会进入到这儿response = {"ToUserName": resp_dict.get("FromUserName", ""),"FromUserName": resp_dict.get("ToUserName", ""),"CreateTime": int(time.time()),"MsgType": "text","Content": resp_dict.get('EventKey')}print resp_dict.get('Ticket')else:response = Noneelse:response = {"ToUserName": resp_dict.get('FromUserName'),"FromUserName": resp_dict.get('ToUserName'),"CreateTime": int(time.time()),"MsgType": "text","Content": u"哈哈哈哈",}if response:response = {"xml": response}response = xmltodict.unparse(response)else:response = ''return make_response(response)else:return 'errno', 403if __name__ == '__main__':app.run(host='0.0.0.0',port=8000)

View Code

生成自定义菜单  

# -*- coding: utf-8 -*-
# filename: menu.py
import urllib
from gentate_token import AccessTokenclass Menu(object):def __init__(self):passdef create(self, postData, accessToken):postUrl = "https://api.weixin.qq.com/cgi-bin/menu/create?access_token=%s" % accessTokenif isinstance(postData, unicode):postData = postData.encode('utf-8')urlResp = urllib.urlopen(url=postUrl, data=postData)print urlResp.read()def query(self, accessToken):postUrl = "https://api.weixin.qq.com/cgi-bin/menu/get?access_token=%s" % accessTokenurlResp = urllib.urlopen(url=postUrl)print urlResp.read()def delete(self, accessToken):postUrl = "https://api.weixin.qq.com/cgi-bin/menu/delete?access_token=%s" % accessTokenurlResp = urllib.urlopen(url=postUrl)print urlResp.read()# 获取自定义菜单配置接口def get_current_selfmenu_info(self, accessToken):postUrl = "https://api.weixin.qq.com/cgi-bin/get_current_selfmenu_info?access_token=%s" % accessTokenurlResp = urllib.urlopen(url=postUrl)print urlResp.read()if __name__ == '__main__':myMenu = Menu()postJson = """
    {"button":[{"type": "click","name": "个人信息","key":  "geren"},{"type": "click","name": "发展历史","key": "fazhan"},{"type": "click","name": "联系我们","key": "contact"}]}"""
    Pjson = '''
{"button": [{"type": "click", "name": "今日歌曲", "key": "V1001_TODAY_MUSIC"}, {"name": "菜单", "sub_button": [{"type": "view", "name": "搜索", "url": "http://www.soso.com/"}, {"type": "view", "name": "个人博客", "url": "http://www.cnblogs.com/crazymagic/"}]}]
}'''
    accessToken =AccessToken.get_access_token()# myMenu.delete(accessToken)myMenu.create(Pjson, accessToken)

Menu.py

转载于:https://www.cnblogs.com/crazymagic/p/9741459.html

Flask 微信公众号开发相关推荐

  1. python 微信公众号开发[1] 后台服务器端配置与公众号开发配置

    更新时间:2020年3月7日 微信公众号开发的硬件必备条件:(1)申请一个公众号 (2)有公网ip的服务器(最好是阿里云,腾讯云等的云服务器)(3)解析到(2)中服务器地址的域名(阿里云,腾讯云购买即 ...

  2. Python微信公众号开发平台

    上大学的时候,对微信公众号开发浅尝辄止的玩了一下,感觉还是挺有意思的. //www.jb51.net/article/133677.htm后来服务器到期了,也就搁置了.由于发布web程序,使用PHP很 ...

  3. Python微信公众号开发

    摘要: 大三上的时候,对微信公众号开发浅尝辄止的玩了一下,感觉还是挺有意思的.http://blog.csdn.net/marksinoberg/article/details/54235271 后来 ...

  4. 微信公众号开发的一些方法总结

    概述 微信公众号开发,其实就是微信使用者.微信公众号平台和自身服务器的http消息交互:在这一系列过程中,微信公众号平台充当了中介和转发作用(如图1所示).需要注意的是,微信公众号平台向自身服务器转发 ...

  5. python微信公众号开发教程_python微信公众号开发简单流程实现

    本文为大家分享了python微信公众号开发的简单过程,供大家参考,具体内容如下 网上有很多微信公众号的开发教程,但是都是好几年前的了,而且很多都是抄袭其他人的,内容几乎一模一样.真的无语了.只好自己总 ...

  6. 使用Python进行微信公众号开发(二)接收消息

    写在前面 <使用Python进行微信公众号开发>系列文章将与大家分享如何使用Python一步步搭建微信公众号后台服务器. 效果体验 扫码"是雯子吖"公众号进行体验 配置 ...

  7. 微信公众号开发本地环境开发_如何在5分钟内使HTTPS在本地开发环境上工作

    微信公众号开发本地环境开发 Almost any website you visit today is protected by HTTPS. If yours isn't yet, it shoul ...

  8. 微信公众号开发用书php,php微信公众号开发(3)php实现简单微信文本通讯

    <PHP实战:PHP微信公众号开发(3)PHP实现简单微信文本通讯>要点: 本文介绍了PHP实战:PHP微信公众号开发(3)PHP实现简单微信文本通讯,希望对您有用.如果有疑问,可以联系我 ...

  9. 【微信公众号开发】获取并保存access_token、jsapi_ticket票据(可用于微信分享、语音识别等等)...

    步骤一:首先得开通公众号(目的是 获得appid.AppSecret.设置安全域名)~ [公众号设置]→[功能设置] 设置相应的域名 步骤二:编写帮助类WeixinLuyinHelper中的代码 #r ...

最新文章

  1. Ubuntu10.04安装Flash插件
  2. ES shard unassigned的解决方法汇总
  3. java short 后缀_自学java的新手问个问题,为什么写个代码中的int能自动转
  4. Any-Proxy在线反向代理源码
  5. 利用mfc项目opengl旋转正方形_C++学习之路:适合C++新手的练手项目,高薪之路必备项目...
  6. OAuth2.0学习(1-11)新浪开放平台微博认证-使用OAuth2.0调用微博的开放API
  7. 嵌入式C高质量编程培训心得笔记
  8. 三星固态860evo安装_固态要涨价了?我赶紧屯了这几块好货
  9. 新宝线上股票大箱体终于向上突破站稳了
  10. cv2.error: OpenCV(4.5.3) C:\Users\runneradmin\AppData\Local\Temp\pip-req-build-q3d_8t8e\opencv\modul
  11. Excel如何快速生成二维码图片?
  12. easyar 实现模型的旋转和缩放
  13. 在VMware上安装Ubuntu详细教程
  14. python京东抢购 github_GitHub - DevGuan/jd-autobuy: Python爬虫,京东自动登录,在线抢购商品...
  15. LoRa及LoRaWAN简介
  16. 【Games104】 如果构建游戏世界
  17. Scrapy是什么?Scrapy怎么用?Scrapy进阶使用[链接提取器、自动登录、图片(文件)下载器](基于scrapy2.0+编写) ๑乛◡乛๑ Scrapy框架使用方法
  18. switch c语言格式,switch语句格式是什么?
  19. 普通路由器改4g路由器_工业级路由器凭什么牛?智能组网、4G全是干货!
  20. 中国程序员VS美国程序员,作为程序员的你有何感想?

热门文章

  1. android 百度地图高德地图开发
  2. 网络游戏--《天下贰》
  3. windows10运行Google开源四足程序框架google_motion
  4. 广西发现全球唯一融丹霞地貌与喀斯特地貌于一体的奇观,未来必火
  5. 【CV论文阅读笔记】使用DRN网络(双重回归网络)解决SR问题
  6. 手动实现userAgent解析
  7. 按摩椅产品如何做好软文推广利用软文来打造为产品引流宣传
  8. 前线消息:ODF大会倒计时25天,你想了解的都在这里
  9. 用Python写一个命令行火车票查看器
  10. 视频合并分割软件如何合并视频