把博客从百度迁移了过来

上周末花了两天时间,终于把博客迁移了过来,所有文章与图片都完整地保留在新博客中。

其实很早就有把博客转成独门博客的想法,一直拖没实现。最近正好趁百度空间升级,来了情绪,索性一下子换了过来。对了百度空间,用了四年了,感情也非常深,但是它有几个明显的缺点,让我一直耿耿于怀。首先就是写文章的时候,特别是写技术性的文章,帖代码没有格式化代码的功能,导致帖出来的代码格式非常乱,很难看。而且编辑器功能也比较弱,没办法定制。还有由于众所周知的原因,我发布的很多文章时不时会被和谐掉。前两天整理博客文章的时候,发现一共掉了一百多篇文章,可恶啊。不过还好百度官方提供了一个导出所有文章的功能,所以才很容易把所有文章迁移过来。还有百度的图片上传功能,简直弱爆了,上传的时候极不稳定,速度也不快,而且还限制大小为500K,超过500K它强制性压缩你的图片,当然压缩后的图片简直不能看。所以我发图片的时候还会多一道工序,就是检查大小,如果发现超过了500K,就用convert命令压缩一下图片。在发新文章的时候,它还会对文章内容进行关键词检查,发现在敏感词,就不让发布。

对此我还专门写了一个python脚本,专门对内容进行敏感词检查,我之前的文章有讲过。但是有的敏感词,并不在我的收录范围之内, 这时候就采用比较笨的办法“二分法”来找敏感词,就是一段一段地删除,看删除到哪里的时候就能发表成功,那么说明关键词就在那一段里面,逐一缩小范围,直到找出敏感词。有时候就为这一点就折腾好长时间才能发表成功,真是蛋疼。最后导火索就是百度空间即将进行的升级,把一个博客性质的系统活生生地变成了社交化的工具,类似点点网的那种,非常不爽,找不到那种个人领地的感觉了。还有就是升级的过程中,又把我的图片什么的搞丢了。百度每次升级都会出级这种技术问题,上一次丢了我好多文章,刚发的就莫名其妙地不见了,升级完后才恢复回来。

换博客还有一个好处,就是有了自己的服务器,这样很多东西就有条件捣鼓了,自己的一些想法,就随时可以写出来放在服务器上面跑。虽然发费了一些经济上的代价,不过我觉得就长远来说,肯定是值的。有的事情,迟早是要迈出第一步,能早就早点。

上面讲了迁移博客的背景与原因,下面着重讲一讲迁移的过程。由于文章与图片比较多,文章有将近2000篇,图片2500张左右,所以不可能人工手动来做这个事情啦。为此我写了很多个脚本,用了很多命令行工具,如grep/ack,sed,vim等。由于百度官方已经提供完整的文章打包下载功能,所以文章就省事了,虽然我也写过下载文章的python脚本,但是下载不了那些设置成隐私的文章。基于现有的文章,我迁移方案思路如下:

1、整理百度打包的文章,将文章的文件、发表时间、文章名按一定格式组织好,以便进行后处理。
2、获取文章分类,对每篇文章,从网上拉取文章的标题与分类,并与第一步中的文章名进行匹配,将文章分类加到第一步生成的列表中去。
3、对第二步处理后的列表进行分类检查,因为文章标题匹配肯定会有一些文章匹配不上,还有隐私的文章肯定匹配不上,对这些单独进行手动添加分类
4、对文章内容里面的图片链接进行处理。将图片下载到本地,然后将链接替换成相对路径(后面会结合wordpress系统的路径再一次处理)。
5、经过以上步骤,所有的数据都准备好了,最后一步就是将这些数据按照wordpress数据库指定的格式存到wordpress数据库中去。

第一步:

主要对打包下载好的索引文件blog_index.html进行处理,去掉不必要的信息,整理成比较规律的列表。这一步主要利用vim来实现,用vim的正规匹配替换功能。例如:

:%s/<\li>/<\/li>\r/g              #把li后进行换行
:%s/<li class=".\{-}href ="//g    #.\{-}为vim的非贪婪匹配

按照类型的步骤后,最后的效果如下:

第二步:

主要写了一个python脚本来实现下载分类信息 getCategory.py:

# coding=utf-8
import urllib
#import os
import codecs
import thread

def GetURLS(url):
    list0 = []
    s1 = '<div class=tit><a href="'
    content = ''
    try:
        content = urllib.urlopen(url).read().decode('gbk')
    except:
        print url
    i = content.find(s1)
    if(i <> -1):
        content = content[i+len(s1):len(content)]
    while True:
        i = content.find(s1)
        if(i == -1):
            break
        content = content[i+len(s1):len(content)]
        i = content.find('"')
        str0 = content[0:i]
        str0 = 'http://hi.baidu.com'+str0
        list0.append(str0)
        content = content[i:len(content)]
    return list0

def GetContent(url):
    s1 = u'title="查看该分类中所有文章">类别:'
    s2 = '</a>'
    #print url
    content = urllib.urlopen(url).read().decode('gbk')
    #print content
    i = content.find('<title>')+7
    content = content[i:len(content)]
    j = content.find('_')
    title = content[0:j]
    i = content.find(s1)
    content = content[i+len(s1):len(content)]
    j = content.find(s2)
    content = content[:j]
    return content, title

def ToHTML(content, name):
    name = name.replace('*', '')
    name = name.replace('/', '')
    name = name.replace('\\', '')
    name = name.replace(':', '')
    name = name.replace('?', '')
    name = name.replace('"', '')
    name = name.replace('<', '')
    name = name.replace('>', '')
    name = name.replace('|', '')
    name = name.replace('#', 'Sharp')
    filea = codecs.open('category.txt', 'a', 'utf-8')
    filea.write(name + ' | ' +content + '\r\n')
    filea.close()

def downOne(url, sum0):
    content, title = GetContent(url)
    #mylock.acquire()   #获取同步锁
    #title = str(sum0)+','+title
    #print title
    #mylock.release()  #释放同步锁
    ToHTML(content, title)
    print title

HiName = 'hacklzt'
baseurl = 'http://hi.baidu.com/'+HiName+'/blog/index/'
i = 0#博客分页
sum0 = 0 #文章总数

#多线程同步锁
mylock = thread.allocate_lock()
headurl = ''
while True:
    list0 = GetURLS(baseurl+str(i))
    #print list0
    if(len(list0) == 0):
        break
    if(list0[0] == headurl):
        break
    headurl = list0[0]
    for j in list0:
        sum0 = sum0+1
        thread.start_new_thread(downOne,(j, sum0))
        #downOne(j, sum0)
    i = i+1

下载后的格式如下图 category.txt:

然后将上面的结果与上一步的列表进行匹配整合的python脚本 sortCategory.py:

#!/bin/bash
#encoding=utf-8

categorys = {}
content = ''
nocat = 0
for line in open('category.txt'):
    tmp_arr = line.split('|')
    categorys[tmp_arr[0].strip()] = tmp_arr[1].strip()
for line in open('blog_index.html'):
    tmp_arr = line.split('|')
    title = tmp_arr[2].strip()
    cat = title in categorys.keys() and categorys[title] or ''
    if not title in categorys.keys():
        nocat = nocat + 1
    content += line.strip() + ' | ' + cat + '\r\n'
print content
open('index_blog-cat.html', 'w').write(content)
print 'NO:' + str(nocat)  #打印不匹配的总数

第三步:

检查有哪些文章没有匹配上分类,打印出来,同样也是python脚本来完成 checkCategory.py:

#!/bin/bash
#encoding=utf-8

for line in open('index_blog-cat.html'):
    tmp_arr = line.strip().split('|')
    if tmp_arr[-1].strip() == '':
        print line

合并后的格式如图:

图片的文章顺序是我颠倒了的,因为后插入到数据库中时,要先插入老的文章,颠倒文本内容顺序也很简单,一个命令搞定:

tac index_blog-cat.html > index_blog-cat-rsort.html

第四步:

这一步将文章内容(百度打包里面的html文件)中的图片链接进行替换,主要利用grep、ack与sed命令接合来实现,例如:

sed -i 's/src="pics\/hiphotos.baidu.com\/hacklzt\/pic\/item/src="../wp-content/uploads/hibaidu/g' blog/*.html
#注意\的转义作用

考虑到不仅要替换链接,还要将图片下载到本地,于是写了一个shell脚本来完成 pics.sh:

#/bin/bash
#encoding=utf8
grep -o -P ' pics.url  #将图片地址保存
while read line; do
    fname=`echo ${line} |awk -F/ '{print $NF}'`  #找出图片地址的文件名
    echo ${line}'  '${fname}
    regurl=${line//\//\\\/}    #正则匹配时,将/替换成\/
    sed -i "s/${regurl}/pics\/${fname}/g" blog/*.html  #正则替换图片地址

    #echo "curl -o pics/${fname} ${line} > /dev/null"  #下载图片
    curl -o pics/${fname} ${line} > /dev/null  #下载图片
done < pics.url  #从文件中读取每一行进行处理

第五步:

到现在为止,我们要做的数据准备工作已经完成了。最后写了一个php脚本,把文章内容与时间等信息,存到wordpress相应的数据表中。post2wp.php内容如下:

<!--?php
date_default_timezone_set('Asia/Chongqing');
$index_file = 'index_blog-cat.html';
$f_handle = fopen($index_file, 'r');
while(!feof($f_handle)){
    $line = fgets($f_handle);

    if(empty($line))
        break;

    $line_arr = explode('|', $line);

    $blog_file = trim($line_arr[0]);
    $date_time = trim($line_arr[1]);
    $title = htmlspecialchars(trim($line_arr[2]));
    $cat = trim($line_arr[3]);

    processBlog($blog_file, $title, $date_time, $cat);
}

$sql = 'update wp_term_taxonomy w1 set w1.count = (select count(*) from wp_term_relationships
        where term_taxonomy_id = w1.term_taxonomy_id)';
$res = mysql_query($sql);  //更新每个目录下面有多少文章

function processBlog($blog_file, $title, $date_time, $cat){
    $cat_arr = array(
        '默认分类' => 1,
        '工具教程' => 3,
        '技术文章' => 4,
        '个人日记' => 5,
        '编程相关' => 6,
        'delphi编程' => 7,
        '精品文章' => 8,
        '原创作品' => 9,
        '学习笔记' => 10,
        '网页编程' => 11,
        '.net编程' => 12
    );

    $handle = fopen($blog_file, 'r');
    $blog_content = fread($handle, filesize($blog_file));
    $blog_content = mysql_real_escape_string($blog_content);

    $post_time = strtotime($date_time);
    $post_time_gmt = date('Y-m-d h:i:s', $post_time - 8*3600);
    //echo $post_time_gmt; exit();

    $sql = "INSERT INTO wp_posts (
            post_author, post_date, post_date_gmt, post_content, post_title,
            post_excerpt,post_status, comment_status, ping_status, post_password,
            post_name, to_ping, pinged, post_modified, post_modified_gmt,
            post_content_filtered, post_parent, guid, menu_order, post_type,
            post_mime_type, comment_count) VALUES ("
            ."1,'{$date_time}', '{$post_time_gmt}', '{$blog_content}', '{$title}',"
            . "'', 'publish', 'open', 'open', '',"
            . "'', '', '', '{$date_time}', '{$post_time_gmt}', "
            . "'', 0, '', 0, 'post', "
            . "'', 0);";
    //echo $sql; exit();
    mysql_connect('localhost', 'root', 'hacklzt');
    mysql_select_db('wp_blog');
    mysql_query('SET NAMES UTF8;');
    $res = mysql_query($sql);  //插入文章
    if(empty($res)){
        echo "$title  失败";
    }
    $ins_id = mysql_insert_id();
    if(array_key_exists($cat, $cat_arr)){
        $sql = "INSERT INTO wp_term_relationships (object_id, term_taxonomy_id, term_order) ".
            "VALUES ($ins_id, $cat_arr[$cat], 0);";
        $res = mysql_query($sql);   //插入此文章的分类
        if(empty($res)){
            echo "$title  分类失败";
        }else{

        }
    }else{
        echo "$title  分类失败";
    }
}
?>

用命令 php post2wp.php,就可以直接运行该php文件。

OK,收工。

此外,现发现文章里面的链接不对,可以用SQL语句来修正:

UPDATE wp_posts SET post_content = REPLACE( post_content, '原内容', ' 新内容' ) ;

其实上面的脚本我是调试了很久才最终完成的,所以程序不是写出来的,是调出来的,哈哈。