跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客我的书AI学习GitHub 精选镜像AI工具UI配色美学关于
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
Python

Python 实现 Markdown 转 Word 文档(markdown+python-docx 方案)

使用 Python 的 markdown 和 python-docx 库将 Markdown 文件转换为 Word 文档。核心流程是先解析 MD 为 HTML,再渲染至 Word。提供基础版和增强版代码,支持标题、列表、表格、图片等语法。关键优化包括中文字体设置、扩展语法启用及异常处理。此外还推荐了 pandoc 作为无代码替代方案。

不羁发布于 2026/3/15更新于 2026/10/24104 浏览

Python 实现 Markdown 转 Word(markdown+python-docx 方案)

一、核心方案说明

要实现 Markdown 文件 → Word(.docx) 文件 的格式转换,核心思路是: ✅ 先用 python-markdown 库把 Markdown 文本/文件解析成 HTML 格式; ✅ 再用 python-docx 库将解析后的 HTML 内容,逐节点渲染到 Word 文档中,完成最终转换。

二、完整环境安装(一键执行)

该方案依赖 3 个核心库,直接在终端执行以下命令安装所有依赖:

pip install python-markdown python-docx beautifulsoup4 
  • python-markdown:核心 Markdown 解析库,负责 MD → HTML;
  • python-docx:核心 Word 操作库,负责生成/编辑 .docx 文档;
  • beautifulsoup4:辅助解析 HTML 节点,方便精准提取内容渲染到 Word。

三、完整可运行代码(直接复用)

版本 1:基础版(支持绝大多数 MD 语法,满足日常需求)

支持标题(1-6 级)、段落、加粗、斜体、有序列表、无序列表、超链接、图片、换行等核心语法,代码可直接复制运行:

import markdown
from docx import Document
from docx.shared import Pt, Inches
from docx.enum.text import WD_ALIGN_PARAGRAPH, WD_PARAGRAPH_ALIGNMENT
from docx.oxml.ns import qn
from bs4 import BeautifulSoup
import os

def markdown_to_word(md_file_path, docx_file_path=None):
    """ Markdown 文件转 Word 文档核心函数
    :param md_file_path: 源 Markdown 文件路径(必填,如:./test.md)
    :param docx_file_path: 输出 Word 文件路径(可选,默认同目录同名.docx)
    """
    # 1. 校验源文件是否存在
    if not os.path.exists(md_file_path):
        print(f"错误:源文件 {md_file_path} 不存在!")
        return
    # 2. 默认输出路径(同目录、同名,后缀替换为.docx)
     docx_file_path  :
        docx_file_path = os.path.splitext(md_file_path)[]+
    
     (md_file_path,, encoding=)  f:
        md_content = f.read()
    
    html_content = markdown.markdown(
        md_content,
        extensions=[, , ],
        extension_configs={}
    )
    
    doc = Document()
    
    doc.styles[].font.name = 
    doc.styles[]._element.rPr.rFonts.(qn(),)
    doc.styles[].font.size = Pt()
    
    soup = BeautifulSoup(html_content,)
    parse_html_node(soup, doc)
    
    doc.save(docx_file_path)
    ()

 ():
    
    
     node.name  [  i  (,)]:
        level = (node.name[])
        p = doc.add_paragraph()
        run = p.add_run(node.get_text(strip=))
        
        run.font.size = Pt(- level *)
        run.font.bold = 
        run.font.name = 
        run._element.rPr.rFonts.(qn(),)
        p.alignment = WD_ALIGN_PARAGRAPH.LEFT
    
     node.name == :
        p = doc.add_paragraph()
        parse_inline_content(node, p)
    
     node.name == :
         li  node.find_all(, recursive=):
            p = doc.add_paragraph(style=)
            parse_inline_content(li, p)
    
     node.name == :
         li  node.find_all(, recursive=):
            p = doc.add_paragraph(style=)
            parse_inline_content(li, p)
    
     node.name == :
        doc.add_paragraph()
    
     child  node.children:
         child.name:
            parse_html_node(child, doc)

 ():
    
     content  node.contents:
         (content,):
            
            run = paragraph.add_run(content)
            run.font.name = 
            run._element.rPr.rFonts.(qn(),)
            run.font.size = Pt()
         content.name == :
            
            run = paragraph.add_run(content.get_text())
            run.font.bold = 
            run.font.name = 
            run._element.rPr.rFonts.(qn(),)
         content.name == :
            
            run = paragraph.add_run(content.get_text())
            run.font.italic = 
            run.font.name = 
            run._element.rPr.rFonts.(qn(),)
         content.name == :
            
            text = content.get_text()
            link = content.get(,)
            run = paragraph.add_run()
            run.font.color.rgb = 
            run.font.name = 
            run._element.rPr.rFonts.(qn(),)


 __name__ == :
    
    INPUT_MD_FILE = 
    
    OUTPUT_DOCX_FILE = 
    markdown_to_word(INPUT_MD_FILE, OUTPUT_DOCX_FILE)
if
is
None
0
".docx"
# 3. 读取 Markdown 文件内容(指定 UTF-8 编码,避免中文乱码)
with
open
"r"
"utf-8"
as
# 4. Markdown → HTML(启用扩展,支持更完整语法)
'extra'
'sane_lists'
'nl2br'
# 5. 初始化 Word 文档对象
# 6. 全局样式配置(统一字体、避免中文宋体异常)
'Normal'
'宋体'
'Normal'
set
'w:eastAsia'
'宋体'
'Normal'
12
# 7. 解析 HTML 并渲染到 Word
"html.parser"
# 8. 保存 Word 文档
print
f"转换成功!Word 文件已保存至:{docx_file_path}"
def
parse_html_node
node, doc
"""递归解析 HTML 节点,映射为 Word 对应样式"""
# 处理标题(h1-h6)
if
in
f'h{i}'
for
in
range
1
7
int
1
True
# 标题样式:字号随级别递减,加粗
20
2
True
'黑体'
set
'w:eastAsia'
'黑体'
# 处理段落
elif
'p'
# 处理无序列表
elif
'ul'
for
in
'li'
False
'List Bullet'
# 处理有序列表
elif
'ol'
for
in
'li'
False
'List Number'
# 处理换行
elif
'br'
# 递归处理子节点(兼容嵌套结构)
for
in
if
def
parse_inline_content
node, paragraph
"""解析行内元素(加粗、斜体、超链接等),添加到指定段落"""
for
in
if
isinstance
str
# 纯文本内容
'宋体'
set
'w:eastAsia'
'宋体'
12
elif
'strong'
# 加粗文本(MD:**内容**)
True
'宋体'
set
'w:eastAsia'
'宋体'
elif
'em'
# 斜体文本(MD:*内容*)
True
'宋体'
set
'w:eastAsia'
'宋体'
elif
'a'
# 超链接(MD:[文本](链接))
'href'
''
f"{text}({link})"
None
'宋体'
set
'w:eastAsia'
'宋体'
# ------------------- 调用示例 -------------------
if
"__main__"
# 替换为你的 Markdown 文件路径(相对路径/绝对路径均可)
"./test.md"
# 可选:指定输出 Word 路径,如 "./转换结果.docx"
None
版本 2:增强版(额外支持 表格、代码块、图片 语法)

日常使用中表格、代码块、图片是高频 MD 语法,在基础版上扩展该能力,满足更复杂的转换需求:

import markdown
from docx import Document
from docx.shared import Pt, Inches, RGBColor
from docx.enum.text import WD_ALIGN_PARAGRAPH
from docx.enum.table import WD_TABLE_ALIGNMENT, WD_ALIGN_VERTICAL
from docx.oxml.ns import qn
from bs4 import BeautifulSoup
import os

def markdown_to_word(md_file_path, docx_file_path=None):
    if not os.path.exists(md_file_path):
        print(f"错误:源文件 {md_file_path} 不存在!")
        return
    if docx_file_path is None:
        docx_file_path = os.path.splitext(md_file_path)[0]+".docx"
    with open(md_file_path,"r", encoding="utf-8") as f:
        md_content = f.read()
    # 启用表格、代码块扩展
    html_content = markdown.markdown(
        md_content,
        extensions=['extra','sane_lists','nl2br','codehilite'],
        extension_configs={}
    )
    doc = Document()
    doc.styles['Normal'].font.name = '宋体'
    doc.styles['Normal']._element.rPr.rFonts.set(qn('w:eastAsia'),'宋体')
    doc.styles['Normal'].font.size = Pt(12)
    soup = BeautifulSoup(html_content,"html.parser")
    parse_html_node(soup, doc)
    doc.save(docx_file_path)
    print(f"转换成功!Word 文件已保存至:{docx_file_path}")

def parse_html_node(node, doc):
    # 基础节点(标题、段落、列表)- 同基础版,此处省略,完整代码包含
    if node.name in [f'h{i}' for i in range(1,7)]:
        level = int(node.name[1])
        p = doc.add_paragraph()
        run = p.add_run(node.get_text(strip=True))
        run.font.size = Pt(20- level *2)
        run.font.bold = True
        run.font.name = '黑体'
        run._element.rPr.rFonts.set(qn('w:eastAsia'),'黑体')
    elif node.name == 'p':
        p = doc.add_paragraph()
        parse_inline_content(node, p)
    elif node.name == 'ul':
        for li in node.find_all('li', recursive=False):
            p = doc.add_paragraph(style='List Bullet')
            parse_inline_content(li, p)
    elif node.name == 'ol':
        for li in node.find_all('li', recursive=False):
            p = doc.add_paragraph(style='List Number')
            parse_inline_content(li, p)
    # 新增:处理表格
    elif node.name == 'table':
        rows = node.find_all('tr')
        row_count = len(rows)
        col_count = len(rows[0].find_all(['th','td'])) if row_count > 0 else 0
        if row_count == 0 or col_count == 0:
            return
        # 创建 Word 表格
        table = doc.add_table(rows=row_count, cols=col_count)
        table.alignment = WD_TABLE_ALIGNMENT.CENTER
        for r_idx, row in enumerate(rows):
            cells = row.find_all(['th','td'])
            for c_idx, cell in enumerate(cells):
                tc = table.cell(r_idx, c_idx)
                tc.vertical_alignment = WD_ALIGN_VERTICAL.CENTER
                p = tc.paragraphs[0]
                parse_inline_content(cell, p)
                # 表头样式加粗
                if cell.name == 'th':
                    for run in p.runs:
                        run.font.bold = True
    # 新增:处理代码块
    elif node.name == 'pre':
        code_node = node.find('code')
        if code_node:
            p = doc.add_paragraph()
            run = p.add_run(code_node.get_text())
            run.font.name = 'Consolas' # 代码专用等宽字体
            run.font.size = Pt(10)
            run.font.color.rgb = RGBColor(0,0,0) # 黑色
    # 新增:处理图片(MD:![描述](图片路径))
    elif node.name == 'img':
        img_src = node.get('src','')
        img_alt = node.get('alt','图片')
        if os.path.exists(img_src):
            try:
                doc.add_picture(img_src, width=Inches(4)) # 限制图片宽度
                p = doc.add_paragraph(img_alt)
                p.alignment = WD_ALIGN_PARAGRAPH.CENTER
            except Exception as e:
                doc.add_paragraph(f"图片加载失败:{img_src} | 错误:{str(e)}")
        else:
            doc.add_paragraph(f"图片不存在:{img_src}(描述:{img_alt})")
    for child in node.children:
        if child.name:
            parse_html_node(child, doc)

def parse_inline_content(node, paragraph):
    # 行内元素(加粗、斜体、超链接)- 同基础版
    for content in node.contents:
        if isinstance(content,str):
            run = paragraph.add_run(content)
            run.font.name = '宋体'
            run._element.rPr.rFonts.set(qn('w:eastAsia'),'宋体')
            run.font.size = Pt(12)
        elif content.name == 'strong':
            run = paragraph.add_run(content.get_text())
            run.font.bold = True
        elif content.name == 'em':
            run = paragraph.add_run(content.get_text())
            run.font.italic = True
        elif content.name == 'a':
            text = content.get_text()
            link = content.get('href','')
            run = paragraph.add_run(f"{text}({link})")

# ------------------- 调用示例 -------------------
if __name__ == "__main__":
    INPUT_MD_FILE = "./test.md" # 替换为你的 MD 文件路径
    markdown_to_word(INPUT_MD_FILE)

四、使用方法(3 步极简操作)

步骤 1:准备源文件

将需要转换的 Markdown 文件(如 test.md)放在代码同目录下,或填写绝对路径(如 C:/文档/我的笔记.md)。

步骤 2:修改文件路径

在代码末尾的 if __name__ == "__main__": 块中,替换 INPUT_MD_FILE 为你的 MD 文件路径:

INPUT_MD_FILE = "./你的文件.md" # 相对路径
# 或 INPUT_MD_FILE = "D:/project/note.md" # 绝对路径
步骤 3:运行代码

直接执行该 Python 文件,终端输出 转换成功! 即完成,转换后的 Word 文件会保存在同目录(默认)或你指定的路径下。

五、关键优化点(解决常见坑)

✅ 坑 1:中文乱码/字体异常
  • 解决方案:为所有文字节点指定中文字体(宋体/黑体),通过 qn('w:eastAsia') 强制生效;
  • 核心代码:run._element.rPr.rFonts.set(qn('w:eastAsia'), '宋体')。
✅ 坑 2:Markdown 语法解析不全
  • 解决方案:启用 python-markdown 的扩展库(extra/codehilite),支持表格、代码块、脚注等扩展语法。
✅ 坑 3:列表嵌套/格式错乱
  • 解决方案:用 recursive=False 限制列表子节点解析层级,配合 Word 内置的 List Bullet/List Number 样式,保证列表格式统一。
✅ 坑 4:图片加载失败
  • 解决方案:增加图片路径校验,若路径不存在则在 Word 中提示错误信息,避免程序崩溃。

六、支持的 Markdown 语法清单

✅ 基础语法(基础版 + 增强版均支持)
  1. 标题:# 一级标题 ~ ###### 六级标题
  2. 段落:自然换行/空行分隔
  3. 加粗:**加粗内容**
  4. 斜体:*斜体内容*
  5. 无序列表:- 列表项 1 / * 列表项 1
  6. 有序列表:1. 列表项 1 / 2. 列表项 2
  7. 超链接:[链接文本](链接地址)
✅ 扩展语法(仅增强版支持)
  1. 表格:| 表头 1 | 表头 2 | + |---|---| + | 内容 1 | 内容 2 |
  2. 代码块:python 代码内容
  3. 图片:![图片描述](图片本地路径)
  4. 换行:\n 或 <br>

七、备选方案(更轻量化,一行命令转换)

如果需要极简、无代码的转换方案,推荐使用成熟工具 pandoc,比手动开发的脚本支持的语法更全、兼容性更强:

1.安装 pandoc
  • Windows:winget install pandoc
  • Mac:brew install pandoc
  • Linux:sudo apt install pandoc
2.一行命令转换
pandoc -s 你的文件.md -o 输出文件.docx 

✅ 优势:支持所有 Markdown 语法(公式、脚注、目录、引用等),无需编写代码,转换速度极快。

总结

  1. 手动开发方案(python-markdown + python-docx):适合需要自定义转换规则(如字体、样式、格式)的场景,代码可灵活扩展;
  2. 工具方案(pandoc):适合快速批量转换,无需开发,开箱即用,兼容性最优;
  3. 核心避坑:处理中文时必须指定中文字体 + UTF-8 编码,解析列表时限制递归层级。

两种方案均可满足 Markdown → Word 的转换需求,可根据实际场景选择。

目录

  1. Python 实现 Markdown 转 Word(markdown+python-docx 方案)
  2. 一、核心方案说明
  3. 二、完整环境安装(一键执行)
  4. 三、完整可运行代码(直接复用)
  5. 版本 1:基础版(支持绝大多数 MD 语法,满足日常需求)
  6. ------------------- 调用示例 -------------------
  7. 版本 2:增强版(额外支持 表格、代码块、图片 语法)
  8. ------------------- 调用示例 -------------------
  9. 四、使用方法(3 步极简操作)
  10. 步骤 1:准备源文件
  11. 步骤 2:修改文件路径
  12. 或 INPUTMDFILE = "D:/project/note.md" # 绝对路径
  13. 步骤 3:运行代码
  14. 五、关键优化点(解决常见坑)
  15. ✅ 坑 1:中文乱码/字体异常
  16. ✅ 坑 2:Markdown 语法解析不全
  17. ✅ 坑 3:列表嵌套/格式错乱
  18. ✅ 坑 4:图片加载失败
  19. 六、支持的 Markdown 语法清单
  20. ✅ 基础语法(基础版 + 增强版均支持)
  21. ✅ 扩展语法(仅增强版支持)
  22. 七、备选方案(更轻量化,一行命令转换)
  23. 1.安装 pandoc
  24. 2.一行命令转换
  25. 总结

更多推荐文章

查看全部
  • Flutter 应用架构演进:从 v1.0 基础骨架到 v2.0 Riverpod 实战
  • VR、具身智能与人形机器人:构建现实世界的智能接口
  • MySQL 动态分区管理:自动化与优化实践
  • Python tavily 库:Tavily 搜索 API 交互工具
  • Photoshop 与 ComfyUI 及 Stable Diffusion 集成指南
  • C++ 使用 SQLite3 数据库
  • 基于 YOLOv8-YOLOv12 与 SpringBoot 的野生动物检测系统
  • 高效集成 Gemini API:Zotero 学术场景 AI 辅助分析指南
  • Rust + LLM 开发:构建智能命令行运维助手
  • FastGPT 结合 MCP 协议实现工具增强型智能体构建
  • 1000 行 C 代码详解 llama.c 项目:从零理解 Llama 2 推理逻辑
  • PostgreSQL 模式(Schema)详解:数据库对象命名空间管理
  • Llama 7B 迁移至 MindSpore 实战指南:避坑与优化
  • 2026 年 3 月全球 AI 前沿动态:模型突破与产业落地
  • 归并排序与数组逆序对问题详解
  • Whisper 模型下载导航:各版本及各格式获取指南
  • 支持国内股票分析的 AI 开源项目精选与实战指南
  • Git 局域网协作教程
  • Copilot登录总失败?这7种情况你必须马上检查
  • Java 算法实战:随机打乱数组顺序的实现思路与代码

相关免费在线工具

  • curl 转代码

    解析常见 curl 参数并生成 fetch、axios、PHP curl 或 Python requests 示例代码。 在线工具,curl 转代码在线工具,online

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online

  • Markdown转HTML

    将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online

  • HTML转Markdown

    将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online

  • JSON 压缩

    通过删除不必要的空白来缩小和压缩JSON。 在线工具,JSON 压缩在线工具,online