我的公众号开发(第一步)简单功能实现

作者:JiawuZhang
出品:JiawuLab(ID:jiawulab)

实验记录系列是JiawuLab原创栏目,通过真实项目的操作,记录整个实验过程。
旨在通过一步步过程,无基础的朋友都能直接上手。

大家好,我是JiawuZhang,本次实验记录的项目是--微信公众号开发。

选择这个项目的原因有二:
1、微信公众号还是大部分人(包括我自己)获取信息的一个重要渠道;
2、正好我手上有个公众号;

好了,闲话不多说,直接进入主题。

效果演示

先放出实验过程中效果图,闪耀登场:

消息回复演示
关注回复演示
关键字回复演示

这么有个性的回复是不是很好玩,不再是固定的几个回复,具体的实现方法请往下看。

您也可以关注公众号:JiawuLab,来体验更多功能。

前期准备工作

一、准备一个微信公众号,我的是个人订阅号,未认证。(ps:个人公众号,不能认证)

认不认证都不要紧,只是接口权限多少的问题,未认证的接口已经能满足这次实验目的。

(具体权限可见-公众号后台-开发-接口权限)

红框内是已获得的接口权限

二、准备一台服务器,现在提供云服务器的厂家很多,大家可自行准备。

建议选择独立ip的服务器,(共享ip会便宜些,但是不推荐,具体原因后面会说到。)

我选择的是某厂的1核2G1M(1核cpu,2G内存,1M带宽)的独立ip服务器,具体品牌就不说了,怕有广告嫌疑。

这个配置应付常规公众号足够了,但是如果你的公众号的流量特别大,需要根据自己的情况增加配置。

三、服务器上安装python环境,我的环境是python3.7.4, Centos7.5。

如果你不清楚怎么安装,可关注公众号:JiawuLab,给我留言,这里不展开。

开发方向

微信公众号开发,官方的技术文档,写的很详细,也有范例参照。

另外,github上获得3000多星的WeRoBot,也是一个很受欢迎的微信公众号开发框架,有兴趣的朋友可自行了解。

上面提到的相关资源链接,请到文末获取

我们这次以官方开发文档来进行实验

实验开始

首先,进入官方开发文档-入门指引,如下图:

我们只用关注红框中的内容,这里是python2.7版本,服务器上是python3.7.4版本。

所以后面需要对该指引所提到的内容进行版本转化,我会在后面提到如何转,不用担心。

然后需要安装web.py,这是一个python的轻型web开发框架,我们只来用,不用特别学习,如需自行百度。

现在,在服务器中进行安装web.py,很简单,直接用pip安装就好

# 安装web.py
pip(3) install web.py

这里需要注意两点:
1、centos自带python2.7,如果安装python3版本没有改软链接,需要用pip3,检查方式,直接输入python -V,看版本
2、web.py版本,pip安装的直接是0.40正式版,支持python3,不用像网上说的那种指定版本

安装完好,我们测试一下,拷贝下面的代码,保存为main.py文件

# -*- coding: utf-8 -*-
# filename: main.py
import web

urls = (
    '/wx', 'Handle',
)

class Handle(object):
    def GET(self):
        return "pika pika pikaqiu!"

if __name__ == '__main__':
    app = web.application(urls, globals())
    app.run()

然后找到当前文件所在目录输入python(3) main.py 80,服务器启动成功,本地打开网页,输入http://外网ip:80/wx,显示如下

说明配置没有问题,这里记得一定要服务器将80端口放行。到这里,我们就可以正式进行公众号的配置及操作了。

微信公众号配置

打开微信公众号后台,找到-开发-基本配置-服务器配置,进行如下配置:

ps:这里先按照明文模式进行,后面会改成安全模式,过程都会涉及的。点击提交,出现报错,说Token验证失败,果然和文档中说的一样,出现错误,先不管它。

继续看文档后面的内容,原来是服务器没有进行配置,所以报错,我们按照文档后面的来。

先将之前的main.py文件改成如下:(注意备注)

# -*- coding: utf-8 -*-
# filename: main.py
import web
from handle import Handle  # 将Handle类放进handle.py文件中,再导入进来

urls = (
    '/wx', 'Handle',
)

if __name__ == '__main__':
    app = web.application(urls, globals())
    app.run()

然后新建handle.py文件,里面写入:(注意备注)

# -*- coding: utf-8 -*-
# filename: handle.py

import hashlib
import web

class Handle(object):
    def GET(self):
        try:
            data = web.input()
            if len(data) == 0:
                return "pika pika pikaqiu!"
            signature = data.signature
            timestamp = data.timestamp
            nonce = data.nonce
            echostr = data.echostr
            token = "xxxx" #请按照公众平台官网\基本配置中信息填写

            list = [token, timestamp, nonce]
            list.sort()
            sha1 = hashlib.sha1()
            # map函数在python2和3的功能不同,需要转化
            # map(sha1.update, list)
            sha1.update(list[0].encode('utf-8'))
            sha1.update(list[1].encode('utf-8'))
            sha1.update(list[2].encode('utf-8'))
            hashcode = sha1.hexdigest()
            # 这里print转为python3
            print("handle/GET func: hashcode, signature: ", hashcode, signature)
            if hashcode == signature:
                return echostr
            else:
                return ""
        except Exception as Argument:  # 这里加as 转为python3
            return Argument

备注中已提到python3转化的位置, 然后在token中填入微信公众号中对应的token。

保存后输入python(3) main.py 80,服务器启动。

再次回到微信公众号的配置,点击提交,配置成功,显示如下:


现在微信公众号就配置好了,打通服务器与微信公众号这一步很关键,所有后续功能都是在此基础上开发的。

回复功能实现

到这里,指引列出两个示例,分别是你说我学、图尚往来,展示文字回复和图片回复功能。

另外还展示了获取accessToken、获取临时素材、获取永久素材、自定义菜单等四个功能演示。

现在根据自己的需求,以及获得接口权限来决定选择哪些功能,比如我没有自定义菜单权限,就不用理它。

而图片回复需要用到临时素材或是永久素材功能,来调用不同图片,这些都可根据实际需求来选用。

我需要用到的是文字回复,所以这里实现文字回复演示

官方文档中关于文字回复的机制,以及注意事项很详细,大家先仔细研究一下,比如消息体收到和回复的区别,消息回复流程等。

这里只做一个关键点的提醒,就是回复success问题
1、假如服务器无法保证在五秒内处理回复,则必须回复“success”或者“”(空串),否则微信后台会发起三次重试。
2、三次重试后,依旧没有及时回复任何内容,系统自动在粉丝会话界面出现错误提示“该公众号暂时无法提供服务,请稍后再试”。

这是什么意思呢?是指需要保证你的功能代码,在服务器收到消息后,5秒内做出反应,这也是为什么我不推荐共享IP服务器的缘故。

在功能代码中,我们可能需要做一些递归运算,数据库的查询等花时间的事情,而共享IP服务器会受到其他网站的很大影响。

到时可能你写了很厉害的功能,因为无法在5秒内实现,最后给读者的展示,就是无尽的“该公众号暂时无法提供服务,请稍后再试”

好了,码代码的时间到了,这里直接将示例中的三段代码保存即可。

分别是接受消息代码、回复消息代码、handle类的改写,如下:

接受消息代码,保存为receive.py
# -*- coding: utf-8 -*-
# filename: receive.py

import xml.etree.ElementTree as ET


def parse_xml(web_data):
    if len(web_data) == 0:
        return None
    xmlData = ET.fromstring(web_data)
    msg_type = xmlData.find('MsgType').text
    if msg_type == 'text':
        return TextMsg(xmlData)
    elif msg_type == 'image':
        return ImageMsg(xmlData)


class Msg(object):
    def __init__(self, xmlData):
        self.ToUserName = xmlData.find('ToUserName').text
        self.FromUserName = xmlData.find('FromUserName').text
        self.CreateTime = xmlData.find('CreateTime').text
        self.MsgType = xmlData.find('MsgType').text
        self.MsgId = xmlData.find('MsgId').text


class TextMsg(Msg):
    def __init__(self, xmlData):
        Msg.__init__(self, xmlData)
        self.Content = xmlData.find('Content').text.encode("utf-8")


class ImageMsg(Msg):
    def __init__(self, xmlData):
        Msg.__init__(self, xmlData)
        self.PicUrl = xmlData.find('PicUrl').text
        self.MediaId = xmlData.find('MediaId').text
回复消息代码,保存为reply.py
# -*- coding: utf-8 -*-
# filename: reply.py

import time


class Msg(object):
    def __init__(self):
        pass

    def send(self):
        return "success"


class TextMsg(Msg):
    def __init__(self, toUserName, fromUserName, content):
        self.__dict = dict()
        self.__dict['ToUserName'] = toUserName
        self.__dict['FromUserName'] = fromUserName
        self.__dict['CreateTime'] = int(time.time())
        self.__dict['Content'] = content

    def send(self):
        XmlForm = """
        <xml>
        <ToUserName><![CDATA[{ToUserName}]]></ToUserName>
        <FromUserName><![CDATA[{FromUserName}]]></FromUserName>
        <CreateTime>{CreateTime}</CreateTime>
        <MsgType><![CDATA[text]]></MsgType>
        <Content><![CDATA[{Content}]]></Content>
        </xml>
        """
        return XmlForm.format(**self.__dict)


class ImageMsg(Msg):
    def __init__(self, toUserName, fromUserName, mediaId):
        self.__dict = dict()
        self.__dict['ToUserName'] = toUserName
        self.__dict['FromUserName'] = fromUserName
        self.__dict['CreateTime'] = int(time.time())
        self.__dict['MediaId'] = mediaId

    def send(self):
        XmlForm = """
        <xml>
        <ToUserName><![CDATA[{ToUserName}]]></ToUserName>
        <FromUserName><![CDATA[{FromUserName}]]></FromUserName>
        <CreateTime>{CreateTime}</CreateTime>
        <MsgType><![CDATA[image]]></MsgType>
        <Image>
        <MediaId><![CDATA[{MediaId}]]></MediaId>
        </Image>
        </xml>
        """
        return XmlForm.format(**self.__dict)

上面两个文件只用保存就可以,不过细心的你会发现,里面除了文字,还有图片功能,这个后面再说。

有了接受和回复文件后,我们就要在handle.py文件中对Handle类进行改写,如下:

# filename: handle.py

import hashlib
import reply    # 导入回复文件
import receive  # 导入接收文件
import web


class Handle(object):
    def GET(self):
        try:
            data = web.input()
            if len(data) == 0:
                return "pika pika pikaqu!"
            signature = data.signature
            timestamp = data.timestamp
            nonce = data.nonce
            echostr = data.echostr
            token = 'XXXX'  # 请按照公众平台官网\基本配置中信息填写

            list = [token, timestamp, nonce]
            list.sort()
            sha1 = hashlib.sha1()
            sha1.update(list[0].encode('utf-8'))
            sha1.update(list[1].encode('utf-8'))
            sha1.update(list[2].encode('utf-8'))
            hashcode = sha1.hexdigest()
            print("handle/GET func: hashcode, signature: ", hashcode, signature)
            if hashcode == signature:
                return echostr
            else:
                return ""
        except Exception as Argument:
            return Argument

    def POST(self):    # 新增加的POST函数
        try:
            webData = web.data()
            print("Handle Post webdata is ", webData)
            # 后台打日志
            recMsg = receive.parse_xml(webData)
            if isinstance(recMsg, receive.Msg) and recMsg.MsgType == 'text':
                toUser = recMsg.FromUserName
                fromUser = recMsg.ToUserName
                content = "test"  # 这里是回复内容
                replyMsg = reply.TextMsg(toUser, fromUser, content)
                return replyMsg.send()
            else:
                print("暂且不处理")
                return "success"
        except Exception as Argment:
            return Argment

现在代码都写好了,保存后输入python(3) main.py 80,服务器启动。然后就可以进行测试了。

正好在测试期间有读者回复,下面是截图:

小编与一位读者聊天的后台截图

初步test成功,不过还没有达到我们的要求。现在简单的消息回复功能就实现了。

下一步预告

经过一番摸索,我们打通了服务器与微信公众号,也简单实现了消息回复功能,尽管还不是那么理想。

另外在测试中发现,原来设置的自定义菜单不见了关注也没有回复了回复语音达不到效果。。。

下一步,我们将进行如下实验,对接智能机器人,让回复更有个性关注与关键字回复语音也能很好的回复自定义菜单又出现了等功能。

本期实验记录到此结束,感谢您的阅读。如果您喜欢这期文章,请点赞,支持一下。

您可以关注公众号:JiawuLab,提前体验更多功能,或者给我留言,说说你遇到的问题,我们一起探讨。

文中资源:

1、微信公众号官方文档地址:
https://developers.weixin.qq.com/doc/offiaccount/Getting_Started/Overview.html
2、WeRoBot github地址:
https://github.com/offu/WeRoBot/tree/master/werobot
3、WeRoBot文档地址:
https://werobot.readthedocs.io/zh_CN/latest/

▼▼▼▼▼▼

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