当前位置:首页>python>靠这两个Python脚本,把标书排版从3小时压到3分钟

靠这两个Python脚本,把标书排版从3小时压到3分钟

  • 2026-10-11 15:50:54
靠这两个Python脚本,把标书排版从3小时压到3分钟

干标书/方案这行最磨人的不是写内容,是排版。

方案写完了,往Word里一贴,字体乱了,缩进没了,表格挤成一团,目录页码全对不上。改到半夜还在跟"回车键到底按没按对"较劲。

我们踩了三个月坑,把Markdown转Word这条路跑通了。不是用花里胡哨的在线工具,就是两个能扔到服务器上跑的自写Python脚本。今天全给你。

一、为什么Markdown转Word总是翻车

先搞清楚痛点在哪。以前最常用的路子是:Markdown → HTML → Word,中间夹一层在线转换器。

翻车就翻在这层。

1. 字体丢失:中文默认变成宋体还好,代码块、表格里的英文直接用回Times,混在一起像贴了补丁。

2. 缩进错乱:Markdown的列表层级,转出来的Word要么没缩进要么缩进过头。

3. 表格崩坏:在线工具对中文表格支持差,列宽失控,表头不重复。

4. 图片漂移:图插进去位置不对,标书里一张架构图位置错了,整章都得重排。

结论是啥?别走HTML中转,直接用python-docx按规则生成。 Word文件本质上是个XML,python-docx让你直接操作结构,绕开那些"看一眼再猜"的转换器。

二、第一个脚本:Markdown到Word的自动转换器

先把环境安好。python-docx是唯一硬依赖:

pip3 install python-docx

然后上核心脚本 `markdown2docx.py`。它只做了四件事:识别标题层级、解析列表、画表格、设置中文字体。关键就是那个字体设置——必须单独设置eastAsia字体,否则中文不生效,这是python-docx最大的坑:

def set_run_font(run, name_cn="宋体", size=12, bold=False):     run.font.name = "Times New Roman"          # 西文字体     r = run._element.rPr.rFonts     r.set(qn("w:eastAsia"), name_cn)            # ← 中文必须走这个通道     run.font.size = Pt(size)     run.font.bold = bold

光设 `run.font.name` 只改英文字体,中文显示全靠 `w:eastAsia`。漏了这行,中文就永远用默认字体,怎么设size都不对。

转换的核心逻辑,用正则逐行扫,标题、列表、表格、引用分开处理:

import re from docx import Document from docx.shared import Pt, Inches from docx.oxml.ns import qn  def md_to_docx(md_text, doc):     lines = md_text.split("\n")     i = 0     while i < len(lines):         s = lines[i].strip()         if not s:             i += 1             continue         # 标题:# / ## / ### → 层级1/2/3         h = re.match(r"^(#{1,3})\s+(.*)$", s)         if h:             add_heading(doc, h.group(2), len(h.group(1)))             i += 1             continue         # 表格:以 | 开头的一串行         if s.startswith("|"):             j = i             rows = []             while j < len(lines) and lines[j].strip().startswith("|"):                 raw = [c.strip() for c in lines[j].strip().strip("|").split("|")]                 if not all(re.match(r"^:?-+:?$", c) for c in raw):                     rows.append(raw)                 j += 1             add_table(doc, rows)             i = j             continue         # 普通段落,首行缩进2字符         add_body(doc, s)         i += 1

表格是标书的重灾区,单独抽出。先建表再逐格填,并对齐表头:

def add_table(doc, rows):     n_cols = len(rows[0])     table = doc.add_table(rows=len(rows), cols=n_cols)     table.style = "Table Grid"           # 全边框     table.alignment = WD_TABLE_ALIGNMENT.CENTER     for i, row in enumerate(rows):         for j, cell_text in enumerate(row):             cell = table.cell(i, j)             cell.text = cell_text.strip()   # ← 这里别写成 cell (下面踩坑细说)             for par in cell.paragraphs:                 for run in par.runs:                     set_run_font(run, "宋体", 11, bold=(i == 0))  # 表头加粗

这段代码里藏着一个我实测踩过的坑,一会儿专门讲。

三、第二个脚本:格式自动检测

转出来的Word还得过一道自检,因为人会漏,脚本不会。全角半角标点混入是标书最常见的低级错误——"招商银行:"和"招商银行:"混着用,甲方一眼就看出不专业。

`format_check.py` 干这个活。核心逻辑:扫出中文句子里的半角标点,报出来让作者改:

def check(text):     problems = []     for idx, line in enumerate(text.split("\n"), 1):         s = line.strip()         if s.startswith("```") or not s:   # 跳过代码块和空行             continue         # 保护URL和代码,避免误报         s = re.sub(r"https?://\S+", lambda x: "#"*len(x.group()), s)         # 中文出现半角标点 → 报错         for half in [",", ";", ":", "!", "?", "(", ")"]:             if half in s and re.search(r"[\u4e00-\u9fff]", s):                 problems.append(f"L{idx}: 中文处出现半角『{half}』 <- {s[:40]}")     return problems

实测运行长这样(故意留了个半角冒号):

$ python3 format_check.py dirty.md ⚠️ 发现 4 处问题:   L1: 中文处出现半角『:』,应改全角『:』 (原文: # 这一段的冒号用的是半角:)   L2: 中文处出现半角『,』,应改全角『,』 (原文: ## 检测不到就出问题,,这里逗号也是半角)   L4: 中文处出现半角『?』,应改全角『?』 (原文: > 引用里带了半角问号?这不对)   L6: 中文处出现半角『:』,应改全角『:』 (原文: 文案里有URL: http://example.com/path?a=1 应该保留半角)

一个容易踩的坑:数字编号里的点不能被误报。 "1.1项目概述"、"方案1."这些点必须放行,第一版脚本把干净的文档全报了错。解法是剥离编号语义再查:

s = re.sub(r"(?:\d+\.)+\d*", "", s)   # 先剥掉 1.1 / 1.2.1 这类编号 if "." in s:  # 剩下的点才是真问题     problems.append(...)

四、我踩过的三个坑(都是流血的教训)

坑1:`cell = table.cell(i, j)` 不能写成 `cell.text` 去设。

我第一版写 `add_table` 时,以为遍历出来的 `cell` 可以直接 `.text=`,结果报错 `'str' object has no attribute 'text'`——因为 `for cell in row` 遍历出来的是字符串(表格的一行),不是单元格对象。正确姿势是 `table.cell(行号, 列号)` 定位。

坑2:中文字体必须设 `w:eastAsia`。

光设 `run.font.name`,中文永远是默认黑体方框。这是python-docx文档都不会明说的地方,不设的话转出来中文排版全废。

坑3:格式检查器会误报编号点。

第一版把"1.1项目概述"里的点当成半角句号报错,干净的文档也过不了检。不先剥离编号语义,检查器就是一堆狼来了。

五、完整链路,能直接上服务器跑

整个流程串起来就三步,几十个标书文件都能批量处理:

# 1. 格式自检(先改掉全角半角问题) python3 format_check.py 方案.md  # 2. 转Word(n个文件循环跑) for f in 标书*.md; do   python3 markdown2docx.py "$f" -o "${f%.md}.docx" done  # 3. 检查生成的docx ls -la *.docx

我算过一笔账。以前一份100页的标书,排版耗时2-3小时,返工率高;现在写内容(Markdown)→ 自检 → 一键转Word,排版时间压到3分钟,返工几乎为零。省下来的时间,都用来改内容质量了。

标书这行的核心竞争力从来不是排版,是内容。 排版那点破事,交给脚本,让人去干人该干的活。

投稿:硅基聊斋 / 栏目:专项攻略

最新文章

随机文章