预制构件PDF图纸BOM清单提取与填写
概述
本技能用于从预制混凝土构件(叠合板等)的A4格式PDF详图中,自动提取材料表数据并填写到PCMES BOM导入模板Excel中。
适用场景
- PDF图纸为A4幅面的预制构件详图,每页一个构件
- 每页包含5类表格:底板参数表、底板配筋表、桁架钢筋表、预埋配件明细表、构件数量统计表
- 输出为PCMES 2.0标准格式的BOM导入Excel模板
- 钢筋类型或物料类型超出模板列数时,自动在右侧追加新列
工作流程
第1步:环境准备
确认Python环境已安装所需依赖:
# 使用managed python创建venv(如果尚未创建)
<python_path> -m venv <venv_path>
<venv_pip> install pdfplumber openpyxl PyMuPDF pillow
依赖包:PyMuPDF(fitz,PDF文本提取)、openpyxl(Excel读写)。
第2步:调查PDF结构
每份PDF的构件编号格式、楼栋、楼层、混凝土标号可能不同,需先调查:
import fitz, re
doc = fitz.open(pdf_path)
for i in range(len(doc)):
text = doc[i].get_text()
lines = [l.strip() for l in text.split('\n') if l.strip()]
# 1. 构件编号:匹配 r'(\d+-\d+F-\d+)详图'
# 2. 桁架类型:匹配 r'A\d{2,}(-\d+)?$'(至少2位数字,避免误匹配A2等图纸编号)
# 3. 预埋配件:匹配 r'XH\d+'
# 4. 混凝土标号:构件数量统计表中的C30/C35等
# 5. 楼层信息:含 'F~' 的行
# 6. 钢筋类型:匹配 r'\d+[a-z]?#(?:钢筋|加强筋)' 或 'JQJ'
关键注意:
A2是图纸编号标签(出现在页面底部),不是桁架类型。桁架正则要求至少2位数字:r'A\d{2,}(-\d+)?$'- 钢筋名称可能出现拼接(如"2#钢筋4#加强筋"在同一行),需用
split_concatenated_rebar_names()拆分 - 部分页面可能没有桁架钢筋表(纯底板构件)
第3步:提取PDF数据
使用 scripts/extract_and_fill_bom.py 脚本,或按以下逻辑自行编写:
3.1 底板参数表
正则匹配 底板参数表 后跟构件编号,然后依次提取6个数值:
底板厚、叠合层厚、实际板跨、实际板宽、混凝土体积、底板自重。
3.2 底板配筋表
匹配 底板配筋表\n 到 桁架钢筋表|预埋配件明细表|构件数量统计表|合计 之间的文本。
解析逻辑:
- 过滤掉表头行(含"钢筋编号"、"钢筋规格"等关键词的行)
- 用
split_concatenated_rebar_names()拆分拼接的钢筋名称 - 逐行扫描,遇到钢筋名称(如"1#钢筋"、"1a#钢筋"、"JQJ")后,收集后续所有数值
- 最后3个数值为:单根长、总长、总重
- 数量 = round(总长 / 单根长)
3.3 桁架钢筋表
匹配 桁架钢筋表\n 到 预埋配件明细表|构件数量统计表|合计 之间的文本。
解析逻辑:
- 过滤表头行和构件编号行
- 过滤
A2等图纸编号(非桁架类型) - 匹配桁架名称:
r'A\d{2,}(-\d+)?$'(A80, A80-1, A90, A90-1, A130, A130-1, A140, A140-1等) - 收集后续数值:道数、单道长度、单根重、总长、总重
3.4 预埋配件明细表
匹配 预埋配件明细表\n 到 构件数量统计表|合计 之间的文本。
解析逻辑:按 XH\d+ 编号 → 名称 → 数量 → 备注 的4行一组解析。
配件编码对照:
- XH1 → 八角照明金属线盒
- XH4 → 消防金属线盒
- XH5 → 弱电金属线盒
- XH6 → 动力金属线盒
3.5 构件数量统计表
匹配 构件数量统计表\n 到 合计 之间的文本。提取楼层(如"4F~11F")、混凝土强度(如C30)。
第4步:填写Excel
4.1 列布局
详见 references/column_mapping.md。核心结构:
| 列范围 | 内容 | 格式 | |--------|------|------| | A-K | 构件基本信息 | 类型/型号/体积/重量/混凝土/尺寸/模具/备注 | | L-O | 楼层信息 | 楼栋/单层数量/起始层/终止层 | | P-X | 基础钢筋(3组×3列) | 1#钢筋(Φ10)、2#钢筋(Φ10)、JQJ(Φ8) | | Y-AG | 基础桁架钢筋(3组×3列) | A80、A80-1、A90 | | AH-AK | 预埋配件(4列) | XH4/XH1/XH5/XH6 | | AL+ | 扩展列(按需添加) | 新桁架/新钢筋类型 |
钢筋列格式(3列):单根长(mm) / 数量(根) / 总重(KG) 桁架列格式(3列):单道长度 / 道数 / 总重 物料列格式(1列):数量
4.2 扩展列规则
当出现模板中没有的钢筋/桁架类型时,从AL列开始向右追加:
- 先放新桁架类型(3列一组),再放新钢筋类型(3列一组)
- 表头第3行写规格(如Φ8、Φ14)或桁架代号(如A140),不写钢筋代号
- 新列样式从同类型已有列复制(钢筋从P列,桁架从Y列,物料从AH列)
4.3 清理模板公式
模板中AI-AL列可能有 #REF! 公式(引用已删除的工作表),需用空字符串 '' 覆盖(用 None 可能不生效)。
第5步:表格美化
使用 scripts/beautify_bom.py 脚本,或按以下逻辑:
- 删除说明行:如需删除原第1-2行(标题+说明),必须先
unmerge_cells所有合并区域,再delete_rows,最后重新创建合并区域(openpyxl的delete_rows不会自动调整合并单元格范围) - 表头配色:第1行深蓝底(#4472C4)白字,第2-3行浅蓝底(#8EA9DB)白字
- 数据行:交替底色(白色/#F2F7FC)
- 边框:统一浅蓝细线(#B4C6E7)
- 列宽:型号14、尺寸10、数量8、长度12、重量10
- 冻结窗格:C4(固定表头3行+前2列)
- 隐藏网格线:
ws.sheet_view.showGridLines = False
第6步:验证
填写完成后需交叉验证:
- 构件总数是否与PDF页数一致
- 每个构件的钢筋/桁架类型是否与PDF一致
- 楼栋、楼层、混凝土标号是否正确
- 抽查3-5个构件的数值与PDF原文比对
使用脚本
extract_and_fill_bom.py
主提取脚本,接受命令行参数:
python extract_and_fill_bom.py <pdf_path> <xlsx_template> <xlsx_output> <building> <floor_start> <floor_end> <concrete>
示例:
python extract_and_fill_bom.py \
"C:/Users/.../3#楼4-11层详图0710-1.pdf" \
"C:/Users/.../PCMES_2.0_xxx_BOM导入_9928.xlsx" \
"C:/Users/.../output.xlsx" \
"3#" "4F" "11F" "C30"
脚本会自动:
- 扫描所有PDF页面,提取5类表格数据
- 统计所有钢筋/桁架类型
- 在Excel模板中添加扩展列
- 填写数据并复制样式
- 打印验证摘要
beautify_bom.py
美化脚本,接受命令行参数:
python beautify_bom.py <xlsx_path> [--delete-rows <N>]
示例:
python beautify_bom.py "C:/Users/.../output.xlsx" --delete-rows 2
常见问题
Q: PDF提取不到文本?
A: 检查PDF是否为扫描件(图片型PDF)。如果是,需先用OCR处理。本技能仅支持文本型PDF。
Q: 钢筋名称拼接在一起?
A: 如"2#钢筋4#加强筋"在同一行,使用 split_concatenated_rebar_names() 函数拆分。该函数用正则 r'\d+[a-z]?#(?:钢筋|加强筋)' 的 finditer 定位每个名称的起止位置。
Q: delete_rows后合并单元格错位?
A: openpyxl的 delete_rows 不会自动调整 merged_cells.ranges。必须先 unmerge_cells 所有合并区域,删行后再重新 merge_cells。
Q: 不同构件同一钢筋类型规格不同?
A: 钢筋表头写最常见规格(取众数),不单独加规格列。如1a#钢筋多数为Φ8,个别为Φ10/Φ12,表头写Φ8。
Q: 桁架类型误匹配A2?
A: A2是图纸编号,不是桁架。正则改为 r'A\d{2,}(-\d+)?$',要求至少2位数字。
Q: 模板中有#REF!公式?
A: AI-AL列可能有引用已删除工作表的VLOOKUP公式。用空字符串 '' 覆盖,不要用 None(可能不生效)。
微信扫一扫