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

来源:互联网 发布:决战武神弓进阶数据 编辑:程序博客网 时间:2024/04/29 21:30

需求:公司需要开发微信公众号,并且和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_exemptdef 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 parseStringdef 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 timedef 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 Imageimport qrcodefrom io import StringIO, BytesIOdef 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和二维码,太丑陋了。

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

1 0
原创粉丝点击