數(shù)據(jù)的解析是不可避免的,我們繼續(xù)推進(jìn)我們的代碼。
下一個目標(biāo),是在回復(fù)了消息的基礎(chǔ)上更一步。不是固守地回復(fù)一個 hello
,而是用戶發(fā)什么文本,就回復(fù)什么文本。
看到前面的 xml 數(shù)據(jù),可能有人就直接暴力地操上正則表達(dá)式,去抽取各字段的內(nèi)容了。這當(dāng)然也行,畢竟這套 xml 格式是很簡單的。當(dāng)然,即使如此,你寫的正則表達(dá)式也不一定總能正式工作了,比如我發(fā)的消息內(nèi)容就是 ]]>
,那么微信服務(wù)器過來的數(shù)據(jù)就是:
<xml><ToUserName><![CDATA[gh_b47caeadeeb7]]></ToUserName> <FromUserName><![CDATA[ov_QzuF0iskLIXqu0r71qOLmZV6B]]></FromUserName> <CreateTime>1407301203</CreateTime> <MsgType><![CDATA[text]]></MsgType> <Content><![CDATA[]]>]]></Content> <MsgId>6044312642707056806</MsgId> </xml>
有些糾結(jié)了吧。你可以拿 ]]>
這些字符串去試一下其它的公眾賬號的反應(yīng),上面的內(nèi)容不是合格的 XML 。
正則不是不能寫,但是解析 xml ,還是上專業(yè)的工具更合適,也更高效與方便。同時我們不光要解析 XML 數(shù)據(jù),在回復(fù)時還需要構(gòu)建 XML 數(shù)據(jù),雖然在構(gòu)建上使用 lxml 的 api 不一定比直接上模板直觀。
lxml
是 Python 的第三方模塊,是對 C 實現(xiàn)的 libxml
的封裝。SAE 已經(jīng)預(yù)裝了此模塊,謝天謝地。此項目的官網(wǎng)在 http://lxml.de/ 。你可以需要去上面看看文檔和示例代碼。
使用 lxml
解析比較小的 xml 時,xpath
是一個強(qiáng)大的工具,你可能需要額外地去了解一下 xpath
的基本使用。
要在 SAE 中使用 lxml 這個模塊,需要在 config.yaml
中單獨配置一下,先在下面的文檔看看 SAE 支持的預(yù)裝模塊及對應(yīng)的版本:
http://sae.sina.com.cn/doc/python/runtime.html#pre-installed-package-list
修改 config.yaml
文件:
name: xxx version: 1 libraries: - name: lxml version: "2.3.4"
前面說過了,除了解析 XML ,我們還使用 lxml 生成響應(yīng)的 XML 數(shù)據(jù)。lxml 提供的 E-factory 這個 API 用來生成 XML 數(shù)據(jù)很好用。不過壞消息是它不支持節(jié)點的填充內(nèi)容為 CDATA
,好消息是在 github 上的 lxml 項目的開發(fā)源碼中,已經(jīng)添加了這個特性。
這里我們就要自己動手搞點東西了,我們把 github 上的 builder.py
的最新源碼拿下來單獨用。為了組織代碼方便,我們先在項目目錄中創(chuàng)建一個 packages
目錄,第三方模塊就往這里放。
mkdir packages cd packages wget https://raw.githubusercontent.com/lxml/lxml/master/src/lxml/builder.py -Olxml_builder.py
這樣就可以了。現(xiàn)在慢慢地我們的邏輯在增加,每做一次修改,都要把代碼提交到 SAE 上,然后打開手機(jī),發(fā)送消息來檢查代碼的正確于否,這樣做太痛苦了。還記得之前做的 server.py
么,我們應(yīng)該在本機(jī)完成更多的測試工作。
在不添加驗證邏輯的情況下,和微信服務(wù)器的交互,僅僅是處理 POST 的數(shù)據(jù)而已,這點我們在本機(jī)很容易實現(xiàn)。
創(chuàng)建一個 sample
目錄,把之前在 storage 中保存的那幾個文本,圖片,音頻的請求數(shù)據(jù)存到文件中去。
現(xiàn)在我們項目的目錄樹是這樣的了:
. ├── config.yaml ├── index.wsgi ├── packages │ ├── lxml_builder.py ├── sample │ ├── wx-img.xml │ ├── wx-txt.xml │ └── wx-voice.xml └── server.py
開始在 index.wsgi
中實現(xiàn)我們的邏輯,用戶發(fā)什么,我們就回復(fù)什么:
# -*- coding: utf-8 -*- import sys import os sys.path.insert(0, os.path.join(os.path.dirname(__file__), 'packages')) import re from lxml import etree from lxml_builder import E from lxml.etree import CDATA def application(environ, start_response): if environ.get('REQUEST_METHOD', 'GET') == 'GET': q = environ.get('QUERY_STRING') m = re.findall('echostr=(.*)', q)[0] s = m.split('&', 1)[0] start_response('200 ok', [('content-type', 'text/plain')]) return [s] length = environ.get('CONTENT_LENGTH', 0) length = int(length) body = environ['wsgi.input'].read(length) try: root = etree.XML(body.decode('utf8')) except: start_response('200 ok', [('content-type', 'text/xml')]) return [''] me = root.xpath('/xml/ToUserName/text()')[0] user = root.xpath('/xml/FromUserName/text()')[0] create = root.xpath('/xml/CreateTime/text()')[0] type = root.xpath('/xml/MsgType/text()')[0] content = root.xpath('/xml/Content/text()')[0] id = root.xpath('/xml/MsgId/text()')[0] xml = ( E.xml( E.ToUserName(CDATA(user)), E.FromUserName(CDATA(me)), E.CreateTime(CDATA(create)), E.MsgType(CDATA('text')), E.Content(CDATA('\n'.join([me, user, create, type, content, id]))), ) ) s = etree.tostring(xml, encoding='utf-8') start_response('200 ok', [('content-type', 'text/xml')]) return [s]
這里我們不光回復(fù)了用戶輸入的內(nèi)容,還把整個請求信息都得現(xiàn)一遍。
先在本機(jī)跑跑,啟動 python server.py
,然后使用 curl
發(fā)送保存 sample
目錄下的數(shù)據(jù)看看結(jié)果:
curl -d @sample/wx-txt.xml 'http://localhost:8888'
得到正確的 xml 響應(yīng)就沒問題了。把代碼提交到 SAE,用手機(jī)上的微信試試了。
更多建議: