使用Python生成博客目錄並自動更新README
各位朋友,大家好,我是Payne,歡迎大家關注我的博客,我的博客地址是:https://qinyuanpei.github.io。首先在這裡祝大家春節快樂,作為過完年以後的第一篇文章,博主想寫點內容風格相對輕鬆的內容。自從博主的博客採用 TravisCI 提供的持續集成(CI)服務以以來,博客的更新部署變得越來越簡單,所有的流程都被簡化為Git工作流下的提交(commit)和推送(push)操作。考慮到博客是託管在 Github 上的,一直希望可以自動更新倉庫主頁的README文件,這樣可以顯示每次提交代碼後的變更歷史。基於這樣一個構想,我想到了為博客生成目錄並自動更新README,其好處是可以為讀者建立良好的文檔導航,而且Markdown是一種簡單友好的文檔格式,Github等代碼託管平台天生就支持Markdown文檔的渲染。關於博客採用 TravisCI 提供持續集成(CI)服務相關內容,可以參考 持續集成在Hexo自動化部署上的實踐 這篇文章。
好了,現在考慮如何為博客生成目錄,我們這裡需要三個要素,即標題、鏈接和時間。標題和時間可以直接從 _posts 目錄下的Markdown文檔中讀取出來,鏈接從何而來呢?我最初想到的辦法是讀取每個Markdown文檔的文件名,因為我的使用習慣是採用英文命名,這樣當博客的永久鏈接(permalink)採用默認的:year/:month/:day/:title/形式時,每個Markdown文檔的文件名等價於文章鏈接。事實證明這是一個愚蠢的想法,因為當你改變了永久鏈接(permalink)的形式時,這種明顯投機的策略就會徹底的失敗。相信你在瀏覽器種打開這篇文章時,已然注意到鏈接形式發生了變化,當然這是我們在稍後的文章中討論的話題啦。至此,我們不得不尋找新的思路,那麼這個問題該如何解決呢?
我意識到我的博客配置了 hexo-generator-json-content 插件,這個插件最初的目的是為博客提供離線的搜索能力,該插件會在博客的根目錄里生成一個content.json文件,而這個文件中含有我們想要的一切信息,因此我們的思路轉變為解析這個文件,人生苦短啊,我果斷選擇了我最喜歡的Python,這裡我們會提取出所有的文章信息,按照日期由近到遠排序後生成列表。Python強大到讓我覺得這篇文章無法下筆,所以這裡直接給出代碼啦:
# -*- coding: utf-8 -*-import osimport reimport sysimport jsonimport datetime# 文檔實體結構定義class Post: def __init__(self,date,link,title): self.date = date self.link = link self.title = title def getTitle(self): return self.title def getLink(self): return https://qinyuanpei.github.io/ + self.link def getDate(self): d = re.findall(rd{4}-d{1,2}-d{1,2},self.date)[0] t = re.findall(rd{2}:d{2}:d{2},self.date)[0] dt = %s %s % (d,t) return datetime.datetime.strptime(dt,%Y-%m-%d %H:%M:%S)# 從JSON中載入文檔數據def loadData(): json_file = open(./public/content.json,rb) json_data = json.load(json_file) for item in json_data: yield Post(item[date],item[path],item[title])# 從列表生成Markdown文件def mkMarkdown(items): mdfile = open(README.md,mode=wt,encoding=utf-8) itemTpl = * {0} - [{1}]({2})
for item in items: mdfile.write(itemTpl.format( datetime.datetime.strftime(item.getDate(),%Y-%m-%d), item.getTitle(), item.getLink() ))if(__name__ == "__main__"): items = sorted(loadData(),key=lambda x:x.getDate(),reverse=True) mkMarkdown(items)
這裡需要注意的有兩個地方,第一,從JSON中解析出來的日期形式為:2018-02-23T01:32:45.000Z。對於這個形式的日期,博主先後嘗試了內建的time模塊和第三方的datetime模塊,發現均無法直接轉換為日期類型,所以首先採用正則匹配出日期和時間,然後再組合為標準的%Y-%m-%d %H:%M:%S的格式,這樣就可以使用datetime模塊進行處理啦,我還是想吐槽人類對各種各樣format的執著,這些通配符在不同的語言中存在差別,就像SQL和正則引擎或多或少地存在兼容性問題一樣。如果有朋友知道如何對這種日期形式進行轉換,歡迎在博客中評論留言,再次謝謝大家。第二,使用內置函數sorted()對數據進行排序,lambda表達式使用起來非常棒,因為默認是升序排列地,而我們需要的是日期由近到遠,所以這裡選擇了降序排列。
現在我們更新博客時的流程將發生變化,首先通過 hexo generate 或 hexo g命令生成博客,這樣Hexo會為我們生成 * content.json,然後我們執行這段Python腳本,就可以生成REAMD.md文件,這裡我們將這個文件推送到blog分支。相對應地,我們修改 TravisCI 的腳本文件 *.travis.yml 文件如下:
script: - hexo clean - hexo generate - cp README.md ./public/README.md
顯然,這是告訴 TravisCI 在生成博客以後,將 README.md 文件複製到輸出文件,這樣當我們推送博客(指生成的靜態頁面)到 master 分支的時候,它會和 blog 分支同步共享同一份 README 。我想一定有朋友會問我,難道生成 README.md 文件的步驟不能交給 TravisCI 來處理?一定要在推送到 blog 分支以前手動地去執行腳本嗎?我最初嘗試過讓 TravisCI 去執行這個 Python 腳本,可我發現一個殘酷的事實時,我們這個虛擬機環境是 nodejs 的,這在我們定義 .travis.yml 文件時就指定了,因此這個環境中可能是沒有 Python 支持的。起初我以為 Linux 系統自帶 Python , 因此嘗試在 .travis.yml 文件中使用 pip 安裝相關依賴,然後我發現持續集成服務華麗麗地掛了,因為 TravisCI 默認的 Python 版本是 Python2.7 , 除非我們指定的是一個 Python 的語言環境,所以這種想法不得不作罷,暫時就手動更新好啦。
好了,這篇文章核心的內容就這麼多,下面想說些關於 Hexo 的延伸話題。 Hexo 是一個基於 nodejs 的靜態博客生成器,按理說使用 nodejs 去擴展功能是最佳的實踐方式,所以即使 Python 再強大,我們在這裡看到的依然存在著天然的割裂感, 我們能不能將執行Python腳本的這個過程合併到 hexo generate 或者 hexo g這個步驟中去呢? 通過官方文檔中關於事件和生成器的描述,我們獲得了兩種新的思路,分別是在生成頁面以後通過 child_process 模塊調用 python 腳本、通過 Locals 變數獲取全部文章信息後生成Markdown。從方案是否優雅的角度上來講,我個人更傾向於第二種方案。基本的代碼如下:
//方案一hexo.on(generateAfter, function(post){ //TODO:通過content.json文件生成markdown文檔});//方案二hexo.extend.generator.register("markdown", function(locals){ var posts = locals.posts; //TODO:通過posts屬性生成markdown文檔});
顯然,我是不會寫 nodejs 的,如果有時間和精力的話,我可能會考慮採用第二種方案寫一個插件,可是像我這麼懶的一個人,還是不要提前立 flag 啦,畢竟人生苦短吶,我都選擇使用 Python 這門語言來寫啦,我幹嘛非要再花時間去迎合它呢?好啦,這篇文章就是這樣啦,本文中的腳本可以到 這裡 來獲取,本文生成的目錄可以到 這裡 來訪問,再次謝謝大家!
推薦閱讀:
※使用webhook結合python腳本實現自動化部署
※Sublime Text 3中怎麼更換python的版本?
※Python將list連續元素和非連續元素分開轉換為指定字元串
※python中的協程(yield)內部是怎麼實現的?python和lua在yield的實現原理上有什麼區別?