使用 Python 3 编写系列实用脚本
课程简介:本课程教授如何使用 Python 3 编写一个转换 markdown 为 HTML 的脚本文件,这个脚本能够进行批量处理操作,可以自定义 HTML 的 CSS 样式(默认没有样式),可以自定义输出目录(默认为 markdown 文件所在目录)
本课程由 FrostSigh 发布在 实验楼 ,完整教程及在线练习地址: 使用 Python 3 编写系列实用脚本
1. Markdown to HTML
2. 文本文件编码检测与转换
3. 文件校验
第一部分 Markdown to HTML
一、实验简介
1.1 知识点
- Python 3 的模块的安装
- Python 3 基础知识
- 使用os模块进行路径相关的操作
- markdown模块的使用
- 使用 BeautifulSoup模块格式化 HTML
- 命令行选项的实现
二、实验步骤
2.1 安装包
首先更新一下软件源
$ sudo apt-get update
安装 pip3,pip 是 Python2 的软件包管理系统,使用它来安装 Python2 的模块非常简便,而 pip3 则是对应于 Python3 的版本
$ sudo apt-get install python3-pip
安装 markdown,本实验使用这个模块编译 Markdown 文件
$ sudo pip3 install markdown
安装 BeautifulSoup,本实验使用 BeautifulSoup 仅是为了格式化 HTML 代码为可读性更强的代码,不需要可以不安装
$ sudo pip3 install beautifulsoup4
2.2 编写 MarkdownToHtml 类
类名定义为 MarkdownToHtml 吧
class MarkdownToHtml:
我们的类有4个属性,3个方法属性,1个数据属性
因为 markdown 模块生成的 HTML 代码是不包括 head 标签的,这样会可能造成浏览器中渲染 HTML 时显示乱码,而且我们有时可能也需要添加 CSS 来美化网页
所以我们需要一个数据属性存放生成 HTML 代码时用到的 head 标签以及准备存放可能用到的 style 标签
headTag = '<head><meta charset="utf-8" /></head>'
构造方法,有一个参数 cssFilePath ,默认值为 None
def __init__(self,cssFilePath = None):
if cssFilePath != None:
self.genStyle(cssFilePath)
方法 genStyle() 读取外部 CSS 文件的内容并存放到 headTag 变量中
关于文件读写操作,最常见的是这样的操作:
f = open(cssFilePath,'r')
cssString = f.read()
f.close()
然而更推荐使用关键字 with 处理文件对象,它的先进之处在于文件用完后会自动关闭,就算发生异常也没关系。它是 try finally 块的简写
with open(cssFilePath,'r') as f:
cssString = f.read()
因此,方法 genStyle() 的代码如下
def genStyle(self,cssFilePath):
with open(cssFilePath,'r') as f:
cssString = f.read()
self.headTag = self.headTag[:-7] + '<style type="text/css">' + cssString + '</style></head>'
接下来是方法 markdownToHtml() ,作用是编译 Markdown 输出 HTML
有3个参数,分别是 Markdown 源码路径,输出文件目录(可选),输出文件名称(可选)
def markdownToHtml(self, sourceFilePath, destinationDirectory = None, outputFileName = None):
若是方法调用时没指定输出目录和输出文件名,那么我们将默认使用源文件目录和源文件名
if not destinationDirectory:
# 未定义输出目录则将源文件目录(注意要转换为绝对路径)作为输出目录
destinationDirectory = os.path.dirname(os.path.abspath(sourceFilePath))
if not outputFileName:
# 未定义输出文件名则沿用输入文件名
outputFileName = os.path.splitext(os.path.basename(sourceFilePath))[0] + '.html'
os.path.abspath() 将参数路径转为绝对路径并返回
os.path.dirname() 获得参数路径的目录部分并返回(如 "\home\a.txt" 为参数,返回 "\home")
os.path.basename() 返回参数路径字符串中的完整文件名(文件名+后缀名)
os.path.splitext() 将参数转换为包含文件名和后缀名两个元素的元组并返回
关于 os 模块更多详情,参见 官方文档
还有一个问题,输出文件的路径我们通过拼接 destinationDirectory,outputFileName 这两个变量中的字符串得到,若是变量 destinationDirectory 中的字符串并不以 '/' 字符结尾,那么输出文件的路径会是错误的,所以需要处理一下:
if destinationDirectory[-1] != '/':
destinationDirectory += '/'
然后读取源文件
文件打开函数 open() 有一个参数是 encoding ,若是不指定它,那么将会默认使用平台的编码,而在 Windows 命令行里默认是 GBK 编码的,对于使用 UTF-8 编码格式的 Mardown 文件将会出错,因此为了在任意平台保证脚本正确,无论是读取 Markdown 还是之后的写入 HTML 文件都需要传入这个参数 encoding='utf8'
with open(sourceFilePath,'r', encoding='utf8') as f:
markdownText = f.read()
之后使用 markdown.markdown() 这个函数编译 markdownText 变量中的 Markdown 字符串
markdown.markdown() 有一个位置参数和若干可选参数,除了 markdown 字符串,我们还要用到参数 output_format 来设定输出 HTML 的版本
另外,前面说过 markdown 模块生成的 HTML 代码是不包括 head 标签的,需要加上它
rawHtml = self.headTag + markdown.markdown(markdownText,output_format='html5')
把变量 rawHtml 中的 HTML 的字符串写入文件
with open(destinationDirectory + outputFileName, 'w', encoding='utf8') as f:
f.write(rawHtml)
有一个可选的步骤是,把上面一段代码改成下面这段,因为 markdown 模块生成的 HTML 代码可阅读性很差,可以把 rawHtml 中的 HTML 代码用 BeautifulSoup 模块格式化为可读性更强的代码
beautifyHtml = BeautifulSoup(rawHtml,'html5lib').prettify()
with open(destinationDirectory + outputFileName, 'w', encoding='utf8') as f:
f.write(beautifyHtml)
至此,方法 markdownToHtml() 编写完成,类 MarkdownToHtml 编写完成
2.3 完成脚本
上一小节完成了 MarkdownToHtml 类,这一节将完成其它部分
需要在脚本文件开头导入模块,建议把自带模块放在第三方模块前面
import sys
import os
# 不使用 BeautifulSoup 模块可不导入
from bs4 import BeautifulSoup
import markdown
脚本文件支持 3 种命令行参数,分别是一个到多个 Markdown 文件路径,选项 -s 和选项 -o,
选项 -s 是 CSS 文件路径,选项 -o 是输出目录
剩下的代码所做的工作是这样的:根据命令行参数来使用类 MarkdownToHtml 的实例编译一个到多个 Markdown 源文件
本实验的剩余代码除了用到了下面两个函数,只是运用 Python 的基本语法来处理一些逻辑,这里不会细讲(给出的代码有较为详细的注释),各位同学可以自己先尝试来编写这一部分代码
os.path.isfile() 检测参数是否为文件路径,是返回 True,否则返回 False
os.path.isdir() 检测参数是否为目录,是返回 True,否则返回 False
本实验剩余的所有代码如下:
if __name__ == "__main__":
mth = MarkdownToHtml()
# 做一个命令行参数列表的浅拷贝,不包含脚本文件名
argv = sys.argv[1:]
# 目前列表 argv 可能包含源文件路径之外的元素(即选项信息)
# 程序最后遍历列表 argv 进行编译 markdown 时,列表中的元素必须全部是源文件路径
outputDirectory = None
if '-s' in argv:
cssArgIndex = argv.index('-s') +1
cssFilePath = argv[cssArgIndex]
# 检测样式表文件路径是否有效
if not os.path.isfile(cssFilePath):
print('Invalid Path: '+cssFilePath)
sys.exit()
mth.genStyle(cssFilePath)
# pop 顺序不能随意变化
argv.pop(cssArgIndex)
argv.pop(cssArgIndex-1)
if '-o' in argv:
dirArgIndex = argv.index('-o') +1
outputDirectory = argv[dirArgIndex]
# 检测输出目录是否有效
if not os.path.isdir(outputDirectory):
print('Invalid Directory: ' + outputDirectory)
sys.exit()
# pop 顺序不能随意变化
argv.pop(dirArgIndex)
argv.pop(dirArgIndex-1)
# 至此,列表 argv 中的元素均是源文件路径
# 遍历所有源文件路径
for filePath in argv:
# 判断文件路径是否有效
if os.path.isfile(filePath):
mth.markdownToHtml(filePath, outputDirectory)
else: