最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Python實(shí)現(xiàn)Markdown轉(zhuǎn)Word文檔的工具詳解

 更新時(shí)間:2026年04月01日 09:28:31   作者:未來轉(zhuǎn)換  
這篇文章主要為大家詳細(xì)介紹了一個(gè)基于Python的Markdown轉(zhuǎn)Word文檔工具的實(shí)現(xiàn)方案,該工具主要使用python-docx庫,能夠?qū)arkdown文件自動(dòng)轉(zhuǎn)換為排版規(guī)范的Word文檔,希望對(duì)大家有所幫助

一、背景與動(dòng)機(jī)

在日常開發(fā)中,技術(shù)文檔通常以 Markdown 格式編寫,但交付給客戶或非技術(shù)人員時(shí),往往需要 Word(DOCX)格式。手工復(fù)制粘貼不僅效率低,還會(huì)丟失格式?;?Python 的 python-docx 庫,可以編寫一個(gè)自動(dòng)化腳本,將 Markdown 文件批量轉(zhuǎn)換為排版美觀的 Word 文檔。

二、技術(shù)選型

依賴庫版本用途
python-docx0.8+創(chuàng)建和修改 DOCX 文件
re內(nèi)置正則匹配 Markdown 語法
docx.shared設(shè)置字體大小、顏色、英寸等單位
docx.enum.text段落對(duì)齊方式等枚舉
docx.oxml.ns操作 Word 底層 XML 命名空間

核心依賴只有一個(gè):

pip install python-docx

三、整體架構(gòu)

Markdown 文件(.md)
       │
       ▼
  讀取文件內(nèi)容(UTF-8 編碼)
       │
       ▼
  逐行解析 Markdown 語法
  ┌────────────────────┐
  │ # ~ #####          │ → 標(biāo)題(h1 ~ h5)
  │ ```代碼塊 ```   │ → 代碼塊(Consolas 字體)
  │ | 表格 | 表格 |    │ → 帶邊框表格
  │ - 列表 / 1. 列表   │ → 無序/有序列表
  │ **粗體**           │ → 加粗文本
  │ `行內(nèi)代碼`         │ → 等寬高亮文本
  │ ---                │ → 分隔線
  │ 流程圖字符         │ → 等寬小號(hào)文本
  │ 普通文本           │ → 正文段落
  └────────────────────┘
       │
       ▼
  生成 DOCX 文檔并保存

四、核心實(shí)現(xiàn)

4.1 文檔初始化與默認(rèn)字體

Word 默認(rèn)字體不支持中文,需要手動(dòng)設(shè)置中文字體(微軟雅黑):

from docx import Document
from docx.shared import Pt
from docx.oxml.ns import qn

doc = Document()

# 設(shè)置默認(rèn)字體
doc.styles['Normal'].font.name = '微軟雅黑'
doc.styles['Normal']._element.rPr.rFonts.set(qn('w:eastAsia'), '微軟雅黑')
doc.styles['Normal'].font.size = Pt(11)

關(guān)鍵點(diǎn)

  • font.name 只設(shè)置了西文字體
  • 需要通過底層 XML rFonts.set(qn('w:eastAsia'), '微軟雅黑') 單獨(dú)設(shè)置中文字體,否則中文會(huì)回退到宋體

4.2 標(biāo)題解析

通過正則匹配 # 的數(shù)量判斷標(biāo)題級(jí)別:

if line.startswith('# '):
    p = doc.add_heading(line[2:].strip(), level=1)
    p.alignment = WD_ALIGN_PARAGRAPH.LEFT
elif line.startswith('## '):
    p = doc.add_heading(line[3:].strip(), level=2)
elif line.startswith('### '):
    p = doc.add_heading(line[4:].strip(), level=3)

doc.add_heading(text, level) 支持級(jí)別 1~5,Word 會(huì)自動(dòng)應(yīng)用對(duì)應(yīng)的標(biāo)題樣式。

4.3 代碼塊處理

代碼塊用三個(gè)反引號(hào)包裹,需要維護(hù)一個(gè)狀態(tài)標(biāo)志:

in_code_block = False
code_content = []

while i < len(lines):
    line = lines[i]

    if line.strip().startswith('```'):
        if not in_code_block:
            in_code_block = True
            code_content = []
        else:
            # 代碼塊結(jié)束,寫入文檔
            code_text = '\n'.join(code_content)
            p = doc.add_paragraph()
            run = p.add_run(code_text)
            run.font.name = 'Consolas'
            run.font.size = Pt(9)
            run.font.color.rgb = RGBColor(39, 86, 136)  # 深藍(lán)色
            p.style = 'No Spacing'  # 取消段落間距
            in_code_block = False
        i += 1
        continue

    if in_code_block:
        code_content.append(line)
        i += 1
        continue

關(guān)鍵點(diǎn)

  • p.style = 'No Spacing' — 取消代碼塊上下方的段落間距,避免代碼塊之間出現(xiàn)大段空白
  • 使用 Consolas 等寬字體,字體大小設(shè)為 9pt(比正文?。伾O(shè)為深藍(lán)色以區(qū)分正文

4.4 表格解析

Markdown 表格使用 | 分隔列,--- 分隔表頭和數(shù)據(jù)行:

| 字段 | 類型 | 說明 |
|------|------|------|
| id   | Long | 主鍵 |
| name | String | 名稱 |

解析邏輯:

# 收集連續(xù)的表格行
table_lines = []
while i < len(lines) and lines[i].startswith('|'):
    table_lines.append(lines[i])
    i += 1

# 解析表格數(shù)據(jù),跳過分隔行
rows = []
for tl in table_lines:
    if '---' not in tl:
        cells = [c.strip() for c in tl.split('|')[1:-1]]
        rows.append(cells)

# 創(chuàng)建 Word 表格
table = doc.add_table(rows=len(rows), cols=len(rows[0]))
table.style = 'Light Grid Accent 1'

for ri, row_data in enumerate(rows):
    for ci, cell_data in enumerate(row_data):
        cell = table.rows[ri].cells[ci]
        cell.text = cell_data
        set_cell_border(cell)  # 設(shè)置邊框

        if ri == 0:  # 表頭加粗
            run = cell.paragraphs[0].runs[0]
            run.font.bold = True
            run.font.size = Pt(11)
        else:
            run = cell.paragraphs[0].runs[0]
            run.font.size = Pt(10)

4.5 單元格邊框設(shè)置

python-docx 創(chuàng)建的表格默認(rèn)可能缺少邊框,需要通過底層 XML 手動(dòng)添加:

from docx.oxml import OxmlElement

def set_cell_border(cell):
    tcPr = cell._element.get_or_add_tcPr()
    tcBorders = OxmlElement('w:tcBorders')

    for border_name in ['top', 'left', 'bottom', 'right']:
        border = OxmlElement(f'w:{border_name}')
        border.set(qn('w:val'), 'single')
        border.set(qn('w:sz'), '4')        # 邊框?qū)挾龋?/8 磅為單位)
        border.set(qn('w:space'), '0')
        border.set(qn('w:color'), '000000')  # 黑色
        tcBorders.append(border)

    tcPr.append(tcBorders)

原理:Word 的單元格邊框定義在 w:tcBorders XML 元素中,python-docx 的高級(jí) API 沒有直接暴露此功能,需要操作 OOXML 底層元素。

4.6 行內(nèi)格式解析

粗體(**text**)

elif '**' in line:
    p = doc.add_paragraph()
    parts = re.split(r'\*\*(.+?)\*\*', line)
    for j, part in enumerate(parts):
        if j % 2 == 1:      # 奇數(shù)索引 = 粗體部分
            run = p.add_run(part)
            run.bold = True
        else:                 # 偶數(shù)索引 = 普通文本
            if part:
                p.add_run(part)
    # 如果段落為空則移除
    if not p.text.strip():
        p._element.getparent().remove(p._element)

核心思路:用正則 re.split(r'\*\*(.+?)\*\*', line) 將文本按粗體標(biāo)記分割,奇數(shù)位就是粗體內(nèi)容,偶數(shù)位是普通文本。

行內(nèi)代碼(`code`)

elif '`' in line:
    p = doc.add_paragraph()
    parts = re.split(r'`(.+?)`', line)
    for j, part in enumerate(parts):
        if j % 2 == 1:      # 奇數(shù)索引 = 代碼部分
            run = p.add_run(part)
            run.font.name = 'Consolas'
            run.font.size = Pt(10)
            run.font.color.rgb = RGBColor(199, 37, 78)  # 紅色
        else:
            if part:
                p.add_run(part)

4.7 列表解析

# 無序列表(- / * / +)
elif re.match(r'^\s*[\-\*\+]\s+', line):
    p = doc.add_paragraph(line.strip()[2:].strip(), style='List Bullet')

# 有序列表(1. 2. 3.)
elif re.match(r'^\s*\d+\.\s+', line):
    p = doc.add_paragraph(
        re.sub(r'^\s*\d+\.\s+', '', line),
        style='List Number'
    )

4.8 流程圖與特殊字符

Markdown 中的流程圖(如用 ┌│└─├→ 等字符繪制的圖)需要等寬小號(hào)字體才能對(duì)齊:

flow_chars = ['┌', '│', '└', '─', '├', '┼', '┬', '┐', '┘', '┤', '┴', '▲', '▼', '→']

if any(x in line for x in flow_chars):
    p = doc.add_paragraph()
    run = p.add_run(line)
    run.font.name = 'Consolas'
    run.font.size = Pt(8)      # 更小的字號(hào)
    p.style = 'No Spacing'     # 無間距

五、批量轉(zhuǎn)換主函數(shù)

import os

def main():
    md_files = [
        '/path/to/doc1.md',
        '/path/to/doc2.md'
    ]

    output_files = [
        '/path/to/doc1.docx',
        '/path/to/doc2.docx'
    ]

    for md_file, output_file in zip(md_files, output_files):
        print(f'正在轉(zhuǎn)換: {md_file}')

        with open(md_file, 'r', encoding='utf-8') as f:
            md_content = f.read()

        title = md_content.split('\n')[0].replace('#', '').strip()

        doc = markdown_to_docx(md_content, title)
        doc.save(output_file)
        print(f'已生成: {output_file}')

六、支持的 Markdown 語法匯總

語法Markdown 寫法轉(zhuǎn)換效果
一級(jí)標(biāo)題# 標(biāo)題Word Heading 1
二級(jí)標(biāo)題## 標(biāo)題Word Heading 2
三~五級(jí)標(biāo)題### ~ #####Word Heading 3~5
代碼塊` ```code ````Consolas 9pt 藍(lán)色
行內(nèi)代碼`code`Consolas 10pt 紅色
粗體**text**加粗
無序列表- itemList Bullet 樣式
有序列表1. itemList Number 樣式
表格| col | col |帶邊框的 Word 表格
分隔線---下劃線
流程圖┌───┐Consolas 8pt 等寬

七、局限性與優(yōu)化方向

當(dāng)前局限

  • 不支持圖片(Markdown 的 ![alt](url) 未解析)
  • 不支持超鏈接
  • 不支持嵌套列表(多層縮進(jìn)的列表)
  • 行內(nèi)格式只支持粗體和行內(nèi)代碼,不支持斜體、刪除線
  • 表格不支持合并單元格

優(yōu)化方向

# 1. 圖片支持:下載網(wǎng)絡(luò)圖片并插入
from docx.shared import Inches
p = doc.add_paragraph()
run = p.add_run()
run.add_picture('image.png', width=Inches(5.0))

# 2. 超鏈接支持
from docx.oxml.ns import qn
from docx.oxml import OxmlElement

def add_hyperlink(paragraph, text, url):
    part = paragraph.part
    r_id = part.relate_to(url, 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink', is_external=True)
    hyperlink = OxmlElement('w:hyperlink')
    hyperlink.set(qn('r:id'), r_id)
    new_run = OxmlElement('w:r')
    rPr = OxmlElement('w:rPr')
    new_run.append(rPr)
    text_elem = OxmlElement('w:t')
    text_elem.text = text
    new_run.append(text_elem)
    hyperlink.append(new_run)
    paragraph._p.append(hyperlink)
    return hyperlink

更優(yōu)的替代方案

如果對(duì)格式要求更高,可以考慮以下成熟工具:

工具特點(diǎn)安裝方式
pandoc功能最全,支持幾乎所有 Markdown 擴(kuò)展語法brew install pandoc
markdown2docx輕量級(jí),專為中文優(yōu)化pip install markdown2docx
mammoth反向轉(zhuǎn)換(DOCX → HTML/Markdown)pip install mammoth
# pandoc 一行命令即可完成轉(zhuǎn)換
pandoc input.md -o output.docx --reference-doc=template.docx

pandoc 支持 --reference-doc 參數(shù),可以指定一個(gè) Word 模板文件來控制輸出樣式(字體、顏色、頁邊距等),這是最推薦的方案。

八、完整代碼

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import re
from docx import Document
from docx.shared import Pt, Inches, RGBColor
from docx.enum.text import WD_ALIGN_PARAGRAPH
from docx.oxml.ns import qn


def set_cell_border(cell):
    """設(shè)置單元格邊框"""
    from docx.oxml import OxmlElement
    tcPr = cell._element.get_or_add_tcPr()
    tcBorders = OxmlElement('w:tcBorders')
    for border_name in ['top', 'left', 'bottom', 'right']:
        border = OxmlElement(f'w:{border_name}')
        border.set(qn('w:val'), 'single')
        border.set(qn('w:sz'), '4')
        border.set(qn('w:space'), '0')
        border.set(qn('w:color'), '000000')
        tcBorders.append(border)
    tcPr.append(tcBorders)


def markdown_to_docx(md_content, title):
    """將 Markdown 內(nèi)容轉(zhuǎn)換為 DOCX 文檔"""
    doc = Document()
    doc.styles['Normal'].font.name = '微軟雅黑'
    doc.styles['Normal']._element.rPr.rFonts.set(qn('w:eastAsia'), '微軟雅黑')
    doc.styles['Normal'].font.size = Pt(11)

    lines = md_content.split('\n')
    i = 0
    in_code_block = False
    code_content = []

    while i < len(lines):
        line = lines[i]

        # 代碼塊
        if line.strip().startswith('```'):
            if not in_code_block:
                in_code_block = True
                code_content = []
            else:
                p = doc.add_paragraph()
                run = p.add_run('\n'.join(code_content))
                run.font.name = 'Consolas'
                run.font.size = Pt(9)
                run.font.color.rgb = RGBColor(39, 86, 136)
                p.style = 'No Spacing'
                in_code_block = False
            i += 1
            continue

        if in_code_block:
            code_content.append(line)
            i += 1
            continue

        # 標(biāo)題
        if line.startswith('# '):
            doc.add_heading(line[2:].strip(), level=1)
        elif line.startswith('## '):
            doc.add_heading(line[3:].strip(), level=2)
        elif line.startswith('### '):
            doc.add_heading(line[4:].strip(), level=3)

        # 表格
        elif line.startswith('|') and '|' in line:
            table_lines = []
            while i < len(lines) and lines[i].startswith('|'):
                table_lines.append(lines[i])
                i += 1
            rows = []
            for tl in table_lines:
                if '---' not in tl:
                    cells = [c.strip() for c in tl.split('|')[1:-1]]
                    rows.append(cells)
            if rows:
                table = doc.add_table(rows=len(rows), cols=len(rows[0]))
                table.style = 'Light Grid Accent 1'
                for ri, row_data in enumerate(rows):
                    for ci, cell_data in enumerate(row_data):
                        cell = table.rows[ri].cells[ci]
                        cell.text = cell_data
                        set_cell_border(cell)
            continue

        # 列表
        elif re.match(r'^\s*[\-\*\+]\s+', line):
            doc.add_paragraph(line.strip()[2:].strip(), style='List Bullet')
        elif re.match(r'^\s*\d+\.\s+', line):
            doc.add_paragraph(re.sub(r'^\s*\d+\.\s+', '', line), style='List Number')

        # 粗體
        elif '**' in line:
            p = doc.add_paragraph()
            parts = re.split(r'\*\*(.+?)\*\*', line)
            for j, part in enumerate(parts):
                if j % 2 == 1:
                    run = p.add_run(part)
                    run.bold = True
                elif part:
                    p.add_run(part)

        # 行內(nèi)代碼
        elif '`' in line:
            p = doc.add_paragraph()
            parts = re.split(r'`(.+?)`', line)
            for j, part in enumerate(parts):
                if j % 2 == 1:
                    run = p.add_run(part)
                    run.font.name = 'Consolas'
                    run.font.size = Pt(10)
                    run.font.color.rgb = RGBColor(199, 37, 78)
                elif part:
                    p.add_run(part)

        # 普通段落
        elif line.strip():
            doc.add_paragraph(line.strip())

        i += 1

    return doc

九、總結(jié)

本腳本通過逐行解析 Markdown 文本,利用 python-docx 庫生成對(duì)應(yīng)格式的 Word 文檔,主要涉及以下關(guān)鍵技術(shù)點(diǎn):

  • 中文字體設(shè)置:通過 OOXML 底層 XML 設(shè)置東亞字體
  • 狀態(tài)機(jī)解析:用 in_code_block 標(biāo)志處理多行代碼塊
  • 正則分割:用 re.split 處理行內(nèi)格式(粗體、行內(nèi)代碼)
  • OOXML 操作:通過 OxmlElement 手動(dòng)添加表格邊框
  • 樣式控制:使用 No Spacing 樣式消除代碼塊間距

對(duì)于簡單的文檔轉(zhuǎn)換需求,這個(gè)腳本足夠使用;如果需要更完整的格式支持,建議使用 pandoc 等成熟工具。

到此這篇關(guān)于Python實(shí)現(xiàn)Markdown轉(zhuǎn)Word文檔的工具詳解的文章就介紹到這了,更多相關(guān)Python Markdown轉(zhuǎn) Word內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評(píng)論

喀喇沁旗| 嫩江县| 循化| 泽普县| 吉安县| 新和县| 浮山县| 永修县| 古丈县| 三穗县| 南靖县| 双流县| 普兰县| 西吉县| 曲阜市| 嘉鱼县| 皋兰县| 南雄市| 乌拉特前旗| 襄樊市| 措美县| 沁阳市| 关岭| 延庆县| 礼泉县| 嘉黎县| 天长市| 海安县| 武邑县| 鄄城县| 依安县| 马鞍山市| 集贤县| 黄浦区| 长沙县| 五华县| 辽宁省| 友谊县| 邵武市| 民县| 常德市|