自动化Python脚本:用Typora将Markdown文件转为html文件

2 篇文章 0 订阅
2 篇文章 0 订阅

本文介绍自动化 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 文件时文件名输入框的界面坐标、退出软件时“丢弃”按钮的界面坐标。( 😃 自动获取控件坐标的脚本见 https://blog.csdn.net/he_nan/article/details/106225372

注意:界面坐标是一个范围,只要保证鼠标点击该坐标后对应的输入框或按钮被选中即可。另外,在运行脚本输入文本时,需保证系统的输入法始终是英文状态,否则可能会导致输入失败;如果无法控制输入法自动切换,还可以输入打开文件时“打开”按钮的界面坐标和导出 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 即可。

  • 4
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值