在制作网页的时候,有时需要将在网页上显示md文件,但是将Md文件嵌入HTML的操作极为繁琐,或者遇到某些网站为了安全和用户隐私禁用了JS,这时就需要将Md文件转换为HTML了。而这个脚本解决了这个问题。而且支持调整缩进大小,支持GUI的同时支持命令行,便于批量操作。
下面是要安装的库
pip install markdown
pip install bs4
import os
import re
import sys
#GUI库
import tkinter as tk
import tkinter.filedialog as filedialog
import tkinter.messagebox as messagebox
import tkinter.ttk as ttk
import markdown
from bs4 import BeautifulSoup
Markdown是一种轻量级标记语言,它以纯文本形式(易读、易写、易更改)编写文档,并最终以HTML格式发布。下面是md和HTML对照表,我们只需要根据对照表将MD语法替换为HTML并进行缩进就可以完成基本的转化。Markdown 是一种轻量级的标记语言,可用于在纯文本文档中添加格式化元素。Markdown 由 John Gruber 于 2004 年创建,如今已成为世界上最受欢迎的标记语言之一。
使用 Markdown 与使用 Word 类编辑器不同。在 Word 之类的应用程序中,单击按钮以设置单词和短语的格式,并且,更改立即可见。而 Markdown 与此不同,当你创建 Markdown 格式的文件时,可以在文本中添加 Markdown 语法,以指示哪些单词和短语看起来应该有所不同。
例如,要表示标题,只须在短语前面添加一个井号即可(例如, # Heading One)。或者要加粗一个短语,只须在短语前后各加两个星号即可(例如,this text is bold)。可能需要一段时间才能习惯在文本中看到 Markdown 语法,尤其是如果你已习惯了所见即所得的应用程序。
部分对照表:
类型 | MD | HTML |
---|---|---|
标题1 | # 标题1 |
|
标题2 | ## 标题1 |
|
标题3 | ### 标题1 |
|
标题4 | #### 标题1 |
|
标题5 | ##### 标题1 |
|
标题6 | ###### 标题1 |
|
段落 | 段落 |
|
粗体 | just love **bold text** |
just love bold text |
斜体 | Italicized text is the *cat's meow*. |
Italicized text is the cat's meow |
# 导入re模块,用于正则表达式匹配
import re
# 保存原始的BeautifulSoup.prettify方法
orig_prettify = BeautifulSoup.prettify
# 创建一个正则表达式对象,用于匹配每行开头的空白字符
r = re.compile(r"^(\s*)", re.MULTILINE)
def prettify(self, encoding=None, formatter="minimal", indent_width=2):
# 这个函数是对原始的BeautifulSoup.prettify方法的修改,用于调整缩进宽度
# 使用正则表达式替换每行开头的空白字符,乘以指定的缩进宽度
return r.sub(r"\1" * indent_width, orig_prettify(self, encoding, formatter))
# 用修改后的prettify函数替换原始的BeautifulSoup.prettify方法
BeautifulSoup.prettify = prettify
def md2html(input_text, indent=2, model=True):
# 这个函数用于将markdown文本转换为html文本,并格式化输出
# 定义一个扩展列表,包含一些markdown的扩展功能
extensions = [
"abbr",
"admonition",
"attr_list",
"codehilite",
"def_list",
"extra",
"fenced_code",
"footnotes",
"legacy_attrs",
"legacy_em",
"md_in_html",
"meta",
"nl2br",
"sane_lists",
"smarty",
"tables",
"toc",
]
# 定义一个html文档的头部,包含DOCTYPE声明和meta标签
head = """
"""
# 定义一个html文档的尾部,包含结束标签
end = """
"""
# 如果model参数为True,则在markdown文本前后添加头部和尾部,生成完整的html文档
if model:
html_text = head + markdown.markdown(input_text, extension=extensions) + end
# 如果model参数为False,则只生成markdown文本对应的html内容
else:
html_text = markdown.markdown(input_text, extensions=extensions)
# 使用BeautifulSoup解析html文本,并指定解析器为html.parser
soup = BeautifulSoup(html_text, features="html.parser")
# 使用prettify方法格式化html文本,并指定缩进宽度
formatted_text = soup.prettify(indent_width=indent)
# 返回格式化后的html文本
return formatted_text
# 定义一个类,叫FileChooser
class FileChooser:
# 定义初始化方法,创建一个tkinter变量,用来存储文件路径
def __init__(self):
self.file_path = tk.StringVar()
# 给变量添加一个属性,指向自己
self.file_path.choose_file = self.file_path
# 定义一个方法,用来弹出对话框,选择文件,并返回文件路径
def select_file(self):
# 设置变量的值为选择的文件路径
self.file_path.choose_file.set(filedialog.askopenfilename())
# 返回变量的值
return self.file_path.get()
# 定义一个方法,用来解析Markdown文件,接受文件路径,缩进,和模型作为参数,并返回HTML文本
def markdown_parser(self, file_path, indent, model):
# 打开文件,以只读和utf-8编码的方式
with open(file_path, "r", encoding="utf-8") as f:
# 调用md2html函数,把文件内容,缩进,和模型作为参数,并返回HTML文本
return md2html(f.read(), indent=indent, model=model)
# 定义一个方法,用来保存HTML文本,接受HTML文本作为参数
def to_new(self, html_text):
# 弹出对话框,让用户选择保存的文件名和类型,默认扩展名为.html
filename = filedialog.asksaveasfilename(
filetypes=[("HTML", "*.html"), ("所有文件", "*.*")], defaultextension=".html"
)
# 如果用户选择了文件名
if filename:
# 打开文件,以写入和utf-8编码的方式
with open(filename, "w", encoding="utf-8") as f:
# 把HTML文本写入文件
f.write(html_text)
然后写一个GUI,封装为GUI类。
class GUI(ttk.Frame, FileChooser):
def __init__(self, master=None):
super().__init__(master)
FileChooser.__init__(self)
self.master = master
self.master.title("Markdown转HTML")
self.master.geometry("325x100")
self.master.resizable(False, False)
self.grid()
self.create_widgets()
def create_widgets(self):
self.left_frame = ttk.Frame(self, width=200, height=200)
self.left_frame.grid(row=0, column=0, padx=10, pady=10)
self.right_frame = ttk.Frame(self, width=200, height=200)
self.right_frame.grid(row=0, column=1, padx=10, pady=10)
self.browse_button = ttk.Button(
self.left_frame, text="浏览", command=self.choose_file
)
self.browse_button.grid(row=0, column=0, sticky=tk.W)
self.file_label = ttk.Label(self.left_frame, text="请选择一个文件")
self.file_label.grid(row=1, column=0, columnspan=2, sticky=tk.W)
self.entry_var = tk.StringVar()
self.entry_var.set("请输入缩进宽度(默认为2)")
self.entry = ttk.Entry(
self.left_frame, textvariable=self.entry_var, justify=tk.CENTER
)
self.entry.grid(row=2, column=0, columnspan=2, sticky=tk.W)
self.export_button = ttk.Button(
self.right_frame, text="导出", command=self.output
)
self.export_button.grid(row=1, column=0)
self.cb = tk.BooleanVar()
self.cb.set(True)
self.check_button = ttk.Checkbutton(
self.right_frame, text="是否完整导出", variable=self.cb
)
self.check_button.grid(row=0, column=0)
def choose_file(self):
file_name = self.select_file()
chosen_file = os.path.abspath(file_name)
self.file_label.config(text=chosen_file)
def output(self):
file_chosen = self.file_label.cget("text")
if not os.path.isfile(file_chosen):
messagebox.showwarning("错误", "请先选择文件")
return ""
else:
if self.entry_var.get() == "请输入缩进宽度(默认为2)":
indent_width = 2
self.entry_var.set(indent_width)
else:
try:
indent_width = int(self.entry_var.get())
except ValueError:
messagebox.showwarning("错误", "请输入正确的缩进宽度")
return ""
model = self.cb.get()
html_text = self.markdown_parser(file_chosen, indent_width, model)
self.to_new(html_text)
最后是程序的主函数。如果在运行时没有传入任何参数,就启动GUI界面;如果传递了参数,就以命令行模式运行。
命令行下用法:
python3 xxx.py [md文件名.md] [输出HTML文件名] [缩进距离]
def main()
if len(sys.argv) == 1:
root = tk.Tk()
gui = GUI(master=root)
gui.master.mainloop()
elif len(sys.argv) != 4 and len(sys.argv) != 5:
sys.stderr.write(
f"Usage: {sys.argv[0]} input_file output_file completeness(CPL/INC) indent_width(Optional, default 2)"
)
sys.exit(1)
else:
input_file = sys.argv[1]
output_file = sys.argv[2]
model = sys.argv[3] == "CPL"
indent_width = int(sys.argv[4]) if len(sys.argv) == 5 else 2
try:
with open(input_file, "r", encoding="utf-8") as in_file:
program = in_file.read()
except IOError:
sys.stderr.write(f"Cannot open input file: {input_file}")
sys.exit(2)
try:
with open(output_file, "w", encoding="utf-8") as out_file:
out_file.write(md2html(program, indent=indent_width, model=model))
except IOError:
print(f"Cannot open output file: {output_file}")
sys.exit(3)
sys.exit(0)
if __name__ =="__main__":
main()
感谢你的观看
欢迎大家尝试作者的另一个工具密码强度检测
下面是整个程序源代码:
import os
import re
import tkinter as tk
import tkinter.ttk as ttk
import tkinter.filedialog as filedialog
import tkinter.messagebox as messagebox
import sys
import markdown
from bs4 import BeautifulSoup
orig_prettify = BeautifulSoup.prettify
r = re.compile(r"^(\s*)", re.MULTILINE)
def prettify(self, encoding=None, formatter="minimal", indent_width=2):
return r.sub(r"\1" * indent_width, orig_prettify(self, encoding, formatter))
BeautifulSoup.prettify = prettify
def md2html(input_text, indent=2, model=True):
extensions = [
"abbr",
"admonition",
"attr_list",
"codehilite",
"def_list",
"extra",
"fenced_code",
"footnotes",
"legacy_attrs",
"legacy_em",
"md_in_html",
"meta",
"nl2br",
"sane_lists",
"smarty",
"tables",
"toc",
]
head = """
"""
end = """
"""
if model:
html_text = head + markdown.markdown(input_text, extension=extensions) + end
else:
html_text = markdown.markdown(input_text, extensions=extensions)
soup = BeautifulSoup(html_text, features="html.parser")
formatted_text = soup.prettify(indent_width=indent)
return formatted_text
class FileChooser:
def __init__(self):
self.file_path = tk.StringVar()
self.file_path.choose_file = self.file_path
def select_file(self):
self.file_path.choose_file.set(filedialog.askopenfilename())
return self.file_path.get()
def markdown_parser(self, file_path, indent, model):
with open(file_path, "r", encoding="utf-8") as f:
return md2html(f.read(), indent=indent, model=model)
def to_new(self, html_text):
filename = filedialog.asksaveasfilename(
filetypes=[("HTML", "*.html"), ("所有文件", "*.*")], defaultextension=".html"
)
if filename:
with open(filename, "w", encoding="utf-8") as f:
f.write(html_text)
class GUI(ttk.Frame, FileChooser):
def __init__(self, master=None):
super().__init__(master)
FileChooser.__init__(self)
self.master = master
self.master.title("Markdown转HTML")
self.master.geometry("325x100")
self.master.resizable(False, False)
self.grid()
self.create_widgets()
def create_widgets(self):
self.left_frame = ttk.Frame(self, width=200, height=200)
self.left_frame.grid(row=0, column=0, padx=10, pady=10)
self.right_frame = ttk.Frame(self, width=200, height=200)
self.right_frame.grid(row=0, column=1, padx=10, pady=10)
self.browse_button = ttk.Button(
self.left_frame, text="浏览", command=self.choose_file
)
self.browse_button.grid(row=0, column=0, sticky=tk.W)
self.file_label = ttk.Label(self.left_frame, text="请选择一个文件")
self.file_label.grid(row=1, column=0, columnspan=2, sticky=tk.W)
self.entry_var = tk.StringVar()
self.entry_var.set("请输入缩进宽度(默认为2)")
self.entry = ttk.Entry(
self.left_frame, textvariable=self.entry_var, justify=tk.CENTER
)
self.entry.grid(row=2, column=0, columnspan=2, sticky=tk.W)
self.export_button = ttk.Button(
self.right_frame, text="导出", command=self.output
)
self.export_button.grid(row=1, column=0)
self.cb = tk.BooleanVar()
self.cb.set(True)
self.check_button = ttk.Checkbutton(
self.right_frame, text="是否完整导出", variable=self.cb
)
self.check_button.grid(row=0, column=0)
def choose_file(self):
file_name = self.select_file()
chosen_file = os.path.abspath(file_name)
self.file_label.config(text=chosen_file)
def output(self):
file_chosen = self.file_label.cget("text")
if not os.path.isfile(file_chosen):
messagebox.showwarning("错误", "请先选择文件")
return ""
else:
if self.entry_var.get() == "请输入缩进宽度(默认为2)":
indent_width = 2
self.entry_var.set(indent_width)
else:
try:
indent_width = int(self.entry_var.get())
except ValueError:
messagebox.showwarning("错误", "请输入正确的缩进宽度")
return ""
model = self.cb.get()
html_text = self.markdown_parser(file_chosen, indent_width, model)
self.to_new(html_text)
def main()
if len(sys.argv) == 1:
root = tk.Tk()
gui = GUI(master=root)
gui.master.mainloop()
elif len(sys.argv) != 4 and len(sys.argv) != 5:
sys.stderr.write(
f"Usage: {sys.argv[0]} input_file output_file completeness(CPL/INC) indent_width(Optional, default 2)"
)
sys.exit(1)
else:
input_file = sys.argv[1]
output_file = sys.argv[2]
model = sys.argv[3] == "CPL"
indent_width = int(sys.argv[4]) if len(sys.argv) == 5 else 2
try:
with open(input_file, "r", encoding="utf-8") as in_file:
program = in_file.read()
except IOError:
sys.stderr.write(f"Cannot open input file: {input_file}")
sys.exit(2)
try:
with open(output_file, "w", encoding="utf-8") as out_file:
out_file.write(md2html(program, indent=indent_width, model=model))
except IOError:
print(f"Cannot open output file: {output_file}")
sys.exit(3)
sys.exit(0)
if __name__ =="__main__":
main()
谢谢你看到最后,制作不易,点个赞不过分吧!(>_O)