作者:JiawuZhang
出品:JiawuLab(ID:jiawulab)
實(shí)驗(yàn)記錄系列是JiawuLab原創(chuàng)欄目集嵌,通過(guò)真實(shí)項(xiàng)目的操作,記錄整個(gè)實(shí)驗(yàn)過(guò)程酱讶。
旨在通過(guò)一步步過(guò)程衬衬,無(wú)基礎(chǔ)的朋友都能直接上手。
大家好询筏,我是JiawuZhang,本次實(shí)驗(yàn)記錄的項(xiàng)目是--微信公眾號(hào)開(kāi)發(fā)竖慧。
選擇這個(gè)項(xiàng)目的原因有二:
1嫌套、微信公眾號(hào)還是大部分人(包括我自己)獲取信息的一個(gè)重要渠道;
2圾旨、正好我手上有個(gè)公眾號(hào)踱讨;
好了,閑話不多說(shuō)砍的,直接進(jìn)入主題痹筛。
效果演示
先放出實(shí)驗(yàn)過(guò)程中效果圖,閃耀登場(chǎng):
這么有個(gè)性的回復(fù)是不是很好玩,不再是固定的幾個(gè)回復(fù)帚稠,具體的實(shí)現(xiàn)方法請(qǐng)往下看谣旁。
您也可以關(guān)注公眾號(hào):JiawuLab,來(lái)體驗(yàn)更多功能滋早。
前期準(zhǔn)備工作
一榄审、準(zhǔn)備一個(gè)微信公眾號(hào),我的是個(gè)人訂閱號(hào)杆麸,未認(rèn)證搁进。(ps:個(gè)人公眾號(hào),不能認(rèn)證)
認(rèn)不認(rèn)證都不要緊昔头,只是接口權(quán)限多少的問(wèn)題饼问,未認(rèn)證的接口已經(jīng)能滿足這次實(shí)驗(yàn)?zāi)康摹?/p>
(具體權(quán)限可見(jiàn)-公眾號(hào)后臺(tái)-開(kāi)發(fā)-接口權(quán)限)
二、準(zhǔn)備一臺(tái)服務(wù)器减细,現(xiàn)在提供云服務(wù)器的廠家很多,大家可自行準(zhǔn)備赢笨。
建議選擇獨(dú)立ip的服務(wù)器未蝌,(共享ip會(huì)便宜些,但是不推薦茧妒,具體原因后面會(huì)說(shuō)到萧吠。)
我選擇的是某廠的1核2G1M(1核cpu,2G內(nèi)存桐筏,1M帶寬)的獨(dú)立ip服務(wù)器纸型,具體品牌就不說(shuō)了,怕有廣告嫌疑梅忌。
這個(gè)配置應(yīng)付常規(guī)公眾號(hào)足夠了狰腌,但是如果你的公眾號(hào)的流量特別大,需要根據(jù)自己的情況增加配置牧氮。
三琼腔、服務(wù)器上安裝python環(huán)境,我的環(huán)境是python3.7.4踱葛, Centos7.5丹莲。
如果你不清楚怎么安裝,可關(guān)注公眾號(hào):JiawuLab尸诽,給我留言甥材,這里不展開(kāi)。
開(kāi)發(fā)方向
微信公眾號(hào)開(kāi)發(fā)性含,官方的技術(shù)文檔洲赵,寫(xiě)的很詳細(xì),也有范例參照。
另外板鬓,github上獲得3000多星的WeRoBot悲敷,也是一個(gè)很受歡迎的微信公眾號(hào)開(kāi)發(fā)框架,有興趣的朋友可自行了解俭令。
上面提到的相關(guān)資源鏈接后德,請(qǐng)到文末獲取。
我們這次以官方開(kāi)發(fā)文檔來(lái)進(jìn)行實(shí)驗(yàn)抄腔。
實(shí)驗(yàn)開(kāi)始
首先瓢湃,進(jìn)入官方開(kāi)發(fā)文檔-入門(mén)指引,如下圖:
我們只用關(guān)注紅框中的內(nèi)容赫蛇,這里是python2.7版本绵患,服務(wù)器上是python3.7.4版本。
所以后面需要對(duì)該指引所提到的內(nèi)容進(jìn)行版本轉(zhuǎn)化悟耘,我會(huì)在后面提到如何轉(zhuǎn)落蝙,不用擔(dān)心。
然后需要安裝web.py暂幼,這是一個(gè)python的輕型web開(kāi)發(fā)框架筏勒,我們只來(lái)用,不用特別學(xué)習(xí)旺嬉,如需自行百度管行。
現(xiàn)在,在服務(wù)器中進(jìn)行安裝web.py邪媳,很簡(jiǎn)單捐顷,直接用pip安裝就好
# 安裝web.py
pip(3) install web.py
這里需要注意兩點(diǎn):
1、centos自帶python2.7雨效,如果安裝python3版本沒(méi)有改軟鏈接迅涮,需要用pip3,檢查方式徽龟,直接輸入python -V
逗柴,看版本
2、web.py版本顿肺,pip安裝的直接是0.40正式版戏溺,支持python3,不用像網(wǎng)上說(shuō)的那種指定版本
安裝完好屠尊,我們測(cè)試一下旷祸,拷貝下面的代碼,保存為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()
然后找到當(dāng)前文件所在目錄輸入python(3) main.py 80
讼昆,服務(wù)器啟動(dòng)成功托享,本地打開(kāi)網(wǎng)頁(yè),輸入http://外網(wǎng)ip:80/wx
,顯示如下
說(shuō)明配置沒(méi)有問(wèn)題闰围,這里記得一定要服務(wù)器將80端口放行赃绊。到這里,我們就可以正式進(jìn)行公眾號(hào)的配置及操作了羡榴。
微信公眾號(hào)配置
打開(kāi)微信公眾號(hào)后臺(tái)碧查,找到-開(kāi)發(fā)-基本配置-服務(wù)器配置,進(jìn)行如下配置:
ps:這里先按照明文模式進(jìn)行校仑,后面會(huì)改成安全模式忠售,過(guò)程都會(huì)涉及的。點(diǎn)擊提交迄沫,出現(xiàn)報(bào)錯(cuò)稻扬,說(shuō)Token驗(yàn)證失敗,果然和文檔中說(shuō)的一樣羊瘩,出現(xiàn)錯(cuò)誤泰佳,先不管它。
繼續(xù)看文檔后面的內(nèi)容尘吗,原來(lái)是服務(wù)器沒(méi)有進(jìn)行配置逝她,所以報(bào)錯(cuò),我們按照文檔后面的來(lái)摇予。
先將之前的main.py文件改成如下:(注意備注)
# -*- coding: utf-8 -*-
# filename: main.py
import web
from handle import Handle # 將Handle類放進(jìn)handle.py文件中汽绢,再導(dǎo)入進(jìn)來(lái)
urls = (
'/wx', 'Handle',
)
if __name__ == '__main__':
app = web.application(urls, globals())
app.run()
然后新建handle.py文件吗跋,里面寫(xiě)入:(注意備注)
# -*- 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" #請(qǐng)按照公眾平臺(tái)官網(wǎng)\基本配置中信息填寫(xiě)
list = [token, timestamp, nonce]
list.sort()
sha1 = hashlib.sha1()
# map函數(shù)在python2和3的功能不同侧戴,需要轉(zhuǎn)化
# 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轉(zhuǎn)為python3
print("handle/GET func: hashcode, signature: ", hashcode, signature)
if hashcode == signature:
return echostr
else:
return ""
except Exception as Argument: # 這里加as 轉(zhuǎn)為python3
return Argument
備注中已提到python3轉(zhuǎn)化的位置, 然后在token中填入微信公眾號(hào)中對(duì)應(yīng)的token跌宛。
保存后輸入python(3) main.py 80
酗宋,服務(wù)器啟動(dòng)。
再次回到微信公眾號(hào)的配置疆拘,點(diǎn)擊提交蜕猫,配置成功,顯示如下:
現(xiàn)在微信公眾號(hào)就配置好了哎迄,打通服務(wù)器與微信公眾號(hào)這一步很關(guān)鍵回右,所有后續(xù)功能都是在此基礎(chǔ)上開(kāi)發(fā)的。
回復(fù)功能實(shí)現(xiàn)
到這里漱挚,指引列出兩個(gè)示例翔烁,分別是你說(shuō)我學(xué)、圖尚往來(lái)旨涝,展示文字回復(fù)和圖片回復(fù)功能蹬屹。
另外還展示了獲取accessToken、獲取臨時(shí)素材、獲取永久素材慨默、自定義菜單等四個(gè)功能演示贩耐。
現(xiàn)在根據(jù)自己的需求,以及獲得接口權(quán)限來(lái)決定選擇哪些功能厦取,比如我沒(méi)有自定義菜單權(quán)限潮太,就不用理它。
而圖片回復(fù)需要用到臨時(shí)素材或是永久素材功能蒜胖,來(lái)調(diào)用不同圖片消别,這些都可根據(jù)實(shí)際需求來(lái)選用。
我需要用到的是文字回復(fù)台谢,所以這里實(shí)現(xiàn)文字回復(fù)演示寻狂。
官方文檔中關(guān)于文字回復(fù)的機(jī)制,以及注意事項(xiàng)很詳細(xì)朋沮,大家先仔細(xì)研究一下蛇券,比如消息體收到和回復(fù)的區(qū)別,消息回復(fù)流程等樊拓。
這里只做一個(gè)關(guān)鍵點(diǎn)的提醒纠亚,就是回復(fù)success問(wèn)題:
1、假如服務(wù)器無(wú)法保證在五秒內(nèi)處理回復(fù)筋夏,則必須回復(fù)“success”或者“”(空串)蒂胞,否則微信后臺(tái)會(huì)發(fā)起三次重試。
2条篷、三次重試后骗随,依舊沒(méi)有及時(shí)回復(fù)任何內(nèi)容,系統(tǒng)自動(dòng)在粉絲會(huì)話界面出現(xiàn)錯(cuò)誤提示“該公眾號(hào)暫時(shí)無(wú)法提供服務(wù)赴叹,請(qǐng)稍后再試”鸿染。
這是什么意思呢?是指需要保證你的功能代碼乞巧,在服務(wù)器收到消息后涨椒,5秒內(nèi)做出反應(yīng),這也是為什么我不推薦共享IP服務(wù)器的緣故绽媒。
在功能代碼中蚕冬,我們可能需要做一些遞歸運(yùn)算,數(shù)據(jù)庫(kù)的查詢等花時(shí)間的事情是辕,而共享IP服務(wù)器會(huì)受到其他網(wǎng)站的很大影響囤热。
到時(shí)可能你寫(xiě)了很厲害的功能,因?yàn)闊o(wú)法在5秒內(nèi)實(shí)現(xiàn)免糕,最后給讀者的展示赢乓,就是無(wú)盡的“該公眾號(hào)暫時(shí)無(wú)法提供服務(wù)忧侧,請(qǐng)稍后再試”
好了,碼代碼的時(shí)間到了牌芋,這里直接將示例中的三段代碼保存即可蚓炬。
分別是接受消息代碼、回復(fù)消息代碼躺屁、handle類的改寫(xiě)肯夏,如下:
接受消息代碼,保存為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
回復(fù)消息代碼犀暑,保存為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)
上面兩個(gè)文件只用保存就可以驯击,不過(guò)細(xì)心的你會(huì)發(fā)現(xiàn),里面除了文字耐亏,還有圖片功能徊都,這個(gè)后面再說(shuō)。
有了接受和回復(fù)文件后广辰,我們就要在handle.py文件中對(duì)Handle類進(jìn)行改寫(xiě)暇矫,如下:
# filename: handle.py
import hashlib
import reply # 導(dǎo)入回復(fù)文件
import receive # 導(dǎo)入接收文件
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' # 請(qǐng)按照公眾平臺(tái)官網(wǎng)\基本配置中信息填寫(xiě)
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函數(shù)
try:
webData = web.data()
print("Handle Post webdata is ", webData)
# 后臺(tái)打日志
recMsg = receive.parse_xml(webData)
if isinstance(recMsg, receive.Msg) and recMsg.MsgType == 'text':
toUser = recMsg.FromUserName
fromUser = recMsg.ToUserName
content = "test" # 這里是回復(fù)內(nèi)容
replyMsg = reply.TextMsg(toUser, fromUser, content)
return replyMsg.send()
else:
print("暫且不處理")
return "success"
except Exception as Argment:
return Argment
現(xiàn)在代碼都寫(xiě)好了,保存后輸入python(3) main.py 80
择吊,服務(wù)器啟動(dòng)李根。然后就可以進(jìn)行測(cè)試了。
正好在測(cè)試期間有讀者回復(fù)几睛,下面是截圖:
初步test成功房轿,不過(guò)還沒(méi)有達(dá)到我們的要求。現(xiàn)在簡(jiǎn)單的消息回復(fù)功能就實(shí)現(xiàn)了所森。
下一步預(yù)告
經(jīng)過(guò)一番摸索囱持,我們打通了服務(wù)器與微信公眾號(hào),也簡(jiǎn)單實(shí)現(xiàn)了消息回復(fù)功能必峰,盡管還不是那么理想洪唐。
另外在測(cè)試中發(fā)現(xiàn)钻蹬,原來(lái)設(shè)置的自定義菜單不見(jiàn)了吼蚁,關(guān)注也沒(méi)有回復(fù)了,回復(fù)語(yǔ)音達(dá)不到效果问欠。肝匆。。
下一步顺献,我們將進(jìn)行如下實(shí)驗(yàn)旗国,對(duì)接智能機(jī)器人,讓回復(fù)更有個(gè)性注整;關(guān)注與關(guān)鍵字回復(fù)能曾;語(yǔ)音也能很好的回復(fù)度硝;自定義菜單又出現(xiàn)了等功能。
本期實(shí)驗(yàn)記錄到此結(jié)束寿冕,感謝您的閱讀蕊程。如果您喜歡這期文章,請(qǐng)點(diǎn)贊驼唱,支持一下藻茂。
您可以關(guān)注公眾號(hào):JiawuLab,提前體驗(yàn)更多功能玫恳,或者給我留言辨赐,說(shuō)說(shuō)你遇到的問(wèn)題,我們一起探討京办。
文中資源:
1掀序、微信公眾號(hào)官方文檔地址:
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/
▼▼▼▼▼▼