从上一篇闭关纪要文章闭关纪要19.Google Datastore API的疑似BUG?之后,因为这个BUG,至今没有得到解决,因此,我暂时停止了需要大批量向Google Datastore传递大量数据的程序的研究,不过还是继续在进行GAE相关的研究,特别是,我在发现我在万网申请的虚拟主机在铁通居然上不了,心中很郁闷,因此更加倾向于将我的网站的更多功能转移到GAE上了。
我原先以为下载和解析XML是一个很广泛的使用,在GAE上肯定使用起来很容易的,可是我慢慢发现,没有这么容易,Google App Engine居然在这个过程上BUG不少,我花了好久终于找到了一个能够完成XML下载和运行的方案。
我根据网上大家的讨论,使用过ElementTree,和SimpleXMLTreeBuilder,最终都出现这样或那样的BUG,例如"illegal character in content",还有几个error,我没有具体的error内容是什么了,而且在处理包含中文的XML的时候,又会出现更复杂的问题,因此,我专门的介绍一下我成功实现的一个RSS文件读取功能,希望对也要实现同样功能的用户有所帮助。
我最后采用的XML解析类是minidom,从名字看起来,是一个很简单的XML解析,简单与否我倒是不在意,只要能解析就行,要不然我就必须要通过正则表达式自己去匹配内容,可就累得多了。
我的简单实现代码如下(我这个是一个随机取一个笑话返回的REST程序,运行结果如下:http://service.dituren.cn/services/joke_random?id=30&c=onJokeLoaded,在客户端请求的时候,随机的使用一个id,服务端会自动下载一个RSS之中的内容,并将相应的结果返回。
![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif)
joke_random.py
1
# -*- coding: utf-8 -*- #
2
import wsgiref.handlers
3
import cgi
4
import types
5
from datetime import *
6
from xml.dom import minidom
7
from google.appengine.ext import db
8
from google.appengine.ext import webapp
9
from google.appengine.api import urlfetch
10![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
11
#定义Rss条目的数据类
12
class RssData(db.Model):
13
category = db.CategoryProperty(required=True)
14
rssUrl = db.LinkProperty(required=True)
15
downloadTime = db.DateTimeProperty(auto_now_add= True )
16
title = db.StringProperty(required=True)
17
content = db.TextProperty()
18![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
19
class JokeRandom(webapp.RequestHandler):
20
#根据指定的url,返回minidom的解析结果文档,如果解析失败,则不返回内容
21
def parse(self, url ) :
22
result = urlfetch.fetch(url)
23
if result.status_code == 200:
24
content=result.content
25
#这一句用来将返回内容里面的xml文件头的encoding="gb2312"替换成encoding="utf-8"
26
content=content.replace("gb2312","utf-8")
27
#下面这一句用来进行编码转换,本来应该也是gb2312的,可是我调用的这个XML不够规范,他返回的实际编码是gbk,可是文件头里面写的却是gb2312
28
#因此,要进行一个这样的转换
29
#很多中文网站都喜欢以这样变态的方式输出,因此特别要注意了
30
content=content.decode("gbk").encode('UTF_8')
31
#将utf-8编码的XML传递给minidom
32
return minidom.parseString(content)
33![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
34
def getJoke(self,id):
35
query=RssData.all()
36
query.filter('category = ','Joke')
37
#根据请求的条目编号,请求指定数目的条目
38
results=query.fetch(id+1)
39
number=len(results)
40
#如果已经有数据,并且没有过期,则直接返回指定数据
41
if number>0 and datetime.now()-results[0].downloadTime<timedelta(7):
42
#这里必须取模,因为数据未必有这么多条
43
return results[id%number]
44
rssUrl='http://www.xiaohuayoumo.com/plus/rss/5.xml'
45
#下载和解析rss内容
46
rss = self.parse(rssUrl)
47![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
48
if not(rss):
49
if number>0:
50
return results[id%number]
51
else:
52
return None
53![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
54
if number>0:
55
query=RssData.all()
56
query.filter('category = ','Joke')
57
results=query.fetch(1000)
58
db.delete(results)
59
number=0
60
#从RSS文件之中解析条目并添加到数据库之中
61
for item in rss.getElementsByTagName('item'):
62
title=item.getElementsByTagName('title')[0].firstChild.nodeValue
63
content=item.getElementsByTagName('description')[0].firstChild.nodeValue
64
joke=RssData(category="Joke",rssUrl=rssUrl,title=title)
65
joke.content=db.Text(content)
66
joke.put()
67
number=number+1
68![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
69
if number==0:
70
return None
71
return self.getJoke(id)
72![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
73
def get(self):
74
if self.request.headers.get('If-Modified-Since',''):
75
self.response.set_status(304)
76
return
77
#设置缓存
78
self.response.headers['Content-Type'] = 'text/html; charset=UTF-8'
79
self.response.headers.add_header("Expires", (datetime.now()+timedelta(7)).strftime("%a, %d %b %Y %H:%M:%S GMT"))
80
self.response.headers.add_header("Last-Modified", datetime.now().strftime("%a, %d %b %Y %H:%M:%S GMT"))
81
#输出REST的调用函数名称
82
self.response.out.write(self.request.get('c'))
83
id=self.request.get('id')
84
if not id:
85
id=0
86
joke=self.getJoke(int(id))
87
self.response.out.write('(')
88
if joke :
89
self.response.out.write('{title:\"'+joke.title+'\",content:\"'+joke.content+'\"}')
90
self.response.out.write(")")
91
def main():
92
application = webapp.WSGIApplication(
93
[('/services/joke_random', JokeRandom)],
94
debug=True)
95
wsgiref.handlers.CGIHandler().run(application)
96
if __name__ == "__main__":
97
main()
从以上的代码,就实现了一个这样的过程,客户端请求一个指定编号的笑话条目的时候,服务端请求一个rss文件,并且返回指定编号的条目,并将内容保存到数据库之中,以便下次查询的时候不再需要下载。
我的程序最终目的是能够在客户端实现显示一个随机的笑话,效果可以从http://www.dituren.cn/ 上面看到,每次地图加载完成之前,在地图显示区域先显示一个随机的笑话,因为是随机的,所以不会每次显示同一个笑话,而且因为有一定的缓存技术,加载速度比较快,可以用来在用户等待地图加载的时候看个笑话消遣用。