闭关纪要20.在Google App Engine之中解析RSS

        从上一篇闭关纪要文章闭关纪要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之中的内容,并将相应的结果返回。

 

ContractedBlock.gif ExpandedBlockStart.gif joke_random.py
 1# -*- coding: utf-8 -*- #   
 2import wsgiref.handlers
 3import cgi
 4import types
 5from datetime import *
 6from xml.dom import minidom 
 7from google.appengine.ext import db
 8from google.appengine.ext import webapp
 9from google.appengine.api import urlfetch
10
11#定义Rss条目的数据类
12class 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
19class 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
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>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
48    if not(rss):
49      if number>0:
50        return results[id%number]
51      else:
52        return None
53
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
69    if number==0:
70      return None
71    return self.getJoke(id)
72
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(")")
91def main():
92  application = webapp.WSGIApplication(
93                                       [('/services/joke_random', JokeRandom)],
94                                       debug=True)
95  wsgiref.handlers.CGIHandler().run(application)
96if __name__ == "__main__":
97  main()

 

        从以上的代码,就实现了一个这样的过程,客户端请求一个指定编号的笑话条目的时候,服务端请求一个rss文件,并且返回指定编号的条目,并将内容保存到数据库之中,以便下次查询的时候不再需要下载。

        我的程序最终目的是能够在客户端实现显示一个随机的笑话,效果可以从http://www.dituren.cn/  上面看到,每次地图加载完成之前,在地图显示区域先显示一个随机的笑话,因为是随机的,所以不会每次显示同一个笑话,而且因为有一定的缓存技术,加载速度比较快,可以用来在用户等待地图加载的时候看个笑话消遣用。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值