2008年7月2日

有备无患:pinyin-urlify - 根据汉字自动生成拼音 URL

根据文章标题中的英语单词自动生成文章的 URL 是一个很好的体验,比如标题为“Girl's death sparks riot in China”,会被转换成“girls-death-sparks-riot-china”这样与之对应的 URL。这样既可获得 Cool URIs,又便于用户记忆,同时也有利于 SEOBloggerWordpress 也都支持类似体验。而中文的文章标题,也可以先将其中的汉字转化为拼音后再组成相应的 URL,比如标题为“俯卧撑”,可以被转换成“fu-wo-cheng”。

pinyin-urlify 是我这几天写的能根据汉字自动生成拼音 URL 的 Python 脚本。它可以将汉字映射成有英语字母组成的拼音,和多种西方语言的字母(拉丁语、希腊语、俄语等)映射成相近的英语字母。要说明的是,汉字-拼音的映射表是从 pyzh 项目中获得,而其他西方语言的映射表则是从 Django 项目中获得,特表感谢

pinyin-urlify 支持自定义停用词列表(stop words)和保留词列表(reserved words)。停用词列表中的单词都会被过滤,不会出现在生成的 URL 中。生成的 URL 如果和保留词列表中的某一个单词匹配,则会被替换成默认的 URL 字符串。pinyin-urlify 可以指定 URL 的最大长度。不过,如果遇到有较多汉字组成的文章标题的话,实际生成的 URL 可能会比限定的最大长度小一些。我希望能在以后修正这一点。

使用示例

>>> from urlify import urlify
>>> urlify(u'三个俯卧撑引发的血案', default='blog_post',
...        max_length=50, stop_words=[u'is', u'a', u'an'],
...        reserved_words=[u'new', u'edit'])
u'san-ge-fu-wo-cheng-yin-fa-de-xue'


欢迎大家用 svn 检出 pinyin-urlify 的副本来使用。欢迎任何意见或建议 :-)

$ svn checkout http://pinyin-urlify.googlecode.com/svn/trunk/ pinyin-urlify

标签:

2008年5月9日

有备无患:在 Google App Engine上应用豆瓣 API 的授权认证

上次说到了在 App Engine 上应用的豆瓣的 Python 客户端,但是那个版本(0.1.1)的客户端并没有包括 OAuth 授权认证的功能。如果想要让用户授权,以访问那些受保护的资源,以及添加、修改或删除用户的收藏,需要从豆瓣 Python 客户端的 SVN 中获取最新的开发版本(r22)。据豆瓣的开发人员 hongqn 说,OAuth Client 基本开发完毕,已经进入内测 bug 的阶段。

和 GData Python 客户端一样,开发版本的 OAuth Client 也是用 Python 自带的 httplib 模块来处理 HTTP 请求,所以原始的客户端不能直接在 App Engine 上使用,必须先将 httplib 替换成 urlfetch。现在只需要修改两个函数,但是豆瓣如果能像 GData Python 客户端一样把使用 httplib 的部分封装起来,甚至提供一个使用 urlfetch 的替换模块就更好了,希望豆瓣能采纳这个建议。下面是具体步骤,如果有什么问题,还请留言告知。

1,从 trunk 中 checkout 最新的豆瓣 Python 客户端开发版本
$ svn co http://douban-python.googlecode.com/svn/trunk/ douban-python/

2,修改客户端的 OAuth Client
$ cp douban-python/douban ~/doupye/douban -rf
$ cd ~/doupye/douban/
$ gvim client.py

client.py

# import httplib
from google.appengine.api import urlfetch

class OAuthClient:
    ... ...
    def fetch_token(self, oauth_request):
        # 被注释掉的是原来使用 httplib 的部分
        # connection = httplib.HTTPConnection("%s:%d" % (self.server, 80))
        # connection.request('GET', oauth_request.http_url,
        #     headers=oauth_request.to_header())
        # response = connection.getresponse()
        # r = response.read()
        url = oauth_request.http_url
        result = urlfetch.fetch(url, headers=oauth_request.to_header())
        r = result.content
        ... ...

    def access_resource(self, method, url, body=None):
        ... ...
        # connection = httplib.HTTPConnection("%s:%d" % (self.server, 80))
        # connection.request(method, url, body=body,
        #     headers=headers)
        # return connection.getresponse()
        result = urlfetch.fetch(url, payload=body, method=method, headers=headers)
        return result.content

3,使用 OAuth 授权的过程如下,在 App Engine SDK 提供的控制台(Interactive Console)中运行:
from douban.client import OAuthClient

client = OAuthClient(key=MY_API_KEY, secret=MY_SECRET)

# 获取未授权的Request Token
key, secret = client.get_request_token()
print key, secret
>>> c14023315549fe3743c17993ff4dfaa5 91af6245103ec3b7

# 获取请求用户授权的页面的 URL
url = client.get_authorization_url(key, secret)
print url
>>> http://www.douban.com/service/auth/authorize?oauth_token=a9e487ac36e0ba9efdba970534a22fce

# 将 URL 复制到浏览器中,用户可以选择同意或者拒绝授权

# 用户完成授权后,使用授权后的 Request Token 换取 Access Token
key, secret = client.get_access_token(key, secret)
if key:
    # 使用 Access Token 登录
    login = client.login(key, secret)
    print login
>>> True

# 访问受保护资源
collections = client.access_resource(method='GET',
    url='http://api.douban.com/people/wyt/collection?cat=book')
for entry in collections.entry:
    print entry.title.text
>>>
听过 人として軸がぶれている
想读 Antipatterns
想读 新企业的起源与演进
看过 .hack//G.U. Trilogy
想听 ワイルドストロベリー
在听 The Flower Book
在听 E=MC²
听过 越长大越孤单
想读 Investing 101
想读 The Ecology of Commerce

标签: , ,

2008年4月21日

有备无患:在 Google App Engine 上应用豆瓣 Python 客户端

Google App Engine 是 Google 四月初推出的一个网络应用开发平台,它提供了一体化的分布式服务器群、供快速开发的网络应用开发框架、最多500MB的数据存储,以及可自动升级的后台流量统计服务。换言之,App Engine 让开发人员专注于应用本身,Google 将提供应用运行及维护所需要的一切平台资源。

App Engine 目前只支持 Python 作为唯一的开发语言(wyt:谁让“Python 之父” Guido van Rossum 也在 Google 工作呢,近水楼台先得月)。所以,如果想在 App Engine 上利用豆瓣开放的书影音和用户数据,我们可以使用豆瓣提供的 Python 客户端来开发。另外,由于以前的 GData Python 客户端 都是用 httplib 模块来处理 HTTP 请求,而 App Engine 则规定必须通过其自带的 urlfetch 才能实现,所以为了让豆瓣 Python 客户端所必须的 gdata.service 模块能够正常的在 App Engine 上工作,我们还需要版本号大于1.0.12.1的 gdata-python-client。下面是具体步骤,如果有什么问题,还请留言告知。

1,下载 gdata.py-1.0.12.1.tar.gz
2,解压缩到当前目录,并编译 gdata

$ tar xf gdata.py-1.0.12.1.tar.gz
$ cd gdata.py-1.0.12.1/
$ ./setup.py build

3,将编译好的 atom 和 gdata 目录复制到项目目录 ~/doupye/

$ cp build/lib/* ~/doupye/

4,下载 douban-python-0.1.1.tar.gz
5,解压缩到当前目录,并编译 douban-python

$ cd ..
$ tar xf douban-python-0.1.1.tar.gz
$ cd douban-python-0.1.1/
$ ./setup.py build

6,将编译好的 douban 目录复制到项目目录 ~/doupye/

$ cp build/lib/* ~/doupye/

7,修改豆瓣的 Python 客户端文件 service.py

$ cd ~/doupye/douban/
$ gvim service.py

service.py

import gdata.service
# 添加下面两行,让所有的 HTTP 请求调用 App Engine 的 urlfetch (?)
import gdata.urlfetch
gdata.service.http_request_handler = gdata.urlfetch


演示网页

http://doupye.appspot.com/demo/douban_python_client/

标签: , ,

2008年4月16日

有备无患:订阅所有豆瓣用户的广播

以前写过输出单个用户的友邻广播 feeds 的脚本,后来豆瓣把原来的“友邻”一分为二成双向的“朋友”和单向的“关注”,那个脚本就过时了。所以我重写了 pydmb.py,并让它能够输出多层友邻关系的豆瓣广播 feeds 的 OPML 文件根据六度分割理论,平均只需要六层关系就可以联系到任何两个互不相识的人。那么,从任何一个豆瓣用户出发输出六层友邻关系的广播,是不是最终也可以得到几乎所有豆瓣用户的广播呢?我没有试过,因为即使只输出了两层友邻关系的广播,我已经得到 8825个 feeds,如果输出六层的话会吓到人的。

使用方法

1,下载 pydmb.py

pydmb-0.2.tar.gz

2,解压缩到当前目录。

3,运行脚本。这里举一个例子,比如你想获得 keso阿北的三层朋友(不包括他们关注的人)的广播 feeds,可以输入命令:

$ ./pydmb.py keso ahbei --friend --depth 3

4,需要帮助可以运行命令:

$ ./pydmb.py --help
Usage: python pydmb.py [-fc] [-d DEPTH] user1 user2 ...

Options:
  -h, --help            show this help message and exit
  -f, --friend          output douban miniblog rss of your friends
  -c, --contact         output douban miniblog rss of your contact
  -d DEPTH, --depth=DEPTH
                        the depth of relationship to output


2008-04-18 UPDATE:

NullPointer 留言说想看看六度连接的试验结果,我也很感兴趣,所以昨天先试了试二度和三度的连接。结果从阿北出发的二度空间能连接到10214个人,三度空间能连接到117113个。

>>> from pydmb import *
>>> graph = UserGraph('ahbei')
>>> graph.search(2, 'fc')
>>> len(graph.dict.keys())
10214
... ...
>>> graph.search(3, 'fc')
>>> len(graph.dict.keys())
117113

结果还算理想,可是用的时间比较厉害,二度连接还好只用了十几分钟,但三度连接用了将近八个小时。照这样推算,分析六度连接(理论上说,就是要分析将近140万豆瓣用户的朋友和关注的人)可能会超过800个小时。所以用这个脚本来做就不太现实了,如果要做的话,最好把 urllib2BeautifulSoup 换成更快的库,然后用两个线程分别来抓取和分析网页,这样效率会高一些。

标签: ,

2008年3月16日

有备无患:为Blogger传统模板(FTP发布)添加标签云

Blogger的传统模板一直不支持标签的输出。虽然有hack可以从feed中抓取标签列表,但不能分析每个标签所含的文章数,所以仍然不能生成随标签使用频率而字体大小和颜色变化的真正意义上的“标签云”。

所以,我写了一个python的自动化脚本,可以从ftp服务器上分析blogger文件,计算出每个标签的使用频率,并将这些信息保存在服务器上,供模板中的javascript脚本调用。效果么,你可以看看这个演示网页,或者点击我blog下方widget dock左起第四个“文件夹”图标(wyt:还在用IE6的同学们应该看不到。因为IE6不支持widget dock用的透明png,所以我把widget dock给隐藏起来了,实际上“标签云”还在那里。IE7、Firefox、Opera、Safari等应该都没问题)

使用方法很简单,要执行脚本首先得有python。Linux大多默认安装,Windoze上安装Python的方法见这篇文章

另外,脚本中用到了BeautifulSoup分析HTML文件,simpleJSON生成JSON格式的数据流。你也必须安装这两个python模块。如果你和我一样用Gentoo,just emerge it。Windoze用户可以用easy install工具快速安装。

接下来下载压缩文件:konatag.tar.gz,解压缩用得到两个文件konatag.py和konatag.js。

konatag.tar.gz

修改konatag.py其中的ftp服务器,用户名,密码以及归档(archives)的路径参数,以便脚本可以访问你的ftp服务器。

if __name__ == '__main__':
    options = dict(
        server='yourftpserver',    # 服务器地址不用包含ftp://
        username='yourusername',   # 用户名
         password='yourpassword',   # 密码
         path='/path/to/archives/'  # blogger的归档路径
    )
    ftp = FTPServer(**options)
    blogger = Blogger(ftp)

保存退出之后,在终端(wyt:也就是Windoze的命令行)执行:

$ python konatag.py

事实上,如果高兴的话,可以像我一样把这个脚本扔到crontab的计划任务中,每星期执行一次自动更新你的Blogger的标签信息。

执行完脚本之后,登录ftp,在你的blogger归档目录中会多出一个konatag.json的文件,这是一个包含JSON格式数据的文本文件。不用去管它,将konatag.js上传到任何你能记住的目录。接下来我们将要修改Blogger模板。需要说明的是,konatag.js使用prototype库,所以在模板里还必须包含相应的js文件。

<!-- 将js的调用加入<head> -->
<script type="text/javascript" src="/scripts/prototype.js"></script>
<script type="text/javascript" src="/scripts/konatag.js"></script>
<!-- or: 如果你想直接调用我这儿的javascript脚本的话 -->
<script type="text/javascript" src="http://demos.luliban.com/scripts/prototype.js"></script>
<script type="text/javascript" src="http://demos.luliban.com/scripts/konatag.js"></script>

<!-- 这个div是“标签云”的容器,加入<body>中
     id可以随意指定,但要与下面的javascript对象的第一个参数相一致 -->
<div id="konatag"></div>

<script type=text/javascript>//<![CDATA[ 
    // Konatag的第一个参数是刚才提到的div的id
    // 第二个参数是脚本上传的JSON数据的路径
    new Konatag('konatag', '/blog/archives/konatag.json', {
       startSize: 13,    // 使用频率最低的标签的字号,单位是px,请随意设定,下同
        stopSize: 24,     // 使用频率最高的标签的字号,单位同样是px
       startColor: '#3d81ee',    // 使用频率最低的标签的颜色
        stopColor: '#930fe3'      // 使用频率最高的标签的颜色
    });
//]]></script>

这样就大功告成,只需要重新发布一遍模板就可以了。有什么意见,建议和砖头,欢迎留言。

标签: , ,

2007年11月26日

有备无患:如何在GTalk / Skype / MSN上收听豆瓣的友邻广播

友邻广播是豆瓣的Twitter式微内容发布服务。令人遗憾的是,和Twitter不同,豆瓣广播既不能通过IM或手机收发内容,也不能通过API扩展功能。对于广播的深度中毒者,可能希望至少能够在IM里接收,这里有一个能够在GTalk / Skype / MSN上收听友邻广播的办法。

如何在GTalk / Skype / MSN上收听豆瓣的友邻广播

第一步,抓取友邻们的广播feed。如果你不希望全手动去找的话,我写了一个简单的自动抓取这些feed的python脚本pydmb.py。

下载pydmb.tar.gz

不过,首先你需要安装PythonuTidyLib。(wyt:Python有啥自带又好用的htmltidy库?)

Linux上大多默认装有Python,所以只需安装uTidyLib。如果你使用Gentoo的话,可以直接emerge:

# echo "dev-python/utidylib ~x86" >> /etc/portage/package.keywords
# emerge -av utidylib

下载pydmb.tar.gz之后,解压缩到当前目录,然后运行:

$ python pydmb.py -u uid
(比如,我的豆瓣主页是:http://www.douban.com/people/wyt/,那么我的uid就是wyt。)

脚本执行完之后,你会在目录下找到一个export.opml文件,里面包含了所有友邻的feed。

第二步,在哪吒上订阅这些feed。哪吒是一个feed的订阅、提醒与分享服务,通过哪吒的GTalk/Skype/MSN机器人,可以在这些IM上获得feed更新消息。你可以在哪吒上导入刚才用脚本输出的OPML文件。然后,按照哪吒的说明,你还需要在GTalk、Skype或者MSN上添加哪吒的IM机器人。

大功告成,等着哪吒抓取这些feed就好。以我的经验来看,第一次抓取的等待时间比较长,大约花了四个小时哪吒才开始陆陆续续处理这些feed。不过之后的更新速度还不错,一般在7~14分钟内都会把最新的广播推送过来。另外,导出的OPML文件也可以一样用于Google Reader和抓虾等feed阅读器,或者其他和哪吒类似的IM机器人。
如果有什么意见建议或bug,无论是在这里,还是在pydmb.py脚本的项目主页上都欢迎留言。下一步我希望可以抓取更多层关系的友邻广播feed,也就是说可以看到朋友的朋友的。。。朋友的朋友的广播。不过要等到豆瓣有友邻的API以后。

标签: , ,

2007年9月23日

有备无患:在支持FastCGI的Lighttpd服务器上部署Django的笔记

Django是一个鼓励快速开发和简练设计的基于Python的高级网络框架。这篇文章详细记录了我在本地部署Django的过程,服务器是支持FastCGI的Lighttpd,操作系统是Gentoo Linux。条条大路通罗马,网上也有很多Django的教程,但这可能是更适合Django本地学习和更有效率的一条捷径。欢迎FIX-ME

开读之前,请注意一下时效性:Django版本:0.96,Lighttpd版本:1.4.18,日期:2007年9月。

一、安装Lighttpd


无论什么时候,在Gentoo上安装软件都是一件愉快的事情,因为我们可以通过USE flags来选择需要安装的包。如果你和我一样记不住这些USE flags的具体意义,不妨用euse查看一下,例如:


# euse -i fastcgi

Lighttpd包括下列USE flags:

  • bzip2 - 为mod_compress提供bzip2支持
  • fastcgi - 提供FastCGI支持,使用Django或PHP都需要FastCGI来提升性能
  • gdbm -提供GNU数据库支持
  • ipv6 - 提供IPv6支持
  • lua - 为mod_cml提供Lua支持
  • memcache - 为for mod_cml提供内存缓存支持
  • mysql - 为mod_mysql_vhost提供MySQL的设定支持
  • pcre - 为Lighttpd配置文件中正则表达式提供PCRE支持
  • php - 提供mod_fastcgi/php的设定支持
  • rrdtool - 为一些统计图形提供RRDtool支持
  • ssl - 为HTTPS提供OpenSSL支持
  • doc -提供安装文档支持
  • fam - 为减少stat()的响应数,提供fam/gamin支持
  • ldap - 提供LDAP支持
  • minimal - 最小化安装,不包括安装文档和不使用的模块
  • webdav - 提供WebDAV支持
  • xattr -提供额外的属性支持

和Apache有内嵌的Python解释器(mod_python)不同,Lighttpd只能通过CGI或者FastCGI方式执行Python脚本。CGI的先天不足导致它在大型Web应用上效率不佳。FastCGI在设计上继承了CGI不受操作系统、服务器和开发语言的限制,又利用一个类似常驻型CGI的设计大幅提高了执行效率。所以,我们在安装Lighttpd的时候,需要把fastcgi选入USE flags。至于其他选项,大家就各取所需了。


# echo "www-servers/lighttpd fastcgi ssl fam" >> /etc/portage/package.use
# emerge -av lighttpd

二、安装Django


接下来安装Django。本地安装Django很轻松,直接emerge就可以了:


# echo "dev-python/django examples sqlite" >> /etc/portage/package.use
# emerge -av django

emerge完以后,我们试试Django是否安装正确。运行Python,输入:


>>> from django.core.handlers.wsgi import WSGIHandler

也可以把其他Django的组件导入Python。如果没有出错信息,表示Django安装完成了。

三、安装flup


flup是一个基于Python的架设FastCGI服务器的模块,这也是需要安装的。


# emerge -av flup

然后,我们试试flup是否安装正确。运行python,输入:


>>> from flup.server.fcgi_fork import WSGIServer

如果没有出错信息,表示flup安装完成。

四、配置Lighttpd和FastCGI


Lighttpd自带的配置相当完整,只需要稍作修改就可以让Lighttpd支持FastCGI。
首先修改lighttpd.conf,自定义Lighttpd的主目录和日志目录等。


# {{{ variables
var.basedir = "/var/www/localhost"
var.logdir = "/var/log/lighttpd"
var.statedir = "/var/lib/lighttpd"
# }}}

你可以把所有的配置都塞到lighttpd.conf中,但是,Lighttpd默认你把CGI和FastCGI的配置文件分别放到了mod_cgi.conf和mod_fastcgi.conf文件中,并在lighttpd.conf中include它们。这样的结构更加简洁明白,本文也采用这种方法配置Lighttpd。


# {{{ includes
include "mime-types.conf"
include "mod_cgi.conf"
include "mod_fastcgi.conf"
# }}}

为2.6核心版本的Linux优化性能。


# for >= linux-2.6
server.event-handler = "linux-sysepoll"

指定用mod_fastcgi处理.py文件。


static-file.exclude-extensions = (".php", ".pl", ".py", ".cgi", ".fcgi")

在lighttpd.conf中,我们还可以设定一些其他参数,比如server.max-worker、server.max-keep-alive-requests、server.max-keep-alive-idle等,来优化Lighttpd的性能,让Lighttpd在大型的Web应用中表现更好。

接下来,修改mod_cgi.conf。


server.modules += ("mod_cgi")

alias.url = (
"/cgi-bin/" => var.basedir + "/cgi-bin/"
)

$HTTP["url"] =~ "^/cgi-bin/" {
    # disable directory listings
    dir-listing.activate = "disable"
    # only allow cgi's in this directory
    cgi.assign = (
        ".pl" => "/usr/bin/perl",
        ".py" => "/usr/bin/python",
        ".cgi" => "/usr/bin/perl"
    )
}

修改mod_fastcgi.conf。



server.modules += ("mod_fastcgi")
fastcgi.server = (
    "/main.fcgi" => (
        "main" => (
            "socket" => var.basedir + "/htdocs/tmp/django.socket",
        )
    ),
)

最后,我们重新启动Lighttpd,并把lighttpd加入启动项。


# /etc/init.d/lighttpd restart
# rc-update add lighttpd default

这样子就万事OK了,我们已经成功的把Django部署在支持FastCGI的Lighttpd服务器上了。接下去就可以开始设计自己的Django项目了。

标签: , ,