部落格大搬家

2 分鐘閱讀

半夜無眠

由於不久前才在Github上架設了這個blog,如果不把之前在痞客邦寫的文章搬遷過來,感覺會有點空虛。 其實要備份痞客邦的文章非常方便,只要在後台管理介面的設定管理內的匯入/匯出就可以輸出.txt檔案。 有趣的問題來了,輸出的檔案格式似乎跟markdown不太相同,要一篇一篇手動修改,對目前只有30幾篇文章,我的渣部落格而言應該很快就能搞定,但是對於比較大規模的部落格能有較系統化的處理方式嗎? 想嘗試用Python處理文檔,由備份文件自動產生post的想法就一直在腦海中揮之不去,輾轉難眠之下我決定開一個Github repository專門來處理各種blog的疑難雜症。

程式規劃

之前在學習Python的時候,發現了這一本書完全免費公開在網路上,而其中的第8章節就有講述到檔案的讀寫。 只要有這些基礎知識,應該不難將痞客邦匯出的.txt軟為符合Jekyll格式的.md文檔。 然而在動手開始寫程式前,有一個完整的規劃,是寫好程式的第一步。

  1. 拆解文章: 由於匯出的文件總結了全部的文章,並且還包含了comments以及有的沒的分隔字串。所以將每篇文章,拆成對應的一份份.md檔是首要動作。
  2. 修正格式: 對於每個.md文檔,對metadata及內文都要整理成符合Jeykyll的markdown格式。

路徑設定

    # 設定匯出檔案的位置,我是放在/pixnet_backup/blog-export-mt-KWbuster-20170714081020.txt
    file_path = os.path.join(os.path.dirname(__file__), 'pixnet_backup', 'blog-export-mt-KWbuster-20170714081020.txt')
    # 設定輸出.md檔案的資料夾
    post_dir = os.path.join(os.path.dirname(__file__), 'posts')
    # 產生存放.md文檔的資料夾
    if not os.path.exists(post_dir):
        os.makedirs(post_dir)

拆解文章

第一步是找到分割每篇post的字串,觀察過後小弟自以為的決定TITLE應該是個關鍵的字串,如此一來就可以分割並產生一份份的.md文檔。

    # 讀取.txt檔案,依照分行存成list
    with open(file_path, 'rb') as read_file:
        file_list = read_file.readlines()

    # 依照文件中的"TITLE"來區分出每一篇post
    init_ind, end_ind = 0, 0
    while init_ind < len(file_list) and end_ind < len(file_list):
        # 藉由"TITLE"字串分割post
        if (end_ind != 0) and ('TITLE'.encode('utf-8') in file_list[end_ind]):
            file_content, file_name = generate_post(init_ind, end_ind, file_list)

            # 將file_content寫入markdown文檔
            if 'publish'.encode('utf-8') in file_content[5]:
                print('process... %s' %file_name)
                with open(post_dir + '/' + file_name, 'wb') as write_file:
                    write_file.writelines(file_content)
            init_ind = end_ind
        end_ind += 1

    # last post
    file_content, file_name = generate_post(init_ind, end_ind, file_list)

    if 'publish'.encode('utf-8') in file_content[5]:
        print('process... %s' %file_name)
        with open(post_dir + '/' + file_name, 'wb') as write_file:
            write_file.writelines(file_content)

這部分會用到一個小小的副程式,用意在於由.txt內的文件得到每篇文章.md檔名應該要有的檔案格式,範例如下:

    def generate_post(init_ind, end_ind, file_list):
        file_content = file_list[init_ind:end_ind - 1]
        title = file_list[init_ind].decode("utf-8")[7:-1]
        date = file_list[init_ind+2].decode("utf-8")[6:16]
        file_name = '%s-%s-%s-%s.md' %(date[-4:], date[:2], date[-7:-5], title[:-1]) # title[:-1]是當結尾是'\r'時的解法
        return file_content, file_name

修正格式

目前拆解出來的.md文件慢慢開始有點感覺囉,接下來就是分別對metadata、content部分進行格式修改。

    # 逐一讀取前一個步驟產生的.md文檔
    post_files = [x[2] for x in os.walk(post_dir)][0]
    for post_file in post_files:
        with open(post_dir + '/' +post_file, 'rb') as read_file:
            post_content = read_file.readlines()

        print('Markdown...%s' %post_file)
        # 將文檔分為metadata與content部分
        for ind in range(len(post_content)):
            if 'BODY'.encode('utf-8') in post_content[ind]:
                metadata = post_content[:ind]
                content = post_content[ind+1:-1]

        # 分別以下列兩個副程式處理格式
        output_metadata = generate_metadata(metadata)
        output_content = generate_content(content)        

        with open(post_dir + '/' +post_file, 'wb') as write_file:
            write_file.writelines(output_metadata+output_content)

處理格式用的副程式如下,可能還需要微調。

    def generate_metadata(metadata):
        output_metadata = []
        output_metadata.append(b'---\n')
        output_metadata.append(b'layout: single\n')

        for line in metadata:
            # 設定標題
            if 'TITLE'.encode('utf-8') in line:
                output_metadata.append(b'title: %s' %line[7:])
            # 設定發文時間
            if 'DATE'.encode('utf-8') in line:
                if 'AM'.encode('utf-8') in line:
                    output_metadata.append(b'date: %s-%s-%s %s:%s:%s\n' %(line[12:16], line[6:8], line[9:11], line[17:19], line[20:22], line[23:25]))
                else:
                    hour = int(line[17:19])
                    if hour < 12:
                        time = str(hour+12).encode('utf-8')
                    else:
                        time = str(hour).encode('utf-8')
                    output_metadata.append(b'date: %s-%s-%s %s:%s:%s\n' %(line[12:16], line[6:8], line[9:11], time, line[20:22], line[23:25]))
            # 設定文章類別
            if 'PRIMARY CATEGORY'.encode('utf-8') in line:
                output_metadata.append(b'categories:\n')
                output_metadata.append(b'- %s\n' %line[18:-2])
            # 設定標記
            if 'TAGS'.encode('utf-8') in line:
                tag_list=str(line[6:-2], 'utf-8').split(',')
                output_metadata.append(b'tags:\n')
                for tag in tag_list:
                    output_metadata.append(b'- %s\n' %(tag[1:-1].encode('utf-8')))
        output_metadata.append(b'---\n')
        output_metadata.append(b'\n')
    def generate_content(content):
        output_content = []
        for line in content:
            #空格直接忽略
            if '&nbsp;'.encode('utf-8') in line:
                pass
            # <p></p>也可以直接無視
            elif '<p>'.encode('utf-8') in line:
                output_content.append(b'%s\n' %line[3:-6])
            # 回覆留言部分直接跳過
            elif 'COMMENT'.encode('utf-8') in line:
                output_content = output_content[:-1]
                break
            else:
                output_content.append(line)
        return output_content

細節修正

經過一個下午的奮戰,終於將所有的post轉換成.md,但是奮戰還沒結束,我發現竟然舊的blog的標題全部不見了,類別也沒在固定的位置,慘慘慘! 最終問題是post中的title,應該是不能有特殊的符號斷開,經過測試後終於搞定,詳見Github。 但是還沒結束呢,剩下才是痛苦的地方,因為有大量的照片圖片要搬遷,以及每篇部落格的段落要重新編排,剩餘的主要工作如下:

  • 文章目錄
  • 文章簡介
  • 文章內的連結、欄位格式修正
  • 文章內的圖片:痞客邦非常不貼心,沒有圖片備份的功能,參考相關網站的介紹後,下載了WGet(目前2017-05-24還有更新)對相簿圖片進行備份,但是文章使用圖片還是得人工修改。

時間真的夠嗎?

  • [2017-08-31] 網路上找了老半天如何設定痞客邦文章頁面的跳轉,結果中文網站盡是一堆…ㄌㄐ。 但是在StackOverflow大神的引領下,終於整理出下方跳轉的javascript,將這段嵌入到文章開頭即可。
    <p>
    <script type="text/javascript">// <![CDATA[

    var ARTICLE_MAIN = document.getElementById("article-main");
    if(ARTICLE_MAIN){
        var delayMillis = 5000; //5 second

        setTimeout(function(){
            window.location= "跳轉頁面URL";//your code to be executed after 5 second

            }, delayMillis);
        }
    // ]]></script>

    </p>
    <p><a href="文字連結URL">文章搬家囉5秒後跳轉</a></p>

留言