ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

用python-docx自动生成信息化建设预算docx:从拆解到批量产出

用python-docx自动生成信息化建设预算docx:从拆解到批量产出 简介面向单位信息化管理部门的预算编制参考文档聚焦信息网络及软硬件设备的年度运维成本规划。内容从总体工作目标与具体任务出发明确设备日常维护、数据备份、安全监管、主干网络维护及网站信息更新等费用归口并列示详细的支出管理程序与预算明细表框架可直接作为编制模板或流程梳理依据。资源包含一个可编辑的Word文档docx格式大小465KB便于按本单位实际情况直接修改后使用。目前已有387人学习适合信息化管理人员、财务人员或行政后勤岗位参考。整体来看文档结构完整并围绕维修、备份、网络安全、网站维护等细项作出归口安排有助于规范预算申报与审批能够快速搭建年度信息化预算体系并降低维护成本。1. 信息化建设预算 docx 的痛点比想象中深每年年底信息化部门都要交一份信息化建设预算.docx。真正折腾人的不是算账而是文档本身三个同事各做一版格式能差出三种从 Excel 粘回来的数据进到 Word 表格里行高、边框、字体全乱领导改了几个数文件名就变成信息化建设预算最终版-v3-真最终版.docx。这个标题看起来平平无奇背后其实是结构化数据与文档格式之间的反复转换。对 IT 从业者来说最靠谱的解法不是继续手工排版而是把 docx 当成一种可解析、可生成、可校验的文件格式来对待。这篇按这个顺序展开先拆 docx 的内部结构再用 python-docx 生成预算文档然后把预算 Excel 批量变成 docx最后讲交付前的校验和兼容性坑。代码都能直接跑参数会交代清楚。2. 拆解信息化建设预算 docx先搞懂 docx 本身2.1 docx 是 zip 包不是一个文件几乎所有 .docx 文档本质上都是一个 zip 压缩包把扩展名改成 .zip 就能直接解开。里面真正存正文的是 word/document.xml所有标题、段落、表格都以 XML 元素的形式排在里面样式在 word/styles.xml图片在 word/media/页码、页眉页脚各有各的 xml 文件。这意味着两件事第一任何 docx 都可以用命令行拆开看内容不依赖 Office第二网上那些docx 文件打不开的问题一半以上是压缩包结构损坏而不是内容写错。在预算文档这个场景里理解这一点直接关系到排错效率。别人说文档打不开第一步不是重做而是先确认 zip 包是否完好cd /tmp cp 信息化建设预算.docx budget_check.zip unzip -t budget_check.zipunzip -t只做测试不解压逐个检查压缩包内文件的 CRC 校验。正常输出末尾会出现No errors detected in compressed data of budget_check.zip之类的提示如果报错基本可以断定文件在传输或存储过程中损坏需要回到源文件重新导出。养成这个习惯后你不会再一上来就怀疑是内容问题。2.2 一份信息化建设预算的标准骨架拆开格式之后再看内容。信息化建设预算文档虽然有单位差异但提交给决策层或财务的口径基本一致通常包含四块编制说明背景、依据、编制周期、汇总表按科目给总额、明细表每条预算的依据和金额、实施与采购计划时间节点、付款方式。其中汇总表和明细表是审查重点格式上经常出问题的是表格列宽不一致、金额对齐混乱、科目层级错位。用一个能覆盖多数场景的表格结构作为基线预算科目明细项金额万元编制依据优先级硬件采购服务器 2 台42.00等保改造要求P1软件采购数据库许可80.00业务系统扩容P1云资源对象存储 50TB18.50备份策略P2外包人力驻场开发 6 人月54.00项目交付计划P1运维服务年度维保25.00合同延续P2科目这一列对应汇总表的行明细项是后续审计要追溯的颗粒度金额列统一用万元并保留两位小数编制依据必须写到具体文件或制度不能只写估的。这套结构定了后面的生成代码才有依据。2.3 用 python-docx 读取现有预算文档的最小代码接手别人留下的信息化建设预算.docx时先别急着改把结构和内容读出来看一遍。python-docx 是处理 docx 最常用的库读取本身只需要几行from docx import Document doc Document(信息化建设预算.docx) for i, para in enumerate(doc.paragraphs): if para.text.strip(): print(i, para.style.name, para.text) for t_idx, table in enumerate(doc.tables): print(f--- 表格 {t_idx}: {len(table.rows)} 行 x {len(table.columns)} 列) for row in table.rows[:5]: print( | .join(cell.text.strip() for cell in row.cells))这段代码做了两件事按段落索引打印所有非空段落以及它们使用的样式名再按表格索引打印行数和列数每张表只预览前 5 行防止控制台被刷屏。doc.paragraphs只包含 body 下的直接段落表格里的文字不算在内所以要单独遍历doc.tables。看到样式名和表格规模后你基本就能判断这份文档的排版是基于内置样式还是全靠手工调字号——后者在批量生成时最麻烦。提示python-docx 只处理 docx不处理老式 .doc。遇到 .doc 文件先另存为 docx 再读取别在它的能力边界上硬试。3. 用 python-docx 生成一份可提交的信息化建设预算3.1 搭骨架标题、默认字体、页面方向生成文档和手工排版顺序相反先把全局样式定下来再填内容。单位提交的预算文档对字体有硬性要求最常见的组合是正文宋体小四、标题黑体加粗。python-docx 设置中文字体有个容易踩的坑只设font.name只改西文字体中文字体必须同时写进w:eastAsia属性from docx import Document from docx.shared import Pt, Cm from docx.oxml.ns import qn from docx.enum.text import WD_ALIGN_PARAGRAPH doc Document() # 全局样式西文字体交给 font.name中文走 eastAsia normal doc.styles[Normal] normal.font.name Times New Roman normal.font.size Pt(12) normal.element.rPr.rFonts.set(qn(w:eastAsia), 宋体) title doc.add_paragraph() title.alignment WD_ALIGN_PARAGRAPH.CENTER run title.add_run(2026 年度信息化建设预算) run.bold True run.font.size Pt(22) doc.add_paragraph(编制单位信息技术部 编制日期2026 年 1 月 15 日) doc.add_paragraph()qn(w:eastAsia)的作用是把命名空间补齐写出来的 XML 才会带上中文东亚字体设置这一步漏掉Word 里看到的仍是默认等线字体。标题用单独一个段落手动加粗放大比改Heading样式更可控因为内置标题样式自带一套颜色和段间距在正式公文场景里经常要额外覆盖。页面方向这里用默认的纵向如果预算表格列太宽可以在生成前改doc.sections[0].orientation同时要把页面宽高对调否则内容照样溢出。3.2 预算明细表表格生成、列宽与表头重复预算文档的表格是核心资产生成时直接决定审查体验。python-docx 的表格模型是一个Table对象add_table之后要手动套样式最常用的是Table Grid保证所有单元格带边框from docx.enum.table import WD_TABLE_ALIGNMENT # 先建一行表头再用 add_row 追加正文行 table doc.add_table(rows1, cols5) table.style Table Grid table.alignment WD_TABLE_ALIGNMENT.CENTER headers [序号, 预算科目, 明细项, 金额万元, 编制依据] for idx, text in enumerate(headers): table.rows[0].cells[idx].text text rows_data [ [1, 硬件采购, 服务器 2 台, 42.00, 等保改造要求], [2, 软件采购, 数据库许可, 80.00, 业务系统扩容], [3, 云资源, 对象存储 50TB, 18.50, 备份策略], [4, 外包人力, 驻场开发 6 人月, 54.00, 项目交付计划], [5, 运维服务, 年度维保, 25.00, 合同延续], ] for data in rows_data: cells table.add_row().cells for idx, value in enumerate(data): cells[idx].text valueadd_table(rows1, cols5)先生成一行表头后续用add_row()追加正文行表头天然排在第一位。Table Grid是内置样式python-docx 新建的默认文档里就有直接可用。列宽的控制不能只设column.width还要对每一行的cell.width赋值否则 Word 打开后列宽会按内容自适应widths [Cm(1.2), Cm(2.8), Cm(4.0), Cm(2.5), Cm(5.0)] for row in table.rows: for idx, width in enumerate(widths): row.cells[idx].width width这里的规律是同一列的列宽必须逐行重复设置只设table.columns[idx].width在页面渲染时经常不生效这是 python-docx 一个已知的语义差异。预算表如果跨页最好让表头在每一页重复显示给表格第一行打上表头标记即可。3.3 序号和总额两种会变的内容别写死明细表里的序号在增删行之后容易重排总额要在全部明细填完以后才能算。这两个数据如果手写进代码改一次明细就要改两处批量生成时必出事故。常见做法是序号在填充阶段动态生成总额在最后单独加一行汇总让它跟随明细数据计算total sum(float(data[3]) for data in rows_data) total_row table.add_row().cells total_row[1].text 合计 total_row[2].text total_row[3].text f{total:.2f} total_row[4].text 汇总行只有一个合计标签和金额其他单元格留空语义上不重复。金额格式用f{total:.2f}强制两位小数和明细列保持一致。这样增删明细后重新跑一遍脚本序号由枚举生成、总额由数据算出来文档永远处于一致状态。投入人力、服务器这类明细时金额单位务必统一到万元混合使用元和万元是预算文档最常见的数据错误脚本里可以在填充前做一次单位校验发现负数或超阈值金额直接抛异常退出。4. 数据驱动把预算 Excel 批量转成 docx 文档4.1 预算数据先落 Excel再喂给 python-docx预算明细在真实流程里通常先存在 Excel 里财务要表格决策层要 docx。与其在 Word 里手动抄一遍不如让 Excel 当唯一数据源python-docx 只负责渲染。用 pandas 读入明细import pandas as pd df pd.read_excel(预算明细.xlsx, sheet_name2026) df.columns [预算科目, 明细项, 金额, 编制依据, 优先级] df df.dropna(subset[明细项]) df[金额] pd.to_numeric(df[金额], errorscoerce) assert df[金额].notna().all(), 存在无法解析的金额请检查 Excel 原始数据 assert (df[金额] 0).all(), 金额不能为负数sheet_name2026指定工作表避免读错 sheet。强制改列名是为了让后续代码不依赖 Excel 里的表头写法明细项是必填列用dropna清掉空行。pd.to_numeric配合两道断言把 Excel 里常见的42万42 万这类文本直接暴露出来不让脏数据流进文档。4.2 占位符替换模板与数据分离预算文档的编制说明、依据等大段文字每次生成大同小异逐段用代码拼太啰嗦。更常见做法是维护一份填好占位符的模板 docx正文里写{{编制依据}}、{{预算总额}}这类标记生成时统一替换doc Document(预算模板.docx) mapping { {{编制单位}}: 信息技术部, {{预算总额}}: f{df[金额].sum():.2f}, {{编制日期}}: 2026-01-15, {{编制依据}}: 《信息化项目建设管理办法》及年度经营计划, } for para in doc.paragraphs: for key, value in mapping.items(): if key in para.text: # 段落命中后再改 run避免整体重写丢掉格式 for run in para.runs: if key in run.text: run.text run.text.replace(key, value)替换必须落在run级别而不是段落级别原因是 Word 会把一个段落拆成多个 run文字、格式变更都会产生新的 run 切分。如果直接para.text para.text.replace(...)python-docx 会用纯文本把整个段落重写原有的加粗、下划线全部丢失。这里先判断key in para.text缩小范围再遍历para.runs逐个替换通常情况下占位符会完整落在一个 run 内一次替换就够。模板里写占位符时不要给局部加特殊格式否则占位符被拆成两段替换会静默遗漏。4.3 按部门批量出文档文件名带上版本号信息化建设预算经常要按部门或项目分别出稿。把生成逻辑包成一个函数用部门名做参数循环调用from pathlib import Path def build_budget_doc(dept_name: str, dept_df: pd.DataFrame, version: str) - str: doc Document(预算模板.docx) # 模板第一段就是标题直接替换文本 doc.paragraphs[0].text f{dept_name} 2026 年度信息化建设预算 table doc.add_table(rows1, cols5) table.style Table Grid for idx, h in enumerate([预算科目, 明细项, 金额万元, 编制依据, 优先级]): table.rows[0].cells[idx].text h for _, row in dept_df.iterrows(): cells table.add_row().cells cells[0].text row[预算科目] cells[1].text row[明细项] cells[2].text f{row[金额]:.2f} cells[3].text row[编制依据] cells[4].text row[优先级] assert len(dept_df) 0, f{dept_name} 没有明细数据 out Path(output) / f信息化建设预算-{dept_name}-v{version}.docx out.parent.mkdir(exist_okTrue) doc.save(out) return str(out) for dept, dept_df in df.groupby(项目归属部门): build_budget_doc(dept, dept_df, 1.0)函数签名里的dept_name同时进入标题和文件名文件名带版本号是后续追溯的底线操作。groupby(项目归属部门)按部门切分数据每个部门独立成稿。文件名里的v1.0是本次生成的版本不代表最终交付版本后续无论谁改了内容都递增版本号而不是命名成最终版。批量跑完之后output目录里每个部门一个 docx再用第 5 章的方法统一校验一遍。注意批量循环里如果某张表数据为空add_row不会执行表格只剩表头。函数里的assert len(dept_df) 0会在空数据时直接停住避免生成废文档。5. 交付前校验 docx 完整性的几个硬技巧5.1 先跑一遍 zip 完整性检查批量生成的 docx 在复制、上传、微信传输后都有可能损坏。交付前对全量文件做一次压缩包完整性检查比在 Word 里逐个打开快得多cd output for f in *.docx; do unzip -t $f /dev/null || echo 损坏: $f done静默模式下只有损坏文件会打印出来几十个文档几秒扫完。这一步能拦截掉大部分文件打不开的投诉。5.2 重读文档核对表格行数和金额生成了不代表内容对重读一遍做规则校验from docx import Document for f in Path(output).glob(*.docx): doc Document(str(f)) table doc.tables[0] rows len(table.rows) - 1 total table.rows[-1].cells[2].text print(f.name, rows, total)判定逻辑明细行数应对应 Excel 源数据条数总额文本要能解析成数字。对不上就回查数据源别在 Word 里手工补。如果明细超过 30 行而总额还是 0.00多半是金额列下标取错了位置检查表格列序再跑。5.3 用文档属性记录版本与作者信息docx 支持写入核心属性这些信息在资源管理器里就能看到适合做版本和归属标记doc.core_properties.author IT 预算组 doc.core_properties.title 2026 信息化建设预算 doc.core_properties.comments 版本 v1.0生成于 2026-01-15 doc.save(信息化建设预算-信息技术部-v1.0.docx)属性适用内容效果author编制部门资源管理器作者列可见comments版本说明右键属性可见不干扰正文title文档标题搜索结果里显示的名称最后补一个和 WPS 相关的体验建议很多同事用 WPS 打开文档默认双击新建的是 wps 格式而不是 docx跨软件传递后无法预览 doc的报错大多来自格式混用。交付时统一 .docx 后缀别另存成老版 .doc预览服务基本都能正常渲染。如果后续还要在文档里批量插入网络图、拓扑图别用截图粘贴把图转为 EMF 或 PNG 后通过add_picture插入并设置widthCm(14)同一套生成脚本就能继续跑下去。本文还有配套的精品资源点击获取
返回列表