从施工规范中提取结构化数据,解析CSI章节、要求、报审文件及产品数据。
构造规格提取器
功能概述
构造规格提取器是一项面向实际任务的技能,主要用于Overview.;Extractive structure data from 构造规格文档.;
核心要点
- Parse CSI MasterFormat 路段, 确定要求, 提交文件, 产品。
- 它将相关步骤、工具调用和结果整理方式集中到统一流程中,帮助使用者更快完成目标并减少重复操作。
- 使用时应结合输入条件选择合适的执行方式,核对必要参数、依赖环境与输出内容,并按原始要求处理异常情况。
使用与执行
该技能适合需要稳定复用相关能力的场景,可作为自动化工作流的一部分,也便于后续检查、调整和扩展。从功能定位来看,该技能强调把分散的操作要求整理成清晰、可复用的处理流程,使用户能够围绕既定目标快速准备输入、选择执行方式并获得结构化结果。实际使用前应先确认任务范围、数据来源、运行环境、必要权限和关键参数,再依据技能说明逐步执行;
结果检查与注意事项
若输入条件不完整,应先补齐信息或采用保守配置,避免因错误假设导致结果偏离需求。执行过程中需要关注工具调用是否成功、接口或依赖是否可用、输出格式是否符合预期,并对异常提示、缺失字段和边界情况进行处理;涉及批量任务时,还应保存进度,避免中断后重复操作。
建筑工程规范提取器
概述
从建筑工程规范文档中提取结构化数据。解析 CSI MasterFormat 章节,识别技术要求、报审文件(submittals)、产品标准,并汇总可用于成本估算与采购的可执行数据。
商业价值
自动化规范提取可实现:
- 更快的成本估算:快速识别工程范围与技术要求
- 更精准的采购执行:精确提取产品规格参数
- 报审文件追踪:识别全部必需提交的报审项
- 合规性核查:将规范条款与相关标准进行比对验证
技术实现
from dataclasses import dataclass, field
from typing import List, Dict, Any, Optional
import re
import pdfplumber
from pathlib import Path
@dataclass
class SpecSection:
number: str # 例如:"03 30 00"
title: str
part1_general: Dict[str, Any]
part2_products: Dict[str, Any]
part3_execution: Dict[str, Any]
raw_text: str
@dataclass
class ProductRequirement:
section: str
manufacturer: str
product_name: str
model: str
standards: List[str]
properties: Dict[str, str]
@dataclass
class SubmittalRequirement:
section: str
submittal_type: str # 如 shop drawings、samples、product data 等
description: str
timing: str
copies: int
@dataclass
class SpecExtractionResult:
document_name: str
total_pages: int
sections: List[SpecSection]
products: List[ProductRequirement]
submittals: List[SubmittalRequirement]
standards_referenced: List[str]
class SpecificationExtractor:
"""从建筑工程规范中提取结构化数据。"""
# CSI MasterFormat 匹配模式
CSI_SECTION_PATTERN = r'^(d{2}s?d{2}s?d{2})s*[-–]s*(.+?)$'
PART_PATTERN = r'^PARTs+(d+)s*[-–]s*(.+?)$'
ARTICLE_PATTERN = r'^(d+.d+)s+([A-Z][A-Zs]+)$'
# 报审类型关键词映射
SUBMITTAL_TYPES = {
'shop drawings': 'Shop Drawings',
'product data': 'Product Data',
'samples': 'Samples',
'certificates': 'Certificates',
'test reports': 'Test Reports',
'manufacturer instructions': 'Manufacturer Instructions',
'warranty': 'Warranty',
'maintenance data': 'Maintenance Data',
'mock-ups': 'Mock-ups',
}
# 常见标准制定机构正则模式
STANDARD_PATTERNS = [
r'ASTMs+[A-Z]d+',
r'ANSIs+[A-Z]?d+',
r'ACIs+d+',
r'AISCs+d+',
r'AWSs+[A-Z]d+',
r'ASCEs+d+',
r'ULs+d+',
r'FMs+d+',
r'NFPAs+d+',
r'IBCs+d+',
]
def __init__(self):
self.sections: Dict[str, SpecSection] = {}
def extract_from_pdf(self, pdf_path: str) -> SpecExtractionResult:
"""从 PDF 文件中提取规范数据。"""
path = Path(pdf_path)
all_text = ""
page_count = 0
with pdfplumber.open(pdf_path) as pdf:
page_count = len(pdf.pages)
for page in pdf.pages:
text = page.extract_text() or ""
all_text += text + "nn"
# 解析章节
sections = self._parse_sections(all_text)
# 提取产品需求
products = self._extract_products(sections)
# 提取报审需求
submittals = self._extract_submittals(sections)
# 提取引用标准
standards = self._extract_standards(all_text)
return SpecExtractionResult(
document_name=path.name,
total_pages=page_count,
sections=sections,
products=products,
submittals=submittals,
standards_referenced=standards
)
def _parse_sections(self, text: str) -> List[SpecSection]:
"""从规范文本中解析 CSI 章节。"""
sections = []
lines = text.split('n')
current_section = None
current_part = None
current_content = []
for line in lines:
line = line.strip()
if not line:
continue
# 检查是否为章节标题
section_match = re.match(self.CSI_SECTION_PATTERN, line, re.IGNORECASE)
if section_match:
# 保存上一章节
if current_section:
sections.append(self._finalize_section(current_section, current_content))
current_section = {
'number': section_match.group(1).replace(' ', ''),
'title': section_match.group(2).strip(),
'parts': {}
}
current_content = []
current_part = None
continue
# 检查是否为 Part 标题
part_match = re.match(self.PART_PATTERN, line, re.IGNORECASE)
if part_match and current_section:
part_num = part_match.group(1)
part_name = part_match.group(2).strip()
current_part = f"part{part_num}"
current_section['parts'][current_part] = {
'name': part_name,
'content': []
}
continue
# 将内容追加至当前 Part
if current_section and current_part:
current_section['parts'][current_part]['content'].append(line)
elif current_section:
current_content.append(line)
# 保存最后一个章节
if current_section:
sections.append(self._finalize_section(current_section, current_content))
return sections
def _finalize_section(self, section_data: Dict, general_content: List[str]) -> SpecSection:
"""使用已解析的 Parts 数据完成章节对象构建。"""
parts = section_data.get('parts', {})
part1 = self._parse_part_content(parts.get('part1', {}).get('content', []))
part2 = self._parse_part_content(parts.get('part2', {}).get('content', []))
part3 = self._parse_part_content(parts.get('part3', {}).get('content', []))
return SpecSection(
number=section_data['number'],
title=section_data['title'],
part1_general=part1,
part2_products=part2,
part3_execution=part3,
raw_text='n'.join(general_content)
)
def _parse_part_content(self, content: List[str]) -> Dict[str, Any]:
"""将 Part 内容解析为结构化数据。"""
result = {
'articles': {},
'items': []
}
current_article = None
for line in content:
# 检查是否为 Article 标题
article_match = re.match(self.ARTICLE_PATTERN, line)
if article_match:
current_article = article_match.group(1)
result['articles'][current_article] = {
'title': article_match.group(2),
'items': []
}
continue
# 追加至当前 Article 或通用 items 列表
if current_article and current_article in result['articles']:
result['articles'][current_article]['items'].append(line)
else:
result['items'].append(line)
return result
def _extract_products(self, sections: List[SpecSection]) -> List[ProductRequirement]:
"""从 Part 2 中提取产品需求。"""
products = []
for section in sections:
part2 = section.part2_products
for article_num, article in part2.get('articles', {}).items():
if 'MANUFACTURERS' in article['title'].upper():
for item in article['items']:
# 提取制造商名称
if item.strip().startswith(('A.', 'B.', 'C.', '1.', '2.', '3.')):
mfr_name = re.sub(r'^[A-Zd]+.s*', '', item).strip()
products.append(ProductRequirement(
section=section.number,
manufacturer=mfr_name,
product_name='',
model='',
standards=[],
properties={}
))
elif 'MATERIALS' in article['title'].upper() or 'PRODUCTS' in article['title'].upper():
for item in article['items']:
# 提取材料要求
standards = self._extract_standards(item)
if standards:
products.append(ProductRequirement(
section=section.number,
manufacturer='',
product_name=item[:100],
model='',
standards=standards,
properties={}
))
return products
def _extract_submittals(self, sections: List[SpecSection]) -> List[SubmittalRequirement]:
"""从 Part 1 中提取报审需求。"""
submittals = []
for section in sections:
part1 = section.part1_general
for article_num, article in part1.get('articles', {}).items():
if 'SUBMITTAL' in article['title'].upper():
for item in article['items']:
item_lower = item.lower()
for keyword, submittal_type in self.SUBMITTAL_TYPES.items():
if keyword in item_lower:
submittals.append(SubmittalRequirement(
section=section.number,
submittal_type=submittal_type,
description=item.strip(),
timing='Prior to fabrication',
copies=3
))
break
return submittals
def _extract_standards(self, text: str) -> List[str]:
"""从文本中提取被引用的标准编号。"""
standards = []
for pattern in self.STANDARD_PATTERNS:
matches = re.findall(pattern, text, re.IGNORECASE)
standards.extend(matches)
return list(set(standards))
def generate_submittal_log(self, result: SpecExtractionResult) -> str:
"""根据提取结果生成报审日志。"""
lines = ["# Submittal Log", ""]
lines.append(f"**Project Specs:** {result.document_name}")
lines.append(f"**Total Submittals:** {len(result.submittals)}")
lines.append("")
lines.append("| # | Section | Type | Description | Status |")
lines.append("|---|---------|------|-------------|--------|")
for i, sub in enumerate(result.submittals, 1):
desc = sub.description[:50] + "..." if len(sub.description) > 50 else sub.description
lines.append(f"| {i} | {sub.section} | {sub.submittal_type} | {desc} | Pending |")
return "n".join(lines)
def generate_product_schedule(self, result: SpecExtractionResult) -> str:
"""根据提取结果生成产品清单。"""
lines = ["# Product Schedule", ""]
# 按章节分组
by_section = {}
for prod in result.products:
if prod.section not in by_section:
by_section[prod.section] = []
by_section[prod.section].append(prod)
for section, products in sorted(by_section.items()):
lines.append(f"## Section {section}")
lines.append("")
for prod in products:
if prod.manufacturer:
lines.append(f"- **Manufacturer:** {prod.manufacturer}")
if prod.product_name:
lines.append(f"- **Product:** {prod.product_name}")
if prod.standards:
lines.append(f"- **Standards:** {', '.join(prod.standards)}")
lines.append("")
return "n".join(lines)
def generate_report(self, result: SpecExtractionResult) -> str:
"""生成完整的规范提取报告。"""
lines = ["# Specification Extraction Report", ""]
lines.append(f"**Document:** {result.document_name}")
lines.append(f"**Pages:** {result.total_pages}")
lines.append(f"**Sections Found:** {len(result.sections)}")
lines.append("")
# 章节摘要
lines.append("## Sections Extracted")
for section in result.sections:
lines.append(f"- **{section.number}** - {section.title}")
lines.append("")
# 引用标准
if result.standards_referenced:
lines.append("## Standards Referenced")
for std in sorted(set(result.standards_referenced)):
lines.append(f"- {std}")
lines.append("")
# 报审摘要
lines.append("## Submittals Required")
lines.append(f"Total: {len(result.submittals)}")
by_type = {}
for sub in result.submittals:
by_type[sub.submittal_type] = by_type.get(sub.submittal_type, 0) + 1
for t, count in sorted(by_type.items()):
lines.append(f"- {t}: {count}")
lines.append("")
# 产品/制造商摘要
lines.append("## Products/Manufacturers")
lines.append(f"Total: {len(result.products)}")
return "n".join(lines)
快速开始
# 初始化提取器
extractor = SpecificationExtractor()
# 从 PDF 提取数据
result = extractor.extract_from_pdf("Project_Specifications.pdf")
print(f"Found {len(result.sections)} sections")
print(f"Found {len(result.submittals)} submittals")
print(f"Found {len(result.products)} product requirements")
# 生成报审日志
submittal_log = extractor.generate_submittal_log(result)
print(submittal_log)
# 生成产品清单
product_schedule = extractor.generate_product_schedule(result)
print(product_schedule)
# 生成完整报告
report = extractor.generate_report(result)
print(report)
依赖项
pip install pdfplumber
热门AI工具
相关专题
本专题聚焦Go-Zero的API开发,拆解.api描述文件的语法结构、路由与分组定义、参数绑定与校验规则,说明统一响应体、JWT鉴权、跨域与接口文档生成的落地方式,并给出网关聚合与接口测试的实践建议。
0
2026.11.12
本专题围绕Go-Zero的安装与工具链配置展开,讲清Go版本要求、goctl安装方式、代理镜像、protoc依赖与环境变量设置,说明多版本共存、升级回退与CI流水线安装的做法,并整理安装阶段的常见报错与验证步骤。
0
2026.11.12
本专题整理Go-Zero的入门知识,从框架定位、环境准备到跑通第一个API服务,讲清目录分层、配置加载、日志与中间件的基本用法,并对比Gin、Kratos的差异,帮助Go开发者建立整体认知,判断它是否适合当前项目。
0
2026.11.12
本专题从实际API接口调试场景出发,讲解Postman身份认证的完整配置流程,包括常见认证协议介绍、授权参数设置、访问令牌填写、OAuth 2.0授权及认证失败排查,帮助用户正确完成接口授权与访问验证。
0
2026.11.06
围绕Postman HTTP请求头配置展开,讲解Headers选项的具体使用方式,包括Key和Value参数填写、请求头信息添加、常见Header字段设置及请求头修改方法,帮助用户解决接口调试过程中遇到的请求头配置问题。
0
2026.11.06
详细介绍Postman接口请求的创建与发送方法,涵盖GET和POST请求、URL配置、请求参数、Headers请求头、Body请求体设置以及响应结果查看,帮助用户掌握Postman接口请求的完整操作流程。
0
2026.11.06
DBeaver数据导入专题,整理Excel、CSV、SQL、TXT等常见文件的数据导入方法,涵盖目标数据库选择、数据表创建、字段映射、数据类型设置及导入参数配置等操作,帮助用户将本地文件中的数据写入数据库,并了解批量导入、重复数据处理和导入失败等常见问题的解决方法。
0
2026.11.05
DBeaver安全防护专题,围绕数据库连接安全、账号密码保护和访问权限管理展开,整理SSL/TLS加密连接、SSH隧道、数据库凭据保存及证书验证等配置方法,同时介绍敏感信息保护、远程连接风险和高权限账号使用等注意事项,帮助用户提高数据库日常管理的安全性。
0
2026.11.05
本专题聚焦DBeaver数据库连接配置与管理,覆盖从新建连接、选择数据库类型到填写连接参数和验证连接的完整流程。针对MySQL、PostgreSQL、SQL Server等常见数据库,整理相应的连接设置方法,并提供驱动缺失、认证失败和连接异常等问题的排查方向。
0
2026.11.04
热门下载
相关下载
精品课程
共6课时 | 12.1万人学习
共79课时 | 161.8万人学习
共6课时 | 54.7万人学习