<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet href="http://feeds.feedburner.com/~d/styles/rss2chinesetwfull.xsl" type="text/xsl" media="screen"?><?xml-stylesheet href="http://feeds.feedburner.com/~d/styles/itemcontent.css" type="text/css" media="screen"?><rss xmlns:geo="http://www.w3.org/2003/01/geo/wgs84_pos#" xmlns:creativeCommons="http://backend.userland.com/creativeCommonsRssModule" version="2.0"><channel><title>Luliban blog</title><link>http://luliban.com/blog/</link><language>en</language><managingEditor>noreply@blogger.com (wyt)</managingEditor><lastBuildDate>Thu, 07 Aug 2008 08:32:16 -0500</lastBuildDate><generator>Blogger http://www.blogger.com</generator><openSearch:totalResults xmlns:openSearch="http://a9.com/-/spec/opensearchrss/1.0/">111</openSearch:totalResults><openSearch:startIndex xmlns:openSearch="http://a9.com/-/spec/opensearchrss/1.0/">1</openSearch:startIndex><openSearch:itemsPerPage xmlns:openSearch="http://a9.com/-/spec/opensearchrss/1.0/">25</openSearch:itemsPerPage><description></description><geo:lat>31</geo:lat><geo:long>121</geo:long><creativeCommons:license>http://creativecommons.org/licenses/by-nc-sa/3.0/</creativeCommons:license><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" href="http://feeds.feedburner.com/luliban" type="application/rss+xml" /><feedburner:feedFlare xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" href="http://add.my.yahoo.com/rss?url=http%3A%2F%2Ffeeds.feedburner.com%2Fluliban" src="http://us.i1.yimg.com/us.yimg.com/i/us/my/addtomyyahoo4.gif">Subscribe with My Yahoo!</feedburner:feedFlare><feedburner:feedFlare xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" href="http://www.newsgator.com/ngs/subscriber/subext.aspx?url=http%3A%2F%2Ffeeds.feedburner.com%2Fluliban" src="http://www.newsgator.com/images/ngsub1.gif">Subscribe with NewsGator</feedburner:feedFlare><feedburner:feedFlare xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" href="http://www.bloglines.com/sub/http://feeds.feedburner.com/luliban" src="http://www.bloglines.com/images/sub_modern11.gif">Subscribe with Bloglines</feedburner:feedFlare><feedburner:feedFlare xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" href="http://www.netvibes.com/subscribe.php?url=http%3A%2F%2Ffeeds.feedburner.com%2Fluliban" src="http://www.netvibes.com/img/add2netvibes.gif">Subscribe with Netvibes</feedburner:feedFlare><feedburner:feedFlare xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" href="http://fusion.google.com/add?feedurl=http%3A%2F%2Ffeeds.feedburner.com%2Fluliban" src="http://buttons.googlesyndication.com/fusion/add.gif">Subscribe with Google</feedburner:feedFlare><feedburner:feedFlare xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" href="http://www.pageflakes.com/subscribe.aspx?url=http%3A%2F%2Ffeeds.feedburner.com%2Fluliban" src="http://www.pageflakes.com/ImageFile.ashx?instanceId=Static_4&amp;fileName=ATP_blu_91x17.gif">Subscribe with Pageflakes</feedburner:feedFlare><feedburner:feedFlare xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" href="http://www.live.com/?add=http%3A%2F%2Ffeeds.feedburner.com%2Fluliban" src="http://tkfiles.storage.msn.com/x1piYkpqHC_35nIp1gLE68-wvzLZO8iXl_JMledmJQXP-XTBOLfmQv4zhj4MhcWEJh_GtoBIiAl1Mjh-ndp9k47If7hTaFno0mxW9_i3p_5qQw">Subscribe with Live.com</feedburner:feedFlare><item><title>有备无患：一个 jQuery 的时间选择插件</title><link>http://luliban.com/blog/2008/08/yet-another-jquery-time-picker-plugin.html</link><category>javascript</category><author>noreply@blogger.com (wyt)</author><pubDate>Tue, 05 Aug 2008 22:29:59 -0500</pubDate><guid isPermaLink="false">tag:blogger.com,1999:blog-7286295415331143255.post-1076549706385638809</guid><description>&lt;a href="http://plugins.jquery.com/project/yatimepicker"&gt;Yet another jQuery time picker plugin.&lt;/a&gt; Google 上兜了一圈，没找到中意的时间选择（Time Picker）插件，于是自己写了一个。&lt;a href="http://plugins.jquery.com/project/yatimepicker"&gt;timepicker&lt;/a&gt; 是一个弹出式的对话框，分为上下两个部分，上半部分是可以分别设定时分秒的计数器，下半部分提供一些常用的时间选项，比如“Now”，“Noon”，“8 p.m.”等。通过聚焦（focus）到相应的 input 控件，可以激活 timepicker。选择好时间后，点击对话框底部的 Close 按钮会将结果保存到 input 控件中，而点击 Clear 按钮会清除 input 控件中已存的时间。键盘上的 Tab 键和 ESC 键也有与之对应的功能。&lt;br /&gt;
&lt;br /&gt;
这是我的第一个 &lt;a href="http://plugins.jquery.com/" rel="nofollow"&gt;jQuery 插件&lt;/a&gt;，欢迎大家使用，我也会继续完善这个插件。如果有任何的 Bug，建议和意见，欢迎在文章后留言，或者 Email 给我，或者在&lt;a href="http://plugins.jquery.com/project/yatimepicker"&gt;插件的主页&lt;/a&gt;写下你的评论。:-)&lt;br /&gt;
&lt;br /&gt;
&lt;h4&gt;
使用方法&lt;/h4&gt;
&lt;pre class="codes"&gt;&lt;code&gt;$('#time-picker').timepicker();&lt;/code&gt;&lt;/pre&gt;
&lt;br /&gt;
&lt;h4&gt;
截图&lt;/h4&gt;&lt;br /&gt;
&lt;div class="separator" style="clear: both;"&gt;
&lt;a href="http://demos.luliban.com/timepicker/screenshot.png" imageanchor="1" style="background-color: transparent; margin-left: 1em; margin-right: 1em;"&gt;&lt;img alt="Yet another jQuery time picker plugin." height="252" src="http://demos.luliban.com/timepicker/screenshot.png" width="200" /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;h4&gt;
演示&lt;/h4&gt;
&lt;a href="http://demos.luliban.com/timepicker/timepicker.html"&gt;演示网址&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;div&gt;&lt;iframe height="300" src="http://demos.luliban.com/timepicker/timepicker.html" style="border: 1px solid rgb(0, 0, 0);" width="300"&gt;&lt;/iframe&gt;&lt;/div&gt;
&lt;br /&gt;&lt;br /&gt;
&lt;h4&gt;
下载&lt;/h4&gt;
你可以选择下载压缩档。&lt;br/&gt;
&lt;a href="http://luliban.com/attachments/timepicker.tar.gz"&gt;&lt;img alt="下载" src="/images/download90.gif" style="border: medium none ;" /&gt;&lt;/a&gt;&lt;br/&gt;
&lt;br /&gt;
或者用 Git 从架设在 &lt;a href="http://github.com/"&gt;GitHub&lt;/a&gt; 的软件仓库中检出源代码使用。&lt;br /&gt;
&lt;pre class="codes"&gt;&lt;code&gt;$ git clone git://github.com/wuyuntao/timepicker.git&lt;/code&gt;&lt;/pre&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~f/luliban?a=r30ChK"&gt;&lt;img src="http://feeds.feedburner.com/~f/luliban?i=r30ChK" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/luliban?a=nPRV4k"&gt;&lt;img src="http://feeds.feedburner.com/~f/luliban?i=nPRV4k" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/luliban?a=DyhEek"&gt;&lt;img src="http://feeds.feedburner.com/~f/luliban?i=DyhEek" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/luliban?a=a7OyEK"&gt;&lt;img src="http://feeds.feedburner.com/~f/luliban?i=a7OyEK" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;</description></item><item><title>有备无患：Google Reader 风的豆瓣广播 widget</title><link>http://luliban.com/blog/2008/08/google-reader-style-douban-miniblog.html</link><category>javascript</category><category>blogger</category><category>豆瓣</category><author>noreply@blogger.com (wyt)</author><pubDate>Sun, 03 Aug 2008 22:47:20 -0500</pubDate><guid isPermaLink="false">tag:blogger.com,1999:blog-7286295415331143255.post-2672898672410251723</guid><description>最近，豆瓣连续发布了几个重要的 API 更新，包括了备受期待的&lt;a href="http://www.douban.com/group/topic/3790826/" id="m2tk" rel="nofollow" title="[7.26]豆瓣API更新"&gt;友邻&lt;/a&gt;和&lt;a href="http://www.douban.com/group/topic/3835188/" id="q.sk" rel="nofollow" title="[7.31]豆瓣广播API发布"&gt;广播&lt;/a&gt;相关的接口。特别是广播的 API，自从用豆瓣广播取代了 &lt;a href="http://twitter.com/" id="r3kp" rel="nofollow" title="Twitter"&gt;Twitter&lt;/a&gt; 当作吐口水的工具之后，我一直期待广播也有像 &lt;a href="https://addons.mozilla.org/en-US/firefox/addon/5081" id="iao-" rel="nofollow" title="TwitterFox"&gt;TwitterFox&lt;/a&gt; 一样的客户端，有个 widget 可以在 blog 上显示最近的状态，而这些都是要以广播的 API 为基础的。&lt;br /&gt;
&lt;br /&gt;
言归正传，我写了一个类似 Google Reader 的分享阅读剪辑（Shared Item Clips）风格的豆瓣广播 widget，可以放在 blog 的侧边栏。使用的方法很简单，只需要将下面这段代码插入到你的 blog 模板中。（wyt：如果不知道怎么操作的话，可以参考&lt;a href="http://www.douban.com/about?topic=badge" id="kia4" rel="nofollow" title="豆瓣秀指南"&gt;豆瓣秀指南&lt;/a&gt;。）&lt;br /&gt;
&lt;br /&gt;
&lt;pre class="codes"&gt;&lt;code&gt;&amp;lt;script type="text/javascript" src="http://luliban.com/scripts/miniblog.js?&lt;b id="nkc9"&gt;username=wyt&amp;amp;amp;maxresults=16&amp;amp;amp;style=blue&lt;/b&gt;"&amp;gt;&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;br /&gt;
注意上面代码中粗体的部分，首先我们要将``wyt``换成自己的用户名；``maxresults``为 widget 显示的广播条数（最大值为50，默认为10，可以省略）；``style``表示 widget 的配色，和 Google Reader 分享阅读的配色基本一致，包括：black、blue、gray、green、khaki、pink、slate 和 none。默认为 green，如果输入的 style 不在上述配色中，则返回 none。none 为不带任何 style 的原始 HTML，适合希望自定义广播风格的 blogger。&lt;br /&gt;
&lt;br /&gt;
最终效果如下图。想看看实物 demo 的话，可以点击&lt;a href="http://demos.luliban.com/douban/miniblog-widget.html" id="o483" title="Douban Miniblog Widget Demo"&gt;这里&lt;/a&gt;。要说明的是，看得到漂亮的圆角效果的只有 Firefox 用户（wyt：这也是 Google Reader 的风格之一，:p），IE、&lt;a href="http://www.opera.com/" id="tw.2" rel="nofollow" title="Opera"&gt;Opera&lt;/a&gt; 和 &lt;a href="http://www.apple.com/safari/" id="i1_t" rel="nofollow" title="Safari"&gt;Safari&lt;/a&gt; 用户对不住了。如果有任何的建议或意见，欢迎留言 :-)&lt;br /&gt;
&lt;br /&gt;

&lt;div&gt;
&lt;a href="http://luliban.com/blog/uploaded_images/douban-miniblog-widget-demo.png"&gt;&lt;img src="http://luliban.com/blog/uploaded_images/douban-miniblog-widget-demo.png" alt="豆瓣广播widget" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~f/luliban?a=916vfK"&gt;&lt;img src="http://feeds.feedburner.com/~f/luliban?i=916vfK" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/luliban?a=IftQUk"&gt;&lt;img src="http://feeds.feedburner.com/~f/luliban?i=IftQUk" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/luliban?a=nwc7Kk"&gt;&lt;img src="http://feeds.feedburner.com/~f/luliban?i=nwc7Kk" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/luliban?a=ymmXyK"&gt;&lt;img src="http://feeds.feedburner.com/~f/luliban?i=ymmXyK" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;</description></item><item><title>有备无患：在 Django 上使用 reCAPTCHA 生成验证码</title><link>http://luliban.com/blog/2008/07/django-recaptcha.html</link><category>django</category><category>python</category><author>noreply@blogger.com (wyt)</author><pubDate>Fri, 18 Jul 2008 03:47:54 -0500</pubDate><guid isPermaLink="false">tag:blogger.com,1999:blog-7286295415331143255.post-6974686256222263370</guid><description>&lt;div&gt;&lt;a href="http://luliban.com/blog/uploaded_images/recaptcha-screenshot-732412.png" imageanchor="1" &gt;&lt;img src="http://luliban.com/blog/uploaded_images/recaptcha-screenshot-732412.png"  /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;a href="http://recaptcha.net/" rel="nofollow" title="reCAPTCHA: Stop Spam, Read Books"&gt;reCAPTCHA&lt;/a&gt; 是由 &lt;a href="http://www.captcha.net/" rel="nofollow" title="CAPTCHA"&gt;CAPTCHA&lt;/a&gt; 的原作者，也是人类计算学（Human Computation）的专家 &lt;a href="http://www.cs.cmu.edu/%7Ebiglou/" rel="nofollow" title="Luis von Ahn"&gt;Luis von Ahn&lt;/a&gt; 所设计一个免费的 anti-bot 服务，它在随机提取的单词上加上一些扭曲和可阻碍电脑自动识别的噪声，以利用人类远强于电脑的图形识别能力来检查屏幕前坐着的是人类，还是 bot 或自动脚本。因此，reCAPTCHA 经常被用于生成网站注册或者文章评论的验证码。&lt;br /&gt;
&lt;br /&gt;
除此之外，reCAPTCHA 还有一项非常公益的设计。虽然 &lt;a href="http://zh.wikipedia.org/wiki/OCR" rel="nofollow" title="OCR"&gt;OCR&lt;/a&gt; （全称 Optical Character Recognitio，中译光学字元识别）早已被投入使用，但是对那些印刷模糊或分辨度不足的实体书籍而言，OCR 的识别率难说完美。而 reCAPTCHA 将这些上无法由电脑自动识别的单词扫描下来，以图片的形式存入数据库，并在网络上以验证码（CAPTCHA）的形式交由人类来破译，而破译的结果将被用于帮助世界上的实体书加快数字化的进程。&lt;br /&gt;
&lt;br /&gt;
你可能会问，既然电脑无法识别这些单词，那么 reCAPTCHA 系统又怎么知道用户填写的是正确的验证码呢？reCAPTCHA 的网站上有&lt;a href="http://recaptcha.net/learnmore.html" id="wd:-" rel="nofollow" title="如下解释"&gt;详细的解释&lt;/a&gt;。&lt;br /&gt;
&lt;br /&gt;
一个不能被 OCR 正确识别的新单词，总是会和另一个已知答案的单词一起提交给用户。接下来用户需要同时识别这两个单词。如果用户正确识别出其中已知答案的那个单词，系统即假设用户所识别出的未知答案的新单词也可能是正确的。如果其他的用户也识别出相同的答案，系统则逐步提高这种可能性。最终当这种可能性超过某个阈值时，系统就可以将其认作已知答案的单词了。&lt;br /&gt;
&lt;br /&gt;
言归正传，reCAPTCHA 有&lt;a href="http://recaptcha.net/resources.html" rel="nofollow" title="丰富的 API"&gt;丰富的 API&lt;/a&gt;，包括对 PHP、JAVA、Ruby，自然还有 Python 的支持。如果要在 Django 上使用 reCAPTCHA，我们可以从 PyPI 下载 &lt;a href="http://pypi.python.org/pypi/recaptcha-client" rel="nofollow" title="reCAPTCHA 的 Python 客户端"&gt;reCAPTCHA 的 Python 客户端&lt;/a&gt;，或者直接用 easy_install 安装。&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;pre class="commands"&gt;&lt;code&gt;&lt;b&gt;# easy_install recaptcha-client&lt;/b&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;br /&gt;
不知道为什么，虽然安装成功了，但是我不能导入 recaptcha 模块（wyt：import captcha 或 import recaptcha 都没有用，哪位如果顺利导入过的话，能不能分享一下你的经验呢？先谢了），所以我是把 egg 中的脚本直接复制到我的 Django 项目目录下了。&lt;br /&gt;
&lt;br /&gt;
接下来，我们需要&lt;a href="https://admin.recaptcha.net/recaptcha/createsite/" title="在 reCAPTCHA 申请一对公钥／私钥"&gt;在 reCAPTCHA 注册并申请一对公钥／私钥&lt;/a&gt;，并将其保存在 settings.py 中。&lt;br /&gt;
&lt;br /&gt;
&lt;h4&gt;

settings.py&lt;/h4&gt;
&lt;pre class="codes"&gt;&lt;code&gt;&lt;span&gt;1&lt;/span&gt; &lt;span style="color: rgb(96, 160, 176); font-style: italic;"&gt;# reCAPTCHA keys&lt;/span&gt;&lt;span&gt;&lt;/span&gt;
&lt;span&gt;2&lt;/span&gt; &lt;span&gt;RECAPTCHA_PUBLIC_KEY&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span style="color: rgb(102, 102, 102);"&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span style="color: rgb(64, 112, 160);"&gt;"6LdqgAIAAAUSHDmo4IIBmsjUsduAUMUoBDZc3J_T"&lt;/span&gt;&lt;span&gt;&lt;/span&gt;
&lt;span&gt;3&lt;/span&gt; &lt;span&gt;RECAPTCHA_PRIVATE_KEY&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span style="color: rgb(102, 102, 102);"&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span style="color: rgb(64, 112, 160);"&gt;"6LdqgAIAAbjsJKLbj2KOjPO6D4isfJ_AzLSO_256"&lt;/span&gt;&lt;span&gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;br /&gt;
然后，我们要为注册表单添加验证码了。在 &lt;a href="http://www.djangosnippets.org/" title="Django Snippets"&gt;Django Snippets&lt;/a&gt; 上，&lt;a href="http://www.djangosnippets.org/users/oggy/" title="oggy"&gt;oggy&lt;/a&gt; 已经将 reCAPTCHA 抽象成一个 RecaptchaForm 类，所以我们只要让注册表单（Registration Form）继承这个类，就可以为注册表单添加验证码的功能了。由于 Django newform 默认是根据 field 的定义顺序来生成表单，所以在继承 RecaptchaForm 的时候，也应该把它放在最后继承。&lt;br /&gt;
&lt;br /&gt;
&lt;h4&gt;

recaptcha/forms.py&lt;/h4&gt;
&lt;pre class="codes"&gt;&lt;code&gt;&lt;span&gt;01&lt;/span&gt; &lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span style="color: rgb(14, 132, 181); font-weight: bold;"&gt;django&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;import&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;newforms&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;as&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;forms&lt;/span&gt;
&lt;span&gt;02&lt;/span&gt; &lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span style="color: rgb(14, 132, 181); font-weight: bold;"&gt;django.newforms&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;import&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ValidationError&lt;/span&gt;
&lt;span&gt;03&lt;/span&gt; &lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span style="color: rgb(14, 132, 181); font-weight: bold;"&gt;django.conf&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;import&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;settings&lt;/span&gt;
&lt;span&gt;04&lt;/span&gt; &lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span style="color: rgb(14, 132, 181); font-weight: bold;"&gt;recaptcha&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;import&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;captcha&lt;/span&gt;
&lt;span class="lineno special"&gt;05&lt;/span&gt; 
&lt;span&gt;06&lt;/span&gt; 
&lt;span&gt;07&lt;/span&gt; &lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span style="color: rgb(14, 132, 181); font-weight: bold;"&gt;RecaptchaWidget&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;forms&lt;/span&gt;&lt;span style="color: rgb(102, 102, 102);"&gt;.&lt;/span&gt;&lt;span&gt;Widget&lt;/span&gt;&lt;span&gt;):&lt;/span&gt;
&lt;span&gt;08&lt;/span&gt; &lt;span&gt;    &lt;/span&gt;&lt;span style="color: rgb(64, 112, 160); font-style: italic;"&gt;""" A Widget which "renders" the output of captcha.displayhtml """&lt;/span&gt;
&lt;span&gt;09&lt;/span&gt; &lt;span&gt;    &lt;/span&gt;&lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span style="color: rgb(6, 40, 126);"&gt;render&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span style="color: rgb(0, 112, 32);"&gt;self&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span style="color: rgb(102, 102, 102);"&gt;*&lt;/span&gt;&lt;span&gt;args&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span style="color: rgb(102, 102, 102);"&gt;**&lt;/span&gt;&lt;span&gt;kwargs&lt;/span&gt;&lt;span&gt;):&lt;/span&gt;
&lt;span class="lineno special"&gt;10&lt;/span&gt; &lt;span&gt;        &lt;/span&gt;&lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;return&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;captcha&lt;/span&gt;&lt;span style="color: rgb(102, 102, 102);"&gt;.&lt;/span&gt;&lt;span&gt;displayhtml&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;settings&lt;/span&gt;&lt;span style="color: rgb(102, 102, 102);"&gt;.&lt;/span&gt;&lt;span&gt;RECAPTCHA_PUBLIC_KEY&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;
&lt;span&gt;11&lt;/span&gt; 
&lt;span&gt;12&lt;/span&gt; &lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span style="color: rgb(14, 132, 181); font-weight: bold;"&gt;DummyWidget&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;forms&lt;/span&gt;&lt;span style="color: rgb(102, 102, 102);"&gt;.&lt;/span&gt;&lt;span&gt;Widget&lt;/span&gt;&lt;span&gt;):&lt;/span&gt;
&lt;span&gt;13&lt;/span&gt; &lt;span&gt;    &lt;/span&gt;&lt;span style="color: rgb(64, 112, 160); font-style: italic;"&gt;"""&lt;/span&gt;
&lt;span&gt;14&lt;/span&gt; &lt;span style="color: rgb(64, 112, 160); font-style: italic;"&gt;    A dummy Widget class for a placeholder input field which will&lt;/span&gt;
&lt;span class="lineno special"&gt;15&lt;/span&gt; &lt;span style="color: rgb(64, 112, 160); font-style: italic;"&gt;    be created by captcha.displayhtml&lt;/span&gt;
&lt;span&gt;16&lt;/span&gt; 
&lt;span&gt;17&lt;/span&gt; &lt;span style="color: rgb(64, 112, 160); font-style: italic;"&gt;    """&lt;/span&gt;
&lt;span&gt;18&lt;/span&gt; &lt;span&gt;    &lt;/span&gt;&lt;span style="color: rgb(96, 160, 176); font-style: italic;"&gt;# make sure that labels are not displayed either&lt;/span&gt;
&lt;span&gt;19&lt;/span&gt; &lt;span&gt;    &lt;/span&gt;&lt;span&gt;is_hidden&lt;/span&gt;&lt;span style="color: rgb(102, 102, 102);"&gt;=&lt;/span&gt;&lt;span style="color: rgb(0, 112, 32);"&gt;True&lt;/span&gt;
&lt;span class="lineno special"&gt;20&lt;/span&gt; &lt;span&gt;    &lt;/span&gt;&lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span style="color: rgb(6, 40, 126);"&gt;render&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span style="color: rgb(0, 112, 32);"&gt;self&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span style="color: rgb(102, 102, 102);"&gt;*&lt;/span&gt;&lt;span&gt;args&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span style="color: rgb(102, 102, 102);"&gt;**&lt;/span&gt;&lt;span&gt;kwargs&lt;/span&gt;&lt;span&gt;):&lt;/span&gt;
&lt;span&gt;21&lt;/span&gt; &lt;span&gt;        &lt;/span&gt;&lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;return&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span style="color: rgb(64, 112, 160);"&gt;''&lt;/span&gt;
&lt;span&gt;22&lt;/span&gt; 
&lt;span&gt;23&lt;/span&gt; &lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span style="color: rgb(14, 132, 181); font-weight: bold;"&gt;RecaptchaForm&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;forms&lt;/span&gt;&lt;span style="color: rgb(102, 102, 102);"&gt;.&lt;/span&gt;&lt;span&gt;Form&lt;/span&gt;&lt;span&gt;):&lt;/span&gt;
&lt;span&gt;24&lt;/span&gt; &lt;span&gt;    &lt;/span&gt;&lt;span style="color: rgb(64, 112, 160); font-style: italic;"&gt;"""&lt;/span&gt;
&lt;span class="lineno special"&gt;25&lt;/span&gt; &lt;span style="color: rgb(64, 112, 160); font-style: italic;"&gt;    A form class which uses reCAPTCHA for user validation.&lt;/span&gt;
&lt;span&gt;26&lt;/span&gt; &lt;span style="color: rgb(64, 112, 160); font-style: italic;"&gt;   &lt;/span&gt;
&lt;span&gt;27&lt;/span&gt; &lt;span style="color: rgb(64, 112, 160); font-style: italic;"&gt;    If the captcha is not guessed correctly, a ValidationError is raised&lt;/span&gt;
&lt;span&gt;28&lt;/span&gt; &lt;span style="color: rgb(64, 112, 160); font-style: italic;"&gt;    for the appropriate field&lt;/span&gt;
&lt;span&gt;29&lt;/span&gt; &lt;span style="color: rgb(64, 112, 160); font-style: italic;"&gt;    """&lt;/span&gt;
&lt;span class="lineno special"&gt;30&lt;/span&gt; &lt;span&gt;    &lt;/span&gt;&lt;span&gt;recaptcha_challenge_field&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span style="color: rgb(102, 102, 102);"&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;forms&lt;/span&gt;&lt;span style="color: rgb(102, 102, 102);"&gt;.&lt;/span&gt;&lt;span&gt;CharField&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;widget&lt;/span&gt;&lt;span style="color: rgb(102, 102, 102);"&gt;=&lt;/span&gt;&lt;span&gt;DummyWidget&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;
&lt;span&gt;31&lt;/span&gt; &lt;span&gt;    &lt;/span&gt;&lt;span&gt;recaptcha_response_field&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span style="color: rgb(102, 102, 102);"&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;forms&lt;/span&gt;&lt;span style="color: rgb(102, 102, 102);"&gt;.&lt;/span&gt;&lt;span&gt;CharField&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;widget&lt;/span&gt;&lt;span style="color: rgb(102, 102, 102);"&gt;=&lt;/span&gt;&lt;span&gt;RecaptchaWidget&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;label&lt;/span&gt;&lt;span style="color: rgb(102, 102, 102);"&gt;=&lt;/span&gt;&lt;span style="color: rgb(64, 112, 160);"&gt;''&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;
&lt;span&gt;32&lt;/span&gt; 
&lt;span&gt;33&lt;/span&gt; &lt;span&gt;    &lt;/span&gt;&lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span style="color: rgb(6, 40, 126);"&gt;__init__&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span style="color: rgb(0, 112, 32);"&gt;self&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;request&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span style="color: rgb(102, 102, 102);"&gt;*&lt;/span&gt;&lt;span&gt;args&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span style="color: rgb(102, 102, 102);"&gt;**&lt;/span&gt;&lt;span&gt;kwargs&lt;/span&gt;&lt;span&gt;):&lt;/span&gt;
&lt;span&gt;34&lt;/span&gt; &lt;span&gt;        &lt;/span&gt;&lt;span style="color: rgb(0, 112, 32);"&gt;super&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;RecaptchaForm&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span style="color: rgb(0, 112, 32);"&gt;self&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span style="color: rgb(102, 102, 102);"&gt;.&lt;/span&gt;&lt;span&gt;__init__&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span style="color: rgb(102, 102, 102);"&gt;*&lt;/span&gt;&lt;span&gt;args&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span style="color: rgb(102, 102, 102);"&gt;**&lt;/span&gt;&lt;span&gt;kwargs&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;
&lt;span class="lineno special"&gt;35&lt;/span&gt; &lt;span&gt;        &lt;/span&gt;&lt;span style="color: rgb(0, 112, 32);"&gt;self&lt;/span&gt;&lt;span style="color: rgb(102, 102, 102);"&gt;.&lt;/span&gt;&lt;span&gt;_request&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span style="color: rgb(102, 102, 102);"&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;request&lt;/span&gt;
&lt;span&gt;36&lt;/span&gt; 
&lt;span&gt;37&lt;/span&gt; &lt;span&gt;    &lt;/span&gt;&lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span style="color: rgb(6, 40, 126);"&gt;clean_recaptcha_response_field&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span style="color: rgb(0, 112, 32);"&gt;self&lt;/span&gt;&lt;span&gt;):&lt;/span&gt;
&lt;span&gt;38&lt;/span&gt; &lt;span&gt;        &lt;/span&gt;&lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;if&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span style="color: rgb(64, 112, 160);"&gt;'recaptcha_challenge_field'&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;in&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span style="color: rgb(0, 112, 32);"&gt;self&lt;/span&gt;&lt;span style="color: rgb(102, 102, 102);"&gt;.&lt;/span&gt;&lt;span&gt;cleaned_data&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;
&lt;span&gt;39&lt;/span&gt; &lt;span&gt;            &lt;/span&gt;&lt;span style="color: rgb(0, 112, 32);"&gt;self&lt;/span&gt;&lt;span style="color: rgb(102, 102, 102);"&gt;.&lt;/span&gt;&lt;span&gt;validate_captcha&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;
&lt;span class="lineno special"&gt;40&lt;/span&gt; &lt;span&gt;        &lt;/span&gt;&lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;return&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span style="color: rgb(0, 112, 32);"&gt;self&lt;/span&gt;&lt;span style="color: rgb(102, 102, 102);"&gt;.&lt;/span&gt;&lt;span&gt;cleaned_data&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span style="color: rgb(64, 112, 160);"&gt;'recaptcha_response_field'&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;
&lt;span&gt;41&lt;/span&gt; 
&lt;span&gt;42&lt;/span&gt; &lt;span&gt;    &lt;/span&gt;&lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span style="color: rgb(6, 40, 126);"&gt;clean_recaptcha_challenge_field&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span style="color: rgb(0, 112, 32);"&gt;self&lt;/span&gt;&lt;span&gt;):&lt;/span&gt;
&lt;span&gt;43&lt;/span&gt; &lt;span&gt;        &lt;/span&gt;&lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;if&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span style="color: rgb(64, 112, 160);"&gt;'recaptcha_response_field'&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;in&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span style="color: rgb(0, 112, 32);"&gt;self&lt;/span&gt;&lt;span style="color: rgb(102, 102, 102);"&gt;.&lt;/span&gt;&lt;span&gt;cleaned_data&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;
&lt;span&gt;44&lt;/span&gt; &lt;span&gt;            &lt;/span&gt;&lt;span style="color: rgb(0, 112, 32);"&gt;self&lt;/span&gt;&lt;span style="color: rgb(102, 102, 102);"&gt;.&lt;/span&gt;&lt;span&gt;validate_captcha&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;
&lt;span class="lineno special"&gt;45&lt;/span&gt; &lt;span&gt;        &lt;/span&gt;&lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;return&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span style="color: rgb(0, 112, 32);"&gt;self&lt;/span&gt;&lt;span style="color: rgb(102, 102, 102);"&gt;.&lt;/span&gt;&lt;span&gt;cleaned_data&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span style="color: rgb(64, 112, 160);"&gt;'recaptcha_challenge_field'&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;
&lt;span&gt;46&lt;/span&gt; 
&lt;span&gt;47&lt;/span&gt; &lt;span&gt;    &lt;/span&gt;&lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span style="color: rgb(6, 40, 126);"&gt;validate_captcha&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span style="color: rgb(0, 112, 32);"&gt;self&lt;/span&gt;&lt;span&gt;):&lt;/span&gt;
&lt;span&gt;48&lt;/span&gt; &lt;span&gt;        &lt;/span&gt;&lt;span&gt;rcf&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span style="color: rgb(102, 102, 102);"&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span style="color: rgb(0, 112, 32);"&gt;self&lt;/span&gt;&lt;span style="color: rgb(102, 102, 102);"&gt;.&lt;/span&gt;&lt;span&gt;cleaned_data&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span style="color: rgb(64, 112, 160);"&gt;'recaptcha_challenge_field'&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;
&lt;span&gt;49&lt;/span&gt; &lt;span&gt;        &lt;/span&gt;&lt;span&gt;rrf&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span style="color: rgb(102, 102, 102);"&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span style="color: rgb(0, 112, 32);"&gt;self&lt;/span&gt;&lt;span style="color: rgb(102, 102, 102);"&gt;.&lt;/span&gt;&lt;span&gt;cleaned_data&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span style="color: rgb(64, 112, 160);"&gt;'recaptcha_response_field'&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;
&lt;span class="lineno special"&gt;50&lt;/span&gt; &lt;span&gt;        &lt;/span&gt;&lt;span&gt;ip_address&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span style="color: rgb(102, 102, 102);"&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span style="color: rgb(0, 112, 32);"&gt;self&lt;/span&gt;&lt;span style="color: rgb(102, 102, 102);"&gt;.&lt;/span&gt;&lt;span&gt;_request&lt;/span&gt;&lt;span style="color: rgb(102, 102, 102);"&gt;.&lt;/span&gt;&lt;span&gt;META&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span style="color: rgb(64, 112, 160);"&gt;'REMOTE_ADDR'&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;
&lt;span&gt;51&lt;/span&gt; &lt;span&gt;        &lt;/span&gt;&lt;span&gt;check&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span style="color: rgb(102, 102, 102);"&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;captcha&lt;/span&gt;&lt;span style="color: rgb(102, 102, 102);"&gt;.&lt;/span&gt;&lt;span&gt;submit&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;rcf&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;rrf&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;settings&lt;/span&gt;&lt;span style="color: rgb(102, 102, 102);"&gt;.&lt;/span&gt;&lt;span&gt;RECAPTCHA_PRIVATE_KEY&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ip_address&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;
&lt;span&gt;52&lt;/span&gt; &lt;span&gt;        &lt;/span&gt;&lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;if&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;not&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;check&lt;/span&gt;&lt;span style="color: rgb(102, 102, 102);"&gt;.&lt;/span&gt;&lt;span&gt;is_valid&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;
&lt;span&gt;53&lt;/span&gt; &lt;span&gt;            &lt;/span&gt;&lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;raise&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ValidationError&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span style="color: rgb(64, 112, 160);"&gt;'You have not entered the correct words'&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;h4&gt;

user/forms.py&lt;/h4&gt;
&lt;pre class="codes"&gt;&lt;code&gt;&lt;span&gt;01&lt;/span&gt; &lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span style="color: rgb(14, 132, 181); font-weight: bold;"&gt;myproject.recaptcha.forms&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;import&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;RecaptchaForm&lt;/span&gt;
&lt;span&gt;02&lt;/span&gt; 
&lt;span&gt;03&lt;/span&gt; &lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span style="color: rgb(14, 132, 181); font-weight: bold;"&gt;RegistrationForm&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;forms&lt;/span&gt;&lt;span style="color: rgb(102, 102, 102);"&gt;.&lt;/span&gt;&lt;span&gt;Form&lt;/span&gt;&lt;span&gt;):&lt;/span&gt;
&lt;span&gt;04&lt;/span&gt; &lt;span&gt;    &lt;/span&gt;&lt;span style="color: rgb(64, 112, 160); font-style: italic;"&gt;"""&lt;/span&gt;
&lt;span class="lineno special"&gt;05&lt;/span&gt; &lt;span style="color: rgb(64, 112, 160); font-style: italic;"&gt;    Form for registering a new user account.&lt;/span&gt;
&lt;span&gt;06&lt;/span&gt; &lt;span style="color: rgb(64, 112, 160); font-style: italic;"&gt;     &lt;/span&gt;
&lt;span&gt;07&lt;/span&gt; &lt;span style="color: rgb(64, 112, 160); font-style: italic;"&gt;    """&lt;/span&gt;
&lt;span&gt;08&lt;/span&gt; 
&lt;span&gt;09&lt;/span&gt; &lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span style="color: rgb(14, 132, 181); font-weight: bold;"&gt;RegistrationFormWithRecaptcha&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;RegistrationForm&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;RecaptchaForm&lt;/span&gt;&lt;span&gt;):&lt;/span&gt;
&lt;span class="lineno special"&gt;10&lt;/span&gt; &lt;span&gt;    &lt;/span&gt;&lt;span style="color: rgb(64, 112, 160); font-style: italic;"&gt;"""&lt;/span&gt;
&lt;span&gt;11&lt;/span&gt; &lt;span style="color: rgb(64, 112, 160); font-style: italic;"&gt;    Subclass of ``RegistrationForm`` and ``RecaptchaForm`` which can tell whether its user&lt;/span&gt;
&lt;span&gt;12&lt;/span&gt; &lt;span style="color: rgb(64, 112, 160); font-style: italic;"&gt;    is a human or a computer.&lt;/span&gt;
&lt;span&gt;13&lt;/span&gt; 
&lt;span&gt;14&lt;/span&gt; &lt;span style="color: rgb(64, 112, 160); font-style: italic;"&gt;    """&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~f/luliban?a=1krvdJ"&gt;&lt;img src="http://feeds.feedburner.com/~f/luliban?i=1krvdJ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/luliban?a=JZqFVj"&gt;&lt;img src="http://feeds.feedburner.com/~f/luliban?i=JZqFVj" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/luliban?a=UkqkFj"&gt;&lt;img src="http://feeds.feedburner.com/~f/luliban?i=UkqkFj" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/luliban?a=5Ajd7J"&gt;&lt;img src="http://feeds.feedburner.com/~f/luliban?i=5Ajd7J" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;</description></item><item><title>有备无患：浏览器端缓存（基于 jQuery）</title><link>http://luliban.com/blog/2008/07/jquery.html</link><category>javascript</category><author>noreply@blogger.com (wyt)</author><pubDate>Fri, 04 Jul 2008 08:31:25 -0500</pubDate><guid isPermaLink="false">tag:blogger.com,1999:blog-7286295415331143255.post-4901571834404718695</guid><description>&lt;p&gt;最近在翻《&lt;a rel="nofollow" title="Ajax 设计模式" href="http://www.douban.com/subject/2121078/"&gt;Ajax 设计模式&lt;/a&gt;》，所以想把其中的一部分模式实现出来练手 &lt;a rel="nofollow" title="jQuery" href="http://jquery.com/"&gt;jQuery&lt;/a&gt;，今天写了一个简单的引入 LRU 算法的浏览器端缓存（Browser Side Cache）。&lt;br /&gt;
&lt;br /&gt;
浏览器端缓存用来保留服务器返回的查询结果。这种缓存是一个 Javascript 里类似映射的对象，存储成对的查询结果；查询是缓存的键（Key），服务器返回的结果是缓存的值（Value）。因此，每当浏览器需要查询服务器时，先检查缓存。如果该查询是缓存中的一个键，则与键对应的值将被当作结果，而不必再向服务器查询。&lt;br /&gt;
&lt;br /&gt;
LRU （Least Recently Used）是将存储在缓存中的自上一次获取之后，最长时间未被使用的项目（Item）丢弃的一种算法。可以用两个数组（Array）来实现，其一是用来存储查询结果的键值对（Key-Value Pairs），其二是一个先进先出（FIFO）的队列，每一个新项目被塞入队列的尾部，并随着后续项目的跟进，逐渐逼近队列的头部。当队列全满时，每次向尾部塞入一个新项目，就要从头部弹出一个旧项目。而每当一个项目被缓存查询时，它会被送回到队列的尾部，这样可以确保最长时间未被使用的项目总是在开头处。&lt;br /&gt;
&lt;br /&gt;
另外，还有一种常用的缓存算法，LFU （Least Frequently Used），它将自上一次获取后最少被使用的项目丢弃。&lt;br /&gt;
&lt;br /&gt;
顺便一说，下面贴出的代码是由&lt;a id="umnt10" title="代码发芽网" href="http://www.fayaa.com/code/home/"&gt;代码发芽网&lt;/a&gt;生产的纯 HTML。代码发芽网是一个“无需插件支持 Blog 代码高亮，支持近百种编程语言，多种配色主题支持，代码版本管理”的代码片段管理网站。因为不是由 Javascript 脚本来高亮处理，所以在 feed 里也一样可以看到效果，这一点很赞。不过，由于用处不大的 id 和 class 也包含在生成的 HTML 里，所以体积偏大。比如说下面这段代码，在删除了多余的 id 和 class，套上代码专用的 &lt;a id="umnt11" rel="nofollow" title="pre" href="http://www.w3schools.com/TAGS/tag_pre.asp"&gt;pre&lt;/a&gt; 和 &lt;a id="umnt12" rel="nofollow" title="code" href="http://www.w3schools.com/TAGS/tag_phrase_elements.asp"&gt;code&lt;/a&gt; 标签，并将 &amp;amp;nbsp; 还原成空格之后，体积可以缩小一半仅17KB（原来35KB），更适合 blog 来贴代码。&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;h4&gt;cache.js&lt;/h4&gt;
&lt;pre class="codes"&gt;&lt;code&gt;&lt;span&gt;01&lt;/span&gt; &lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;function&lt;/span&gt; &lt;span&gt;Cache&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;size&lt;/span&gt;&lt;span&gt;)&lt;/span&gt; &lt;span&gt;{&lt;/span&gt;
&lt;span&gt;02&lt;/span&gt;     &lt;span style="color: rgb(96, 160, 176); font-style: italic;"&gt;/* A browser side LRU cache &lt;/span&gt;
&lt;span&gt;03&lt;/span&gt; &lt;span style="color: rgb(96, 160, 176); font-style: italic;"&gt;     * Author: Wu Yuntao &amp;lt;http://luliban.com/blog/&amp;gt;&lt;/span&gt;
&lt;span&gt;04&lt;/span&gt; &lt;span style="color: rgb(96, 160, 176); font-style: italic;"&gt;     * License: GPLv3&lt;/span&gt;
&lt;span&gt;05&lt;/span&gt; &lt;span style="color: rgb(96, 160, 176); font-style: italic;"&gt;     *&lt;/span&gt;
&lt;span&gt;06&lt;/span&gt; &lt;span style="color: rgb(96, 160, 176); font-style: italic;"&gt;     * Usage:&lt;/span&gt;
&lt;span&gt;07&lt;/span&gt; &lt;span style="color: rgb(96, 160, 176); font-style: italic;"&gt;     * var cache = new Cache(10);   // create a new cache object&lt;/span&gt;
&lt;span&gt;08&lt;/span&gt; &lt;span style="color: rgb(96, 160, 176); font-style: italic;"&gt;     * cache.put('w', 'wiki');      // put an item into cache&lt;/span&gt;
&lt;span&gt;09&lt;/span&gt; &lt;span style="color: rgb(96, 160, 176); font-style: italic;"&gt;     * cache.get('w');              // get the value of item with key&lt;/span&gt;
&lt;span&gt;10&lt;/span&gt; &lt;span style="color: rgb(96, 160, 176); font-style: italic;"&gt;     * cache.remove('w');           // remove an item with specified key&lt;/span&gt;
&lt;span&gt;11&lt;/span&gt; &lt;span style="color: rgb(96, 160, 176); font-style: italic;"&gt;     * cache.initialize()           // re-initialize the cache&lt;/span&gt;
&lt;span&gt;12&lt;/span&gt; &lt;span style="color: rgb(96, 160, 176); font-style: italic;"&gt;     * cache.size(10)               // resize the cache&lt;/span&gt;
&lt;span&gt;13&lt;/span&gt; &lt;span style="color: rgb(96, 160, 176); font-style: italic;"&gt;     */&lt;/span&gt;
&lt;span&gt;14&lt;/span&gt;     &lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;initialize&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;size&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;
&lt;span&gt;15&lt;/span&gt; &lt;span&gt;}&lt;/span&gt;
&lt;span&gt;16&lt;/span&gt; 
&lt;span&gt;17&lt;/span&gt; &lt;span&gt;Cache&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;prototype&lt;/span&gt; &lt;span style="color: rgb(102, 102, 102);"&gt;=&lt;/span&gt; &lt;span&gt;{&lt;/span&gt;
&lt;span&gt;18&lt;/span&gt;     &lt;span&gt;initialize&lt;/span&gt;&lt;span style="color: rgb(102, 102, 102);"&gt;:&lt;/span&gt; &lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;function&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;size&lt;/span&gt;&lt;span&gt;)&lt;/span&gt; &lt;span&gt;{&lt;/span&gt;
&lt;span&gt;19&lt;/span&gt;         &lt;span style="color: rgb(96, 160, 176); font-style: italic;"&gt;/* Initialize cache.&lt;/span&gt;
&lt;span&gt;20&lt;/span&gt; &lt;span style="color: rgb(96, 160, 176); font-style: italic;"&gt;         * ``size`` is the number of maxmium items this cache should hold.&lt;/span&gt;
&lt;span&gt;21&lt;/span&gt; &lt;span style="color: rgb(96, 160, 176); font-style: italic;"&gt;         * Default is maxium integer.&lt;/span&gt;
&lt;span&gt;22&lt;/span&gt; &lt;span style="color: rgb(96, 160, 176); font-style: italic;"&gt;         */&lt;/span&gt;
&lt;span&gt;23&lt;/span&gt;         &lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;_keys&lt;/span&gt; &lt;span style="color: rgb(102, 102, 102);"&gt;=&lt;/span&gt; &lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;new&lt;/span&gt; &lt;span style="color: rgb(0, 112, 32);"&gt;Array&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;
&lt;span&gt;24&lt;/span&gt;         &lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;_items&lt;/span&gt; &lt;span style="color: rgb(102, 102, 102);"&gt;=&lt;/span&gt; &lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;new&lt;/span&gt; &lt;span style="color: rgb(0, 112, 32);"&gt;Array&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;
&lt;span&gt;25&lt;/span&gt;         &lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;_size&lt;/span&gt; &lt;span style="color: rgb(102, 102, 102);"&gt;=&lt;/span&gt; &lt;span&gt;size&lt;/span&gt; &lt;span style="color: rgb(102, 102, 102);"&gt;||&lt;/span&gt; &lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;_size&lt;/span&gt; &lt;span style="color: rgb(102, 102, 102);"&gt;||&lt;/span&gt; &lt;span style="color: rgb(0, 112, 32);"&gt;Number&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;MAX_VALUE&lt;/span&gt;&lt;span style="color: rgb(102, 102, 102);"&gt;;&lt;/span&gt;
&lt;span&gt;26&lt;/span&gt;     &lt;span&gt;}&lt;/span&gt;&lt;span style="color: rgb(102, 102, 102);"&gt;,&lt;/span&gt;
&lt;span&gt;27&lt;/span&gt; 
&lt;span&gt;28&lt;/span&gt;     &lt;span&gt;is_empty&lt;/span&gt;&lt;span style="color: rgb(102, 102, 102);"&gt;:&lt;/span&gt; &lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;function&lt;/span&gt;&lt;span&gt;()&lt;/span&gt; &lt;span&gt;{&lt;/span&gt;
&lt;span&gt;29&lt;/span&gt;         &lt;span style="color: rgb(96, 160, 176); font-style: italic;"&gt;/* Check if cache is empty&lt;/span&gt;
&lt;span&gt;30&lt;/span&gt; &lt;span style="color: rgb(96, 160, 176); font-style: italic;"&gt;         */&lt;/span&gt;
&lt;span&gt;31&lt;/span&gt;         &lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;return&lt;/span&gt; &lt;span&gt;(&lt;/span&gt;&lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;_keys&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;length&lt;/span&gt; &lt;span style="color: rgb(102, 102, 102);"&gt;==&lt;/span&gt; &lt;span style="color: rgb(64, 160, 112);"&gt;0&lt;/span&gt; &lt;span style="color: rgb(102, 102, 102);"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;_items&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;length&lt;/span&gt; &lt;span style="color: rgb(102, 102, 102);"&gt;==&lt;/span&gt; &lt;span style="color: rgb(64, 160, 112);"&gt;0&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;
&lt;span&gt;32&lt;/span&gt;     &lt;span&gt;}&lt;/span&gt;&lt;span style="color: rgb(102, 102, 102);"&gt;,&lt;/span&gt;
&lt;span&gt;33&lt;/span&gt; 
&lt;span&gt;34&lt;/span&gt;     &lt;span&gt;size&lt;/span&gt;&lt;span style="color: rgb(102, 102, 102);"&gt;:&lt;/span&gt; &lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;function&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;size&lt;/span&gt;&lt;span&gt;)&lt;/span&gt; &lt;span&gt;{&lt;/span&gt;
&lt;span&gt;35&lt;/span&gt;         &lt;span style="color: rgb(96, 160, 176); font-style: italic;"&gt;/* Resize cache if ``size`` is specified, or return accual size of cache.&lt;/span&gt;
&lt;span&gt;36&lt;/span&gt; &lt;span style="color: rgb(96, 160, 176); font-style: italic;"&gt;         */&lt;/span&gt;
&lt;span&gt;37&lt;/span&gt;         &lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;if&lt;/span&gt; &lt;span&gt;(&lt;/span&gt;&lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;typeof&lt;/span&gt; &lt;span&gt;size&lt;/span&gt; &lt;span style="color: rgb(102, 102, 102);"&gt;==&lt;/span&gt; &lt;span style="color: rgb(64, 112, 160);"&gt;'undefined'&lt;/span&gt;&lt;span&gt;)&lt;/span&gt; &lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;return&lt;/span&gt; &lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;_keys&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;length&lt;/span&gt;&lt;span style="color: rgb(102, 102, 102);"&gt;;&lt;/span&gt;
&lt;span&gt;38&lt;/span&gt;         &lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;return&lt;/span&gt; &lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;_size&lt;/span&gt; &lt;span style="color: rgb(102, 102, 102);"&gt;=&lt;/span&gt; &lt;span&gt;size&lt;/span&gt;&lt;span style="color: rgb(102, 102, 102);"&gt;;&lt;/span&gt;
&lt;span&gt;39&lt;/span&gt;     &lt;span&gt;}&lt;/span&gt;&lt;span style="color: rgb(102, 102, 102);"&gt;,&lt;/span&gt;
&lt;span&gt;40&lt;/span&gt; 
&lt;span&gt;41&lt;/span&gt;     &lt;span&gt;put&lt;/span&gt;&lt;span style="color: rgb(102, 102, 102);"&gt;:&lt;/span&gt; &lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;function&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;key&lt;/span&gt;&lt;span style="color: rgb(102, 102, 102);"&gt;,&lt;/span&gt; &lt;span&gt;value&lt;/span&gt;&lt;span&gt;)&lt;/span&gt; &lt;span&gt;{&lt;/span&gt;
&lt;span&gt;42&lt;/span&gt;         &lt;span style="color: rgb(96, 160, 176); font-style: italic;"&gt;/* Put a new item into cache, if the size of cache reaches limit,&lt;/span&gt;
&lt;span&gt;43&lt;/span&gt; &lt;span style="color: rgb(96, 160, 176); font-style: italic;"&gt;         * cache will remove the least recently used (LRU) automatically.&lt;/span&gt;
&lt;span&gt;44&lt;/span&gt; &lt;span style="color: rgb(96, 160, 176); font-style: italic;"&gt;         * ``key`` of an item should be a string.&lt;/span&gt;
&lt;span&gt;45&lt;/span&gt; &lt;span style="color: rgb(96, 160, 176); font-style: italic;"&gt;         * ``value`` of an item could be anything, string, array or object.&lt;/span&gt;
&lt;span&gt;46&lt;/span&gt; &lt;span style="color: rgb(96, 160, 176); font-style: italic;"&gt;         * If ``value`` is not defined, returns null.&lt;/span&gt;
&lt;span&gt;47&lt;/span&gt; &lt;span style="color: rgb(96, 160, 176); font-style: italic;"&gt;         */&lt;/span&gt;
&lt;span&gt;48&lt;/span&gt;         &lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;if&lt;/span&gt; &lt;span&gt;(&lt;/span&gt;&lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;typeof&lt;/span&gt; &lt;span&gt;value&lt;/span&gt; &lt;span style="color: rgb(102, 102, 102);"&gt;!=&lt;/span&gt; &lt;span style="color: rgb(64, 112, 160);"&gt;'undefined'&lt;/span&gt;&lt;span&gt;)&lt;/span&gt; &lt;span&gt;{&lt;/span&gt;
&lt;span&gt;49&lt;/span&gt;             &lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;_keys&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;push&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;key&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;
&lt;span&gt;50&lt;/span&gt;             &lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;_items&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;key&lt;/span&gt;&lt;span&gt;]&lt;/span&gt; &lt;span style="color: rgb(102, 102, 102);"&gt;=&lt;/span&gt; &lt;span&gt;value&lt;/span&gt;&lt;span style="color: rgb(102, 102, 102);"&gt;;&lt;/span&gt;
&lt;span&gt;51&lt;/span&gt; 
&lt;span&gt;52&lt;/span&gt;             &lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;if&lt;/span&gt; &lt;span&gt;(&lt;/span&gt;&lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;_keys&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;length&lt;/span&gt; &lt;span style="color: rgb(102, 102, 102);"&gt;&amp;gt;&lt;/span&gt; &lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;_size&lt;/span&gt;&lt;span&gt;)&lt;/span&gt; &lt;span&gt;{&lt;/span&gt;
&lt;span&gt;53&lt;/span&gt;                 &lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;remove_least&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;
&lt;span&gt;54&lt;/span&gt;             &lt;span&gt;}&lt;/span&gt;
&lt;span&gt;55&lt;/span&gt;         &lt;span&gt;}&lt;/span&gt;
&lt;span&gt;56&lt;/span&gt;         &lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;return&lt;/span&gt; &lt;span&gt;value&lt;/span&gt;&lt;span style="color: rgb(102, 102, 102);"&gt;;&lt;/span&gt;
&lt;span&gt;57&lt;/span&gt;     &lt;span&gt;}&lt;/span&gt;&lt;span style="color: rgb(102, 102, 102);"&gt;,&lt;/span&gt;
&lt;span&gt;58&lt;/span&gt; 
&lt;span&gt;59&lt;/span&gt;     &lt;span&gt;get&lt;/span&gt;&lt;span style="color: rgb(102, 102, 102);"&gt;:&lt;/span&gt; &lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;function&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;key&lt;/span&gt;&lt;span&gt;)&lt;/span&gt; &lt;span&gt;{&lt;/span&gt;
&lt;span&gt;60&lt;/span&gt;         &lt;span style="color: rgb(96, 160, 176); font-style: italic;"&gt;/* Retrieve an item by its key and move it to the tail of cache.&lt;/span&gt;
&lt;span&gt;61&lt;/span&gt; &lt;span style="color: rgb(96, 160, 176); font-style: italic;"&gt;         * If the item of ``key`` does not exist, returns null.&lt;/span&gt;
&lt;span&gt;62&lt;/span&gt; &lt;span style="color: rgb(96, 160, 176); font-style: italic;"&gt;         */&lt;/span&gt;
&lt;span&gt;63&lt;/span&gt;         &lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;if&lt;/span&gt; &lt;span&gt;(&lt;/span&gt;&lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;typeof&lt;/span&gt; &lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;_items&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;key&lt;/span&gt;&lt;span&gt;]&lt;/span&gt; &lt;span style="color: rgb(102, 102, 102);"&gt;==&lt;/span&gt; &lt;span style="color: rgb(64, 112, 160);"&gt;'undefined'&lt;/span&gt;&lt;span&gt;)&lt;/span&gt; &lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;return&lt;/span&gt; &lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;null&lt;/span&gt;&lt;span style="color: rgb(102, 102, 102);"&gt;;&lt;/span&gt;
&lt;span&gt;64&lt;/span&gt;         &lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;var&lt;/span&gt; &lt;span&gt;used_key&lt;/span&gt; &lt;span style="color: rgb(102, 102, 102);"&gt;=&lt;/span&gt; &lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;_remove_key&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;key&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;
&lt;span&gt;65&lt;/span&gt;         &lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;_keys&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;push&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;used_key&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;
&lt;span&gt;66&lt;/span&gt;         &lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;return&lt;/span&gt; &lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;_items&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;key&lt;/span&gt;&lt;span&gt;];&lt;/span&gt;
&lt;span&gt;67&lt;/span&gt;     &lt;span&gt;}&lt;/span&gt;&lt;span style="color: rgb(102, 102, 102);"&gt;,&lt;/span&gt;
&lt;span&gt;68&lt;/span&gt; 
&lt;span&gt;69&lt;/span&gt;     &lt;span&gt;remove_least&lt;/span&gt;&lt;span style="color: rgb(102, 102, 102);"&gt;:&lt;/span&gt; &lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;function&lt;/span&gt;&lt;span&gt;()&lt;/span&gt; &lt;span&gt;{&lt;/span&gt;
&lt;span&gt;70&lt;/span&gt;         &lt;span style="color: rgb(96, 160, 176); font-style: italic;"&gt;/* Manually remove the least recently used item. */&lt;/span&gt;
&lt;span&gt;71&lt;/span&gt;         &lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;if&lt;/span&gt; &lt;span&gt;(&lt;/span&gt;&lt;span style="color: rgb(102, 102, 102);"&gt;!&lt;/span&gt;&lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;is_empty&lt;/span&gt;&lt;span&gt;())&lt;/span&gt; &lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;remove&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;_keys&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span style="color: rgb(64, 160, 112);"&gt;0&lt;/span&gt;&lt;span&gt;]);&lt;/span&gt;
&lt;span&gt;72&lt;/span&gt;     &lt;span&gt;}&lt;/span&gt;&lt;span style="color: rgb(102, 102, 102);"&gt;,&lt;/span&gt;
&lt;span&gt;73&lt;/span&gt; 
&lt;span&gt;74&lt;/span&gt;     &lt;span&gt;remove&lt;/span&gt;&lt;span style="color: rgb(102, 102, 102);"&gt;:&lt;/span&gt; &lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;function&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;key&lt;/span&gt;&lt;span&gt;)&lt;/span&gt; &lt;span&gt;{&lt;/span&gt;
&lt;span&gt;75&lt;/span&gt;         &lt;span style="color: rgb(96, 160, 176); font-style: italic;"&gt;/* Remove an item by its key.&lt;/span&gt;
&lt;span&gt;76&lt;/span&gt; &lt;span style="color: rgb(96, 160, 176); font-style: italic;"&gt;         * If the item of ``key`` does not exist, returns null.&lt;/span&gt;
&lt;span&gt;77&lt;/span&gt; &lt;span style="color: rgb(96, 160, 176); font-style: italic;"&gt;         */&lt;/span&gt;
&lt;span&gt;78&lt;/span&gt;         &lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;if&lt;/span&gt; &lt;span&gt;(&lt;/span&gt;&lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;typeof&lt;/span&gt; &lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;_items&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;key&lt;/span&gt;&lt;span&gt;]&lt;/span&gt; &lt;span style="color: rgb(102, 102, 102);"&gt;==&lt;/span&gt; &lt;span style="color: rgb(64, 112, 160);"&gt;'undefined'&lt;/span&gt;&lt;span&gt;)&lt;/span&gt; &lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;return&lt;/span&gt; &lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;null&lt;/span&gt;&lt;span style="color: rgb(102, 102, 102);"&gt;;&lt;/span&gt;
&lt;span&gt;79&lt;/span&gt;         &lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;_remove_key&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;key&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;
&lt;span&gt;80&lt;/span&gt;         &lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;return&lt;/span&gt; &lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;_remove_value&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;key&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;
&lt;span&gt;81&lt;/span&gt;     &lt;span&gt;}&lt;/span&gt;&lt;span style="color: rgb(102, 102, 102);"&gt;,&lt;/span&gt;
&lt;span&gt;82&lt;/span&gt; 
&lt;span&gt;83&lt;/span&gt;     &lt;span&gt;_remove_key&lt;/span&gt;&lt;span style="color: rgb(102, 102, 102);"&gt;:&lt;/span&gt; &lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;function&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;key&lt;/span&gt;&lt;span&gt;)&lt;/span&gt; &lt;span&gt;{&lt;/span&gt;
&lt;span&gt;84&lt;/span&gt;         &lt;span style="color: rgb(96, 160, 176); font-style: italic;"&gt;/* Remove the ``key`` in ``this._keys``.&lt;/span&gt;
&lt;span&gt;85&lt;/span&gt; &lt;span style="color: rgb(96, 160, 176); font-style: italic;"&gt;         */&lt;/span&gt;
&lt;span&gt;86&lt;/span&gt;         &lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;var&lt;/span&gt; &lt;span&gt;i&lt;/span&gt; &lt;span style="color: rgb(102, 102, 102);"&gt;=&lt;/span&gt; &lt;span&gt;$&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;inArray&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;key&lt;/span&gt;&lt;span style="color: rgb(102, 102, 102);"&gt;,&lt;/span&gt; &lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;_keys&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;
&lt;span&gt;87&lt;/span&gt;         &lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;return&lt;/span&gt; &lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;_keys&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;splice&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;i&lt;/span&gt;&lt;span style="color: rgb(102, 102, 102);"&gt;,&lt;/span&gt; &lt;span style="color: rgb(64, 160, 112);"&gt;1&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;
&lt;span&gt;88&lt;/span&gt;     &lt;span&gt;}&lt;/span&gt;&lt;span style="color: rgb(102, 102, 102);"&gt;,&lt;/span&gt;
&lt;span&gt;89&lt;/span&gt; 
&lt;span&gt;90&lt;/span&gt;     &lt;span&gt;_remove_value&lt;/span&gt;&lt;span style="color: rgb(102, 102, 102);"&gt;:&lt;/span&gt; &lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;function&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;key&lt;/span&gt;&lt;span&gt;)&lt;/span&gt; &lt;span&gt;{&lt;/span&gt;
&lt;span&gt;91&lt;/span&gt;         &lt;span style="color: rgb(96, 160, 176); font-style: italic;"&gt;/* Remove the item in ``this._items``. */&lt;/span&gt;
&lt;span&gt;92&lt;/span&gt;         &lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;var&lt;/span&gt; &lt;span&gt;value&lt;/span&gt; &lt;span style="color: rgb(102, 102, 102);"&gt;=&lt;/span&gt; &lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;_items&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;key&lt;/span&gt;&lt;span&gt;];&lt;/span&gt;
&lt;span&gt;93&lt;/span&gt;         &lt;span&gt;delete&lt;/span&gt; &lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;_items&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;key&lt;/span&gt;&lt;span&gt;];&lt;/span&gt;
&lt;span&gt;94&lt;/span&gt;         &lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;return&lt;/span&gt; &lt;span&gt;value&lt;/span&gt;&lt;span style="color: rgb(102, 102, 102);"&gt;;&lt;/span&gt;
&lt;span&gt;95&lt;/span&gt;     &lt;span&gt;}&lt;/span&gt;
&lt;span&gt;96&lt;/span&gt; 
&lt;span&gt;97&lt;/span&gt; &lt;span&gt;};&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~f/luliban?a=Xyh5UJ"&gt;&lt;img src="http://feeds.feedburner.com/~f/luliban?i=Xyh5UJ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/luliban?a=PlZ2Jj"&gt;&lt;img src="http://feeds.feedburner.com/~f/luliban?i=PlZ2Jj" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/luliban?a=JhGGcj"&gt;&lt;img src="http://feeds.feedburner.com/~f/luliban?i=JhGGcj" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/luliban?a=nrVYBJ"&gt;&lt;img src="http://feeds.feedburner.com/~f/luliban?i=nrVYBJ" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;</description></item><item><title>有备无患：pinyin-urlify - 根据汉字自动生成拼音 URL</title><link>http://luliban.com/blog/2008/07/pinyin-urlify-url.html</link><category>python</category><author>noreply@blogger.com (wyt)</author><pubDate>Sun, 06 Jul 2008 06:31:06 -0500</pubDate><guid isPermaLink="false">tag:blogger.com,1999:blog-7286295415331143255.post-1320338599645208961</guid><description>&lt;p&gt;根据文章标题中的英语单词自动生成文章的 URL 是一个很好的体验，比如标题为“&lt;a rel="nofollow" title="Girl's death sparks riot in China" href="http://edition.cnn.com/2008/WORLD/asiapcf/06/30/china.riots/index.html" id="hjgz"&gt;Girl's death sparks riot in China&lt;/a&gt;”，会被转换成“girls-death-sparks-riot-china”这样与之对应的 URL。这样既可获得 &lt;a title="Cool URI" rel="nofollow" href="http://www.w3.org/Provider/Style/URI" id="zl1v"&gt;Cool URIs&lt;/a&gt;，又便于用户记忆，同时也有利于 &lt;a rel="nofollow" title="SEO" href="http://www.chinamyhosting.com/seoblog/2006/08/19/whats-seo/" id="s92m"&gt;SEO&lt;/a&gt;，&lt;a rel="nofollow" title="Blogger" href="http://www.blogger.com" id="ied5"&gt;Blogger&lt;/a&gt; 和 &lt;a rel="nofollow" title="Wordpress" href="http://www.wordpress.com/" id="w-.u"&gt;Wordpress&lt;/a&gt; 也都支持类似体验。而中文的文章标题，也可以先将其中的汉字转化为拼音后再组成相应的 URL，比如标题为“&lt;a rel="nofollow" title="俯卧撑" href="http://www.google.com/search?q=俯卧撑+瓮安"&gt;俯卧撑&lt;/a&gt;”，可以被转换成“fu-wo-cheng”。&lt;br /&gt;
&lt;br /&gt;
&lt;a title="pinyin-urlify" href="http://code.google.com/p/pinyin-urlify/" id="hof4"&gt;pinyin-urlify&lt;/a&gt; 是我这几天写的能根据汉字自动生成拼音 URL 的 Python 脚本。它可以将汉字映射成有英语字母组成的拼音，和多种西方语言的字母（拉丁语、希腊语、俄语等）映射成相近的英语字母。要说明的是，汉字-拼音的映射表是从 &lt;a title="pyzh 项目" href="http://code.google.com/p/pyzh/" id="l_q."&gt;pyzh 项目&lt;/a&gt;中获得，而其他西方语言的映射表则是从 &lt;a title="Django 项目" href="http://www.djangoproject.com/" id="z5sj"&gt;Django 项目&lt;/a&gt;中获得，&lt;b id="mrrn"&gt;特表感谢&lt;/b&gt;。&lt;br /&gt;
&lt;br /&gt;
pinyin-urlify 支持自定义停用词列表（stop words）和保留词列表（reserved words）。停用词列表中的单词都会被过滤，不会出现在生成的 URL 中。生成的 URL 如果和保留词列表中的某一个单词匹配，则会被替换成默认的 URL 字符串。pinyin-urlify 可以指定 URL 的最大长度。不过，如果遇到有较多汉字组成的文章标题的话，实际生成的 URL 可能会比限定的最大长度小一些。我希望能在以后修正这一点。&lt;br /&gt;
&lt;br /&gt;
&lt;h4&gt;使用示例&lt;/h4&gt;
&lt;pre class="codes"&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; from urlify import urlify
&amp;gt;&amp;gt;&amp;gt; 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'&lt;/code&gt;&lt;/pre&gt;
&lt;br /&gt;
&lt;br /&gt;
欢迎大家用 svn 检出 pinyin-urlify 的副本来使用。欢迎任何意见或建议 :-)&lt;br /&gt;
&lt;br /&gt;
&lt;pre class="commands"&gt;&lt;code&gt;$ svn checkout http://pinyin-urlify.googlecode.com/svn/trunk/ pinyin-urlify&lt;/code&gt;&lt;/pre&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~f/luliban?a=colSWJ"&gt;&lt;img src="http://feeds.feedburner.com/~f/luliban?i=colSWJ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/luliban?a=0JYAtj"&gt;&lt;img src="http://feeds.feedburner.com/~f/luliban?i=0JYAtj" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/luliban?a=VCyS0j"&gt;&lt;img src="http://feeds.feedburner.com/~f/luliban?i=VCyS0j" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/luliban?a=tIw4OJ"&gt;&lt;img src="http://feeds.feedburner.com/~f/luliban?i=tIw4OJ" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;</description></item><item><title>有备无患：在 GitHub 上用不同的 SSH 公钥部署多个应用</title><link>http://luliban.com/blog/2008/06/github-ssh.html</link><category>linux</category><category>git</category><author>noreply@blogger.com (wyt)</author><pubDate>Sat, 21 Jun 2008 03:36:06 -0500</pubDate><guid isPermaLink="false">tag:blogger.com,1999:blog-7286295415331143255.post-2921259291670305060</guid><description>&lt;p&gt;
&lt;a href="http://github.com" title="GitHub" rel="nofollow"&gt;GitHub&lt;/a&gt; 是一个免费的基于 git 的开源软件仓库托管服务，包括 &lt;a href="http://github.com/rails/rails/tree" rel="nofollow"&gt;Ruby on Rails&lt;/a&gt;， &lt;a href="http://github.com/dchelimsky/rspec/tree" rel="nofollow"&gt;Rspec&lt;/a&gt;，&lt;a href="http://github.com/sstephenson/prototype/tree" rel="nofollow"&gt;Prototype&lt;/a&gt; 在内的许多开源软件都在 GitHub 上架设了软件仓库。
&lt;/p&gt;

&lt;br /&gt;

&lt;p&gt;
如果你有两个或两个以上的 GitHub 仓库，并且想用不同的公钥／私钥来管理这些仓库的话，会遇到这样一个问题。当你 clone 第一个仓库的时候，git 会使用第一对公钥／私钥，这个时候仍然一切正常。可当你需要用另一对公钥／私钥来 clone 第二个仓库的时候，git 默认仍然会使用第一对公钥／私钥，然后 GitHub 就会返回一个授权错误。
&lt;/p&gt;

&lt;br /&gt;

&lt;p&gt;要解决这个问题，首先需要在 ~/.ssh/ 目录下新建一个 config 文件，为不同的项目分别指定公钥／私钥&lt;/p&gt;

&lt;h3&gt;~/.ssh/config&lt;/h3&gt;
&lt;pre class="codes"&gt;&lt;code&gt;Host project-1
    HostName github.com
    User git
    IdentityFile /home/wyt/.ssh/id_rsa_project_1

Host project-2
    HostName github.com
    User git
    IdentityFile /home/wyt/.ssh/id_rsa_project_2
&lt;/code&gt;&lt;/pre&gt;
&lt;br /&gt;

&lt;p&gt;然后用 ssh-keygen 创建对应的公钥／私钥。&lt;/p&gt;

&lt;pre class="commands"&gt;&lt;code&gt;&lt;b&gt;$ ls -l ~/.ssh&lt;/b&gt;
total 24
-rw-r--r-- 1 wyt wyt  244 2008-06-21 13:18 config
-rw------- 1 wyt wyt 1675 2008-06-21 13:04 id_rsa_project_1
-rw-r--r-- 1 wyt wyt  392 2008-06-21 13:04 id_rsa_project_1.pub
-rw------- 1 wyt wyt 1675 2008-05-20 11:24 id_rsa_project_2
-rw-r--r-- 1 wyt wyt  392 2008-05-20 11:24 id_rsa_project_2.pub
-rw-r--r-- 1 wyt wyt  802 2008-05-24 22:44 known_hosts
&lt;/code&gt;&lt;/pre&gt;
&lt;br /&gt;

&lt;p&gt;将公钥上传到 GitHub 上之后，就可以用下面的指令 clone 相应的软件仓库了&lt;/p&gt;

&lt;pre class="commands"&gt;&lt;code&gt;&lt;b&gt;$ git clone git@project-1:username/project-1.git
$ git clone git@project-2:username/project-2.git&lt;/b&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;br /&gt;
&lt;br /&gt;

&lt;h3&gt;参考页面&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://alblue.blogspot.com/2005/08/howto-ssh-logins-using-keys.html" title="[HowTo] SSH logins using keys"&gt;[HowTo] SSH logins using keys&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://github.com/guides/multiple-github-accounts" title="Multiple GitHub Accounts"&gt;Multiple GitHub Accounts&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://logicalawesome.lighthouseapp.com/projects/8570-github/tickets/515-deploying-mulitple-apps-to-the-same-machine-public-key-restrictions#ticket-515-3" title="Deploying Mulitple Apps to the Same Machine Public Key Restrictions"&gt;Deploying Mulitple Apps to the Same Machine Public Key Restrictions&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~f/luliban?a=xQFX8I"&gt;&lt;img src="http://feeds.feedburner.com/~f/luliban?i=xQFX8I" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/luliban?a=8XEqOi"&gt;&lt;img src="http://feeds.feedburner.com/~f/luliban?i=8XEqOi" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/luliban?a=A1YyOi"&gt;&lt;img src="http://feeds.feedburner.com/~f/luliban?i=A1YyOi" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/luliban?a=ooOc1I"&gt;&lt;img src="http://feeds.feedburner.com/~f/luliban?i=ooOc1I" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;</description></item><item><title>悠言悠闲：Firefox 下载日</title><link>http://luliban.com/blog/2008/06/firefox.html</link><category>互联网</category><author>noreply@blogger.com (wyt)</author><pubDate>Tue, 17 Jun 2008 11:06:27 -0500</pubDate><guid isPermaLink="false">tag:blogger.com,1999:blog-7286295415331143255.post-3876905514274792940</guid><description>&lt;a href="http://www.spreadfirefox.com/en-US/worldrecord" &gt;&lt;img border="0" alt="Download Day 2008" title="Download Day 2008" src="http://www.spreadfirefox.com/sites/all/themes/spreadfirefox_RCS/images/download-day/buttons/en-US/dday_badge_fox.png" /&gt;&lt;/a&gt;

&lt;p&gt;Firefox 3正式下载时间为北京时间六月十八日凌晨一点。下载日活动届时开始，请在北京时间六月十九日凌晨一点之前下载Firefox 3。&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~f/luliban?a=gi8AuI"&gt;&lt;img src="http://feeds.feedburner.com/~f/luliban?i=gi8AuI" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/luliban?a=j5XlRi"&gt;&lt;img src="http://feeds.feedburner.com/~f/luliban?i=j5XlRi" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/luliban?a=qfTmEi"&gt;&lt;img src="http://feeds.feedburner.com/~f/luliban?i=qfTmEi" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/luliban?a=31XsgI"&gt;&lt;img src="http://feeds.feedburner.com/~f/luliban?i=31XsgI" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;</description></item><item><title>有备无患：增强豆瓣收藏对话框的 Greasemonkey 脚本</title><link>http://luliban.com/blog/2008/05/enhanced-douban-collecting-dialog.html</link><category>javascript</category><category>greasemonkey</category><category>豆瓣</category><author>noreply@blogger.com (wyt)</author><pubDate>Sun, 01 Jun 2008 01:47:35 -0500</pubDate><guid isPermaLink="false">tag:blogger.com,1999:blog-7286295415331143255.post-4964011711521755373</guid><description>&lt;p&gt;经常使用 &lt;a href="http://del.icio.us/" rel="nofollow"&gt;del.icio.us&lt;/a&gt; 的人，一定也很熟悉 del.icio.us 的跟随键盘输入自动提示标签（Tag Suggest）的功能。它为用户管理大量的标签带来方便，比如不必去费神记忆单词的拼写，避免为同一语义定义多个标签等。这是靠我们的大脑来记忆管理，或者类似&lt;a rel="nofollow" href="http://www.douban.com/"&gt;豆瓣&lt;/a&gt;只提供10个最常用的标签，不能做到的。&lt;br /&gt;

&lt;br /&gt;

这个 &lt;a rel="nofollow" href="http://addons.mozilla.org/firefox/addon/748"&gt;Greasemonkey&lt;/a&gt; 脚本的主要功能，是为豆瓣收藏对话框提供类似 del.icio.us 的标签提示功能。另外还重新布置了读过／在读／想读等按钮的布局，并添加了删除收藏的按钮。&lt;br /&gt;

&lt;br /&gt;

目前脚本的版本为0.1，欢迎试用。有什么建议或意见，&lt;a href="http://luliban.com/blog/2008/05/enhanced-douban-collecting-dialog.html#comments"&gt;欢迎留言&lt;/a&gt;:-)&lt;/p&gt;

&lt;br /&gt;
&lt;div&gt;&lt;a href="http://luliban.com/blog/uploaded_images/screenshot-enhanced-douban-collecting-dialog.png" title="屏幕截图"&gt;&lt;img src="http://luliban.com/blog/uploaded_images/screenshot-enhanced-douban-collecting-dialog.png" alt="Screenshot - Enhanced Douban Collecting Dialog" /&gt;&lt;/a&gt;&lt;/div&gt;

&lt;br /&gt;

&lt;h3&gt;功能&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;类似 &lt;a href="http://del.icio.us/" title="Del.icio.us" rel="nofollow"&gt;del.icio.us&lt;/a&gt; 的随输入自动提示标签（Tag Suggest）&lt;/li&gt;
&lt;li&gt;重新安排了读过／在读／想读等按钮的布局，保证 UI 路径的一致性&lt;/li&gt;
&lt;li&gt;增加了删除收藏的按钮&lt;/li&gt;
&lt;/ul&gt;

&lt;br /&gt;
&lt;h3&gt;下载&lt;/h3&gt;
&lt;a href="http://userscripts.org/scripts/show/27345" title="Enhanced Douban Collecting Dialog"&gt;&lt;img style="border: none;" src="http://luliban.com/images/download90.gif" alt="下载脚本" /&gt;&lt;/a&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;h4&gt;2008-05-31 UPDATE&lt;/h4&gt;
&lt;p&gt;脚本更新至0.2，有下列更新&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;增加了对个人收藏页，标签页，豆瓣猜，TOP250等页面中的收藏对话框的支持&lt;/li&gt;
&lt;li&gt;修正了之前脚本中已知的 BUG&lt;/li&gt;
&lt;/ul&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~f/luliban?a=wHt0XH"&gt;&lt;img src="http://feeds.feedburner.com/~f/luliban?i=wHt0XH" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/luliban?a=BxZ4Th"&gt;&lt;img src="http://feeds.feedburner.com/~f/luliban?i=BxZ4Th" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/luliban?a=2tC5gh"&gt;&lt;img src="http://feeds.feedburner.com/~f/luliban?i=2tC5gh" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/luliban?a=ifSvVH"&gt;&lt;img src="http://feeds.feedburner.com/~f/luliban?i=ifSvVH" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;</description></item><item><title>有备无患：在 Gentoo 上部署 Git + Gitosis 服务器的笔记</title><link>http://luliban.com/blog/2008/05/gentoo-git-gitosis.html</link><category>gentoo</category><category>linux</category><category>git</category><author>noreply@blogger.com (wyt)</author><pubDate>Sat, 21 Jun 2008 03:31:21 -0500</pubDate><guid isPermaLink="false">tag:blogger.com,1999:blog-7286295415331143255.post-6968119548310588834</guid><description>&lt;a title="Git" href="http://git.or.cz/" rel="nofollow"&gt;Git&lt;/a&gt; 是 &lt;a rel="nofollow" href="http://en.wikipedia.org/wiki/Linus_Torvalds" title="Linus Torvalds"&gt;Linus Torvalds&lt;/a&gt; 为了帮助管理 Linux 内核开发而开发的分布式版本控制软件（Distributed SCM）。Git 汲取了 Torvalds 在维护大型的分布式项目开发方面的经验和对文件系统性能的丰富知识，正如其文档所描述的，“是一个快速、可扩展的分布式版本控制系统，它具有极为丰富的命令集，对内部系统提供了高级操作和完全访问。”目前，Linux 内核、X.org 服务器和 Ruby on Rails 等开源项目的版本控制系统都已经切换到 Git。&lt;br /&gt;

&lt;br /&gt;

&lt;a rel="nofollow" title="Gitosis" href="http://eagain.net/gitweb/?p=gitosis.git;a=summary"&gt;Gitosis&lt;/a&gt; 是 &lt;a rel="nofollow" title="Tommi Virtanen" href="http://eagain.net/about/"&gt;Tommi Virtanen&lt;/a&gt; 为了更方便和安全的辅助 Git 架设和管理软件版本库 (Software Repository) 而开发的工具软件。虽然 Git 本身也提供 git-daemon 以架设版本库，但在用户访问控制上做的并不严格。而 Gitosis 允许单个用户帐号管理多个版本库，使用 SSH keys 管理用户认证，不需要 shell 帐号就可以解决多用户访问集中版本库的问题。&lt;br /&gt;

&lt;br /&gt;

需要说明的是，我使用的 Gitosis 是 &lt;a rel="nofollow" title="robbat2" href="http://robbat2.livejournal.com/"&gt;robbat2&lt;/a&gt; 为了部署新的 &lt;a rel="nofollow" title="Gentoo Overlays" href="http://git.overlays.gentoo.org/"&gt;Gentoo Overlays&lt;/a&gt; 而开发的 &lt;a rel="nofollow" title="Gentoo 分支版本" href="http://git.overlays.gentoo.org/gitweb/?p=proj/gitosis-gentoo.git"&gt;Gentoo 分支版本&lt;/a&gt; (Gentoo Fork Version)，和上游版本 (Origin Upstream Version) 相比有如下不同：&lt;br /&gt;

&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;支持 git+ssh://HOST/REPO 风格的相对路径；&lt;/li&gt;
&lt;li&gt;支持以命令行参数方式导入 keys，如 &lt;b&gt;gitosis-init --adminkey=FILE --adminname=STRING；&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;智能处理 SSH keys，支持 SSH1 和 SSH2 keys；&lt;/li&gt;
&lt;li&gt;将上游版本默认的目录权限 0750 更改为 0755，以便用 nobody:nobody 运行 git-daemon。&lt;/li&gt;
&lt;/ul&gt;

&lt;br /&gt;
这篇文章详细记录了我在本地安装和部署 Git + Gitosis 的过程和遇到的问题，希望为大家提供一些参考。欢迎 Fix-Me :-)&lt;br /&gt;


&lt;br /&gt;


&lt;h3&gt;安装 Git + Gitosis&lt;/h3&gt;

安装 git 和 gitosis-gentoo。安装完后，Gentoo 会自动添加 git 用户和组，并将版本库的主目录设为 /var/spool/gitosis/repositories。&lt;br /&gt;

&lt;pre class="commands"&gt;&lt;code&gt;&lt;b&gt;# echo "&amp;gt;=dev-util/git-1.5.5" &amp;gt;&amp;gt; /etc/portage/package.keywords
# echo "&amp;gt;=dev-util/gitosis-gentoo-0.2_p20080203" &amp;gt;&amp;gt; /etc/portage/package.keywords
# emerge -av git gitosis-gentoo&lt;/b&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;br /&gt;

如果你的主机上没有装 SSH 的话，还要安装 openssh， 以及 keychain 用来管理 ssh-agents（可选）。&lt;br /&gt;

&lt;pre class="commands"&gt;&lt;code&gt;&lt;b&gt;# emerge -av openssh keychain&lt;/b&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;br /&gt;

安装完后，启动 sshd，并加入默认启动。&lt;br /&gt;

&lt;pre class="commands"&gt;&lt;code&gt;&lt;b&gt;# /etc/init.d/sshd start
# rc-update add sshd default&lt;/b&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;br /&gt;

&lt;h3&gt;配置 Gitosis&lt;/h3&gt;

首先生成一个 ssh key。&lt;br /&gt;

&lt;pre class="commands"&gt;&lt;code&gt;&lt;b&gt;$ ssh-keygen -t rsa&lt;/b&gt;
Generating public/private rsa key pair.
Enter file in which to save the key (/home/wyt/.ssh/id_rsa):（回车）
Created directory '/home/wyt/.ssh'.
Enter passphrase (empty for no passphrase):（直接回车）
Enter same passphrase again: （直接回车）
Your identification has been saved in /home/wyt/.ssh/id_rsa.
Your public key has been saved in /home/wyt/.ssh/id_rsa.pub.
The key fingerprint is:
dd:33:cc:99:33:00:66:77:ff:00:bb:99:00:22:55:88 wyt@gentoo
&lt;/code&gt;&lt;/pre&gt;


&lt;br /&gt;
我把 ssh key 保存在默认的 ~/.ssh/id_rsa，你也可以选择其他地方。接下来把 id_rsa.pub 上传到服务器。其中的root@gentoo，root 是根用户名，gentoo 是主机名，可以从 /etc/conf.d/hostname 得知。&lt;br /&gt;

&lt;pre class="commands"&gt;&lt;code&gt;&lt;b&gt;$ cat /etc/conf.d/hostname&lt;/b&gt;
hostname="gentoo"
&lt;b&gt;$ scp ~/.ssh/id_rsa.pub root@gentoo:&lt;/b&gt;
Password: （输入 root 帐户密码）
id_rsa.pub&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 100%&amp;nbsp; 392&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 0.4KB/s&amp;nbsp;&amp;nbsp; 00:00&lt;/code&gt;&lt;/pre&gt;
&lt;br /&gt;
上传完毕后，用 gitosis-init 初始化版本库。&lt;br /&gt;
&lt;pre class="commands"&gt;&lt;code&gt;&lt;b&gt;$ sudo su -
# sudo -H -u git gitosis-init &amp;lt; ~/id_rsa.pub&lt;/b&gt;
Initialized empty Git repository in ./
Reinitialized existing Git repository in ./
&lt;/code&gt;&lt;/pre&gt;

&lt;br /&gt;
上面第二条命令中，-u 表示 sudo 将切换至 git 用户，-H 表示 sudo 将切换至该用户的主目录。初始化完成后，将创建一个名为 gitosis-admin 的版本库，用于 Gitosis 的设置和管理。你需要把它 clone 到相应目录。&lt;br /&gt;

&lt;pre class="commands"&gt;&lt;code&gt;&lt;b&gt;$ mkdir git
$ cd git
$  git clone git@gentoo:gitosis-admin.git
$ cd gitosis-admin
$ ls -lh&lt;/b&gt;
-rw-r--r-- 1 wyt wyt 80 2008-05-19 00:10 gitosis.conf
drwxr-xr-x 2 wyt wyt 80 2008-05-19 00:10 keydir&lt;/code&gt;&lt;/pre&gt;
&lt;br /&gt;
gitosis-admin 目录下有一个 gitosis.conf 文件和一个 keydir 目录。gitosis.conf 用来保存项目，用户和权限等版本库配置，而 keydir 用来保存用户的 ssh key。因为 gitosis-admin 也是 Git 的版本库，所以在修改完配置之后，只需要简单的 commit &amp;amp; push，就可以很快很方便的更新服务器的配置了。接下来就用 Gitosis 创建新版本库和添加授权用户。&lt;br /&gt;

&lt;br /&gt;

&lt;h3&gt;创建新的版本库&lt;/h3&gt;
gitosis.conf 里有 gitosis-admin 的默认配置，只要依样画葫芦创建一个新版本库即可。下面举例创建 local_test 版本库，把下面这段 code 追加到 gitosis.conf。&lt;br /&gt;

&lt;br /&gt;

&lt;h4&gt;gitosis.conf&lt;/h4&gt;
&lt;pre class="codes"&gt;&lt;code&gt;[group localteam]
writable = local_test
members = wyt@gentoo
&lt;/code&gt;&lt;/pre&gt;

&lt;br /&gt;

这段 code 定义了新的版本库，包括开发小组的名称 "localteam"，版本库的名称 "local_test"，以及拥有写权限的小组成员 "wyt@gentoo"。接下来把新的版本库所配置 commit &amp;amp; push 到服务器上。&lt;br /&gt;
&lt;pre class="commands"&gt;&lt;code&gt;&lt;b&gt;$ git commit -a -m "添加新的版本库 local_test"
$ git push&lt;/b&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;br /&gt;
接下来在本地创建版本库，然后将其上传到服务器&lt;br /&gt;
&lt;pre class="commands"&gt;&lt;code&gt;&lt;b&gt;$ mkdir local_test
$ cd local_test
$ git init
$ git remote add origin git@gentoo:local_test.git&lt;/b&gt;

# add &amp;amp; commit 一些文件，然后……

&lt;b&gt;$ git push origin master:refs/heads/master&lt;/b&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;br /&gt;

&lt;h3&gt;添加成员用户&lt;/h3&gt;
添加版本库的成员用户的步骤可以分为两步。一是将用户的 SSH 公钥复制到 keydir/ 目录。二是编辑 gitosis.conf，将这些用户的名字加入 "members"。&lt;br /&gt;
&lt;pre class="commands"&gt;&lt;code&gt;&lt;b&gt;$ cd gitosis-admin/
$ cp ~/zhangsan@gentoo.pub keydir/
$ cp ~/lisi@gentoo.pub keydir/
$ git add keydir/zhangsan@gentoo.pub keydir/lisi@gentoo.pub&lt;/b&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;br /&gt;

编辑 gitosis.conf&lt;br /&gt;

&lt;br /&gt;
&lt;h4&gt;gitosis.conf&lt;/h4&gt;
&lt;pre class="codes"&gt;&lt;code&gt;[group localteam]
writable = local_test
members = wyt@gentoo zhangsan@gentoo lisi@gentoo&lt;/code&gt;&lt;/pre&gt;
&lt;br /&gt;
接下来，将设置提交到服务器&lt;br /&gt;
&lt;pre class="commands"&gt;&lt;code&gt;&lt;b&gt;$ git commit -a -m '添加 local_test 版本库成员：张三、李四'
$ git push&lt;/b&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;br /&gt;
&lt;h3&gt;选择需要输出的版本库&lt;/h3&gt;
&lt;br /&gt;
&lt;h4&gt;输出所有版本库&lt;/h4&gt;
编辑 /etc/conf.d/git-daemon&lt;br /&gt;
&lt;pre class="codes"&gt;&lt;code&gt;GITDAEMON_OPTS="--syslog --base-path=/var/spool/gitosis/repositories/ --export-all"&lt;/code&gt;&lt;/pre&gt;

&lt;br /&gt;

&lt;h4&gt;选择输出版本库&lt;/h4&gt;

编辑 /etc/conf.d/git-daemon&lt;br /&gt;

&lt;pre class="codes"&gt;&lt;code&gt;GITDAEMON_OPTS="--syslog --base-path=/var/spool/gitosis/repositories/"&lt;/code&gt;&lt;/pre&gt;

&lt;br /&gt;
然后在每个选择输出的版本库目录中，添加一个 git-daemon-export-ok 文件&lt;br /&gt;
&lt;pre class="commands"&gt;&lt;code&gt;&lt;b&gt;$ sudo touch /var/spool/gitosis/repositories/local_test.git/git-daemon-export-ok&lt;/b&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;br /&gt;
&lt;h3&gt;启动 git-daemon&lt;/h3&gt;
&lt;pre class="commands"&gt;&lt;code&gt;&lt;b&gt;# /etc/init.d/git-daemon start
# rc-update add git-daemon default&lt;/b&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;br /&gt;
如果一切正常的话，我们应该已经大功告成，完成部署 Git + Gitosis 服务器了。现在可以试一下远程 clone。&lt;br /&gt;
&lt;pre class="commands"&gt;&lt;code&gt;&lt;b&gt;$ git clone git://169.254.64.95/local_test.git&lt;/b&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;h4&gt;参考链接&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel="nofollow" href="http://technicalpickles.com/posts/gitosis-on-gentoo" title="Gitosis on Gentoo @ Technical Pickles"&gt;Gitosis on Gentoo @ Technical Pickles&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a rel="nofollow" href="http://gentoo-wiki.com/Gitosis" title="Gitosis - Gentoo Linux Wiki"&gt;Gitosis - Gentoo Linux Wiki&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a rel="nofollow" href="http://scie.nti.st/2007/11/14/hosting-git-repositories-the-easy-and-secure-way" title="Hosting Git repositories, The Easy (and Secure) Way"&gt;Hosting Git repositories, The Easy (and Secure) Way&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a rel="nofollow" href="http://robbat2.livejournal.com/217665.html" title="robbat2: Gitosis on Gentoo, important notes"&gt;robbat2: Gitosis on Gentoo, important notes&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a rel="nofollow" title="Git in a Nutshell" href="http://www.chem.helsinki.fi/%7Ejonas/git_guides/HTML/git_guide/git_guide.html"&gt;Git in a Nutshell&lt;/a&gt;&lt;br /&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;h4&gt;2008-05-20 UPDATE&lt;/h4&gt;
clone 只读版本库：&lt;br /&gt;
&lt;pre class="commands"&gt;&lt;code&gt;&lt;b&gt;$ git clone git://169.254.64.95/local_test.git&lt;/b&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;br /&gt;

clone 可读写版本库：&lt;br /&gt;
&lt;pre class="commands"&gt;&lt;code&gt;&lt;b&gt;$ git clone git+ssh://git@169.254.64.95/local_test.git&lt;/b&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;br /&gt;

创建新的可读写版本库：&lt;br /&gt;
&lt;pre class="commands"&gt;&lt;code&gt;&lt;b&gt;$ git remote add origin git+ssh://git@169.254.64.95/new_local_test.git
$ git push origin master:refs/heads/master&lt;/b&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~f/luliban?a=Rc1ZsH"&gt;&lt;img src="http://feeds.feedburner.com/~f/luliban?i=Rc1ZsH" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/luliban?a=IVfrQh"&gt;&lt;img src="http://feeds.feedburner.com/~f/luliban?i=IVfrQh" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/luliban?a=TjBffh"&gt;&lt;img src="http://feeds.feedburner.com/~f/luliban?i=TjBffh" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/luliban?a=rc2lHH"&gt;&lt;img src="http://feeds.feedburner.com/~f/luliban?i=rc2lHH" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;</description></item><item><title>有备无患：绕过 GHS 用自定义域名发布 Blogger</title><link>http://luliban.com/blog/2008/05/ghs-blogger.html</link><category>blogger</category><category>google</category><author>noreply@blogger.com (wyt)</author><pubDate>Thu, 26 Jun 2008 07:01:01 -0500</pubDate><guid isPermaLink="false">tag:blogger.com,1999:blog-7286295415331143255.post-1472011599496106035</guid><description>2007年1月，&lt;a rel="nofollow" title="Google Blogger" href="http://www.blogger.com"&gt;Google Blogger&lt;/a&gt; 的自定义域名功能推出不到一个星期，就&lt;a rel="nofollow" title="Blogger的自定义域名功能被废" href="http://www.gseeker.com/50226711/bloggerceaeee_56005.php"&gt;被 GFW 给废了&lt;/a&gt;。原因就是 GFW 隔断了国内用户对 Google 的免费主机托管服务 ghs.google.com 的访问。&lt;br /&gt;

&lt;br /&gt;

今天试着将 App Engine 应用部署到自己域名的时候，发现 App Engine 不需要将 CNAME record 指向 ghs.google.com，从而可以避开 GFW。而&lt;a rel="nofollow" title="App Engine URLs" href="http://www.google.com/support/a/bin/answer.py?hl=en&amp;answer=91080"&gt;同样的方法&lt;/a&gt;也可以适用于 Blogger。方法如下：&lt;br /&gt;

&lt;br /&gt;
&lt;ol&gt;
&lt;li&gt;
把 Blogger 的发布方式切换成“自定义域名（Custom Domain）”。在“你的域名（Your Domain）”一栏中填上域名，比如，py.thonic.org。保存设置（Save Settings）。&lt;/li&gt;

&lt;li&gt;在你的域名注册商那里，修改域名（thonic.org）的 DNS 设置。为子域名（py）添加一条 A record，指向下面四个 IP 地址的其中之一。举例来说，就是添加这样一条记录：&lt;b&gt;py.thonic.org  86400  IN  A  216.239.34.21&lt;/b&gt;。
&lt;ul&gt;
&lt;li&gt;216.239.32.21&lt;/li&gt;
&lt;li&gt;216.239.34.21&lt;/li&gt;
&lt;li&gt;216.239.36.21&lt;/li&gt;
&lt;li&gt;216.239.38.21&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;

&lt;/ol&gt;

&lt;br /&gt;

DNS 设置的改动需要一段时间来完成，一般不会超过24小时。这样就可以重新使用 Blogger 的自定义域名功能了，只是，这次又可以持续多久呢？

&lt;br /&gt;
&lt;br /&gt;

&lt;h4&gt;2008-06-26 UPDATE&lt;/h4&gt;
&lt;p&gt;我把上面的 IP 绑定到自己的域名了，ghs.luliban.com。以后也可以像 ghs.google.com 一样，添加一条 CNAME 记录到 ghs.luliban.com 来绑定 Google Blogger 或者 Google Apps 了。&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~f/luliban?a=AzerDH"&gt;&lt;img src="http://feeds.feedburner.com/~f/luliban?i=AzerDH" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/luliban?a=cgs9sh"&gt;&lt;img src="http://feeds.feedburner.com/~f/luliban?i=cgs9sh" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/luliban?a=m29Jph"&gt;&lt;img src="http://feeds.feedburner.com/~f/luliban?i=m29Jph" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/luliban?a=R4UHHH"&gt;&lt;img src="http://feeds.feedburner.com/~f/luliban?i=R4UHHH" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;</description></item><item><title>有备无患：在 Google App Engine上应用豆瓣 API 的授权认证</title><link>http://luliban.com/blog/2008/05/google-app-engine-api.html</link><category>python</category><category>豆瓣</category><category>google</category><author>noreply@blogger.com (wyt)</author><pubDate>Thu, 08 May 2008 23:57:05 -0500</pubDate><guid isPermaLink="false">tag:blogger.com,1999:blog-7286295415331143255.post-855068127276409148</guid><description>上次说到了&lt;a title="有备无患：在 Google App Engine 上应用豆瓣 Python 客户端" href="http://luliban.com/blog/2008/04/google-app-engine-python.html"&gt;在 App Engine 上应用的豆瓣的 Python 客户端&lt;/a&gt;，但是那个版本（&lt;a title="douban-python-0.1.1" href="http://code.google.com/p/douban-python/" rel="nofollow"&gt;0.1.1&lt;/a&gt;）的客户端并没有包括 OAuth 授权认证的功能。如果想要让用户授权，以访问那些受保护的资源，以及添加、修改或删除用户的收藏，需要从豆瓣 Python 客户端的 SVN 中获取最新的开发版本（&lt;a title="r22" href="http://code.google.com/p/douban-python/source/detail?r=22" rel="nofollow"&gt;r22&lt;/a&gt;）。&lt;a title="trunk里的oauth client完成了多少？" href="http://www.douban.com/group/topic/3073216/" rel="nofollow"&gt;据豆瓣的开发人员 hongqn 说&lt;/a&gt;，OAuth Client 基本开发完毕，已经进入内测 bug 的阶段。&lt;br /&gt;

&lt;br /&gt;

和 GData Python 客户端一样，开发版本的 OAuth Client 也是用 Python 自带的 httplib 模块来处理 HTTP 请求，所以原始的客户端不能直接在 App Engine 上使用，必须先将 httplib 替换成 &lt;a title="urlfetch" href="http://code.google.com/appengine/docs/urlfetch/overview.html" rel="nofollow"&gt;urlfetch&lt;/a&gt;。现在只需要修改两个函数，但是豆瓣如果能像 GData Python 客户端一样把使用 httplib 的部分封装起来，甚至提供一个使用 urlfetch 的替换模块就更好了，希望豆瓣能采纳&lt;a title="oauth client用了httplib，可是appengine不支持httplib" href="http://www.douban.com/group/topic/3088924/" rel="nofollow"&gt;这个建议&lt;/a&gt;。下面是具体步骤，如果有什么问题，还请留言告知。&lt;br /&gt;

&lt;br /&gt;

&lt;b&gt;1，从 trunk 中 checkout 最新的豆瓣 Python 客户端开发版本&lt;/b&gt;&lt;br /&gt;

&lt;pre class="commands"&gt;&lt;code&gt;&lt;b&gt;$ svn co http://douban-python.googlecode.com/svn/trunk/ douban-python/
&lt;/b&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;br /&gt;

&lt;b&gt;2，修改客户端的 OAuth Client&lt;/b&gt;&lt;br /&gt;

&lt;pre class="commands"&gt;&lt;code&gt;&lt;b&gt;$ cp douban-python/douban ~/doupye/douban -rf
$ cd ~/doupye/douban/
$ gvim client.py
&lt;/b&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;br /&gt;

&lt;h4&gt;client.py&lt;/h4&gt;
&lt;pre class="codes"&gt;&lt;code&gt;# 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
&lt;/code&gt;&lt;/pre&gt;

&lt;br /&gt;

&lt;b&gt;3，使用 OAuth 授权的过程如下，在 App Engine SDK 提供的控制台（&lt;a title="Interactive Console" href="http://localhost:8080/_ah/admin/interactive" rel="nofollow"&gt;Interactive Console&lt;/a&gt;）中运行：&lt;/b&gt;&lt;br /&gt;

&lt;pre class="commands"&gt;&lt;code&gt;&lt;b&gt;from douban.client import OAuthClient

client = OAuthClient(key=MY_API_KEY, secret=MY_SECRET)&lt;/b&gt;

# 获取未授权的Request Token
&lt;b&gt;key, secret = client.get_request_token()
print key, secret&lt;/b&gt;
&gt;&gt;&gt; c14023315549fe3743c17993ff4dfaa5 91af6245103ec3b7

# 获取请求用户授权的页面的 URL
&lt;b&gt;url = client.get_authorization_url(key, secret)
print url&lt;/b&gt;
&gt;&gt;&gt; http://www.douban.com/service/auth/authorize?oauth_token=a9e487ac36e0ba9efdba970534a22fce

# 将 URL 复制到浏览器中，用户可以选择同意或者拒绝授权

# 用户完成授权后，使用授权后的 Request Token 换取 Access Token
&lt;b&gt;key, secret = client.get_access_token(key, secret)
if key:&lt;/b&gt;
    # 使用 Access Token 登录
    &lt;b&gt;login = client.login(key, secret)
    print login&lt;/b&gt;
&gt;&gt;&gt; True

# 访问受保护资源
&lt;b&gt;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&lt;/b&gt;
&gt;&gt;&gt;
听过 人として軸がぶれている
想读 Antipatterns
想读 新企业的起源与演进
看过 .hack//G.U. Trilogy
想听 ワイルドストロベリー
在听 The Flower Book
在听 E=MC²
听过 越长大越孤单
想读 Investing 101
想读 The Ecology of Commerce
&lt;/code&gt;&lt;/pre&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~f/luliban?a=GHeKcH"&gt;&lt;img src="http://feeds.feedburner.com/~f/luliban?i=GHeKcH" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/luliban?a=nIOteh"&gt;&lt;img src="http://feeds.feedburner.com/~f/luliban?i=nIOteh" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/luliban?a=oaCuTh"&gt;&lt;img src="http://feeds.feedburner.com/~f/luliban?i=oaCuTh" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/luliban?a=6CZrvH"&gt;&lt;img src="http://feeds.feedburner.com/~f/luliban?i=6CZrvH" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;</description></item><item><title>悠言悠闲：火箭不死心还在</title><link>http://luliban.com/blog/2008/04/blog-post_22.html</link><category>nba</category><category>火箭</category><author>noreply@blogger.com (wyt)</author><pubDate>Tue, 22 Apr 2008 08:29:02 -0500</pubDate><guid isPermaLink="false">tag:blogger.com,1999:blog-7286295415331143255.post-3065362534680213650</guid><description>&lt;div&gt;
&lt;object type="application/x-shockwave-flash" width="425px" height="350px" data="http://www.tudou.com/v/Bu_qZGa_upQ"&gt;&lt;param name="movie" value="http://www.tudou.com/v/Bu_qZGa_upQ" /&gt;&lt;/object&gt;
&lt;/div&gt;
&lt;br /&gt;
&lt;p&gt;
&lt;a rel="nofollow" title="不死心还在" href="http://www.douban.com/subject/2969163/"&gt;不死心还在&lt;/a&gt;。下午听到这首歌，真的很合适给今天的火箭当主题曲。即使被ESPN的所有“专家”&lt;a rel="nofollow" href="http://sports.espn.go.com/nba/playoffs2008/series?series=houuth"&gt;一致通过必输爵士&lt;/a&gt;，即使被CCTV“内定”必输所以一场比赛都不转播，即使有一半以上的休斯敦球迷都不相信他们能通过第一轮，即使被世界所背叛，他们也会一步一步的走向胜利的日子。知道T-Mac接受采访时最常说的一句话是什么？我不在乎人们怎么说。只有甘于忍受寂寞和敢与世界为敌的决心，才是让休斯敦火箭，让 Tracy McGrady、Shane Battier、Luis Scola、Dikembe Mutombo、Bobby Jackson、Chuck Hayes、Aaron Brooks 以及 Rick Adelman，能够坚持与爵士为之一战的精神支柱。&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~f/luliban?a=IHhtYzG"&gt;&lt;img src="http://feeds.feedburner.com/~f/luliban?i=IHhtYzG" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/luliban?a=cDXEMig"&gt;&lt;img src="http://feeds.feedburner.com/~f/luliban?i=cDXEMig" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/luliban?a=GehvmCg"&gt;&lt;img src="http://feeds.feedburner.com/~f/luliban?i=GehvmCg" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/luliban?a=SUqeUQG"&gt;&lt;img src="http://feeds.feedburner.com/~f/luliban?i=SUqeUQG" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;</description></item><item><title>有备无患：在 Google App Engine 上应用豆瓣 Python 客户端</title><link>http://luliban.com/blog/2008/04/google-app-engine-python.html</link><category>python</category><category>豆瓣</category><category>google</category><author>noreply@blogger.com (wyt)</author><pubDate>Wed, 07 May 2008 20:58:19 -0500</pubDate><guid isPermaLink="false">tag:blogger.com,1999:blog-7286295415331143255.post-4319665207803818452</guid><description>&lt;div&gt;

&lt;a href="http://luliban.com/blog/uploaded_images/douban-gae.png" title="有备无患：在 Google App Engine 上应用豆瓣 Python 客户端"&gt;
&lt;img src="http://luliban.com/blog/uploaded_images/douban-gae.png" alt="有备无患：在 Google App Engine 上应用豆瓣 Python 客户端" /&gt;
&lt;/a&gt;

&lt;/div&gt;


&lt;p&gt;


&lt;a rel="nofollow" title="Google App Engine" href="http://appengine.google.com/"&gt;Google App Engine&lt;/a&gt; 是 Google 四月初推出的一个网络应用开发平台，它提供了一体化的分布式服务器群、供快速开发的网络应用开发框架、最多500MB的数据存储，以及可自动升级的后台流量统计服务。换言之，App Engine 让开发人员专注于应用本身，Google 将提供应用运行及维护所需要的一切平台资源。&lt;br /&gt;

&lt;br /&gt;

App Engine 目前只支持 Python 作为唯一的开发语言（wyt：谁让“Python 之父” &lt;a rel="nofollow" title="Guido van Rossum" href="http://www.python.org/%7Eguido/"&gt;Guido van Rossum&lt;/a&gt; 也在 Google 工作呢，近水楼台先得月）。所以，如果想在 App Engine 上利用&lt;a rel="nofollow" title="豆瓣" href="http://www.douban.com" &gt;豆瓣&lt;/a&gt;开放的书影音和用户数据，我们可以使用豆瓣提供的 &lt;a rel="nofollow" title="Python 客户端" href="http://code.google.com/p/douban-python/"&gt;Python 客户端&lt;/a&gt;来开发。另外，由于以前的 &lt;a rel="nofollow" title="GData Python 客户端" href="http://code.google.com/p/gdata-python-client/" id="ad6t"&gt;GData Python 客户端&lt;/a&gt; 都是用 httplib 模块来处理 HTTP 请求，而 App Engine 则规定必须通过其自带的 &lt;a rel="nofollow" title="urlfetch" href="http://code.google.com/appengine/docs/urlfetch/overview.html" id="liy5"&gt;urlfetch&lt;/a&gt; 才能实现，所以为了让豆瓣 Python 客户端所必须的 gdata.service 模块能够正常的在 App Engine 上工作，我们还需要版本号大于1.0.12.1的 gdata-python-client。下面是具体步骤，如果有什么问题，还请留言告知。&lt;br /&gt;

&lt;br /&gt;

&lt;b&gt;1，下载 &lt;a title="gdata.py-1.0.12.1.tar.gz" rel="nofollow" href="http://gdata-python-client.googlecode.com/files/gdata.py-1.0.12.1.tar.gz" id="wlnh"&gt;gdata.py-1.0.12.1.tar.gz&lt;/a&gt;&lt;br /&gt;

2，解压缩到当前目录，并编译 gdata&lt;/b&gt;&lt;br /&gt;&lt;/p&gt;

&lt;pre class="commands"&gt;&lt;code&gt;&lt;b&gt;$ tar xf gdata.py-1.0.12.1.tar.gz
$ cd gdata.py-1.0.12.1/
$ ./setup.py build
&lt;/b&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;br /&gt;
&lt;p&gt;&lt;b&gt;
3，将编译好的 atom 和 gdata 目录复制到项目目录 ~/doupye/
&lt;/b&gt;&lt;/p&gt;


&lt;pre class="commands"&gt;&lt;code&gt;&lt;b&gt;$ cp build/lib/* ~/doupye/&lt;/b&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;br /&gt;

&lt;p&gt;&lt;b&gt;
4，下载 &lt;a rel="nofollow" title="douban-python-0.1.1.tar.gz" href="http://douban-python.googlecode.com/files/douban-python-0.1.1.tar.gz" &gt;douban-python-0.1.1.tar.gz&lt;/a&gt;&lt;br /&gt;


5，解压缩到当前目录，并编译 douban-python
&lt;/b&gt;&lt;/p&gt;


&lt;pre class="commands"&gt;&lt;code&gt;&lt;b&gt;$ cd ..
$ tar xf douban-python-0.1.1.tar.gz
$ cd douban-python-0.1.1/
$ ./setup.py build
&lt;/b&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;br /&gt;

&lt;p&gt;&lt;b&gt;
6，将编译好的 douban 目录复制到项目目录 ~/doupye/
&lt;/b&gt;&lt;/p&gt;


&lt;pre class="commands"&gt;&lt;code&gt;&lt;b&gt;$ cp build/lib/* ~/doupye/&lt;/b&gt;&lt;/code&gt;&lt;/pre&gt;


&lt;br /&gt;


&lt;p&gt;&lt;b&gt;
7，修改豆瓣的 Python 客户端文件 service.py
&lt;/b&gt;&lt;/p&gt;

&lt;pre class="commands"&gt;&lt;code&gt;&lt;b&gt;$ cd ~/doupye/douban/
$ gvim service.py
&lt;/b&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;br /&gt;
&lt;h4&gt;service.py&lt;/h4&gt;

&lt;pre class="codes"&gt;&lt;code&gt;import gdata.service
# 添加下面两行，让所有的 HTTP 请求调用 App Engine 的 urlfetch (&lt;a rel="nofollow" title="Release the hounds: Support for App Engine and Contacts API" href="http://googledataapis.blogspot.com/2008/04/release-hounds-support-for-app-engine.html" id="oj1m"&gt;?&lt;/a&gt;)
import gdata.urlfetch
gdata.service.http_request_handler = gdata.urlfetch
&lt;/code&gt;&lt;/pre&gt;

&lt;br /&gt;
&lt;br /&gt;
&lt;p&gt;&lt;h4&gt;演示网页&lt;/h4&gt;
&lt;span style="text-decoration: line-through;"&gt;http://doupye.appspot.com/demo/douban_python_client/&lt;/span&gt;&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~f/luliban?a=nxIpaeG"&gt;&lt;img src="http://feeds.feedburner.com/~f/luliban?i=nxIpaeG" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/luliban?a=hWTdWjg"&gt;&lt;img src="http://feeds.feedburner.com/~f/luliban?i=hWTdWjg" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/luliban?a=DaToTFg"&gt;&lt;img src="http://feeds.feedburner.com/~f/luliban?i=DaToTFg" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/luliban?a=bzLyYgG"&gt;&lt;img src="http://feeds.feedburner.com/~f/luliban?i=bzLyYgG" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;</description></item><item><title>有备无患：订阅所有豆瓣用户的广播</title><link>http://luliban.com/blog/2008/04/blog-post_16.html</link><category>python</category><category>豆瓣</category><author>noreply@blogger.com (wyt)</author><pubDate>Fri, 18 Apr 2008 00:17:45 -0500</pubDate><guid isPermaLink="false">tag:blogger.com,1999:blog-7286295415331143255.post-719603050147822500</guid><description>&lt;p&gt;以前写过&lt;a href="http://luliban.com/blog/2007/11/gtalk-skype-msn.html"&gt;输出单个用户的友邻广播 feeds 的脚本&lt;/a&gt;，后来&lt;a href="http://www.douban.com" rel="nofollow"&gt;豆瓣&lt;/a&gt;把原来的“友邻”一分为二成双向的“朋友”和单向的“关注”，那个脚本就过时了。所以我重写了 pydmb.py，并让它能够输出多层友邻关系的豆瓣广播 feeds 的 &lt;a href="http://tw.info.yahoo.com/rss/rookie.html#sub_4" rel="nofollow"&gt;OPML 文件&lt;/a&gt;。&lt;b&gt;根据&lt;a href="http://zh.wikipedia.org/wiki/%E5%85%AD%E5%BA%A6%E5%88%86%E9%9A%94%E7%90%86%E8%AB%96" rel="nofollow"&gt;六度分割理论&lt;/a&gt;，平均只需要六层关系就可以联系到任何两个互不相识的人。那么，从任何一个豆瓣用户出发输出六层友邻关系的广播，是不是最终也可以得到几乎所有豆瓣用户的广播呢？&lt;/b&gt;我没有试过，因为即使只输出了两层友邻关系的广播，我已经得到 8825个 feeds，如果输出六层的话会吓到人的。&lt;br /&gt;

&lt;br /&gt;

&lt;h3&gt;使用方法&lt;/h3&gt;
1，下载 &lt;a href="http://luliban.com/attachments/pydmb-0.2.tar.gz" title="pydmb-0.2.tar.gz"&gt;pydmb.py&lt;/a&gt;&lt;/p&gt;

&lt;div&gt;
&lt;a href="http://luliban.com/attachments/pydmb-0.2.tar.gz" title="pydmb-0.2.tar.gz"&gt;&lt;img alt="pydmb-0.2.tar.gz" style="border:none;" src="http://luliban.com/images/download90.gif" /&gt;&lt;/a&gt;
&lt;/div&gt;

&lt;p&gt;2，解压缩到当前目录。&lt;br /&gt;

&lt;br /&gt;

&lt;p&gt;3，运行脚本。这里举一个例子，比如你想获得 &lt;a rel="nofollow" href="http://www.douban.com/people/keso"&gt;keso&lt;/a&gt; 和&lt;a rel="nofollow" href="http://www.douban.com/people/ahbei"&gt;阿北&lt;/a&gt;的三层朋友（不包括他们关注的人）的广播 feeds，可以输入命令：&lt;/p&gt;
&lt;pre class="commands"&gt;&lt;code&gt;&lt;b&gt;$ ./pydmb.py keso ahbei --friend --depth 3&lt;/b&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;br /&gt;

&lt;p&gt;4，需要帮助可以运行命令：&lt;/p&gt;

&lt;pre class="commands"&gt;&lt;code&gt;&lt;b&gt;$ ./pydmb.py --help&lt;/b&gt;
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
&lt;/code&gt;&lt;/pre&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;h4&gt;2008-04-18 UPDATE:&lt;/h4&gt;
&lt;p&gt;&lt;a href="http://luliban.com/blog/2008/04/blog-post_16.html#comments"&gt;NullPointer 留言说&lt;/a&gt;想看看六度连接的试验结果，我也很感兴趣，所以昨天先试了试二度和三度的连接。结果从阿北出发的二度空间能连接到10214个人，三度空间能连接到117113个。&lt;/p&gt;
&lt;pre class="commands"&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; from pydmb import *
&amp;gt;&amp;gt;&amp;gt; graph = UserGraph('ahbei')
&amp;gt;&amp;gt;&amp;gt; graph.search(2, 'fc')
&amp;gt;&amp;gt;&amp;gt; len(graph.dict.keys())
10214
... ...
&amp;gt;&amp;gt;&amp;gt; graph.search(3, 'fc')
&amp;gt;&amp;gt;&amp;gt; len(graph.dict.keys())
117113&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;结果还算理想，可是用的时间比较厉害，二度连接还好只用了十几分钟，但三度连接用了将近八个小时。照这样推算，分析六度连接（理论上说，就是要分析将近140万豆瓣用户的朋友和关注的人）可能会超过800个小时。所以用这个脚本来做就不太现实了，如果要做的话，最好把 &lt;a rel="nofollow" href="http://www.python.org/doc/lib/module-urllib2.html"&gt;urllib2&lt;/a&gt; 和 &lt;a rel="nofollow" href="http://www.crummy.com/software/BeautifulSoup/"&gt;BeautifulSoup&lt;/a&gt; 换成更快的库，然后用两个线程分别来抓取和分析网页，这样效率会高一些。&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~f/luliban?a=B2ZBPZG"&gt;&lt;img src="http://feeds.feedburner.com/~f/luliban?i=B2ZBPZG" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/luliban?a=r9odNbg"&gt;&lt;img src="http://feeds.feedburner.com/~f/luliban?i=r9odNbg" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/luliban?a=2byFBQg"&gt;&lt;img src="http://feeds.feedburner.com/~f/luliban?i=2byFBQg" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/luliban?a=QexdhRG"&gt;&lt;img src="http://feeds.feedburner.com/~f/luliban?i=QexdhRG" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;</description></item><item><title>南言北哲：你最想看哪些季后赛的首轮对局？（三）</title><link>http://luliban.com/blog/2008/04/blog-post_12.html</link><category>nba</category><category>火箭</category><author>noreply@blogger.com (wyt)</author><pubDate>Wed, 16 Apr 2008 03:30:26 -0500</pubDate><guid isPermaLink="false">tag:blogger.com,1999:blog-7286295415331143255.post-3963515544537637724</guid><description>&lt;p&gt;
&lt;a rel="nofollow" title="Suns 90, Rockets 101" href="http://sports.espn.go.com/nba/boxscore?gameId=280411010" id="pfy8"&gt;昨天火箭大胜太阳&lt;/a&gt;之后，在常规赛季只剩下最后三场的情况下，&lt;a rel="nofollow" title="NBA Standings" href="http://sports.espn.go.com/nba/standings" id="fqvw"&gt;有四支球队距离西部冠军只有少于等于1场&lt;/a&gt;。这就像在银石跑到第60圈，Kimi、Hamilton 和 Alonso 从 Luffield 弯出来之后，三辆赛车并驾齐驱以超过时速280公里的速度冲向终点一样不可思议。&lt;br /&gt;

&lt;br /&gt;

&lt;b&gt;。。。我滴妈呀，太刺激了～～～&lt;/b&gt;（wyt：请模仿德云社李菁的嗓音进行想象。云里雾里的同学可以参考本文最后的郭德纲相声：）&lt;br /&gt;

&lt;br /&gt;

虽然我也期待一个戏剧性的 ending，比如火箭最终夺冠之类的，但是如果常规赛就此结束，那么今天的排名将最接近我想要看到的季后赛首轮对局，&lt;a title="南言北哲：你最想看哪些季后赛首轮的对局？（一）" href="http://luliban.com/blog/2008/03/blog-post_27.html" id="obv1"&gt;小牛（7）对湖人（2）&lt;/a&gt;，&lt;a title="南言北哲：你最想看哪些季后赛的首轮对局？（二）" href="http://luliban.com/blog/2008/04/blog-post.html" id="cq3s"&gt;太阳（6）对马刺（3）&lt;/a&gt;，以及接下来将讨论的火箭（5）对爵士（4）。这样其实也不错。&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;第三局：火箭 vs 爵士&lt;/h3&gt;

&lt;h4&gt;——从哪里跌倒，就要从哪里爬起来&lt;/h4&gt;
&lt;/p&gt;

&lt;br /&gt;

&lt;div&gt;
&lt;a href="http://luliban.com/blog/uploaded_images/tmac-dribble-ak47.jpg"&gt;
&lt;img id="fq4l" style="width: 430px; height: 594px;" src="http://luliban.com/blog/uploaded_images/tmac-dribble-ak47.jpg" /&gt;
&lt;/a&gt;
&lt;/div&gt;

&lt;br /&gt;

&lt;p&gt;


去年，火箭的第一场和最后一场常规赛都在盐湖城和犹他爵士比赛，两战皆墨。今年，火箭的第二场和倒数第二场常规赛又都在盐湖城，火箭赢了前一场，而后一场在4月15日。去年，火箭在季后赛第一轮的七场大战中输给了爵士，让爵士闯到西部决赛。今年，稳居西部第四的爵士仍然是火箭的季后赛第一轮的对手最有可能遇到的对手。&lt;br /&gt;

&lt;br /&gt;事实上，我也希望它是。去年11月2日以105-96战胜爵士之后，27投17中砍下47分的 T-Mac 看来很平静，“这不能说明什么，这仅仅是赛季的第二场比赛。”可姚明似乎“出卖”了他，“我们像是在打第八场系列赛，我们得到胜利之后才能开始新的赛季。”如果爵士真的再一次成为火箭的季后赛对手的话，那么无论是对T-Mac，还是对火箭队，都是时候来证明自己了，&lt;b&gt;IT'S TIME FOR A REVENGE，是时候报仇雪恨了！&lt;/b&gt;&lt;br /&gt;

&lt;br /&gt;
附：&lt;a rel="nofollow" title="郭德纲相声《李菁开车》" href="http://www.tudou.com/v/z7siMuSVLus"&gt;郭德纲相声《李菁开车》&lt;/a&gt;（口不对声，听听就行）&lt;/p&gt;
&lt;br /&gt;
&lt;div&gt;
&lt;object type="application/x-shockwave-flash" width="425px" height="350px" data="http://www.tudou.com/v/z7siMuSVLus"&gt;&lt;param name="movie" value="http://www.tudou.com/v/z7siMuSVLus" /&gt;&lt;/object&gt;
&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~f/luliban?a=mYGkf4G"&gt;&lt;img src="http://feeds.feedburner.com/~f/luliban?i=mYGkf4G" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/luliban?a=uBrejdg"&gt;&lt;img src="http://feeds.feedburner.com/~f/luliban?i=uBrejdg" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/luliban?a=xmq7wcg"&gt;&lt;img src="http://feeds.feedburner.com/~f/luliban?i=xmq7wcg" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/luliban?a=32X6ezG"&gt;&lt;img src="http://feeds.feedburner.com/~f/luliban?i=32X6ezG" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;</description></item><item><title>悠言悠闲：root敢死队</title><link>http://luliban.com/blog/2008/04/root.html</link><category>linux</category><author>noreply@blogger.com (wyt)</author><pubDate>Fri, 11 Apr 2008 02:36:40 -0500</pubDate><guid isPermaLink="false">tag:blogger.com,1999:blog-7286295415331143255.post-7677387863603087715</guid><description>&lt;p&gt;原来平时那么爱用sudo，还真不觉得。&lt;/p&gt;
&lt;br /&gt;
&lt;pre class="commands"&gt;&lt;code&gt;&lt;b&gt;$ history |awk '{a[$2]++ } END{for(i in a){print a[i] " " i}}'|sort -rn|head&lt;/b&gt;
99 sudo
62 todo
52 python
49 eix
38 mysql
38 cd
32 ls
19 tda
16 vi
15 tde
&lt;/code&gt;&lt;/pre&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~f/luliban?a=IUYrwFG"&gt;&lt;img src="http://feeds.feedburner.com/~f/luliban?i=IUYrwFG" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/luliban?a=6EKWung"&gt;&lt;img src="http://feeds.feedburner.com/~f/luliban?i=6EKWung" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/luliban?a=Bgm3JZg"&gt;&lt;img src="http://feeds.feedburner.com/~f/luliban?i=Bgm3JZg" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/luliban?a=sKXeqHG"&gt;&lt;img src="http://feeds.feedburner.com/~f/luliban?i=sKXeqHG" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;</description></item><item><title>有备无患：用 AideRSS + Google Reader 辅助阅读</title><link>http://luliban.com/blog/2008/04/aiderss-google-reader.html</link><category>google</category><category>互联网</category><category>feed</category><author>noreply@blogger.com (wyt)</author><pubDate>Sun, 11 May 2008 03:35:25 -0500</pubDate><guid isPermaLink="false">tag:blogger.com,1999:blog-7286295415331143255.post-6970860275498506697</guid><description>&lt;p&gt;

前几天我计算出自己的 &lt;a title="有备无患：怎样评价Feed阅读效率" href="http://luliban.com/blog/2008/04/feed.html" id="q6jc"&gt;FRER（Feed 阅读效率评价）&lt;/a&gt;为15.14，这意味着在 Google Reader 读到的大约85%的文章，至少对我来说，是噪声。这里所浪费的时间，虽然不能直接推算，但估计至少有三分之一。为了更有效率的利用这些时间，我尝试用 &lt;a rel="nofollow" title="AideRSS + GoogleReader Firefox extension" href="http://gr.aiderss.com/?downloadsquad" id="c_g0"&gt;AideRSS 为 Google Reader 设计的 Firefox 扩展&lt;/a&gt;来辅助阅读。&lt;br /&gt;

&lt;br /&gt;

&lt;a rel="nofollow" title="AideRSS" href="http://www.aiderss.com/" id="n2tn"&gt;AideRSS&lt;/a&gt; 原本是一款在线的 feed 过滤工具。它通过分析 PageRank，文章评论和 Trackback 的数量，以及文章被 del.icio.us 和 Digg 用户收藏的次数等指标，计算出相应的 &lt;a rel="nofollow" title="PostRank" href="http://blog.aiderss.com/faq/#postrank" id="b8mv"&gt;AideRSS PostRank&lt;/a&gt;（从1.0到10），并以此为用户过滤 feeds 减少信息过载带来的困扰。而 AideRSS 的这个 Firefox 扩展将 PostRank 整合到 Google Reader 中，我们可以在每一篇文章的标题左侧看到相应的 PostRank，在末尾看到相应的评论、Trackback、del.icio.us 和 digg 收藏的具体数目。另外，我们还可根据 PostRank 的高低过滤出 Good，Great 和 Best 的文章来选择阅读。&lt;/p&gt;

&lt;br /&gt;

&lt;div&gt;&lt;a href="http://luliban.com/blog/uploaded_images/aiderss-screenshot.jpg"&gt;&lt;img id="j3h2" style="width: 450px; height: 355px;" src="http://luliban.com/blog/uploaded_images/aiderss-screenshot-450.jpg" /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;p&gt;
&lt;b&gt;几天使用下来，AideRSS 确实能提高一些阅读的命中率，特别是在读流量大更新快的 blogs 的时候，比如CrunchGear，LifeHacker 等。另外读那些只输出摘要的 feeds 的时候，比如新闻类的 feeds，del.icio.us 上的 linux tag 等，我也可以根据 PostRank 快速过滤掉一些文章。&lt;/b&gt;&lt;br /&gt;

&lt;br /&gt;

&lt;b&gt;另一方面，AideRSS 对草根 blogs 不会像&lt;a rel="nofollow" title="豆瓣九点" href="http://9.douban.com/"&gt;豆瓣九点&lt;/a&gt;那么“势利眼”——死盯着订阅数不放，不用去担心 PostRank 会不会把草根的声音屏蔽了的问题。&lt;/b&gt;PostRank 的算法中，似乎流量较少的草根们反而更“容易”拿到高 PostRank。举例子来说，我的《&lt;a class="direct" href="http://luliban.com/blog/2008/03/bloggerftp.html" title="有备无患：为Blogger传统模板（FTP发布）添加标签云"&gt;有备无患：为Blogger传统模板（FTP发布）添加标签云&lt;/a&gt;》只有4个 backlink，2条评论和2个 del.icio.us 收藏，被评到8.3分，而 LifeHacker 的《&lt;a rel="nofollow" href="http://lifehacker.com/376705/bind-papers-together-without-staples-or-clips" title="Bind Papers Together Without Staples or Clips [How To]" &gt;Bind Papers Together Without Staples or Clips [How To]&lt;/a&gt;》有39条评论，7次 del.icio.us，5次 digg，却只有5.3分。&lt;br/&gt;

&lt;br/&gt;

虽然 AideRSS + Google Reader Firefox Extension 还在 private beta 阶段，虽然会拖慢 Google Reader 的响应速度（wyt：要通过 AideRSS 的服务器取数据），虽然还和 Firefox 上最流行的扩展之一 GreaseMonkey 有冲突（wyt：AideRSS 另外提供了一个 GM 脚本，实现和扩展同样的功能），它还是值得去试一试。不过，真正的结论还要等几个星期以后，看看我的 FRER 有没有因此而提高了。&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~f/luliban?a=v8iKYmG"&gt;&lt;img src="http://feeds.feedburner.com/~f/luliban?i=v8iKYmG" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/luliban?a=d8Lt4eg"&gt;&lt;img src="http://feeds.feedburner.com/~f/luliban?i=d8Lt4eg" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/luliban?a=NJj2ZPg"&gt;&lt;img src="http://feeds.feedburner.com/~f/luliban?i=NJj2ZPg" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/luliban?a=hleGKFG"&gt;&lt;img src="http://feeds.feedburner.com/~f/luliban?i=hleGKFG" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;</description></item><item><title>有备无患：怎样评价Feed阅读效率</title><link>http://luliban.com/blog/2008/04/feed.html</link><category>互联网</category><category>feed</category><author>noreply@blogger.com (wyt)</author><pubDate>Mon, 07 Apr 2008 06:44:47 -0500</pubDate><guid isPermaLink="false">tag:blogger.com,1999:blog-7286295415331143255.post-7245259056814930827</guid><description>&lt;div&gt;
&lt;a href="http://luliban.com/blog/uploaded_images/hatsune-miku-800.jpg"&gt;
&lt;img src="http://luliban.com/blog/uploaded_images/hatsune-miku-400.jpg" alt="你可以用葱" /&gt;
&lt;/a&gt;
&lt;/div&gt;

&lt;br /&gt;
&lt;p&gt;

Feed阅读早已超过订购报纸和浏览网页，成为互联网时代获取信息的最重要的渠道之一。但随着信息的大量聚合，噪声、冗余和陈腔滥调的信息也不可避免的混杂其中，谓之“&lt;a href="http://en.wikipedia.org/wiki/Information_overload" rel="nofollow"&gt;信息过载&lt;/a&gt;”。其实，“信息过载”也是“信息欠缺”的形式之一，后者是说绝对的信息缺乏，前者是指相对于噪声的有用信息的缺乏，即“&lt;a href="http://zh.wikipedia.org/wiki/信噪比" rel="nofollow"&gt;信噪比&lt;/a&gt;”过低。&lt;br /&gt;

&lt;br /&gt;

提高信噪比的办法有很多。比如专注于最感兴趣的feeds，删除那些不错但无关紧要的（我们不想了解整个互联网）。比如删除那些热衷于速译国外blogs 却不能自己提供内容的feeds（文字还是原汁原味的好）。又比如用标签为feeds分级，分为必读的A List，选读的B List和爱读不读的ZZ List（人工智慧还不能为我们过滤文章的时候，只能先凑合一下“&lt;a href="http://mr6.cc/?p=1550" rel="nofollow"&gt;工人智慧&lt;/a&gt;”:-）。等等。&lt;br /&gt;

&lt;br /&gt;

&lt;b&gt;可是还有一个问题：如果我们采取了上述这些措施，怎样才能知道“信噪比”是升高了还是降低了，怎样才能量化的评价自己的阅读效率呢？&lt;/b&gt;&lt;br /&gt;

&lt;br /&gt;

为此我略微改变了一下自己的阅读习惯，把每一条值得一读的信息在&lt;a href="http://reader.google.com" rel="nofollow"&gt;Google Reader&lt;/a&gt;中打上星号，以及每隔半个月记录一次自己的feed订阅数。然后用FRER（Feed阅读效率评价）来了解自己最近的阅读效率。FRER是一个通过大多数Feed阅读器都会提供的统计数据（订阅数，已读数，收藏数和推荐数）来计算最近30天的Feed阅读效率，换言之“阅读信噪比”的计算公式。&lt;br /&gt;

&lt;/p&gt;

&lt;br /&gt;

&lt;blockquote&gt;&lt;p&gt;
&lt;b&gt;FRER = (Sub / aSub) * (StI + ShI) / RdI * 100&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
说明：&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;FRER - Feed Reading Efficiency Rating，Feed阅读效率评价&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Sub - Subscription，当前订阅数&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;aSub - Average Subcription，历史平均订阅数&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;StI - Starred Items，内向型“收藏”的文章&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;ShI - Shared Items，外向型“推荐”的文章&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;RdI - Read Items，所有已读的文章&lt;br /&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;br /&gt;

&lt;p&gt;


举例来说，我在Google Reader上订阅的feeds共有381个，这几个月平均的订阅数是373.3个，最近30天我阅读了11246篇文中，其中被我打上星号收藏的文章有1422篇，分享出去的文章有250篇。那么通过FRER计算可以得出我最近一个月的Feed阅读效率是15.14。

&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~f/luliban?a=SOOe3OG"&gt;&lt;img src="http://feeds.feedburner.com/~f/luliban?i=SOOe3OG" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/luliban?a=fwhhvig"&gt;&lt;img src="http://feeds.feedburner.com/~f/luliban?i=fwhhvig" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/luliban?a=8evOtAg"&gt;&lt;img src="http://feeds.feedburner.com/~f/luliban?i=8evOtAg" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/luliban?a=nGH5PKG"&gt;&lt;img src="http://feeds.feedburner.com/~f/luliban?i=nGH5PKG" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;</description></item><item><title>南言北哲：你最想看哪些季后赛的首轮对局？（二）</title><link>http://luliban.com/blog/2008/04/blog-post.html</link><category>nba</category><author>noreply@blogger.com (wyt)</author><pubDate>Mon, 31 Mar 2008 14:13:36 -0500</pubDate><guid isPermaLink="false">tag:blogger.com,1999:blog-7286295415331143255.post-200119758837953318</guid><description>&lt;p&gt;半个星期之前，&lt;a title="南言北哲：你最想看哪些季后赛首轮的对局？（一）" href="http://luliban.com/blog/2008/03/blog-post_27.html"&gt;我还说最想看到小牛排第八挑战西部老大的西部季后赛首轮对局&lt;/a&gt;。半个星期之后，失去Nowitziki的小牛连输两场关键的比赛（&lt;a title="105-118负掘金" href="http://sports.espn.go.com/nba/boxscore?gameId=280327007" rel="nofollow"&gt;105-118负掘金&lt;/a&gt;，&lt;a title="104-114负勇士" href="http://sports.espn.go.com/nba/boxscore?gameId=280330009" rel="nofollow"&gt;104-114负勇士&lt;/a&gt;），战绩滑落到45胜28负，和掘金与勇士并列西部第七。我猜，主教练Avery Johnson多少会怀念一下小牛曾经的最好的没有之一的突破防守球员，Devin Harris，因为他们刚刚被勇士的Monta Ellis砍下30分，而本赛季之前两次对阵，Ellis总共也才拿到不过25