美文网首页
python对接公众号接口

python对接公众号接口

作者: iamdev | 来源:发表于2021-03-10 21:31 被阅读0次

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

    距离上次开发公众号相关的接口,已经过去很久了,也有点生疏了,但是还是记得相关的一些模糊细节,需要处理微信回调发送的信息,涉及到了加解密,还有主动调用微信接口,需要根据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

    相关文章

      网友评论

          本文标题:python对接公众号接口

          本文链接:https://www.haomeiwen.com/subject/ehsgqltx.html