python3 实现Markdown转html小工具
写博客文章一直使用 markdown,markdown 是一个轻量级的标记语言,可以快速创建待格式的简单文档。typra 是一款跨平台而且设计极佳的 markdown 写作工具。markdown 本质上算是简化版的 html。那么,如何自己编写一个 markdown 转换工具呢?本文将带你一起用python3 实现一个 markdown 转 html 的命令行工具,用浏览器查看 html 就能看到带格式的文章了。
准备工作
在开始之前,要安装一些第三方库。
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 格式的文本。
注意
- markdwon 方法返回的结果是 html 文本,但不包含头信息
<head>
,所以后面如果使用的话应该给结果加上,保证html文本的兼容性; - markdwon 方法可以指定输出格式,比如按照 html5 的格式转换;
- 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 类
-
这个类在文件 markdown2html.py 中实现,所以首先要新建这个文件。
-
导入要用到的库
import markdown import os.path as op from bs4 import BeautifulSoup
-
定义类及初始化函数,因为最终转换要生成 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 文件,具体见下一步。 -
实现 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
信息。 -
实现 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
,是为了保证跨平台编码统一,提高兼容性。 -
编写以下代码进行测试,同级目录下存储的
github.css
文件,所以这里指定 css文件 转换README.md
文件:if __name__ == '__main__': m2h = Markdown2Html('github.css') m2h.convert('./README.md', './README.html')
运行:
python3 markdown2html.py
结果:
Markdown2Html 类完成!
脚本
有了上面的 Markdown2Html 类,现在可以编写脚本命令了。
准备文件
-
新建名为
m2h
的文件,添加以下内容,指明脚本解释器为python3
:#!/usr/bin/env python3
-
执行以下命令,为文件添加运行权限:
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 文件。枚举待转换文件列表中的文件,处理文件分以下几步:
-
判断是否指定了统一的输出目录
-
如果指定统一输出目录:检查目录是否存在,若不存在就新建目录;生成 html 文件的路径名称,为了防止输出到统一路径是出现重名而覆盖文件,这里只要发现文件存在就增加后缀序号;
-
如果没有指定统一输出目录:直接生成相应的 html 输出文件的路径即可;
-
-
调用实例
m2h
的convert
方法,指定 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