SAE 使用lxml處理XML數(shù)據(jù)

2018-07-26 18:09 更新

數(shù)據(jù)的解析是不可避免的,我們繼續(xù)推進(jìn)我們的代碼。

下一個(gè)目標(biāo),是在回復(fù)了消息的基礎(chǔ)上更一步。不是固守地回復(fù)一個(gè) hello,而是用戶(hù)發(fā)什么文本,就回復(fù)什么文本。

看到前面的 xml 數(shù)據(jù),可能有人就直接暴力地操上正則表達(dá)式,去抽取各字段的內(nèi)容了。這當(dāng)然也行,畢竟這套 xml 格式是很簡(jiǎn)單的。當(dāng)然,即使如此,你寫(xiě)的正則表達(dá)式也不一定總能正式工作了,比如我發(fā)的消息內(nèi)容就是 ]]> ,那么微信服務(wù)器過(guò)來(lái)的數(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é)了吧。你可以拿 ]]> 這些字符串去試一下其它的公眾賬號(hào)的反應(yīng),上面的內(nèi)容不是合格的 XML 。

正則不是不能寫(xiě),但是解析 xml ,還是上專(zhuān)業(yè)的工具更合適,也更高效與方便。同時(shí)我們不光要解析 XML 數(shù)據(jù),在回復(fù)時(shí)還需要構(gòu)建 XML 數(shù)據(jù),雖然在構(gòu)建上使用 lxml 的 api 不一定比直接上模板直觀(guān)。

lxml 是 Python 的第三方模塊,是對(duì) C 實(shí)現(xiàn)的 libxml 的封裝。SAE 已經(jīng)預(yù)裝了此模塊,謝天謝地。此項(xiàng)目的官網(wǎng)在 http://lxml.de/ 。你可以需要去上面看看文檔和示例代碼。

使用 lxml 解析比較小的 xml 時(shí),xpath 是一個(gè)強(qiáng)大的工具,你可能需要額外地去了解一下 xpath的基本使用。

要在 SAE 中使用 lxml 這個(gè)模塊,需要在 config.yaml 中單獨(dú)配置一下,先在下面的文檔看看 SAE 支持的預(yù)裝模塊及對(duì)應(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"

前面說(shuō)過(guò)了,除了解析 XML ,我們還使用 lxml 生成響應(yīng)的 XML 數(shù)據(jù)。lxml 提供的 E-factory 這個(gè) API 用來(lái)生成 XML 數(shù)據(jù)很好用。不過(guò)壞消息是它不支持節(jié)點(diǎn)的填充內(nèi)容為 CDATA ,好消息是在 github 上的 lxml 項(xiàng)目的開(kāi)發(fā)源碼中,已經(jīng)添加了這個(gè)特性。

這里我們就要自己動(dòng)手搞點(diǎn)東西了,我們把 github 上的 builder.py 的最新源碼拿下來(lái)單獨(dú)用。為了組織代碼方便,我們先在項(xiàng)目目錄中創(chuàng)建一個(gè) packages 目錄,第三方模塊就往這里放。

mkdir packages
cd packages
wget https://raw.githubusercontent.com/lxml/lxml/master/src/lxml/builder.py -Olxml_builder.py

這樣就可以了?,F(xiàn)在慢慢地我們的邏輯在增加,每做一次修改,都要把代碼提交到 SAE 上,然后打開(kāi)手機(jī),發(fā)送消息來(lái)檢查代碼的正確于否,這樣做太痛苦了。還記得之前做的 server.py 么,我們應(yīng)該在本機(jī)完成更多的測(cè)試工作。

在不添加驗(yàn)證邏輯的情況下,和微信服務(wù)器的交互,僅僅是處理 POST 的數(shù)據(jù)而已,這點(diǎn)我們?cè)诒緳C(jī)很容易實(shí)現(xiàn)。

創(chuàng)建一個(gè) sample 目錄,把之前在 storage 中保存的那幾個(gè)文本,圖片,音頻的請(qǐng)求數(shù)據(jù)存到文件中去。

現(xiàn)在我們項(xiàng)目的目錄樹(shù)是這樣的了:

.
├── config.yaml
├── index.wsgi
├── packages
│   ├── lxml_builder.py
├── sample
│   ├── wx-img.xml
│   ├── wx-txt.xml
│   └── wx-voice.xml
└── server.py

開(kāi)始在 index.wsgi 中實(shí)現(xiàn)我們的邏輯,用戶(hù)發(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ù)了用戶(hù)輸入的內(nèi)容,還把整個(gè)請(qǐng)求信息都得現(xiàn)一遍。

先在本機(jī)跑跑,啟動(dòng) python server.py ,然后使用 curl 發(fā)送保存 sample 目錄下的數(shù)據(jù)看看結(jié)果:

curl -d @sample/wx-txt.xml 'http://localhost:8888'

得到正確的 xml 響應(yīng)就沒(méi)問(wèn)題了。把代碼提交到 SAE,用手機(jī)上的微信試試了。


以上內(nèi)容是否對(duì)您有幫助:
在線(xiàn)筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)