十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

python-docx实战:自动生成房地产可行性研究报告

python-docx实战:自动生成房地产可行性研究报告 简介文档为房地产从业者、投资决策者和可研报告编写人员提供了一份完整的花园住宅项目可行性研究实例。内容覆盖申报单位概况、项目背景、建设规模与户型配置、投资估算与资金筹措、经济效益评价、节能节水、征地拆迁及环境生态影响等模块并结合南宁市隆安县华侨经济开发区的实际地块条件给出容积率、绿地率、建筑密度等关键指标。资源包共1个docx文件大小仅70KB便于直接查阅和编辑复用。文档以隆安市场调研和政策依据为起点到研究结论与建议结束完整呈现可研报告的目录体系、编制思路和常用经济数据框架可帮助读者快速掌握房地产项目投资评估与风险分析方法。目前已有63人学习下载适合用于同类项目报告撰写、教学示范或内部评审参考。1. 从《某房地产花园项目可行性研究报告.docx》看一套可落地的报告生成方案一份《某房地产花园项目可行性研究报告.docx》在投拓部门往往意味着一整周的体力活市场数据贴进 Word投资估算放在 Excel财务评价又是另一套表最后人工把图表和单元格拷进几十页的文档里。做过一次的人都清楚真正耗时间的不是分析而是排版和对数。对 IT 从业者来说这份 docx 不应该是一个静态结果而是一条可以由数据驱动生成的技术链路。把报告章节拆成模板把地块指标、成本参数、销售均价存进数据表再用脚本计算财务指标、渲染图表并写入 Word可行性研究报告就能从反复复制粘贴变成可批量产出的报表产品。下面按拆结构、算数据、生成文档、批量复用的顺序讲清楚一套不需要购买商业插件、直接用 Python 就能实现的方案。适合正在做投拓系统、Office 文档批处理和报表自动化的一线工程师参考。2. 拆解可行性研究报告的固定骨架从调研结论到现金流模型2.1 报告里哪些内容可以固化成模板结构做 docx 自动化第一步是先做减法。拿三份不同地块的房地产花园项目报告并排比对章节标题、说明性段落、表格列名都一样的部分基本就是固定骨架。通常一本可行性研究报告的主干是项目概况、市场分析、建设规模与规划方案、建设进度、投资估算、资金筹措、财务评价、风险分析、结论与建议。这个顺序在大多数项目里保持稳定真正变化的只有区位描述、具体参数和财务结论。我一般会把报告结构拆成一张映射表左边写固定内容右边写动态字段并且把字段名和后续数据字典里的键严格统一。这样模板维护成本很低市场分析那段文字无论怎么改都不影响代码逻辑只要保证字段渲染时能拿到值就行。报告章节固定内容动态字段自动化方式项目概况项目名称、区位模板句地块面积、容积率、车位比占位符替换市场分析市场环境描述模板周边均价、去化周期、竞品列表字典渲染投资估算成本科目说明文字土地费、建安费、开发期费用pandas 计算后填表财务评价评价方法说明文字NPV、IRR、回收期、利润率Python 计算并生成图表风险分析风险应对模板句敏感性因素、价格波动区间条件判断生成段落结论建议标准结论句是否可行、建议内容分支逻辑输出提示字段命名尽量带上单位后缀例如area_m2、cost_wan、price_wan_m2避免在 docx 里出现面积 38000 / 平方米这类单位错乱。2.2 把报告内容抽象成字段字典而不是直接拼字符串在写 python-docx 之前先要把整份报告文档抽象成一张字段字典。模板里的每个可变位置对应一个 key比如project_name、land_area、plot_ratio、total_investment。字段值不一定是单个数字也可能是一段由条件生成的多行结论。这种做法的可维护性比按行拼接 Word 段落好很多数字算错了直接查数据源不用逐个打开 Word 找错。下面是一个最小化的数据模型示例用 dataclass 把报告参数汇总成可供模板替换的字典from dataclasses import dataclass, field from typing import Dict dataclass class GardenProjectReport: project_name: str 某房地产花园项目 land_area_m2: float 38000.0 # 净用地面积单位平方米 plot_ratio: float 2.0 # 容积率用于计算计容建筑面积 total_construction_m2: float 0.0 # 计容总建筑面积由面积*容积率得出 land_cost_wan: float 25000.0 # 土地成本单位万元 construction_cost_wan: float 56000.0 # 建安成本单位万元 average_price_wan_m2: float 1.65 # 销售均价单位万元/平方米 def calc_total_area(self) - float: self.total_construction_m2 self.land_area_m2 * self.plot_ratio return self.total_construction_m2 def to_field_dict(self) - Dict[str, object]: return { project_name: self.project_name, land_area: f{self.land_area_m2:,.0f}, plot_ratio: f{self.plot_ratio:.2f}, total_construction_m2: f{self.total_construction_m2:,.0f}, average_price: f{self.average_price_wan_m2:.2f}, }land_area_m2存净用地面积plot_ratio是容积率二者相乘得到计容总建筑面积。land_cost_wan和construction_cost_wan统一用万元避免后续和元混用。to_field_dict里用格式化字符串输出:,表示千分位.0f表示不保留小数。最终这个字典就是模板替换和表格填充的数据源。2.3 模板字段与数据字典的一致性检查字段字典设计好后最容易出问题的环节是模板和字典对不上。报告里写了{{ total_investment }}但代码里返回的 key 是total_invest_wan最后生成的 Word 里就会残留占位符。常见做法是在渲染前做一次键名校验把模板里所有占位符提取出来和字典的 keys 做差集差集为空再继续生成。import re def check_placeholders(template_text: str, data: dict) - None: placeholders set(re.findall(r\{\{\s*(\w)\s*\}\}, template_text)) missing placeholders - set(data.keys()) if missing: raise ValueError(f缺失字段: {missing})这段代码用正则从模板文本中提取所有双花括号变量然后和数据字典的键做差集。只要出现missing不为空就直接抛异常避免把一份带着{{xxx}}的破损报告交给业务方。这个检查函数我会放在每次渲染入口的第一步。3. 用 pandas 算出投资估算与财务指标再转成报告数据3.1 成本科目表一行一个费用项投资估算表在可行性研究报告中通常是按成本科目展开的。数据建模时建议用 pandas 的二维表结构每一行是一个成本科目列包括科目名称、金额、备注后续既能生成报告表格也能直接喂给 matplotlib 画图。import pandas as pd cost_items [ {cost_item: 土地费用, amount_wan: 25000, note: 含土地出让金及契税}, {cost_item: 前期工程费, amount_wan: 3000, note: 勘察设计、报批报建}, {cost_item: 建筑安装工程费, amount_wan: 56000, note: 含主体工程及安装}, {cost_item: 基础设施费, amount_wan: 4200, note: 道路、管网、绿化、景观}, {cost_item: 开发期费用, amount_wan: 6800, note: 管理费用、销售费用、财务费用}, ] cost_df pd.DataFrame(cost_items) cost_df[ratio] cost_df[amount_wan] / cost_df[amount_wan].sum() print(cost_df.round(4))amount_wan这一列所有金额以万元为单位ratio列在构造后是每项成本占总投资的百分比。这里先算占比再进 Word比生成文档时临时除以总数更安全。以后如果要调整某一项成本只要改这个列表里的值表格和图表会自动跟着变。3.2 销售收入、利润、NPV 与 IRR 的计算口径财务评价部分的逻辑比成本表稍复杂需要同时看静态指标和动态指标。常见做法是先用可售面积乘以去化均价得到总收入再用总收入减总投资得到利润总额。现金流模型则把项目生命周期的投入和回款按年份排列计算净现值 NPV 和内部回报率 IRR。import numpy_financial as npf # 基础指标 saleable_ratio 0.92 saleable_area_m2 76000 * saleable_ratio # 可售面积 total_revenue_wan saleable_area_m2 * 1.65 # 总收入约 11.5亿 total_invest_wan cost_df[amount_wan].sum() # 总投资约 9.5亿 profit_before_tax_wan total_revenue_wan - total_invest_wan profit_rate profit_before_tax_wan / total_revenue_wan # 简化现金流前两年投入为主后两年去化回款 cash_flow [ -total_invest_wan * 0.50, # 第1年支出土地及前期费用 -total_invest_wan * 0.30 total_revenue_wan * 0.15, -total_invest_wan * 0.20 total_revenue_wan * 0.45, total_revenue_wan * 0.40, # 第4年清盘 ] npv npf.npv(0.08, cash_flow) # 折现率8%的净现值 irr npf.irr(cash_flow)npf.npv(0.08, cash_flow)的第一个参数是折现率第二个参数必须从第 1 年开始排列不要把第 0 期单独列进去否则会多算一期折现。npf.irr返回的是小数形式后面格式化时需要转成百分比。这个简化模型里假设第 2 年开始有回款实际项目如果预售节点靠后需要调整cash_flow中的系数。注意numpy_financial和旧版的numpy.financialAPI 一致但新环境建议直接pip install numpy-financial避免依赖已经被移除的模块。3.3 把计算结果整理成渲染用数值字典pandas 计算完的是 DataFrameWord 里需要的却是已经格式化过的字符串。因此最后一步是把数值统一转成报告语言金额带千分位、占比显示百分比、NPV 保留整数、IRR 保留两位小数。total_invest_wan cost_df[amount_wan].sum() report_data { total_revenue_wan: f{total_revenue_wan:,.0f}, total_revenue_yi: f{total_revenue_wan / 10000:.2f}, total_invest_wan: f{total_invest_wan:,.0f}, profit_before_tax_wan: f{profit_before_tax_wan:,.0f}, profit_rate: f{profit_rate:.2%}, npv_wan: f{npv:,.0f}, irr: f{irr:.2%}, }这里把原始数值和格式化后字符串区分开原始数值继续用于后续敏感性分析格式化后的字符串直接填入 docx。.2%会把小数值自动乘以 100 并补两位小数比手动* 100再拼接%更不容易出错。报告里涉及亿元口径时用total_revenue_wan / 10000转单位。4. 用 python-docx 把图表和表格渲染进《某房地产花园项目可行性研究报告.docx》4.1 环境准备与中文字体处理生成 docx 最核心的依赖是python-docx图表用matplotlib数据计算用pandas和前面提到的numpy-financial。安装命令如下pip install python-docx pandas matplotlib numpy-financialpython-docx 默认样式在中文环境下需要额外设置字体否则打开 Word 会出现中文字体不生效的情况。关键是同时设置w:eastAsia属性from docx import Document from docx.shared import Pt from docx.oxml.ns import qn doc Document() normal doc.styles[Normal] normal.font.name Times New Roman normal.font.size Pt(12) normal.element.rPr.rFonts.set(qn(w:eastAsia), 宋体)w:eastAsia是 Word 中文字体的内部属性名只设font.name只改变了西文字体中文仍会落到默认主题字体上。这里的qn(w:eastAsia)是把命名空间字符串转成 lxml 可识别的 QName。4.2 报告生成辅助函数标题、段落、表格报告里的标题层级和正文段落会在多个章节重复出现所以先封装四个小函数添加标题、添加正文、添加表格、添加图片。函数越短越好目的是在后续生成时不用重复控制样式。def add_heading(doc, text, level1): doc.add_heading(text, levellevel) def add_para(doc, text, boldFalse): p doc.add_paragraph() run p.add_run(text) run.bold bold return p def add_data_table(doc, headers, rows): table doc.add_table(rowslen(rows) 1, colslen(headers)) table.style Light Grid Accent 1 for i, header in enumerate(headers): table.cell(0, i).text header for r, row in enumerate(rows, start1): for c, value in enumerate(row): table.cell(r, c).text str(value) return tableadd_data_table里Light Grid Accent 1是 python-docx 内置的带边框表格样式行数列数由len(rows)动态计算。如果模板里已经插入了目录后续新增章节需要同步刷新目录这个在最后一部分会处理。4.3 插入投资估算表与财务指标表表头和数据来自前文的cost_df和report_data。投资估算表直接遍历 DataFrame 的每一行把科目、金额、占比、说明写入 Word 表格headers [成本科目, 金额万元, 占比, 说明] table_rows [ [row[cost_item], f{row[amount_wan]:,.0f}, f{row[ratio]:.1%}, row[note]] for _, row in cost_df.iterrows() ] add_data_table(doc, headers, table_rows)财务指标表不需要遍历 DataFrame直接把报告数据字典里的格式化字符串取出。这种写法适合指标数量固定、每项只有一行的情况例如投资利润率17.5% 税后净现值xxxxx万元 内部收益率xx.xx%实际业务中财务指标表还会有基准收益率动态投资回收期等行直接在列表里追加(指标名, 值)元组即可。4.4 用 matplotlib 生成投资结构图并插入 docx报告正文里需要投资估算构成图和敏感性分析图。图形先用 matplotlib 保存成 PNG再通过doc.add_picture插入。关键是中文字体设置否则图里中文会变成方框。import matplotlib.pyplot as plt plt.rcParams[font.sans-serif] [SimHei] plt.rcParams[axes.unicode_minus] False fig, ax plt.subplots(figsize(6, 4)) ax.pie( cost_df[amount_wan], labelscost_df[cost_item], autopct%.1f%%, startangle90, counterclockFalse, ) ax.set_title(投资估算构成图) plt.tight_layout() plt.savefig(investment_structure.png, dpi150) plt.close(fig) doc.add_picture(investment_structure.png, widthCm(12)) doc.paragraphs[-1].alignment WD_ALIGN_PARAGRAPH.CENTERstartangle90让第一个扇区从 12 点方向开始counterclockFalse顺时针排布扇区这样阅读习惯更自然。dpi150是印刷质量下限后续如果要压缩 docx 体积可以降到 100。插入图片后doc.paragraphs[-1]是 add_picture 自动创建的段落直接把它设为居中即可。4.5 生成后校验检查残留占位符和空段落渲染完成后不要直接交付先重新打开 docx 检查一遍。最常见的两个问题字段字典缺 key 导致{{xxx}}残留在正文里以及表格列宽异常导致出现大量空段落。def verify_docx(path): check_doc Document(path) all_text \n.join(p.text for p in check_doc.paragraphs) for table in check_doc.tables: for row in table.rows: for cell in row.cells: all_text cell.text \n if {{ in all_text or }} in all_text: raise ValueError(docx 中存在未替换占位符) print(f校验通过: {path}, 段落数 {len(check_doc.paragraphs)}, 表格数 {len(check_doc.tables)})把段落文本和每个表格单元格文本都拼进同一个字符串再检查双花括号。这个函数在生成流程末尾调用有错误就抛异常保证移交到业务方手里的 docx 一定是干净可读的。5. 多方案批量出稿与 docx 目录自动刷新5.1 一个循环生成多个方案的可行性研究同一块地通常要做容积率高低两个方案对比不同方案只是容积率和楼面价不同。把生成报告的逻辑包成函数后批量出稿就变成循环def generate_report(project_name, land_area, plot_ratio, price): report GardenProjectReport(project_nameproject_name, land_area_m2land_area, plot_ratioplot_ratio, average_price_wan_m2price) report.calc_total_area() data report.to_field_dict() # 计算成本、财务指标、渲染docx doc build_docx(data) doc.save(f{project_name}-可行性研究报告.docx) for scheme in [ {name: 某房地产花园项目-A方案, land: 38000, ratio: 2.0, price: 1.65}, {name: 某房地产花园项目-B方案, land: 38000, ratio: 2.5, price: 1.70}, ]: generate_report(**scheme)每个方案是独立 docx互不影响。如果方案数量多建议再加一步把各方案的 IRR、NPV 汇总成对比表直接在项目级报告中作为附件插入。5.2 给 Word 加目录域打开时自动更新可行性研究报告动辄几十页目录是必需项。python-docx 没有现成的插入目录方法需要通过底层 XML 写入 TOC 域代码。TOC 域的原理是让 Word 在打开文档时根据标题样式重新扫描页码。from docx.oxml import OxmlElement from docx.oxml.ns import qn def add_toc_field(doc): paragraph doc.add_paragraph() fld_begin OxmlElement(w:fldChar) fld_begin.set(qn(w:fldCharType), begin) instr OxmlElement(w:instrText) instr.set(qn(xml:space), preserve) instr.text rTOC \o 1-3 \h \z \u fld_separate OxmlElement(w:fldChar) fld_separate.set(qn(w:fldCharType), separate) placeholder OxmlElement(w:t) placeholder.text 右键更新目录 fld_end OxmlElement(w:fldChar) fld_end.set(qn(w:fldCharType), end) run paragraph.add_run() run._r.extend([fld_begin, instr, fld_separate, placeholder, fld_end])完成插入后还要追加updateFields设置让 Word 首次打开文档时主动弹出是否更新目录并重排页码settings doc.settings.element update_fields OxmlElement(w:updateFields) update_fields.set(qn(w:val), true) settings.append(update_fields) doc.save(某房地产花园项目可行性研究报告.docx)updateFields加在doc.settings.element根节点上key 为w:updateFieldsValue 为布尔字符串。这个配置只对 Microsoft Word 客户端生效LibreOffice 打开时不一定自动重算仍需手动按F9更新目录。如果用户环境主要是 WPS建议在交付说明里写一句打开后按 F9 或右键更新目录。本文还有配套的精品资源点击获取
返回列表