python微信公众号生成专属二维码--你再也不用去求人了

需求:公司需要开发微信公众号,并且和h5无缝对接。由于以前都是运营人员直接在微信公众平台进行编辑的,就从没考虑如何做。前几天运营人员有个需求,也就是我们平日在别人公众号里点击生成专属二维码,现在要求统计谁生成的二维码,谁通过此二维码进行的扫码关注。

小公司,开发2人,原本想直接在网上找一个python的模块,import即可,想到今后要扩展功能会不会很麻烦。就自己干了。(既然微信开发的api,如果很复杂的话,公众号就不会这么火了。)

思路:微信平台就类似中间件, 我们的服务器,以及用户之间进行三角恋的变态关系。
1:和微信平台建立信赖关系。就是配置咱们的服务器和微信的连接。
2:微信事件推送,咱们服务器收到后,进行反馈。(微信有5秒等待时间)
3:调用微信二维码生成接口。(参数二维码)
4:利用python qrcode生成参数二维码,用PIL 将二维码图片和漂亮的背景图片进行合并,paste到设计的指定位置。
4:将图片上传至微信素材
5:将消息发给用户。

整个流程没有任何难点,难点就是特么文档接口之间毫无联系,查起来效率老低了。不知为何市场上却那么多以此谋生的企业???(你只需要最多一天的时间基本搞定所有这些东西)
废话不设了,
1:https://mp.weixin.qq.com/wiki 在文档中点击 接入指南。
token自己输入一个你喜欢并且保密的字符串。
当配置号url,token,EncodingAESKey 我们需要做的就是用我们的服务器在此url链接下返回微信想要的数据。(目的告诉微信相信我哦),这一步通过后,才能进行下面的步骤。
所以按照文档要求:
1)将token、timestamp、nonce三个参数进行字典序排序
2)将三个参数字符串拼接成一个字符串进行sha1加密
3)开发者获得加密后的字符串可与signature对比,标识该请求来源于微信

ajaxLogger = logging.getLogger('ajax')
@csrf_exempt
def wechat_message_views(request):
    result = {}
    result["title"] = "微信消息"
    if request.method == 'GET':
        if request.GET.has_key("signature"):
            signature = request.GET["signature"]
            timestamp = request.GET["timestamp"]
            nonce = request.GET["nonce"]
            echostr = request.GET["echostr"]
            token = "你的touken"
            data_list = [token, timestamp, nonce]
            data_list.sort()
            weixin_sha1 = hashlib.sha1()
            weixin_sha1.update("".join(data_list))
            weixin_sha1 = weixin_sha1.hexdigest()
            if weixin_sha1 == signature:
                response = HttpResponse(echostr)
                ajaxLogger.info("成功")
            else:
                response = HttpResponse("403")
                ajaxLogger.info("非法")
        else:
            ajaxLogger.info(request.get_host())
            response = HttpResponse("ok")

        return response
    return what_you_want_do(request)

其中,@csrf_exempt很重要,不然就403了。

接入成功。

2:接收微信事件推送。
微信文档位置

(1)关注和取关事件推送
(2)扫描带参数二维码事件
就他两个了,最简单暴力的方法就是直接分析微信发送的xml格式。根据xml内容进行函数执行。(这里你可以用高级的python语法就执行)

在我们的服务器url 接口函数那里,我们的 what_you_want_do函数需要进行事件判读,然后分发。你可以用策略模式等高级方法去完善。咱们直接if else…………(记得有仁熊说过,有的人写了一辈子代码,永远的if else)

from xml.dom.minidom import parseString
def weixin_deal_xml(nodes,key):
    try:
        node_data = nodes.getElementsByTagName(key)
        if node_data:
            return node_data[0].childNodes[0].data
        else:
            return []
    except Exception, e:
        ajaxLogger.error( "解析XML错误 : %s"%str(e) )
        return []

def what_you_want_do(request):
    #先要验证request,方法和上面get一样,咱也要知道request是否来自微信
    xml_result = request.body
    try:
        nodes = parseString(xml_result).documentElement
    except Exception, e:
        ajaxLogger.error("报错啦,sb %s" %str(e))
        return HttpResponse("403")
    #研究发现微信的返回参数都有的,就这么干了,最好是封装成函数,说不定今后有变化
    msg_type = weixin_deal_xml(nodes, "MsgType")
    user_open_id = weixin_deal_xml(nodes, "FromUserName")
    wechat_pub_id = weixin_deal_xml(nodes, "ToUserName")
    create_time = weixin_deal_xml(nodes, "CreateTime")

    if not msg_type:
        return HttpResponse("")
    if not user_open_id:
        return HttpResponse("")
    #此处开始处理 事件推送,根据事件推送类型,去处理
    if msg_type == "event":
        event = weixin_deal_xml(nodes, "Event")
        eventkey = weixin_deal_xml(nodes, "EventKey")
        return deal_wechat_event(event, eventkey, user_open_id, wechat_pub_id, create_time)
    else:
        pass

收到不同微信服务器的事件推送,理论我们都应该就行xml的回复。如果是
VIEW : 视图跳转,咱们可以不返回,有需要后台存储一下用户的点击行为
CLICK :(这里参数二维码生成一定要是CLICK事件)。即在生成menu(https://api.weixin.qq.com/cgi-bin/menu/create?access_token={ACCESS_TOKEN})时,一定要将生成专属二维码设置成click。类似 {
“type”:”click”,
“name”:u”邀请好友”,
“key”:”V1001_你的_CODE_KEY”
}
此时,咱们做接收click事件,并通过key来判断是哪一个,然后返回相应的函数。

由于咱们处理生成二维码,还要进行和微信服务器素材的交互行为,但事件推送等待时间有限。咱们采用异步处理模式(gearman处理),先发一个消息提示用户,让用户等待一下,正在生成中。如:点击邀请好友按钮

即what_you_want_do函数,不论收到来自微信的任何消息,都应该返回一个xml消息。这里我们先返回一段话给用户。
根据微信的消息格式,这里一定要注意,微信没有在文档中进行说明,消息必须回复。真是坑爹,一定要回他哦。

import time
def response_to_wechat(touser, fromuser, text_content):
    data =  "<xml><ToUserName><![CDATA[%s]]></ToUserName>\
        <FromUserName><![CDATA[%s]]></FromUserName>\
        <CreateTime>%s</CreateTime>\
        <MsgType><![CDATA[%s]]></MsgType>\
        <Content><![CDATA[%s]]></Content>\
        </xml>"%(touser,
                fromuser,
                int(time.time()),
                "text",
                text_content
                )
    return HttpResponse(data, content_type="application/xml")

调用response_to_wechat 发送一段等待的话给他,在这个之前调用异步生成参数二维码接口。

def deal_wechat_event(event, eventkey, user_open_id, wechat_pub_id, create_time):
    # 点击菜单事件
    if event == "CLICK":
        if eventkey == "V1001_你的_CODE_KEY":
            params = {
                "user_open_id":user_open_id,
                "eventkey":eventkey,
                "wechat_pub_id":wechat_pub_id,
                "create_time":create_time
            }
            call_command('gearman_submit_job','worker_name', json.dumps(params),foreground=False)
            text_content = "协力正为您生成邀请二维码,等待5秒左右即可收到。"
            return response_to_wechat(user_open_id, wechat_pub_id, text_content)

(上面的if ,else 都可以通过python技巧,以及设计模式进行更好的代码维护。自己进行吧。)

异步的worker,我们在里面定义处理函数。
函数一:生成参数二维码,需求里我们要存储生成二维码的用户的信息。(此用户信息即为参数key。当其他人通过扫描生成的二维码时,我们要从推送的信息中查询微信返回的key是哪个用户。)
文档这里写图片描述

我们使用临时二维码,临时素材。(因为永久的生成的个数太少了)

def create_scene_qrcode(user_open_id):
        scene_id = 100000000   #(每个用户不一样,你自己需要进行改变,如自增)
        # 获取参数二维码 url。自己进行二维码图片生成
        url = wechat_qr_imge_url(scene_id)
        image_data = create_wechat_qrcode(url)
        media_id = post_picture_to_weixin(image_data)
        create_time = int(time.time())
        send_user_message(user_open_id, media_id)
#带参数二维码
def wechat_qr_imge_url(final_scene_id):
    ACCESS_TOKEN = get_accesstoken()
    url = "https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token={ACCESS_TOKEN}".format(ACCESS_TOKEN=ACCESS_TOKEN)
    data = {
        "expire_seconds": 1000, 
        "action_name": "QR_SCENE", 
        "action_info": {"scene": 
            {"scene_id": final_scene_id}
            }
        }
    data = json.dumps(data)
    try:
        data_result = requests.post(url,data.encode('utf8'))
        result = data_result.json()
        ajaxLogger.info(str(result))
        if result.has_key("ticket"):
            ticket = result["ticket"]
            url = result["url"]
            return url
    except Exception, e:
        ajaxLogger.error("生成专属参数二维码 {error}".format(error=str(e)))

# 获取二维码ticket后,开发者可用ticket换取二维码图片,也可以将返回的url自行处理,咱们自此处理
#{"ticket":"gQH47joAAAAAAAAAASxodHRwOi8vd2VpeGluLnFxLmNvbS9xL2taZ2Z3TVRtNzJXV1Brb3ZhYmJJAAIEZ23sUwMEmm
# 换取二维码图片
# https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket=TICKET

get_accesstoken方法,实现了使用redis存储token,因为token微信每天获取次数有限制,并且有过期时间。所以咱们使用reids expire。将过期时间和 token过期一致即可。非常简单

create_wechat_qrcode 生成合并图片。
此函数目的就是1:生成二维码,2:将二维码和美丽的背景图片进行合并。

from PIL import Image
import qrcode
from io import StringIO, BytesIO
def create_wechat_qrcode(params):
    #参数可自行调整
    qr = qrcode.QRCode(
        version=2,
        error_correction=qrcode.constants.ERROR_CORRECT_H,
        box_size=10,
        border=1
    )
    #二维码填充内容
    qr.add_data(params)
    qr.make(fit=True)
    img = qr.make_image()
    img = img.convert("RGBA")
    #打开背景图片
    icon = Image.open("你的背景图片地址.png")
    #根据设计将二维码填充到制定位置
    icon.paste(img, ("位置坐标", "位置坐标"), img)
    buf = BytesIO()
    #生成二进制文件,直接发给微信
    icon.save(buf,format="PNG")
    file_content  = buf.getvalue()
    return file_content

此函数将图片上传至微信的临时素材
post_picture_to_weixin

# 上传图消息素材
def post_picture_to_weixin(rawimg):
    ACCESS_TOKEN = get_accesstoken()
    url = "https://api.weixin.qq.com/cgi-bin/media/upload?access_token={ACCESS_TOKEN}&type={TYPE}".format(ACCESS_TOKEN=ACCESS_TOKEN, TYPE="image")
    file_name = str(time.time()).split(".")[0] +  'tmp.png'
    #微信文档有文件上传时的要求
    files = { 'media' : (file_name, rawimg,'image/png')}
    res = requests.post(url, files=files)
    result_data = res.json()
    #media_id 通过media_id给微信用户发送图片消息
    if result_data.has_key("media_id"):
        media_id =  result_data["media_id"]
        return media_id

最后哦,send_user_message给用户主动发消息。(由于我们异步处理,xml格式的回复已经发给用户,现在就得主动发消息给用户。)

def send_user_message(OPENID, MEDIA_ID, msgtype="image"):
    ACCESS_TOKEN = get_accesstoken()
    url = "https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token={ACCESS_TOKEN}".format(ACCESS_TOKEN=ACCESS_TOKEN)
    data = {
        "touser":OPENID,
        "msgtype":msgtype,
        "image":
        {
            "media_id":MEDIA_ID
        }
    }
    if msgtype == "text":
        data["text"] = {
            "content":"Hello World"
        }
    request_weixin(url, data)

def request_weixin(url, data):
    # 有些中文,以及json格式中的,必须使用ensure_ascii=False,不然有时会报错
    data = json.dumps(data, ensure_ascii=False)
    try:
        data_result = requests.post(url,data.encode('utf8'))
        result = data_result.json()
        print result
        # ajaxLogger.info(str(result))
    except Exception, e:
        print e
        # ajaxLogger.error("设置客服失败 {error}".format(error=str(e)))

这里写图片描述
哈哈没有美工,就拿logo放在中间啦,结果logo和二维码,太丑陋了。

至此我们就完成了,生成专属二维码的整个流程和代码。
第一次在这里发博客,如果有错误欢迎大家指出来。如果帮助了大家,希望大家给个赞。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

yinxingpan

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值