python对接公众号接口

昨晚下班回到家,打算把前不久做的图片动漫化的功能集成到微信公众号,效果是,用户发送一张图片到公众号,那么公众号就会回复一张动漫化的图片回去。

距离上次开发公众号相关的接口,已经过去很久了,也有点生疏了,但是还是记得相关的一些模糊细节,需要处理微信回调发送的信息,涉及到了加解密,还有主动调用微信接口,需要根据app id和app secret获取access token等等,由于打算快速开发,所以采用python来做相关的接口集成开发,用java的话没有python这么敏捷和快速。

于是网上找了一下有没有相关的库,总不能自己重新对着微信公众号文档造轮子吧,这样效率太低了,经过一番搜索,找到一个还不错的框架wechatpy

安装非常简单,使用pip即可

pip install wechatpy
# with cryptography (推荐)
pip install wechatpy[cryptography]# with pycryptodome
pip install wechatpy[pycrypto]

安装完毕之后,需要选择一个web框架,从轻量级和快速角度考虑,选择了flask框架来做web开发
安装如下:

pip install Flask

一个简单上手flask的示例


from flask import Flask
app = Flask(__name__)

@app.route('/')
def hello_world():
    return 'Hello, World!'

if __name__ == '__main__':
    app.run(host='127.0.0.1', port=8000, debug=True)

运行之后,打开浏览器127.0.0.1:8000即可看到hello world!非常简单

那接下来继续,打开公众号管理后台,开发->基本配置,服务器配置栏,需要填写微信回调的url地址
然后设置令牌和消息加解密模式,微信会回调这个url接口,确认对接成功,那接下来我们就用wechatpy来处理这段逻辑

定义微信的回调url处理方法,从请求参数获取timestamp和nonce参数,如果是get请求,则进一步获取echostr和signature参数,然后调用check_signature校验是否是来自微信的请求,如果不是则拒绝,如果是则回复echostr,告知微信,这边已经对接好了

from wechatpy.utils import check_signature
@app.route('/wechat', methods=['GET', 'POST'])
def wechat():
    timestamp = request.args.get("timestamp")
    nonce = request.args.get("nonce")
    if request.method == 'GET':
        # token, signature, timestamp, nonce
        echostr = request.args.get("echostr")
        signature = request.args.get("signature")
        if echostr:
            try:
                check_signature(const.token, signature, timestamp, nonce)
                return echostr
            except InvalidSignatureException:
                logging.error("invalid message from request")

注意到这里只处理了GET请求,只有GET请求是不带任何request body的,GET请求是微信发送的Token验证
还有一种是POST请求,这个请求微信会携带一些消息体过来,主要是XML,这些包含了比如图片消息,文本消息,或者一些事件等等,具体可以查看微信公众号的相关文档
这里处理的是被动请求,也就是微信调用我们的服务器。
我们调用微信服务器接口属于主动请求,需要access token

继续完善这个wechat方法

from wechatpy.utils import check_signature
from wechatpy import parse_message
from wechatpy.crypto import WeChatCrypto
from wechatpy.exceptions import InvalidSignatureException, InvalidAppIdException
from wechatpy.replies import ImageReply
#....省略代码
@app.route('/', methods=['GET', 'POST'])
def wechat():
    timestamp = request.args.get("timestamp")
    nonce = request.args.get("nonce")
    if request.method == 'GET':
      #....省略代码
    else:
        xml = request.data
        if xml:
            msg_signature = request.args.get("msg_signature")
            crypto = WeChatCrypto(const.token, const.encodeing_aes_key,
                                  const.app_id)
            try:
                decrypted_xml = crypto.decrypt_message(
                    xml,
                    msg_signature,
                    timestamp,
                    nonce
                )
                msg = parse_message(decrypted_xml)
                logging.info("message from wechat %s " % msg)
            except (InvalidAppIdException, InvalidSignatureException):
                logging.error("cannot decrypt message!")
        else:
            logging.error("no xml body, invalid request!")
    return ""

在else分支处理post请求,注意最上面route我们配置了仅支持get和post请求,那么else分支一定是post方法
通过request.data获取xml数据,这里注意判断一下是否有数据,如果没有数据的情况下,那么直接打印日志no xml body
然后返回即可。

如果有xml数据,那么我们从请求里面获取msg_signature,然后解密消息,这个取决于你的微信公众号的配置,如果无所谓安全,可以直接明文传输,那样就省去了解密消息的步骤。

后面把消息解出来之后,然后打印消息内容
放到服务器上调试一下,往公众号发送文本消息你好,消息显示如下:

2021-03-10 04:00:10,543:INFO:message from wechat TextMessage(OrderedDict([('ToUserName', 'gh_292a97797f58'), ('FromUserName', 'oPXMnwgqQBKAz23ovLYhca6KtIMQ'), ('CreateTime', '1615348809'), ('MsgType', 'text'), ('Content', '你好'), ('MsgId', '23126237789150234')]))

文章一开始提到要做的内容是当用户发送一张图片到公众号,那么公众号就会回复一张动漫化的图片回去。

那么需要处理的消息是图片类型的消息,而不是文本消息,发送一张图片到公众号试试,发现打印消息如下:

2021-03-10 13:08:47,296:INFO:message from wechat ImageMessage(OrderedDict([('ToUserName', 'gh_292a97797f58'), ('FromUserName', 'oPXMnwgkC5M-DKNfigblDIXY3Irw'), ('CreateTime', '1615381726'), ('MsgType', 'image'), ('PicUrl', 'http://mmbiz.qpic.cn/sz_mmbiz_jpg/------'), ('MsgId', '23126702559805937'), ('MediaId', 'UdcKE2XkjLTQcJmRQulERp2GZeTZ5FncAht5XjrKU3Yh1sTucAaQ0E8-------')]))

可以看到图片类型的消息的type是image类型,再结合wechatpy的文档,继续编写代码

图片消息
classwechatpy.messages.ImageMessage(message)[源代码]
图片消息 详情请参阅 http://mp.weixin.qq.com/wiki/10/79502792eef98d6e0c6e1739da387346.html

ImageMessage 的属性:

name value
type image
image 图片的 URL 地址

#.....省去代码
msg = parse_message(decrypted_xml)
logging.info("message from wechat %s " % msg)
if msg.type == "image":
    logging.info("image type message url %s" % msg.image)
    cartoon_file = utils.cartoon_image(msg.image)
    media_id = utils.upload_image(cartoon_file)
    if media_id is not None:
        reply = ImageReply(message=msg)
        reply.media_id = media_id
        xml = reply.render()
        return xml

当msg.type是image的时候,打印image的url地址,然后调用封装好的卡通化image的方法 cartoon_image,具体如何把图片卡通化的方法请参考笔者前面的文章,这里就不重复了。

然后上传卡通过后的图片到微信服务器,获取media_id,这里构建一个wechatclient,传入你的appid和appsecret即可

from wechatpy import WeChatClient
from wechatpy.client.api import WeChatMedia

client = WeChatClient(const.app_id, const.app_secret)

def upload_image(file_path):
    res = WeChatMedia(client).upload("image", open(file_path, "rb"))
    if "media_id" in res:
        return res['media_id']
    logging.error("upload image %s" % res)
    return None

拿到media_id之后,就可以回复图片给用户了。
最后上一个效果图


image.png
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,324评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,356评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,328评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,147评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,160评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,115评论 1 296
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,025评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,867评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,307评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,528评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,688评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,409评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,001评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,657评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,811评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,685评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,573评论 2 353

推荐阅读更多精彩内容