python3 实现Markdown转html小工具

写博客文章一直使用 markdownmarkdown 是一个轻量级的标记语言,可以快速创建待格式的简单文档。typra 是一款跨平台而且设计极佳的 markdown 写作工具。markdown 本质上算是简化版的 html。那么,如何自己编写一个 markdown 转换工具呢?本文将带你一起用python3 实现一个 markdown 转 html 的命令行工具,用浏览器查看 html 就能看到带格式的文章了。

20181018153983700396678.png

准备工作

在开始之前,要安装一些第三方库。

python3-markdown 库

markdown 库是用来编译渲染 markdown 文件的,这是咱编写转换工具的核心。

安装

pip3 install markdown

使用

在这里只使用 markdown 方法,可以将 markdwon 文本转换为 html 格式的文本,如下:

import markdown

markdownText = '''# 测试
- 无序列表1
- 无序列表2
'''

print(markdown.markdown(markdownText, output_format='html5', extensions=['extra']))

上述代码,会打印转换出来的 html 格式的文本。

注意

  1. markdwon 方法返回的结果是 html 文本,但不包含头信息 <head>,所以后面如果使用的话应该给结果加上,保证html文本的兼容性;
  2. markdwon 方法可以指定输出格式,比如按照 html5 的格式转换;
  3. markdwon 方法默认不识别代码高亮语法,可以按照上述演示代码中的方式指定 extensions['extra']^[python3-markdown 解析反引号代码块与代码高亮];

beautifulsoup4 库和 html5lib 库

这里安装 html5lib 库,主要是使用 beautifulsoup4 库的 prettify 方法的时候依赖 html5lib 库。

安装

pip3 install beautifulsoup4 html5lib

使用

beautifulsoup4 库可以过滤分析 html 文本的节点信息和其它 html 文本的处理,多用于爬虫应用,这里使用到 beautifulsoup4 库的美化 html 文本的方法,按照 html5lib 的方式,简单参考如下:

from bs4 import BeautifulSoup

rawHtml = '<head><meta charset="utf-8" /></head><body><h1>Hello</h1><p>hello, world!</p></body>'
prettyHtml = BeautifulSoup(rawHtml, 'html5lib').prettify()

主要是用于将 html 文本格式化,处理的更美观易读。

实现

有了上面安装的 markdown 库,要实现 markdown 转 html 的小工具就变得简单了许多,就不需要自己编写 markdown 语法解析器了。实现思路很简单,编写一个类 Markdown2Html,然后编写一个python脚本形式的命令脚本调用类方法实现文件转换。

Markdown2Html 类

  1. 这个类在文件 markdown2html.py 中实现,所以首先要新建这个文件。

  2. 导入要用到的库

    import markdown
    import os.path as op
    from bs4 import BeautifulSoup 
    
  3. 定义类及初始化函数,因为最终转换要生成 html 文件,那么必然可以使用 css 文件控制样式,所以这里初始化函数可传入一个指定 css 文件配置样式,实现如下:

    class Markdown2Html:
    
        def __init__(self, cssfile=None):
            '''
            初始化 Markdown2Html 类,可传入特定 css 文件作为样式
            '''
            self.headTag = '<head><meta charset="utf-8" /></head>'
            if cssfile:
                self.setStyle(cssfile)
    

    初始化中定义的 headTag 主要是用于完善 markdwon 库得到的 html 文本,<head> 中预留了 css 样式文本的位置。setStyle 方法主要处理传入的 css 文件,具体见下一步。

  4. 实现 Markdown2Html 类的 setStyle 方法:

    def setStyle(self, cssfile=None):
        '''
        设置样式表文件
        '''
        if cssfile is None:
            self.headTag = '<head><meta charset="utf-8" /></head>'
        else:
            with open(cssfile, 'r') as f:
                css = f.read()
                self.headTag = self.headTag[:-7] + f'<style  type="text/css">{css}</style>' + self.headTag[-7:]
    

    setStyle 方法传入 cssfile 参数,如果为空,说明不指定样式,将 html 文本预留头信息设置为初始化状态,如果设置了 css 文件,那么用 css 文件内容生成新的 head 信息。

  5. 实现 markdown 转换为 html 的方法 convert,这里设计方法,可以传入三个参数,先看实现,后解释:

    def convert(self, infile, outfile=None, prettify=False):
        '''
        转换文件
        '''
        if not op.isfile(infile):
            print('请输入正确的 markdown 文件路径!')
            return
    
        if outfile is None:
            outfile = op.splitext(infile)[0] + '.html'
    
        with open(infile, 'r', encoding='utf8') as f:
            markdownText = f.read()
    
        rawhtml = self.headTag + markdown.markdown(markdownText, output_format='html5', extensions=['extra'])
    
        if prettify:
            prettyHtml = BeautifulSoup(rawhtml, 'html5lib').prettify()
            with open(outfile, 'w', encoding='utf8') as f:
                f.write(prettyHtml)
        else:
            with open(outfile, 'w', encoding='utf8') as f:
                f.write(rawhtml)
    

    这里首先判断指定的输入文件路径对应是不是文件,然后检查输出文件有没有指定,之后读取 markdown 文件获取 markdown 文本,调用 markdown 库的 markdown 方法将 markdown 文本转换为 html5 格式的 html 文本,并添加准备好的头信息,最后判断是否需要美化生成的 html 文本,如果是,就先美化,再写入到指定文件,如果不是,则直接将结果写入到指定输出文件。需要注意的是,上述文件操作中都指明了文件编码为 utf8 ,是为了保证跨平台编码统一,提高兼容性。

  6. 编写以下代码进行测试,同级目录下存储的 github.css 文件,所以这里指定 css文件 转换 README.md 文件:

    if __name__ == '__main__':
        m2h = Markdown2Html('github.css')
        m2h.convert('./README.md', './README.html')
    

    运行:

    python3 markdown2html.py
    

    结果:

    20181016153970548190814.png

    Markdown2Html 类完成!

    完整代码参考:tools-with-script/m2h/markdown2html.py

脚本

有了上面的 Markdown2Html 类,现在可以编写脚本命令了。

准备文件

  1. 新建名为 m2h 的文件,添加以下内容,指明脚本解释器为 python3:

    #!/usr/bin/env python3
    
  2. 执行以下命令,为文件添加运行权限:

    chmod +x ./m2h
    

    这样就可以像执行命令一样调用工具了。

脚本实现

导入必要库
import os
import argparse
from markdown2html import Markdown2Html
命令参数

作为一个合格的文件转换命令工具,最起码要有一个占位参数,用来指定待转换的 markdown 文件。另外,还要设计一个可选参数用来指定要使用的 css 样式文件,用来指定转换后的主题,有时候需要将所有的转换文件统一放在一个目录中,所以还要有一个参数用来指定这个路径。最终设计的参数如下:

参数 描述
infiles 指定 markdown 文件,可以是多个 md 文件
–style, -s 指定要使用的 css 文件
–outdir, -o 指定文件输出目录

这里使用 python3 的官方库 argparse 来实现命令参数的过滤,可以阅读 argparse 库的使用 了解这个库的基本使用,将参数过滤封装为函数 getArgs

def get_args():
    '''
    获取命令参数
    '''
    parser = argparse.ArgumentParser()
    parser.add_argument('-s', '--style', help='指定要使用的 css 样式,css文件路径')
    parser.add_argument('-o', '--outdir', help='指定 html 文件输出目录')
    parser.add_argument('infiles', nargs='+', help='指定要转换的 markdown 文件,可以指定多个')
    return parser.parse_args()

其中 infiles 参数指定nargs='+',可以得到的是一个包含大于或等于1个文件路径的列表^[python – argparse选项用于传递列表作为选项]。函数返回的就是过滤后得到的参数信息。

最终实现过程

先贴出代码:

if __name__ == '__main__':
    args = get_args()
    m2h = Markdown2Html()
    if args.style:
        m2h.setStyle(args.style)
    for mdfile in args.infiles:
        if args.outdir:
            if not os.path.exists(args.outdir):
                print(f'新建目录 {args.outdir}')
                os.makedirs(args.outdir)
            filename = os.path.splitext(os.path.basename(mdfile))[0] + '.html'
            outfile = os.path.join(args.outdir, filename)
            count = 1
            while os.path.exists(outfile):
                filename = os.path.splitext(os.path.basename(mdfile))[0] + f'_{count}.html'
                outfile = os.path.join(args.outdir, filename)
                count += 1
        else:
            outfile = os.path.splitext(mdfile)[0] + '.html'
        m2h.convert(mdfile, outfile=outfile, prettify=True)
        print(f'{mdfile} 已转换完成,保存为{outfile}')

m2h文件完整代码,参考tools-with-script/m2h/m2h

首先获取过滤参数,之后声明 Markdown2Html 类的一个实例 m2h,如果执行命令中指定了样式文件,就为实例设置这个 css 文件。枚举待转换文件列表中的文件,处理文件分以下几步:

  1. 判断是否指定了统一的输出目录

    • 如果指定统一输出目录:检查目录是否存在,若不存在就新建目录;生成 html 文件的路径名称,为了防止输出到统一路径是出现重名而覆盖文件,这里只要发现文件存在就增加后缀序号;

    • 如果没有指定统一输出目录:直接生成相应的 html 输出文件的路径即可;

  2. 调用实例 m2hconvert 方法,指定 markdown文件和输出文件,进行转换。

测试

命令使用帮助信息:

$  ./m2h -h
usage: m2h [-h] [-s STYLE] [-o OUTDIR] infiles [infiles ...]

positional arguments:
  infiles               指定要转换的 markdown 文件,可以指定多个

optional arguments:
  -h, --help            show this help message and exit
  -s STYLE, --style STYLE
                        指定要使用的 css 样式,css文件路径
  -o OUTDIR, --outdir OUTDIR
                        指定 html 文件输出目录

我这里同级目录和上一层目录各有一个 README.md 文件,这里尝试转换并输出到同级目录下的 output 目录下,注意现在 output 目录并不存在,并指定 github.css 作为样式文件,执行命令如下:

$ ./m2h README.md ../README.md -s github.css -o ./output
新建目录 ./output
README.md 已转换完成,保存为./output/README.html
../README.md 已转换完成,保存为./output/README_1.html

总结

本文学习了 markdown 库的使用,同时练习了类的编写和使用。工具的完整文件见:m2h


python

2644 字

2018-10-16 17:44 +0800