Python 实现 Markdown 转 Word(docx)文档转换方案
在实际开发中,经常会遇到将 Markdown 文档转换为 Word(.docx)的需求,例如技术文档从 Markdown 迁移到 Word,或自动生成可下载的 Word 报告。
本文基于 python-docx、markdown 和 BeautifulSoup 库,实现一个不依赖接口、直接读取 Markdown 文件并生成 Word 文件的完整方案,支持常见 Markdown 语法。
一、实现思路说明
整体转换流程如下:
- 使用 markdown 库将 Markdown 文本转换为 HTML
- 使用 BeautifulSoup 解析 HTML 结构
- 遍历 HTML 节点,映射为 Word 中的对应元素
- 使用 python-docx 生成并保存 .docx 文件
支持的 Markdown 元素包括:
- 标题(h1–h6)
- 段落
- 无序 / 有序列表
- 代码块
- 行内代码、加粗、斜体
- 表格
二、依赖安装
pip install python-docx markdown beautifulsoup4
三、完整代码实现
from docx import Document
from docx.shared import Pt
from bs4 import BeautifulSoup
import markdown
import uuid
import os
FILE_DIR = "static"
os.makedirs(FILE_DIR, exist_ok=True)
def md_to_word(md_text: str) -> str:
""" 将 Markdown 文本转换为 Word,返回生成的文件名 """
html = markdown.markdown(
md_text, extensions=["extra", "tables", "fenced_code"]
)
soup = BeautifulSoup(html, "html.parser")
doc = Document()
for element in soup.contents:
handle_element(doc, element)
filename = f"{uuid.uuid4().hex}.docx"
file_path = os.path.join(FILE_DIR, filename)
doc.save(file_path)
return filename
():
(el, ) el.name :
el.name [, , , , , ]:
doc.add_heading(el.get_text(), level=(el.name[]))
el.name == :
p = doc.add_paragraph()
add_inline_text(p, el)
el.name == :
li el.find_all(, recursive=):
p = doc.add_paragraph(style=)
add_inline_text(p, li)
el.name == :
li el.find_all(, recursive=):
p = doc.add_paragraph(style=)
add_inline_text(p, li)
el.name == :
code_el = el.find()
code = code_el.get_text() code_el el.get_text()
p = doc.add_paragraph()
run = p.add_run(code)
run.font.name =
run.font.size = Pt()
el.name == :
rows = el.find_all()
cols = rows[].find_all([, ])
table = doc.add_table(rows=(rows), cols=(cols))
table.style =
r, row (rows):
c, cell (row.find_all([, ])):
paragraph = table.rows[r].cells[c].paragraphs[]
run = paragraph.add_run(cell.get_text())
cell.name == :
run.bold =
():
node el.contents:
(node, ):
paragraph.add_run(node)
:
run = paragraph.add_run(node.get_text())
node.name == :
run.bold =
node.name == :
run.italic =
node.name == :
run.font.name =
run.font.size = Pt()
() -> :
(md_file_path, , encoding=) f:
md_text = f.read()
md_to_word(md_text)
__name__ == :
md_path =
filename = md_to_word_from_file(md_path)
(, filename)


