利用Python解析新浪博客javascript生成的评论

【前言】sina2wordpress终于有一个大概模样了,目前版本号为0.1
这是本站项目地址这是代码托管地址

言归正传,目前新浪博客的评论是用JavaScript生成的,直接用urllib2读取的信息中没有评论信息

通过Firefox下的firebug或者Chrome自带的Develop Tools,都可以找到js脚本在运行时GET的数据,具体方法是:打开之后选择“Network”标签,并选择“XHR”分类项,刷新页面并等待加载完成,稍等一会就会看到GET了一条以“comment”开头的html页面。

主要规律是,例如韩寒同学的这篇日志地址是 “http://blog.sina.com.cn/s/blog_4701280b0101854o.html”,那么打开日志页面时GET的就会是 “comment_4701280b0101854o_1.html”,规律不难发现,后面的那个数字就是评论的页数,最后的那个页数可以变为任意整数(没有尝试过上限),当超过实际评论页数时,显示的会是同样的编码,这也就是循环结束条件

此地址为相对路径,打开后是一堆乱码,这就是JavaScript的数据存储形式——json编码。可以简单的类比成Python中的字典,本例中共有两个关键字,一个是“Code”,对应值”A00006″,没发现啥用处,第二个关键字是“data”,其余部分均为data的对应值,利用Python的json模块进行解析可以发现,这就是评论的html代码。不过这里需要注意的是,第二个关键字“data”缺少双引号,直接解析会报错,需要先进行字符串处理,然后再解析。

相关代码如下(完整代码可以参见前言中的代码托管地址):

#根据json解析之后的html代码总结出来的各个关键信息的正则表达式
comment_author_pattern=re.compile(r'<span class="SG_revert_Tit".*?>(.*?)</span>')
comment_url_pattern=re.compile(r'<a href="(.*?)" target="_blank">(.*?)</a>')
comment_time_pattern=re.compile(r'<em class="SG_txtc">(.*?)</em>')
comment_content_pattern=re.compile(r'<div class="SG_revert_Inner SG_txtb".*?>(.*?)</div>', re.S) #这里的re.S很重要

def commentsAnalyze(key): #key为地址中间的标识性字符串
    num=1 #表示评论的页数
    url=r'http://blog.sina.com.cn/s/comment_%s_%d.html' %(key, num) #生成json编码对应的地址
    page=urllib2.urlopen(url).read().replace('data:','\"data\":',1) #给data添加双引号
    while not 'noCommdate' in page: #noCommdata是无评论json编码页面的关键字
        data=json.loads(page)['data'] #<strong>最关键的一部</strong>,json代码的解析并有效部分

        #通过正则匹配出相关信息,其中url是可能存在的,将稍后处理
        author=comment_author_pattern.findall(data)
        url=[]
        time=comment_time_pattern.findall(data)
        content=comment_content_pattern.findall(data)

        #后处理url,判断前面提取出来的author周边是不是带有<a>标签(超链接)即可
        for i in range(len(author)):
            comment_id+=1
            result=comment_url_pattern.search(author[i])
            if result:
                url.append(result.group(1))
                author[i]=result.group(2)
            else: url.append('')

        num+=1 #评论页数+1
        url=r'http://blog.sina.com.cn/s/comment_'+key+'_'+str(num)+'.html' #生成新的页面地址
        page=urllib2.urlopen(url).read().replace('data:','\"data\":',1) #修正data的双引号,重复循环

【后记】感谢 @lqs 学长和 北航开源俱乐部BHOSC的同胞们 在此问题上给予的帮助

精简WordPress eXtended Rss (WXR) 文件格式

经过N次Import和数据库清空,终于将之前的WXR文件格式的完备集简化了。

简化原则是适应Sina2WordPress项目的需要,尽可能精简文件大小,删除了导入无效的(如博客标题、博客链接等)和从新浪博客中无法获取的(如评论者的链接和IP等)信息,还有item之前的的作者、分类和标签信息。作者在导入时可以指定的,反而加上会有可能的错误,分类和标签的信息用post中的分类和标签就可以自动统计了。没想明白为什么非要单独列出来,难道是为了空的分类和标签,那还要它干嘛?

一些必不可少的标签及错误总结:
1、wxr_version,缺少会提示“missing/invalid WXR version number”的错误
2、post_id,否则只会导入第一篇文章
3、status,否则都会显示成draft
4、post_type,否则无法导入
5、comment_approved,否则无法导入

P.S.post_id的问题纠结了好久,为什么官方不好好设计一下呢?至少觉得应该在Settings设定对应的选项的……

< ?xml version="1.0" encoding="UTF-8" ?>

<rss version="2.0"
	xmlns:excerpt="http://wordpress.org/export/1.1/excerpt/"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:wp="http://wordpress.org/export/1.1/"
>
<!--RSS版本号和名字空间的扩展,以上为固定内容-->

<channel>
	<wp:wxr_version>1.1</wp:wxr_version>
	<!--WXR格式版本号-->

	<item><!--页面或者日志内容,每个为一个item-->
		<title>Title test</title>
		<!--标题-->
		<content:encoded>< ![CDATA[Content_test]]></content:encoded>
		<!--这里是正文内容-->
		<wp:post_id>1</wp:post_id>
		<!--页面或日志的序号,两者使用同一序列-->
		<wp:post_date>2002-12-21 07:59:59</wp:post_date>
		<!--发表时间-->
		<wp:comment_status>open</wp:comment_status>
		<!--评论开启情况,open / closed-->
		<wp:status>publish</wp:status>
		<!--页面或日志状态,publish / draft / pending / private-->
		<wp:post_type>post</wp:post_type>
		<!--文章类型,post / page-->
		<wp:is_sticky>0</wp:is_sticky>
		<!--文章是否置顶,0 / 1-->

		<category domain="post_tag" nicename="tag_test">< ![CDATA[Tag Test]]></category>
		<category domain="category" nicename="category_test">< ![CDATA[Category Test]]></category>
		<!--
			日志或页面的标签和分类,可多个
			domain:标签对应post_tag,分类对应category
			nicename:对应标签或分类的URL友好名称
			<![CDATA[]]>:标签或分类的显示名称
		-->

		<wp:comment><!--评论,可多个-->
			<wp:comment_id>1</wp:comment_id>
			<!--自增序号,评论专用-->
			<wp:comment_author>< ![CDATA[anonymous]]></wp:comment_author>
			<!--评论者用户名-->
			<wp:comment_date>2012-12-21 07:59:59</wp:comment_date>
			<!--评论时间-->
			<wp:comment_content>< ![CDATA[Content of Comment]]></wp:comment_content>
			<!--评论内容-->
			<wp:comment_approved>1</wp:comment_approved>
			<!--评论是否被允许-->
			<wp:comment_parent>0</wp:comment_parent>
			<!--父评论,指定所回复的评论-->
		</wp:comment>
	</item>
</channel>
</rss>

WordPress eXtended Rss (WXR)文件格式解析

Sina2WordPress的第一步——解析WXR文件格式

WXR是Wordpress eXtended Rss的缩写,是WordPress针对博客信息特意设定的格式,它最大的优点是兼容性好,包含信息丰富

通过参照导出的文件,初步找到一个完备集(见下方代码),经测试在WP无任何内容情况下无信息缺漏错误现象

下方代码已经尽可能的注释了所有可能的标签和属性,并且由于一些标签和属性与Sina2WordPress关系不大,故未深究

< ?xml version="1.0" encoding="UTF-8" ?>

<rss version="2.0"
	xmlns:excerpt="http://wordpress.org/export/1.1/excerpt/"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:wp="http://wordpress.org/export/1.1/"
>
<!--RSS版本号和名字空间的扩展,以上为固定内容-->

<channel>
	<title>Blog Title</title>
	<!--博客的标题-->
	<link>http://blog.example.com</link>
	<!--博客的链接-->
	<description>Blog Description</description>
	<!--博客的说明/副标题-->
	<pubdate>Dec, 20 Jun 2012 23:59:59 +0000</pubdate>
	<!--WXR文件生成时间-->
	<language>en</language>
	<!--博客的语言,en / zh-cn-->
	<wp:wxr_version>1.1</wp:wxr_version>
	<!--WXR格式版本号-->
	<wp:base_site_url>http://example.com</wp:base_site_url>
	<!--网站根目录地址-->
	<wp:base_blog_url>http://blog.example.com</wp:base_blog_url>
	<!--博客根目录地址-->

	<wp:author><wp:author_id>1</wp:author_id><wp:author_login>admin_test</wp:author_login><wp:author_email>admin@example.org</wp:author_email><wp:author_display_name>< ![CDATA[AdMin test]]></wp:author_display_name><wp:author_first_name>< ![CDATA[AdMin]]></wp:author_first_name><wp:author_last_name>< ![CDATA[test]]></wp:author_last_name></wp:author>
	<!--
		作者列表,可多个
		wp:author_id:自增序号
		wp:author_login:用户名
		wp:author_email:邮箱
		wp:author_display_name:显示的作者名称
		wp:author_first_name、wp:author_last_name:如字面意,可为空,但需有<![CDATA[]]>
		P.S.< ![CDATA[**]]>可以理解成强制文本转换,保留文本中所有字符,以避免非法字符对XML文件的影响(后文不再赘述)
	-->

	<wp:category><wp:term_id>1</wp:term_id><wp:category_nicename>category_test</wp:category_nicename><wp:category_parent></wp:category_parent><wp:cat_name>< ![CDATA[分类测试]]></wp:cat_name></wp:category>
	<!--
		分类列表,可多个
		wp:term_id:自增序号,且分类和标签用的是同一个
		wp:category_nicename:URL友好名称,作为相关URL的一部分
		wp:category_parent:父分类,无即为空
		wp:cat_name:显示的分类名称
	-->

	<wp:tag><wp:term_id>2</wp:term_id><wp:tag_slug>tag_test</wp:tag_slug><wp:tag_name>< ![CDATA[标签测试]]></wp:tag_name></wp:tag>
	<!--
		标签列表,可多个
		wp:term_id:自增序号,与标签使用同一个序列
		wp:tag_slug:URL友好名称,作为相关URL的一部分
		wp:tag_name:显示的标签名称
	-->

	<generator>http://wordpress.org/?v=3.1.3</generator><!--WXR文件生成工具的标识-->

	<item><!--页面或者日志内容,每个为一个item-->
		<title>Title</title>
		<!--标题-->
		<link>http://blog.example.com/title/</link>
		<!--URL地址-->
		<pubdate>Thu, 15 Apr 2010 23:20:03 +0000</pubdate>
		<!--发布时间-->
		<dc:creator>admin</dc:creator>
		<!--文章作者-->
		<guid isPermaLink="false">http://blog.example.com/?page_id=1</guid>
		<!--
			GUID 意为 Global Unique IDentification,即全局唯一标识
			isPermaLink="false" 指示该地址非合法URL地址的属性
		-->
		<description></description>
		<content:encoded>< ![CDATA[Content_test_1]]></content:encoded>
		<!--这里是正文内容-->
		<excerpt:encoded>< ![CDATA[]]></excerpt:encoded>
		<!--文章摘录,供RSS/Atom使用,一般为空-->
		<wp:post_id>2</wp:post_id>
		<!--页面或日志的序号,两者使用同一序列-->
		<wp:post_date>2012-12-21 07:59:5</wp:post_date>
		<!--发表时间-->
		<wp:post_date_gmt>2010-12-20 23:59:59</wp:post_date_gmt>
		<!--发表时间(GMT)-->
		<wp:comment_status>open</wp:comment_status>
		<!--评论开启情况,open / closed-->
		<wp:ping_status>closed</wp:ping_status>
		<!--Ping开启情况,open / closed-->
		<wp:post_name>blog_title</wp:post_name>
		<!--URL友好的名称-->
		<wp:status>publish</wp:status>
		<!--页面或日志状态,publish / draft / pending / private-->
		<wp:post_parent>0</wp:post_parent>
		<!--只用于页面,指示父页面的id-->
		<wp:menu_order>0</wp:menu_order>
		<!--只用与页面,作为导航时的排序权值-->
		<wp:post_type>post</wp:post_type>
		<!--文章类型,post / page-->
		<wp:post_password></wp:post_password>
		<!--文章是否加密-->
		<wp:is_sticky>0</wp:is_sticky>
		<!--文章是否置顶,0 / 1-->

		<category domain="post_tag" nicename="tag_test">< ![CDATA[Tag Test]]></category>
		<category domain="category" nicename="category_test">< ![CDATA[Category]]></category>
		<!--
			日志或页面的标签和分类,可多个
			domain:标签对应post_tag,分类对应category
			nicename:对应标签或分类的URL友好名称
			<![CDATA[]]>:标签或分类的显示名称
		-->

		<wp:postmeta><!--日志或页面的元数据,可多个-->
			<wp:meta_key>_edit_last</wp:meta_key>
			<!--元数据的关键字-->
			<wp:meta_value>< ![CDATA[1]]></wp:meta_value>
			<!--元数据对应关键字的值-->
		</wp:postmeta>

		<wp:comment><!--评论,可多个-->
			<wp:comment_id>1</wp:comment_id>
			<!--自增序号,评论专用-->
			<wp:comment_author>< ![CDATA[anonymous]]></wp:comment_author>>
			<!--评论者用户名-->
			<wp:comment_author_email>anonymous@anonymous.com</wp:comment_author_email>
			<!--评论者邮箱-->
			<wp:comment_author_url>http://blog.anonymous.com</wp:comment_author_url>
			<!--评论者链接-->
			<wp:comment_author_ip>8.8.8.8</wp:comment_author_ip>
			<!--评论者IP-->
			<wp:comment_date>2012-12-21 07:59:59</wp:comment_date>
			<!--评论时间-->
			<wp:comment_date_gmt>2012-12-20 23:59:59</wp:comment_date_gmt>
			<!--评论时间(GMT)-->
			<wp:comment_content>< ![CDATA[Content of Comment]]></wp:comment_content>
			<!--评论内容-->
			<wp:comment_approved>1</wp:comment_approved>
			<!--评论是否被允许-->
			<wp:comment_type></wp:comment_type>
			<!--评论类型,空白表示一般评论,否则会标记位pingback-->
			<wp:comment_parent>0</wp:comment_parent>
			<!--父评论,指定所回复的评论-->
			<wp:comment_user_id>0</wp:comment_user_id>
			<!--如果评论者为注册用户,这里会记录用户ID-->
		</wp:comment>
	</item>
</channel>
</rss>

参考:http://ipggi.wordpress.com/2011/03/16/the-wordpress-extended-rss-wxr-exportimport-xml-document-format-decoded-and-explained/

一点闲思

前几天终于把入党的材料准备差不多了,电子文档暂且忽略不计,光是必须抄写的8000字以上自传和8篇1500字以上思想汇报就不知道让我的手抽筋多少次了,不过还好都结束了,看着整整78页的方格纸,心中多少有点小成就感,就当是党组织的考验吧,你懂的。

抄写的内容是提前准备好打印出来的,所以抄写时脑子基本处于空闲状态,也挺好,难得有一个这样的时间可以用来思考,可以想一想平时忙忙碌碌或者荒废时光时未曾想过的。

前几天那帮搞G的基友,终于小解放了一下,记得那天遇到逆铭同学,我戏称他们开始蛋疼了,却被反击说我已经蛋疼很久了~囧,想想也是,至少最近不是很紧张,准备北大夏令营材料,准备入党材料,实验室的项目说实话没有100%的用心,混了两篇Rank 2会议的第三还是第四作者,折腾ArchLinux,折腾Nook Color,看了几本还不错的书,其他就是看动漫之类的了,虽然一直没太闲,确实都不是太紧急的事,常常还一边听着音乐一边忙活,着实有点懈怠。

眼看大三就要结束了,很水很水很水很水,水的我已经无话可说了,期末就两门。软件工程过程,基本文科性质,与实际开发相距甚远,基本没听什么,考试开卷,不过是必修课,所以还是要好好应对的。科技写作与沟通技巧,说白了就是英语口语,选修课,外教Bryce人很好,上课也基本认真参与了,应该没什么问题。

最近写简历,写个人陈述时才发现自己虽然确实做了一点东西,但是大多数都是大作业,写完就扔,没有推广到用户,更没能长期坚持维护。最近想写点可以长期维护的东西,其实想了好久了,但一直拖沓着,今天先列一部分在这里,权当对自己的鞭策:

1、Sina2WordPress,大致就是研究一下WordPress eXtended Rss(WXR) 文件格式,用Python的Beautiful Soup抓Sina Blog的网页,再做一下转换,没什么难度,主要是要文件和网页格式的研究可能有点繁琐。

2、LaTeX Resume Templete,用了挺长时间的LaTeX了,前段时间写简历的时候也用了网上的一个模板,写了一个不错的样稿,但是总觉得用人家的模板受各种约束,想设计一个扩展性比较好的,另外就是希望能和LaTeX2HTML搭配起来,便于生成HTML页面挂在个人网站上,可能写模板的时候需要注意的东西比较多。

3、FanFou for Android,用了一段时间的性浪微博,但是还是受不了那么多冗余信息和Spam,就算用的人再多我也不去了,以后只用于同步好了。饭否还是很有爱的,但是一直没有一个很好的Android客户端,有一个安能饭否是从一个开源Twitter客户端上改的,所以感觉还是有点怪怪的,想自己写个,正好熟悉一下从未接触过的API方面开发。

4、Douban for Android,也是比较疑惑怎么到现在都没有很好用的Android客户端,有一个aDouban用起来感觉太不爽了。之前也想了挺长时间,除了API的使用,主要就是感觉豆瓣上的功能太多,不知道一时怎么下手了,担心折腾的太乱太复杂。先从需求开始做吧。

P.S. Doit.im确实是个不错的东东,可以按照项目列to-do list,要好好利用。

除了自己,还想了很多家人和爱人的事。奶奶的病在慢慢调养,爸爸妈妈也在为着更好的生活打拼着,我现在能做的除了做好自己的事,也就是经常打个电话回去问问了。一切都还好吧,希望我保送北大的事情能有惊无险的实现,如果实在不行,乘早找个喜欢的工作也不错。和她呢,总不知道怎么说才好,分分合合,吵吵闹闹,因为异地,常常把问题无限放大,不过还好,最后都好了,对彼此有了更多了解,也显得更加亲密了,就这样也已经折腾了2年3个月零2天了,有时候回头想想真觉得挺不容易的,不过冥冥中有一种感觉快要熬出头了,坚信彼此一起走下去吧,以后都要好好的,我深爱着的你。

VPS杂记

入手VPS好久了,用的是LNMP架构,在Evernote中压了一些笔记,一一贴出来示众~

安装lnmp
注:版本可能有更新,0.7为截至2011/06/03的最新版

wget http://soft.vpser.net/lnmp/lnmp0.7.tar.gz
tar zxvf lnmp0.7.tar.gz
cd lnmp0.7/
./ubuntu.sh

创建虚拟主机

/root/vhost.sh

root账户的使用
平时使用普通账户登录,需要使用root权限的时候用su命令,然后再输入root命令,使用完root权限之后可以用ctrl+D退出权限,继续使用普通账户权限,这样可以防止权限的混乱

安装OpenVPN

wget http://vpsnoc.com/scripts/debian-openvpn.sh
chmod +x debian-openvpn.sh
./debian-openvpn.sh

按照提示安装完成后下载keys.tgz,将其解压到OpenVPN安装目录下的config子目录,然后启动OpenVPN就可以使用了

wordpress的rewrite问题
默认的规则有点小问题,替换为以下的即可:

if  (!-e $request_filename)
{
    rewrite (.*) /index.php;
}

WordPress文件权限

chown -R www.www /blog