Blog在我的身边没落了

今天清晰的感到我周围的人不爱写blog了。

意识到这一点已经很长时间了,但今天真正让我感到详细的感慨。起因是我看到了“憂鬱馬戲團”的一篇Movable Type教学文章《Blogrolling 使用教學》。Blogroll我一直以为就是blog页面上的友情链接,给blogger们建立关系用的。而Movable Type貌似没有默认提供这个功能,所以要花一点功夫自己来修改模板。看到了文章后,我就在想我要不要也弄一个,然后才意识到我不用Blogroll好久了。

两年前,不管我是在BSP上写blog,还是在独立空间上写,总会记得把身边好友的blog链接加上去,这似乎是建立blog后的第一件事。那时blog在国内还很热,所以当有同学知道我也有blog后,就赶紧要跟我交换友情链接,就像两个人热衷于交换QQ号一样。尽管有的blog以我当时的眼光来看没有任何用处,纯属幼稚的垃圾,但毕竟是一层关系,我也就加了。不过我那时在网上认识的人不多,因此友情链接固定后就没再怎么变化。这种情形似乎持续到上次我的空间被攻击。那时我的空间上的东西都丢失了,因此我的blog也是重新开始的。似乎从那次我就没再怎么设置过友情链接。有几个我是直接能默写出来地址的,我就直接给加上去了。

这次换了Blog系统,Movable Type的设置里一直没有这个功能,我也就一直给忽略了。今天想起来后,想了一下,发现我根本就不必加友情链接了:要加的那些几乎很早就不更新了。也许是因为Twitter的冲击,blog这种东西在我的身边没落了!

我不禁有些伤感,细想原因:一是大家都忙了,可能平时没有精力再在网上维护自己的那一亩三分地;二是新鲜工具的冲击,Twitter把大家的写作欲望都一点一滴的化解掉了,而目前blog这种东西上的文章毕竟不如在校内上发一篇流传的快。不过从“大局”来看,这未尝不是一件好事:有些人的blog本来就不是因为他想有一个表达的渠道而建立的。大概三、四年前,由方兴东起头,国内开始炒作blog。炒作本身就是把一件事物的价值过分抬高,因此导致在那时你要是没有一个blog就是落伍的表现。很多人注册了帐号,更换了模板之后才发现根本不知道要写什么,于是结果有二:好的一点就是不管它,任由那个帐号荒废;不好的一点就是去网上别人的blog上搜刮,把文章搬到自己的空间上,满足一下自己的虚荣心。而我一些同学不在关注blog,把精力放在更有用的地方,也不会对blog圈带来不好的影响,应该是一件好事啊。

可能是由于环境的原因,我一直觉得blog在我身边算是“精英”文化,而不是新浪鼓吹的“草根”。身边屈指可数的blogger就体现了这一点,而真正写出点有用的东西的则少之又少。反正到现在,我身边一直没出现过大面积开始写blog的情况。可能跟我们这一代与blog热潮相遇的时间点有点关系:blog火的时候,大家忙于学业,没有心思写blog;而到大家上了大学,有了点时间,校内就基本上取代了blog。因此我反而觉得blog是“精英”,校内才是“草根”。

这与我在国外的感觉也很相似。我的加拿大同学很少有写正式blog的。虽然也有很多人没听说过Twitter,但有些我觉得应该写blog的都去Twitter了。另外就是Facebook非常盛行,基本上是大学生必备配备,直有“民间学生证”的感觉。反正国外的新奇玩意远比国内丰富,老外不愁找不到blog的替代品。

放眼国内外,我能想到的blogger们都属于精英型的,国内的比较知名的三表、徐宥之流,都是在相关领域有一定影响力的,其它的一些驻于新浪之类的blogger的声音都很微弱,形不成气势。总之,我感觉blog热潮算是过去了,经过“草根”群众的实践,blog这种东西并不能帮助他们表达生活琐事,于是纷纷投入SNS网络,而blog则成了“精英”的表达工具。

:本文中“草根”的解释参考新浪,“精英”指的是一直坚持blog写作的人们。由于找不到合适的词,只好用低俗解释来代替。

又:刚刚看到了“老白说真话”上的一篇文章《丧失了表达欲》,里面的一段话让我相当认同:

某个人之所以写独立的blog,最真实的目的是想贡献出一些别处没有的思想、观点、看法。而且,这些是发自内心的。

为什么独立blogger不容易荒废?很有启发。

从Twitter更新校内状态成功!

继续上篇文章,紧天吃午饭的时候觉得那里说的两种方向都不太容易解决。Python的那个最诡异,实在不知道是哪里的问题,而且Python那个脚本,用了几个库,好像我目前手上能用的虚拟主机上(Dreamhost和SDF)都没有;Bash的那个,我实在是不想把目前的宝贵时间花在学习一门新语言上。然后就想到了我过去最喜欢的Perl,既然它被称为是一门“粘合语言”,应该在里面可以直接调用yegle写的wget语句,而且前两天用Perl写过海词客户端简化版,里面就用了正则表达式来处理了HTML。相对与海词的那个排版混乱的HTML输出,Twitter给的XML可简单清晰多了。

我不知道Perl里怎么解析XML,不过Twitter的XML很简单,我几下就弄出了需要的最新的tweet文本和那一条tweet的时间。然后用yegle的wget语句提交,一下子就成功了。然后就发现了一个困扰了我长时间的问题:如果tweet里面是中文,就无法提交(实际上是提交了一个空信息)。然后让程序打印初那条tweet的文字,发现都成了写这样的东西,然后就到处研究怎么把它转换成正常的文字。后来从这里知道了需要用HTML::Entities来处理(代码里第38行)。做了这一步后那些wget语句就又好使了。

结下来就是隔一段时间就运行一下这个程序了。cron似乎是我第一个想到的。还有一个方法就是在Perl脚本里用循环,不过这样似乎有点不妥(具体怎么不妥我没有自习琢磨)。我的MacBook虽然基本上保持开机状态,不过把它放在24小时运行的网上的虚拟主机上似乎更不错。我目前有两个虚拟主机帐号,一个是Dreamhost,一个是SDF。结果两个都有问题:Dreamhost上的wget版本太老,不支持命令行里的那些session、cookies功能;SDF则不让我用cron。SDF的问题不大好办,似乎cron是它们专门的用户帐号才能做的,我目前的ARPA帐号不在此范围。我只好试着下载wget的代码在Dreamhost上编译。结果出乎我意料,居然编译成功了!我过去在不是本地的主机上编译软件通常是以失败而告终的。用了新版本的wget,程序就运行正常了。设定好了cron(我让程序2分钟运行一次,查看了Dreamhost的wiki上关于cron的说明,没有提到有什么限制),程序运行OK。

这时还有最后一个问题没解决:如果让这段代码隔一段时间就自动运行,那么如果在这段时间里面没有更新Twitter,那么校内的状态会被不断用同一条tweet刷新。这也是我要保存最新tweet的发布时间的目的。我用的方法是把这一条tweet的发布时间写到一个磁盘文件中去,然后下一次运行的时候比较两个时间,如果两个时间不同,就运行发布的wget命令,然后更新磁盘文件,否则就退出程序。我不知道有没有更好的解决方案,不过我的方法目前还可行。

最后就是把代码贴出来了。我整理了一下,把用户名、帐号、磁盘文件、还有wget程序的地址都弄成了变量,放在代码最前面以方便设定。完事后看起来还不错。

01 #!/usr/bin/perl -w
02
03 # Twitter -> Xiaonei status sync tool.
04 # Author: Feng Liu <liufeng@cnliufeng.com>
05 # Version: 1.0
06 # Date: Tue Apr 21 23:06:40 +0000 2009
07
08 use utf8;
09 use Encode;
10 use LWP::Simple;
11 use HTML::Entities;
12
13 ####################### Settings starts here #######################
14 # Set your base account information here. Don't show this to others!
15 my $twitter_account = '';
16 my $xiaonei_email = '';
17 my $xiaonei_passwd = '';
18
19 # some machine's wget is too old, so you may need to rebuild a newer
20 # version and indicate the path of your own wget here.
21 my $wget_cmd = 'wget';
22
23 # The program needs a log file for keeping the time of your last
24 # tweet. Otherwise you may get your xiaonei status updated to a same
25 # tweet. So please keep this file!
26 my $logfile = '/tmp/twxn.log';
27 ######################## Settings ends here ########################
28
29 my $twitter_url = 'http://twitter.com/statuses/user_timeline/' . $twitter_account . '.xml';
30 my $statuses = get($twitter_url);
31 my @lines = split /\n/, $statuses;
32 my $latest_text = $lines[5];
33 my $latest_time = $lines[3];
34
35 if ($latest_text =~ /<text>(.*)<\/text>/) {
36     $status = $1;
37 };
38 $text = decode_entities($status);
39
40 # If the log file doesn't exist, create a new one.
41 if (!(-e $logfile)) {
42     open LOG,"> /tmp/twxn.log" or die "ERROR: Cannot create log file.";
43     close LOG;
44     print "Created a new log file: $logfile\n";
45 }
46
47 open LOG, "< $logfile" || die "ERROR: Cannot open log file!";
48 $last_time = <LOG>;
49 close LOG;
50
51 if ($last_time ne $latest_time) {
52
53     my $login_cmd = $wget_cmd . ' --no-proxy -O xiaoneilogin.log --post-data="email=' . $xiaonei_email . '&password=' . $xiaonei_passwd . '&autoLogin=true"  --keep-session-cookies --save-cookies=xiaoneicookie http://login.xiaonei.com/Login.do';
54
55     my $post_cmd = $wget_cmd . ' --no-proxy -O xiaoneipost.log --post-data="c=' . $text . '"  --keep-session-cookies --load-cookies=xiaoneicookie http://status.xiaonei.com/doing/update.do --referer=http://status.xiaonei.com/getdoing.do';
56
57     system($login_cmd);
58     system($post_cmd);
59    
60     open LOG, "> $logfile" || die "ERROR: Cannot open log file!";
61     print LOG $latest_time;
62     close LOG;
63 }

最后,感谢半瓶墨水yegle的指教与启发!

校内状态更新有进展

不愧是水木的Linux版主,yegle直接用wget就能成功的修改了校内的状态。不过话说wget、curl这种看似不起眼的命令行工具还真是牛屄,man了一下yegle的代码里面wget的参数,感觉我过去只把wget当成一个下载软件只是大材小用了。

我测试了他的代码。刚开始的时候一直不能成功,后来经yegle在Twitter上指点后也成功了。只是不会更新“目前”的状态,而是修改的“目前”的上一条状态。换句话说,目前的状态显式“什么都没做”,但从个人主页上可以显式状态被修改的历史。

这已经相当程度的解决了问题了。我对用shell处理xml还不熟悉,不能做到一直在后台更新Twitter的消息,有待以后研究。或者再继续修改那个Python脚本。要么就干脆结合Python和Bash,也可以解决问题。

另外,关于上一篇文章中的问题,我昨天回家就给python-twitter发了issue,今天早上看到有人回复,原来从今年四月8日开始,Twitter就不在http header里面支持since参数了,真是很奇怪。另外,发芽网的半瓶墨水回复了我的邮件,给我了一篇“鼓励性”的邮件……

被Python字符串弄崩溃了

受发芽网上的一段“校内网发帖机”的启发,最近想修改那段代码,让它可以部分的实现Twitter状态和校内网同步。之所以说“部分的”是因为我不知道怎么样让它像Facebook的Twitter插件那样Twitter的消息发送完毕后几乎同时(相差不到10秒钟)把Facebook的状态一并更新。另外一点很惭愧,就是我对HTTP编程不熟悉,目前还找不初修改校内的状态要提交的代码,只能修改一下原来的代码,在写日志上面做文章。

就算这样,也还是遇到了不少问题。我目前的打算是像过去WordPress上用过的一个插件,名字大概叫twitter-post那样,总结一天的Twitter消息,成为一篇文章,发送到校内上去。我开始用的是python-twitter这个包,很方便的就能弄到Twitter的消息。不过在它的GetUserTimeline()方法的说明中说到里面有一个since参数,提交一个“HTTP-formatted time”,就可以获得从那个时间点以后的消息。我觉得它应该能让我获得前一整天的所有消息,虽然需要做点处理,删除今天的消息。但这个“HTTP-formatted time”我实在是不知道是什么东西。昨天下午几乎找遍了我能从网上找到的所有表示时间的格式,怎么测试,它都只给我默认的最近20条的消息。可笑的是,python-twitter的代码里有test可以运行,而里面测试since参数的用例里用得Twitter帐号居然总共才不到10条tweets,这样GetUserTime()返回了所有的tweets,当然都是他要的时间段的啦。没办法,我只好去它的Google Groups里发问,结果今天居然找不到我提交的帖子了,难道是开发者觉得我是捣乱的?!!

既然这样不行,只好退一步先把能获得的最近20条tweets一并发上去,先看看效果再花功夫自己来解析前一天的所有tweets。结果很快写出了代码,但测试的时候却不行了。那段代码,我在程序里输入什么样的文字都可以正常发送,但自己从Twitter那里获得的文字就无法发送。Python一点提示都没有。我不光试验了python-twitter API给出的结果,还自己抓取了Twitter给出的XML文件自己parse,得到的结果也无法发送。我让两个程序在提交前输出提交的字符自己来比对,基本上都是一种格式的。简直快疯掉了。

接下来该怎么办?我目前只想到:

  • 继续骚扰python-twitter,谁让他给出的测试用例中有问题呢?

  • 像那段发布“校内网发帖机”的半瓶墨水虚心请教。

  • 昨天晚上yegle在Twitter上对那段代码表示了兴趣,如果我没猜错的话,他应该就是某个BBS的Linux版版主之一,大概Python功力比我这种菜鸟强很多,说不定他能“一语惊醒梦中人”……

我目前的代码如下,它还不能工作。这个是我自己parse Twitter的XML版本,用python-twitter的版本前半部分简洁一些,不过差别不大,两者都有同样的问题。

01 #!/usr/bin/env python
02 # -*- coding: utf-8 -*-
03
04 from xml.dom import minidom
05
06 # 需要先运行wget http://twitter.com/statuses/user_timeline/liufeng.xml
07 tw_url = 'http://twitter.com/statuses/user_timeline/liufeng.xml'
08 xmldoc = minidom.parse('liufeng.xml')
09
10 status = xmldoc.firstChild
11 tweet = status.childNodes[1]
12
13 output = ""
14 count = 1
15 flag = 0
16 for tweet in status.childNodes:
17     if flag == 1:
18         created_at = tweet.childNodes[1].firstChild.toxml()
19         text = tweet.childNodes[5].firstChild.toxml()
20         output += str(count) + " " + text + " " + created_at + "\n"
21         count += 1
22         flag = 0
23     else:
24         flag = 1
25
26
27 import cookielib
28 import urllib2
29 import urllib
30 import time
31
32 cj = cookielib.CookieJar()
33 opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cj))
34 exheaders = [("User-Agent", "Mozilla/4.0 (compatible; MSIE 7.1; Windows NT 5.1; SV1)"),]
35 opener.addheaders = exheaders
36 url_login = 'http://xiaonei.com/Login.do'
37 body = (('email', '*******@gmail.com'), ('password', '*******'))
38 req1 = opener.open(url_login, urllib.urlencode(body))
39 print "Login should be successful.\n"
40
41 body = {'relative_optype':'publisher', 'blogControl':'1'}
42 url_post = 'http://blog.xiaonei.com/NewEntry.do'
43 title = '最近我(说了/做了/想了)什么 %s' % time.asctime()
44 #xt = text.encode('utf-8')
45 print output
46 output = output.encode('utf-8')
47 print " fucked\n\n" + output
48 body['title'] = title
49 body['body'] = output
50 req2 = opener.open(url_post, urllib.urlencode(body))
51
52 #print urllib.urlencode(body)

Find recent content on the main index or look in the archives to find all content.