cdhigh 发表于 2013-07-05 08:37

web.py+Jinja2开源kindle推送网站,全网络首发“在GAE上使用Python生成MOBI“

本帖最后由 cdhigh 于 2013-07-05 08:53 编辑

起因:
最近因为网络上的一些kindle推送服务不太合自己的意,也不想买VIP服务,因此就自己使用web.py+Jinja2实现了此网站:
http://kindleear.appspot.com

用于推送自己感兴趣的RSS或网页内容至自己的kindle。
现已开源在github
https://github.com/cdhigh/kindleear

值得一说的是,此网站有一个首创,第一个移植calibre(一个电子书管理工具)的mobi生成模块用于google app engine(GAE)。

下面说说开发心得:

1.为何不使用GAE自带的webapp框架,而选择web.py?
   其实最重要的就是webapp缺少移植性,只能在gae上使用,反正webapp和web.py都需要学习,为何不学习一个以后在其他平台上也能使用的框架呢?还有,最开始时我也使用了django搭建了初始版本,后来发现使用django太繁琐了,太笨重了,而且时不时有各种各样的问题要解决,一气之下就丢一边去了。然后看了网上对于各种WEB框架的一大堆讨论分析对比,还是按照自己的心意和喜好选定了web.py。

2. 为何不使用web.py自带的模板,而使用jinja2?
   其实我之前已经使用web.py的模板搭建好系统框架了,后来发现Templetor模板(web.py自带模板)的“模板继承”功能太弱,各个页面之间的重复代码太多,就考虑换一个模板系统,因为学过django,所以jinja2无疑是首选,语法类似,并且jinja2也是GAE推荐,根据网上评测,性能也很好,所以就选定了jinja2。
   题外话:我没有使用web.py封装的jinja2接口,而是直接使用jinja2的Environment() API,也是为了更通用,不被绑死在web.py上,比如万一我哪天不爽,想切换成bottle...

3. 模块动态加载(插件机制)
   此网站支持插件机制,类似于calibre的recipe,写一个特定格式的py文件放到books目录下,网站自动加载并显示在“我的订阅”页面。
    核心代码:def LoadBooks():
    for bkfile in os.listdir(os.path.dirname(__file__)):
      if bkfile.endswith('.py') and not bkfile.startswith('__') and not bkfile.endswith("base.py"):
            bookname = os.path.splitext(bkfile)
            mbook = __import__("books." + bookname, fromlist='*')
            bk = mbook.getBook()
            RegisterBook(bk)4.更好的urlfetch。
   有些网站很可恶,如果你不带cookie访问,会返回302,并重定向到同一个地址,如果再次访问,有一定的概率会返回200。
   所以GAE时不时会报Too many redirects异常,又不是每次重现,之前因为这个问题,折腾了我好几天,后来经过google的反复调教,终于明白是cookie的问题,带cookie访问就没这个问题了。
   还有,因为要抓取网页,有时候网页中的一些图片会导致无法访问或访问超时,也会时不时出错,当然了,也是经过万能的google的帮助,在一位前辈那里学会了给urlfetch增加失败重试功能。
   然后,我再将上面两个解决方案合一,参见urlopener.py,使用此类代替urlfetch后,网站稳定了。

5.web.py在gae上使用session。
   web.py的session机制无法直接部署到GAE上,因为GAE不支持本地文件存储。
   这个问题的解决方案也是google来的,特地整理在这里,
   就是使用memcache实现一个Store子类,参见memcachestore.py文件。

6.尽管是个人网站,但是密码也不应该明文保存吧。
    但是也没有必要加密过甚,因此使用最简单的MD5即可:    import hashlib
    passwd = hashlib.md5(password).hexdigest()

cdhigh 发表于 2013-07-05 21:19

一个烦恼:
在移植calibre的mobi模块过程中,遇到了n次很恼火的文件和字符串编码问题,不是解码失败就是编码失败,每次都不是很容易解决,都快要崩溃了。
所以终于明白了python 3的潜力,python 3和以前版本至少从UNICODE的角度看就必须要替代python 2,如果GAE支持了python 3,我将毫不犹豫的马上转向python 3

ssfjhh 发表于 2013-07-08 16:55

楼主闭关一段时间就会有大作呀。

python3肯定是以后的方向,我早就看到了,所以直接就学python3,无视python2。:wink:

不知道gae支持python3有多难,google就是不支持python3。

我发现python3在windows下默认保存编码竟然不是utf-8,感觉很蛋疼。又不知道该怎么修改这个默认值,又或者必须重新编译才能搞定。

cdhigh 发表于 2013-07-08 22:26

回复 3# ssfjhh

有hack的方法,不过不推荐,建议每个文件都在文件头声明编码:
# -*- coding:utf-8 -*-
PYTHON保存时会自动按照编码声明保存。
这也是python推荐的方法。

如果不想这样,也可以自己修改代码:
3.2.7:
C:\Python32\Lib\idlelib\IOBinding.py
392行
return chars.encode('ascii')
将ascii修改为utf-8
==============
2.7.3
C:\Python27\Lib\idlelib\IOBinding.py的399行
return chars.encode('ascii')
将ascii修改为utf-8即可
   

sxcong 发表于 2013-07-09 11:58

推送服务听说nginx的模块比较好
我是自己在libevent为底层自己扩充了一下,适合自定义协议的推送。

ssfjhh 发表于 2013-07-09 13:06

回复 4# cdhigh


   已经改了貌似不行。我用的是Python 3.3.2 你说的这行代码在这个版本中是第397行。      if self.fileencoding == 'BOM':
            return BOM_UTF8 + chars.encode("utf-8")
      # See whether there is anything non-ASCII in it.
      # If not, no need to figure out the encoding.
      try:
            return chars.encode("utf-8")

cdhigh 发表于 2013-07-10 09:58

经过我亲自测试,证明是有效的,不知你为何无效。
只是要注意,对于全文都是英文字符来说,ASCII编码和UTF-8编码等效,如果没有BOM,你无法确认是否是UTF-8还是ASCII编码,所以要证明是否有效,内容至少有中文字符才行。

torvald 发表于 2013-07-20 14:48

lz,在GAE Launcher上怎么缺省的8080端口不能进行调试?
系统自带的guest book的demo却可以?

重度kindle用户,正在学习gae和python中

cdhigh 发表于 2013-07-20 23:29

话说GAE Launcher是经常出问题的,不是连不上就是报各种错误。
自从切换为使用命令行,从来就没有出过问题。
转到:C:\Program Files\Google\google_appengine
执行CMD命令
启动调试服务器:python.exe dev_appserver.py (app目录)
上传代码:python.exe appcfg.py update (app目录)

ssfjhh 发表于 2013-07-21 16:12

回复 7# cdhigh


    又试了下,依然不行,真是奇了怪了。>>> with open('abc.txt', 'w') as f:
        f.write('中华人民共和国,abc')

       
11
>>> with open('abc.txt', 'r',encoding = 'utf-8') as f:
        print(f.readline())

       
Traceback (most recent call last):
File "<pyshell#14>", line 2, in <module>
    print(f.readline())
File "D:\Python33\lib\codecs.py", line 300, in decode
    (result, consumed) = self._buffer_decode(data, self.errors, final)
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xd6 in position 0: invalid continuation byte
>>>
页: [1] 2 3 4
查看完整版本: web.py+Jinja2开源kindle推送网站,全网络首发“在GAE上使用Python生成MOBI“