自動化Python腳本:用Typora將Markdown文件轉爲html文件

本文介紹自動化 Python 腳本,實現用 Typora 將 Markdown 文件轉爲 html 文件。

背景介紹

目前比較流行使用 Markdown 形式的開發文檔,並且使用版本管理工具維護開發文檔的增刪和修改。而對於開發文檔的閱讀者來說,可能他們更多是的閱讀需要,並沒有編輯和修改的需求,因此如果能夠使用瀏覽器進行訪問,則將會是比較愉悅的體驗,還可以避免誤修改的風險。

這裏介紹一款比較優秀並且免費的 Markdown 文檔編輯工具 Typora,其介紹及使用說明可以自行到網上查閱,這裏不再贅述。接下來將介紹如何使用自動化 Python 腳本,實現用 Typora 將 Markdown 文件轉換爲 html 文件,並在 html 文件中添加自定義格式生成目錄大綱,然後用瀏覽器訪問轉換後的 html 文件。

運行環境

  • 操作系統:windows 10
  • 版本管理工具:TFS (Team Foundation Server)
  • Python 3.8.2
  • Typora 0.9.79 (beta)

這裏需要使用到 GUI 自動化工具 PyAutoGUI 和 Pywinauto,如果沒有安裝這兩個工具,可以在聯網的條件下使用如下兩條命令進行安裝

pip install pyautogui
pip install pywinauto

關於這兩個工具的詳細使用,可以自行到網上查閱。

自動化腳本

獲取最新版本

獲取服務器上的最新開發文檔(Markdown 形式)到本地,這裏可以通過調用批處理文件的方式實現。

批處理文件 TfsGetLatest.bat

"C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\Common7\IDE\CommonExtensions\Microsoft\TeamFoundation\Team Explorer\TF.exe" get /all /recursive $/Document D:\AutoScript\Document

其中 $/Document 代表 TFS 服務器路徑,D:\AutoScript\Document 代表本地代碼路徑。

然後,利用 Python 腳本調用批處理文件就可以獲取 TFS 服務器上的最新版本:

import os
os.system(os.path.join(os.getcwd(), 'TfsGetLatest.bat'))

篩選內容有變動的 md 文件

D:\AutoScript\ 目錄下創建文件夾 DocumentBak,用於保存上一次獲取的服務器上的最新版本,如果這是第一次運行,則該目錄爲空。

D:\AutoScript\ 目錄下創建文件夾 DocumentTemp,並將目錄 Document 下的所有內容拷貝到目錄 DocumentTemp 下,然後比較目錄 DocumentTempDocumentBak,篩選出內容有變動的 md 文件,記錄所有的 md 文件,同時刪除沒有變化的 md 文件和圖像文件

# 與備份目錄比較,篩選源目錄下有變動的md文件
def FilterChangedMd(srcDir, bakDir):
    srcDirLen = len(srcDir)
    if not srcDir.endswith('\\'):
        srcDirLen = srcDirLen + 1
        
    allMdNames = []
    changedMdPaths = []
    for root, dirs, files in os.walk(srcDir):
        for name in files:
            # 篩選後綴名爲md的文件
            if FileFormat.IsMdFile(name):
                allMdNames.append(name)
                # 篩選有變化的md文件
                fullPath = os.path.join(root, name)
                subPath = fullPath[srcDirLen:]
                aimPath = os.path.join(bakDir, subPath)
                if not (os.path.exists(aimPath) and filecmp.cmp(fullPath, aimPath)):
                    changedMdPaths.insert(0, fullPath)
                else:
                    os.remove(fullPath)
            elif FileFormat.IsImageFile(name):
                # 刪除沒有變化的圖像文件
                fullPath = os.path.join(root, name)
                subPath = fullPath[srcDirLen:]
                aimPath = os.path.join(bakDir, subPath)
                if os.path.exists(aimPath) and filecmp.cmp(fullPath, aimPath):
                    os.remove(fullPath)
            else:
                os.remove(os.path.join(root, name))
                
    return allMdNames, changedMdPaths

將目錄 Document 下的所有內容拷貝到備份目錄 DocumentBak 下。

修改內容有變動的 md 文件中的其它 md 文件引用

篩選出有變動的 md 文件後,查找文件中引用的 md 文件,並將對應的後綴名修改爲 html

# 修正有變動的md文件,將文件中引用的md文件後綴名修改爲html
def ReviseChangedMd(allMdNames, changedMdPaths):
    for mdPath in changedMdPaths:
        # 將文件所有的內容全部讀取出來,匹配修改後寫入到新文件中
        fOld = open(mdPath, 'r', encoding = 'utf-8')
        fNew = open(mdPath + '.bak', 'w', encoding = 'utf-8')
        # 循環讀取舊文件
        for line in fOld:
            for mdName in allMdNames:
                if mdName in line:
                    mdNewName = mdName[:-2] + FileFormat.HtmlFileSuffix()
                    line = line.replace(mdName, mdNewName)
                    
            fNew.write(line)
            
        fOld.close()
        fNew.close()
        
        # 將舊文件替換爲新文件
        os.remove(mdPath)
        os.rename(mdPath + '.bak', mdPath)

將修正後的 md 文件轉換爲 html 文件

由於 Typora 軟件是多進程,並且使用工具 Pywinauto 查看其 control_type 是 Pane,不能像網上介紹的 notepad 的樣例那樣去使用,因此這裏使用工具 PyAutoGUI,根據界面座標去操作 Typora 軟件。

由於 Typora 軟件打開時會和上次的佈局一致,因此可以事先獲取到要點擊的按鈕的界面座標,及要輸入文本的輸入框的界面座標。這裏要求輸入四個界面座標,分別爲打開文件時文件名輸入框的界面座標、菜單“文件”按鈕的界面座標、導出 HTML 文件時文件名輸入框的界面座標、退出軟件時“丟棄”按鈕的界面座標。( 😃 後續再研究不用輸入界面座標就能自動完成該過程的方法。)

注意:界面座標是一個範圍,只要保證鼠標點擊該座標後對應的輸入框或按鈕被選中即可。另外,在運行腳本輸入文本時,需保證系統的輸入法始終是英文狀態,否則可能會導致輸入失敗;如果無法控制輸入法自動切換,還可以輸入打開文件時“打開”按鈕的界面座標和導出 HTML 文件時“保存”按鈕的界面座標。

import pyautogui
import pywinauto

# 將指定目錄下的單個md文件轉換爲html文件
def ConvertMdToHtml(typoraApp, inFileCoor, fileMenuCoor, outFileCoor, quitFileCoor, mdFile):
    # 打開Typora應用程序
    pywinauto.application.Application(backend="uia").start(typoraApp)
    # 打開md文件
    time.sleep(5)
    pyautogui.hotkey('ctrl', 'o')
    time.sleep(2)
    pyautogui.click(inFileCoor[0], inFileCoor[1])
    pyautogui.typewrite(mdFile)
    pyautogui.press('enter', presses=1, interval=0.5)
    # 導出html
    time.sleep(5)
    pyautogui.click(fileMenuCoor[0], fileMenuCoor[1])
    time.sleep(0.5)
    pyautogui.press('down', presses=14, interval=0.1)
    pyautogui.press('right', presses=1, interval=0.1)
    pyautogui.press('down', presses=1, interval=0.1)
    pyautogui.press('enter', presses=1, interval=0.5)
    time.sleep(2)
    pyautogui.doubleClick(outFileCoor[0], outFileCoor[1])
    pyautogui.typewrite(mdFile[:-2] + FileFormat.HtmlFileSuffix())
    pyautogui.press('enter', presses=1, interval=0.5)
    pyautogui.press('left', presses=1, interval=0.1)
    pyautogui.press('enter', presses=1, interval=0.5)
    # 關閉Typora
    time.sleep(5)
    pyautogui.hotkey('ctrl', 'w')
    time.sleep(1)
    pyautogui.click(quitFileCoor[0], quitFileCoor[1])
    time.sleep(5)
    
# 將指定目錄下的所有md文件轉換爲html文件
def ConvertAllMdToHtml(typoraApp, inFileCoor, fileMenuCoor, outFileCoor, quitFileCoor, mdFilesDir):
    # 篩選指定目錄下的文件
    for root, dirs, files in os.walk(mdFilesDir):
        for name in files:
            if FileFormat.IsMdFile(name):
                mdFile = os.path.join(root, name)
                ConvertMdToHtml(typoraApp, inFileCoor, fileMenuCoor, outFileCoor, quitFileCoor, mdFile)

將轉換後的 html 文件加上自定義格式

在轉換後的 html 文件的 body 的結尾前加上自定義格式,該格式可以生成目錄大綱,格式如下

<!-- <script src="http://code.jquery.com/jquery-1.7.2.min.js"></script> -->
<script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script>
<script src="http://yandex.st/highlightjs/6.2/highlight.min.js"></script>
<script>hljs.initHighlightingOnLoad();</script>
<!--側欄目錄生成代碼-->
<script>
    //標題序號計數器
    var hCount = [0, 0, 0, 0, 0, 0];
    //設置計數器
    function setHCount(number) {
        //當前計數器加一
        hCount[number - 1]++;
        for (var i = number, length = hCount.length; i < length; i++) {
            //子目錄計數器全部置零
            hCount[i] = 0;
        }
    }
    //重命名目錄名稱
    function setHTagValue(item, number) {
        //獲取標題名
        var text = $(item).get(0).innerHTML;
        //初始化空字符串
        var before = "";
        //生成序號
        for (var i = 0, length = hCount.length; i < number; i++) {
            if (i < number - 1)
                before += hCount[i] + ".";
            else
                before += hCount[i] + " ";
        }
        //在標題前面加上序號
        $(item).get(0).innerHTML = before + text;
    }
    function renameHTag(item) {
        var tag = $(item).get(0).localName;
        for (var i = 1; i < 7; ++i) {
            if (tag === "h" + i) {
                setHCount(i - 1);
                setHTagValue(item, i - 1);
            }
        }
    }
    $(document).ready(function () {
        $("h1,h2,h3,h4,h5,h6").each(function (i, item) {
            //給<H>類標籤編號
            renameHTag(item);
            //獲取標籤的名字,h1,還是h2
            var tag = $(item).get(0).localName;
            //爲該標籤設置id屬性
            $(item).attr("id", "wow" + i);
            //添加一個頁內超鏈接,並設置class選擇器
            $("#category").append('<a class="new' + tag + '" href="#wow' + i + '">' + $(item).text() + '</a></br>');
            //爲每一個標題超鏈接的class屬性設置左邊距
            $(".newh1").css("margin-left", 0);
            $(".newh2").css("margin-left", 20);
            $(".newh3").css("margin-left", 40);
            $(".newh4").css("margin-left", 60);
            $(".newh5").css("margin-left", 80);
            $(".newh6").css("margin-left", 100);
        });
        //設置class選擇器爲.book-body的html內容
        $(".book-body").html($(".book-body").nextAll())
    });
</script>

<style type="text/css">
    @media (min-width: 1100px) {
        #category {
            /* 絕對定位 */
            position: fixed;
            /* 目錄顯示的位置 */
            left: 2%;
            top: 55px;
            /* 目錄欄的高度 */
            height: 100%;
            /* 開啓垂直滾動條 */
            overflow-y: scroll;
            /* 開啓水平滾動條 */
            overflow-x: scroll;
        }
    }
    @media (-webkit-max-device-pixel-ratio: 1) {
        ::-webkit-scrollbar-track-piece {
            background-color: #FFF
        }
        ::-webkit-scrollbar {
            width: 6px;
            height: 6px
        }
        ::-webkit-scrollbar-thumb {
            background-color: #c2c2c2;
            background-clip: padding-box;
            min-height: 28px
        }
        ::-webkit-scrollbar-thumb:hover {
            background-color: #A0A0A0
        }
    }
</style>

<!--文章主體部分-->
<div class="book-body" id="book_body" style="width:80%;display:block;"> </div>
<!--目錄欄-->
<p style="position:fixed;left:2%;top:0px;overflow:hidden;"><font color="#FF4500" size=5 face="微軟雅黑">目錄</font></p>
<div class="book-summary" id="category" style="width:15%;display:block;overflow:hidden;font-size:14px" ></div>

將處理後的 html 文件和圖像文件拷貝到目標目錄下

將處理後的 html 文件和圖像文件拷貝到目標目錄下,該目錄可以是本地目錄,也可以是網站目錄(自建內網網站),然後就可以通過瀏覽器訪問 html 文件了。

寫在最後 😃

全部的完整腳本,見附件。

腳本放在與 Document 同級的目錄下,直接執行 Markdown2Html.py 即可。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章