本博客在很早以前就启用了多说社交评论功能,这么多年虽然不能说很完美吧,但在同类产品中多说已经算是做的相当不错的了。但从大概这周1开始,多说后台开始出现大量垃圾评论,其量之大真是这些年从来没有过的!(之前虽然也有垃圾评论,但也就极偶尔的1-2条而已,而且看了下网站访问统计,也没有大量的PV,明显是直接刷的多说评论接口),最后实在无法忍受,开启了所有评论的审核机制,即使这样,后台还是会定时出现大量的新垃圾评论。去多说官方论坛看了下,发现也没人提,官方也没反应,而且看论坛上的消息貌似是已经放弃治疗了,果不其然在前天,多说官方放出了即将关闭多说项目的通知:http://dev.duoshuo.com/threads/58d1169ae293b89a20c57241。其实之前也曾一度想从多说转移到其他的社交评论系统,但几番对比后,发现多说还是有较绝对的优势的,这回却是不得不搬家了!
大致扫了一眼wordpress的社会化评论系统,国内的友言、灯鹭什么的看起来也半死不活的(查灯鹭时看到个貌似是插件作者自己搞的新版本叫”Wordpress连接微博”:http://blogqun.com/doc/wp-connect.html,看着是积极开发的状态,功能也比较丰富,但就是要至少199RMB的使用费,而且看了下前端UI不是我喜欢的风格,后端又有诸多要求,于是就不作考虑了),剩下的就是搜狐的畅言和美帝的disqus,本来想直接disqus得了,功能和前端样式都是我喜欢的类型(本地评论也能顺利导入,虽然丢了头像等信息),但遗憾的是一方面近来disqus被墙的死死的,更重要的是另一方面disqus连接的社交系统都是fb,twitter等,也许以后哪天我决定写全英文博客时才更适合用吧…于是,也排除掉了!
所以,基本上,转移阵地的目标就是搜狐畅言了,看了下畅言的更新频率也还可以,而且功能也算丰富,尤其喜欢那个小印章功能!注册畅言账号后,问题就来了:怎么把现有的评论内容全部迁移过去呢?
wp后台装上畅言的插件后,倒是有同步本地评论的功能,满心欢喜的点了一下,然后插件也痛快的给我报了个错:
同步失败:import to changyan error!
再看畅言后台上的数据导入部分,选项倒是不少,畅言、友言、多说的json数据都可以(官方说明有个很奇葩的“1、从畅言导出的评论格式不支持直接导入畅言”,不明觉厉…),看到有多说,于是直接从多说后台导出一份“包含文章数据+包含评论数据”的zip,上传畅言,又爽快的给我报了个忘记什么内容的错!不过这是刚开始被垃圾评论刷屏的时候试的,后来多说放出要关项目消息之后,再上友言后台,发现选多说json时直接弹出了个小提示框(呵呵,反应够快的),解释了下导入要点(看了以后还是没明白那个二级域名是干啥的!),然后又试了下导入多说导出的json,好,这次不再爽快的报错了,看了下貌似也导入成功了,到后台按提示找到导入的评论一看,我勒个去的!丢了不少评论不说,评论内的回复引用关系也没导入成功,于是果断删除刚导入的“脏数据”,自己动手写个转换工具吧。
仔细看了下畅言文档中的json数据格式(这点还是要给个赞的,文档比较全面规范),决定不从多说数据入手,而改用wordpress本身的导出数据xml(当然,在这之前需要把多说的评论同步回本地,默认情况下多说是会自动回写wp本地评论的),经过几次试验,写了个基本能用的python脚本,不能说多完美转换吧,反正该有的用户名、评论内容、回复引用等都有了。
脚本如下(python 3.x版):
import sys import time import re import xml.etree.cElementTree as ET # 处理中文为转义\u格式并替换可能存在的双引号 def unicode_escape(raw_string): return str(raw_string.encode('unicode_escape')).replace("\\\\", "\\")[2:-1].replace("\"", "\\\"") # 清除所有html标签 def clean_html(raw_html): cleanr = re.compile('<.*?>') cleantext = re.sub(cleanr, '', raw_html) #return unicode_escape(cleantext.replace('\n', '').replace('\r', '')) return unicode_escape(cleantext) # 获取wordpress导出xml中的ns def parse_and_get_ns(file): events = "start", "start-ns" root = None ns = {} for event, elem in ET.iterparse(file, events): if event == "start-ns": if elem[0] in ns and ns[elem[0]] != elem[1]: # NOTE: It is perfectly valid to have the same prefix refer # to different URI namespaces in different parts of the # document. This exception serves as a reminder that this # solution is not robust. Use at your own peril. raise KeyError("Duplicate prefix with different URI found.") ns[elem[0]] = "{%s}" % elem[1] elif event == "start": if root is None: root = elem return ET.ElementTree(root), ns # 默认源xml wp_xml = "D:\\blogc\\k-resblog.wordpress.2017-03-22.xml" # 可能存在的第一参数作为源 if len(sys.argv)>2 : wp_xml = sys.argv[1] # print重定向为输出json文件 sys.stdout = open(wp_xml+".json", "w") #tree = ET.ElementTree(file=wp_xml) tree, ns = parse_and_get_ns(wp_xml) root = tree.getroot() wp_users = [] # 评论id起始偏移,可供需要修复错误时重复导入用 cmt_offset = 1000 # 用户唯一id起始偏移 userid_offset = 1000 # 遍历所有有评论的post挨个处理 for elem in root.iter(tag="item"): post_type = elem.find(ns["wp"]+"post_type") post_comments = elem.findall(ns["wp"]+"comment") if len(post_comments)>0 and post_type.text=="post" or post_type.text=="page": print("{\"title\":\""+unicode_escape(elem.find("title").text)+"\",", end="") print("\"url\":\""+elem.find("link").text+"\",", end="") # 这个时间畅言貌似并没有处理时区问题,也许应该用GMT+8的本地时间 pub_date_gmt_str = elem.find(ns["wp"]+"post_date_gmt").text pub_timestamp = time.mktime(time.strptime(pub_date_gmt_str, '%Y-%m-%d %H:%M:%S')) print("\"ttime\":\""+str(int(pub_timestamp*1000))+"\",", end="") print("\"sourceid\":\""+elem.find(ns["wp"]+"post_id").text+"\",", end="") print("\"parentid\":\"0\",\"categoryid\":\"\",\"ownerid\":\"\",\"metadata\":\"\",", end="") print("\"comments\":[", end="") for post_comm in post_comments: cmt_id = int(post_comm.find(ns["wp"]+"comment_id").text)+cmt_offset print("{\"cmtid\":\""+str(cmt_id)+"\",", end="") cmt_time_str = post_comm.find(ns["wp"]+"comment_date_gmt").text cmt_time = time.mktime(time.strptime(cmt_time_str, '%Y-%m-%d %H:%M:%S')) print("\"ctime\":\""+str(int(cmt_time*1000))+"\",", end="") print("\"content\":\""+clean_html(post_comm.find(ns["wp"]+"comment_content").text)+"\",", end="") cmt_id = int(post_comm.find(ns["wp"]+"comment_parent").text)+cmt_offset print("\"replyid\":\""+str(cmt_id)+"\",", end="") cmt_author = post_comm.find(ns["wp"]+"comment_author").text if cmt_author==None: cmt_author = "本博客网友" try: cmt_userid = wp_users.index(cmt_author)+userid_offset except ValueError: cmt_userid = len(wp_users)+userid_offset wp_users.append(cmt_author) print("\"user\":{\"userid\":\""+str(cmt_userid)+"\",", end="") print("\"nickname\":\""+unicode_escape(cmt_author)+"\",", end="") cmt_author_url = post_comm.find(ns["wp"]+"comment_author_url").text if None==cmt_author_url: cmt_author_url = "" print("\"userurl\":\""+cmt_author_url+"\"", end="") cmt_author_email = post_comm.find(ns["wp"]+"comment_author_email").text if None!=cmt_author_email: print(",\"usermetadata\":{\"email\":\""+cmt_author_email+"\"}", end="") print("},", end="") print("\"ip\":\""+post_comm.find(ns["wp"]+"comment_author_IP").text+"\",", end="") print("\"channeltype\":\"1\"},", end="") print("]}") sys.stdout.flush()
用法是先在wp后台的工具->导出->所有内容,如下:
拿到xml文件,然后改写脚本里的xml路径也好,直接拖到.py上运行也好,就能得到同名的畅言格式的.json文件,然后直接在畅言后台上导入畅言json格式评论即可(如果文件直接传文件过大的话就zip一下,反正我的乡下小博也就生成了几百k的json文件…)。
最后,由于畅言不像多说会默认回写wp本地评论,虽然有手动同步和实验室定时同步,但想想之前同步本地到畅言时报那个错…还是算了吧,我可不想我宝贵的wp本地评论里出现什么脏数据!但这样文章下面的评论数显示就又不正确了,看了下畅言文档,发现有js的获取畅言评论数说明,于是小改了一下,变成现在这样先显示本地评论数,再显示畅言评论数了。等什么时候畅言官方把这正反同步都完善了再改回去好了。
博主友情提示:
如您在评论中需要提及如QQ号、电子邮件地址或其他隐私敏感信息,欢迎使用>>博主专用加密工具v3<<处理后发布,原文只有博主可以看到。