linux+apache+mod_python+wechat_sdk搭建微信公共賬號服務器
轉載請注明本文原作者:FignerLiu
PRE
最近嘗試了下使用python搭建微信公共賬號服務器,實現了簡單的消息收發功能。其中遇到了很多問題,特此記錄下來。
服務器的選擇
如果使用python做開發語言,一般選用以下幾種服務器可以用來做微信公共賬號服務器(如果不全,歡迎大家補充):
- SAE + wsgi
- apache + mod_python
- apache + mod_wsgi
- nginx + wsgi
如果對使用獨立的服務器沒有需求,或者對apache和nginx的配置不熟悉,一般采用SAE + wsgi做服務器。由於手頭正好有閑置的Apache,本着生命在於折騰,從折騰中學習的目的,我選擇了apache + mod_python作為我的服務器。以下的內容均以此為背景。
在后面的開發過程中我體會到,我的這個選擇絕對是最折騰人的 = 。=
sdk的選擇
除了微信官方的sdk外,github上找到兩個第三方sdk:
- wechat-python-sdk
- WeRoBot
這兩個sdk各有優劣,wechat-python-sdk代碼更簡潔易懂,而WeRoBot功能更全,包含微信支付API操作類等wechat-python-sdk不具有的功能。本次我選擇使用了wechat-python-sdk
服務器搭建與配置
首先操作系統我選擇的是放在阿里雲上的centos 5.10
需要安裝如下軟件包:
- apache 我選用的是httpd-2.4.10
- expat expat是用來解析xml等文件的庫,我選用的是expat-2.0.1.tar.gz
- apr Apache Portable Runtime我選用的是apr-1.5.1.tar.gz
- apr-util 我選用的是apr-util-1.5.4.tar.gz
- prce Perl Compatible Regular Expressions 我選用的是pcre-8.36.tar.gz 。 上述expat,apr,apr-util,prce均為安裝Apache時的依賴項,注意各軟件包的版本,如果不一致可能導致不兼容的問題。
- mod_python Apache處理python的模塊,我選用的是mod_python-3.5.0.tgz
- python-devel mod_python的安裝依賴項,我選用的是python-devel-2.6.6-52.el6.x86_64,應和系統使用的python版本一致。
- python-pip python庫安裝工具,用來安裝wechat-sdk
- wechat-sdk 微信平台第三方sdk
- virtualenv(可選) virtualenv的簡介
首先安裝各軟件包
為了統一和方便,我把所有軟件包都安裝到了/usr/local下,均為編譯安裝。
以下是部分安裝配置命令
#pcre ./configure --prefix=/usr/local/pcre #apr ./configure --prefix=/usr/local/apr #apr-util ./configure --prefix=/usr/local/apr-util --with-apr=/usr/local/apr --with-expat=/usr/local/expat/ #apache ./configure --prefix=/usr/local/apache --with-expat=/usr/local/epxat --with-apr=/usr/local/apr --with-apr-util=/usr/local/apr-util --with-pcre=/usr/local/pcre/ 或者 ./configure --prefix=/usr/local/apache --with-expat=/usr/local/epxat --with-apr=/usr/local/apr --with-apr-util=/usr/local/apr-util --with-pcre=/usr/local/pcre/ #mod_python ./configure --prefix=/usr/local/mod_python --with-apxs=/usr/local/apache/bin/apxs
除wechat-sdk外,安裝命令均為make && make install
。wechat-sdk安裝方法為pip install wechat-sdk
配置apache
首先將Apache配置為自啟動服務,可參考這篇文章
服務啟動成功后,在瀏覽器輸入你的服務器ip,可看到如下It works! 字樣,則表示Apache安裝成功。接下來修改Apache配置文件,打開文件/usr/local/apache/httpd.conf,分別在對應位置添加如下配置
在<Directory "/usr/local/apache/htdocs">
下面添加
<Directory "/usr/local/apache/htdocs/daemon/webchat"> SetHandler mod_python PythonHandler index PythonDebug On Order allow,deny Allow from all </Directory>
在<IfModule alias_module>
下面添加
Alias /mywechat "/usr/local/apache/htdocs/daemon/webchat/mywechat.py"
上面的配置的意思是:1.告訴Apache,目錄/usr/local/apache/htdocs/daemon/webchat
將使用mod_python處理客戶端請求,所有請求交給這個目錄下的index.py來處理;2.告訴Apache,為路徑/usr/local/apache/htdocs/daemon/webchat/mywechat.py
起了個別名,當客戶端訪問/mywechat的時候,實際上就是在訪問/usr/local/apache/htdocs/daemon/webchat
。
注,上述配置需要設置了DocumentRoot為/usr/local/apache/htdocs才有效
功能開發
接下來我們只需要向index.py和mywechat.py中添加對應的請求處理邏輯即可!
我們實現最簡單的功能,即接收服務器請求,並給客戶端發送消息。
要完成這一步,我們首先要了解我們都可能收到哪些請求,以及這些請求的作用。
- 首先在你登錄微信公共賬號,並點擊成為開發者后,系統會讓你驗證你的服務器配置,這時微信公共平台服務器會向你的系統發送一個GET請求,來驗證你的服務器,你需要按照微信制定的規則來返回驗證信息。
- 當驗證通過后,微信普通用戶向你發送消息時,普通用戶會發送給你的服務器一個POST請求,這個請求將請求內容包含在一個xml文檔中。
簡單來說,你需要處理這兩種正常請求。此外,在你公共賬號投入商業使用之前,還需要能夠處理別人偽造的非法請求,從而避免信息被竊取。
示例代碼
index.py
#encoding:utf-8 from mod_python import apache import os def handler(req): handler = req.uri[req.uri.rfind('/')+1:] if handler[-3:] == ".py" : handler = handler[0:-3] if not handler == "index" : req.add_handler("PythonHandler", handler); else: req.write("using the default handler : index") return apache.OK
mywechat.py
# -*- coding: utf-8 -*- #!/usr/bin/python from mod_python import apache from mod_python import util import os from wechat_sdk import WechatBasic import sys import _apache parse_qsl = _apache.parse_qsl def handler(req): req.no_cache=True wechat = WechatBasic(token='yourtoken') if req.method == "GET": req.content_type = "text/plain" args=req.args apache.log_error("get req caught!!!") check(req,wechat) elif req.method == "POST": req.content_type = "text/xml" apache.log_error("post req caught!!!") parse(req,wechat) else: apache.log_error("unknown req method") return apache.OK def check(req,wechat): parameters = util.FieldStorage(req) apache.log_error(str(parameters)) signature = parameters.get('signature',None) timestamp = parameters.get('timestamp',None) nonce = parameters.get('nonce',None) echostr = parameters.get('echostr','') if wechat.check_signature(signature=signature, timestamp=timestamp, nonce=nonce): apache.log_error("check succeed") req.write(echostr) return apache.OK else: apache.log_error("check failed") return apache.HTTP_UNAUTHORIZED def parse(req,wechat): #get request body try: clen = int(req.headers_in["content-length"]) except (KeyError,ValueError): raise apache.SERVER_RETURN(apache.HTTP_LENGTH_REQUIRED) req_body = req.read(clen) apache.log_error(req_body) #parse body try: wechat.parse_data(req_body) message = wechat.get_message() response = None if message.type == 'text': if message.content == 'wechat': response = wechat.response_text('^_^') else: text=u'text' response = wechat.response_text(text.encode('utf-8') + message.content.encode('utf-8')) elif message.type == 'image': response = wechat.response_text('image') elif message.type == 'subscribe': response = wechat.response_text('welcome to mywechat!!!') else: response = wechat.response_text('unknown'+message.type) req.write(response) apache.log_error(response) except Exception as e: apache.log_error("\n\n@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\n\n") apache.log_error(str(e)) apache.log_error("\n\n@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\n\n")
這樣簡單的demo就Ok了!
你可以拿起手機給自己的公共賬號發消息來測試了!
tips
- mod_python比較蛋疼的一個地方就是,很多時候出錯了它在log里只說一句:Segment Fault.....,根本就沒什么用,所以你需要在可能出現異常的地方將它們捕獲,並用
apache.log_error(str(e))
將錯誤信息輸出到log中。 - 實際安裝過程中,遇到了很多由於expat不兼容而出現的問題,而官方又沒有說明,所以我在這上面花了很多時間。實際上centos 5.10系統中預裝了expat,但配置Apache安裝項時,指定系統的expat的時候總會報錯,所以決定自己編譯安裝expat。mod_python使用過程中,會加載pyexpat.so,而我的系統中pyexpat.so僅支持版本為2.0.1的expat。由於pyexpat.so為python安裝包內的內容,我們無法更改,所以我們只能安裝版本為2.0.1的expat來解決這個問題。
- 使用這種方式實際使用時我還遇到了這個錯誤“symbol XML_SetHashSalt, version EXPAT_2_0_1_RH not defined in file libexpat.so.1 with link time reference”,通過
strings libexpat.so.1.5.2 |grep expat -i
我發現我安裝的expat中不包含“EXPAT_2_0_1_RH”這個字段,而系統自帶的則包括。
解決方法是
這個解決方法很不漂亮,但是目前沒找到別的解決辦法。cd d /usr/local/expat/lib ln -s /lib64/libexpat.so.1.5.2 libexpat.so.1.5.2
useful links
- 微信公共平台開發者文檔
- 微信公眾平台 Python 開發包文檔
- modpython官方文檔
- python_publisher對post請求content-type的限制
- logging with modpython
- 解決expat版本兼容問題IssuesWithExpatLibrary
TODO
文字編碼問題還沒有解決,當前環境無法給用戶發送中文消息