本實踐將基于天翼云彈性云主機搭建微信公眾號處理后臺,使用Python語言編寫對應的微信消息處理邏輯代碼,接收從微信服務端轉發過來的消息,并返回處理結果給最終用戶。
準備事項
- 申請微信公眾號。
微信公眾號申請鏈接:
- 購買天翼云彈性云主機服務。
如果沒有天翼云賬號,需先注冊天翼云賬號并完成實名認證。
本實踐中,使用公共鏡像CentOS 7.4。
- 購買彈性IP。
建議同時購買彈性IP,后面需要在微信公眾號上配置公網IP的地址。
操作步驟
基礎軟件安裝
本實踐中使用Python+Web.py組合完成微信公眾號開發,需要安裝或升級Python、pip、Web.py框架、WinSCP軟件版本。
- 升級默認Python版本;
CentOS 7.4自帶Python版本比較老,建議升級到Python3。
1)查看Python版本,使用如下命令:
python –version

2)下載Python安裝包,這里以Python 3.6.0版本為例,使用命令:
wget //www.python.org/ftp/python/3.6.0/Python-3.6.0a1.tar.xz

3)解壓安裝包,使用如下命令:
tar xvf Python-3.6.0a1.tar.xz

如果出現“configure: error: no acceptable C compiler found in $PATH”異常提示,是因為未安裝合適的編譯器。
解決方法:
執行以下命令,安裝/升級gcc及其他依賴的包。
sudo yum install gcc-c++
并在隨后提示安裝包是否OK時,輸入y并回車。出現如下圖提示,說明依賴的包安裝成功。

在編譯器安裝完成后,重新執行 ./configure 命令。
4)執行命令:
make && make install
執行成功。但提示pip錯誤,原因是我的系統中少了openssl-devel包,可以先忽略。

5)查看Python3版本,使用命令:
python3 –version

6)執行命令:
python3
出現如下提示,則說明Python3安裝成功。

- 升級默認pip版本;
pip是通用的Python包管理工具。提供了對Python包的查找、下載、安裝、卸載功能。Python3安裝成功后自帶pip3,但版本比較老,建議升級到pip最新版本。同時前面安裝python3提示“Ignoring ensurepip failure: pip 8.1.1 requires SSL/TLS”錯誤,導致pip未成功安裝,所以需要重新安裝pip。
1)安裝openssl-devel包,使用命令:
yum install openssl-devel -y

2)執行命令:
make && make install
出現如下提示說明pip安裝成功。

3)升級pip3,使用命令:
pip3 install --upgrade pip
出現如下提示說明升級pip到最新版本了。

- 安裝Web.py框架;
Web.py官方教程地址:,使用如下命令安裝web.py:
pip3 install web.py==0.40.dev0

- 安裝WinSCP;
通常情況下,我們在本地Windows操作系統上編輯代碼,完成后再上傳至ECS上(CentOS Linux系統)。WinSCP 是一個Windows環境下使用的SSH的開源圖形化SFTP客戶端, 同時支持 SCP 協議。它的主要功能是在本地與遠程計算機間安全地復制文件,并且可以直接編輯文件。
WinSCP安裝鏈接:
上傳代碼
- 新建main.py文件,復制如下代碼:
# -*- coding: utf-8 -*-
# filename: main.py
import web
from handle import Handle
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
import receive
import time
import os
class Handle(object):
def __init__(self):
self.app_root = os.path.dirname(__file__)
self.templates_root = os.path.join(self.app_root, 'templates')
self.render = web.template.render(self.templates_root)
def GET(self):
try:
data = web.input()
if len(data) == 0:
return "hello, this is handle view"
signature = data.signature
timestamp = data.timestamp
nonce = data.nonce
echostr = data.echostr
token = "此處內容與公眾號基本配置里Token字段取值保持一致"
list = [token, timestamp, nonce]
list.sort()
s = list[0] + list[1] + list[2]
hashcode = hashlib.sha1(s.encode('utf-8')).hexdigest()
print( "handle/GET func: hashcode, signature: ", hashcode, signature)
if hashcode == signature:
return echostr
else:
return echostr
except (Exception) as Argument:
return Argument
def POST(self):
try:
webData = web.data()
print("Handle Post webdata is:\n", webData)
#打印消息體日志
recMsg = receive.parse_xml(webData)
if isinstance(recMsg, receive.Msg) and recMsg.MsgType == 'text':
toUser = recMsg.FromUserName
fromUser = recMsg.ToUserName
content = "歡迎" + str(recMsg.Content)
print('Reply message info:\n')
print('toUser =', toUser)
print('fromUser = ', fromUser)
print('content = ', content)
return self.render.reply_text(toUser, fromUser, int(time.time()), content)
else:
print("不支持的消息類型:",recMsg.MsgType)
return "success"
except (Exception) as Argment:
return Argment
- 新建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)
elif msg_type == 'location':
return LocationMsg(xmlData)
elif msg_type == 'event':
return EventMsg(xmlData)
class Event(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.Eventkey = xmlData.find('EventKey').text
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
class ImageMsg(Msg):
def __init__(self, xmlData):
Msg.__init__(self, xmlData)
self.PicUrl = xmlData.find('PicUrl').text
self.MediaId = xmlData.find('MediaId').text
class LocationMsg(Msg):
def __init__(self, xmlData):
Msg.__init__(self, xmlData)
self.Location_X = xmlData.find('Location_X').text
self.Location_Y = xmlData.find('Location_Y').text
class EventMsg(Msg):
def __init__(self, xmlData):
Event.__init__(self, xmlData)
self.Event = xmlData.find('Event').text
- 新建templates文件夾,在文件夾下新建reply_text.xml文件,復制如下代碼:
$def with (toUser,fromUser,createTime,content)
<xml>
<ToUserName><![CDATA[$toUser]]></ToUserName>
<FromUserName><![CDATA[$fromUser]]></FromUserName>
<CreateTime>$createTime</CreateTime>
<MsgType><![CDATA[text]]></MsgType>
<Content><![CDATA[$content]]></Content>
</xml>
- 最終本地代碼文件形成如下:

- 通過WinSCP工具將上述文件與目錄上傳至ECS指定目錄下:

啟動服務
使用如下命令啟動服務:
python3 main.py 80

啟用開發者模式
- 登錄微信公眾平臺,選擇“開發 > 基本配置”,單擊“修改配置”。
- 填寫配置信息,單擊“提交”。
- URL://ECS的彈性公網IP/wx,不用添加80端口。
- Token:需要與handle.py中對應token取值完全一致。
- EncodingAESKey:隨機生成。
- 消息加解密方式:此為示例,選擇簡單的“明文模式”。
- 驗證token成功,單擊“啟用”。
**說明:**如果token驗證失敗,請檢查Token配置與handle.py中GET消息處理代碼是否一致。
驗證
使用微信關注公眾號,任意發送一條文本消息,看是否能夠收到回復。如能收到回復則表明系統處理正常。