Милана
Первый релиз: интеллектуальный помощник инженера-строителя
96aaef2
import spacy
import re
from pathlib import Path
import json
from docx import Document
from collections import defaultdict
import spacy
# загрузка модели
try:
nlp = spacy.load('ru_core_news_md')
print("✅ Модель ru_core_news_md загружена")
except:
try:
nlp = spacy.load('ru_core_news_sm')
print("✅ Модель ru_core_news_sm загружена")
except:
# Автоматическая загрузка модели для Streamlit Cloud
import os
os.system('python -m spacy download ru_core_news_md')
nlp = spacy.load('ru_core_news_md')
print("✅ Модель ru_core_news_md загружена автоматически")
# РАСШИРЕННЫЙ СЛОВАРЬ МАТЕРИАЛОВ
MATERIALS = {
# Металлы и сплавы
'железо', 'железобетон', 'металл', 'сталь', 'алюминий', 'чугун', 'медь',
'латунь', 'бронза', 'титан', 'свинец', 'цинк', 'сплав', 'нихром', 'инвар',
# Виды стали
'оцинкованная сталь', 'нержавеющая сталь', 'сталь углеродистая', 'сталь легированная',
'сталь низколегированная', 'сталь высоколегированная', 'сталь коррозионно-стойкая',
'сталь жаропрочная', 'сталь инструментальная',
# Бетоны и растворы
'бетон', 'асфальтобетон', 'цемент', 'цементобетон', 'армопенобетон',
'пенобетон', 'газобетон', 'керамзитобетон', 'шлакобетон', 'раствор',
'полистиролбетон', 'газосиликат', 'пеносиликат', 'фибробетон', 'силикатобетон',
'жаростойкий бетон', 'кислотоупорный бетон', 'гидротехнический бетон',
# Минераловатные материалы
'минеральная вата', 'каменная вата', 'стекловолокно', 'стекловата',
'базальтовое волокно', 'шлаковата', 'войлок', 'маты минераловатные',
'плиты минераловатные', 'цилиндры минераловатные', 'полуцилиндры минераловатные',
'сегменты минераловатные', 'прошивные маты', 'рулонированные маты',
'ламелла-маты', 'иглопробивные маты', 'холсты', 'рулонное стекловолокно',
# Полимерные материалы
'пенополиуретан', 'пенополистирол', 'пенополиэтилен', 'пенопласт',
'пеностекло', 'пенополимерминерал', 'вспененный каучук', 'поропласт', 'полимербетон',
'пенополиизоцианурат', 'экструзионный пенополистирол', 'вспененный пенополистирол',
'вспененный полиэтилен', 'полиэтилен высокого давления', 'полиэтилен низкого давления',
'полипропилен', 'поливинилхлорид', 'ПВХ', 'фторопласт', 'капролон', 'полиамид',
# Керамические и композитные
'керамзит', 'перлит', 'перлитоцемент', 'совелит', 'керамика',
'огнеупорная керамика', 'керамогранит', 'керамблок', 'вермикулит', 'вспученный вермикулит',
'аглопорит', 'шунгизит', 'туф', 'пемза',
# Асбестосодержащие
'асбест', 'асбестоцемент', 'хризотиловое волокно', 'асбестовые шнуры',
'асбестосодержащие материалы', 'асбестоцементные листы', 'асбестовая бумага',
'асбестовый картон', 'асбестовая ткань',
# Рулонные и пленочные материалы
'стеклоткань', 'стеклохолст', 'фольга', 'алюминиевая фольга',
'полиэтиленовая пленка', 'рубероид', 'битумный лак', 'битум', 'стеклопластик',
'стеклорогожа', 'лакостеклоткань', 'стекловолокнистое полотно', 'кэшированный материал',
'полимерная плёнка', 'армированная плёнка', 'термоусадочная плёнка', 'пароизоляционная плёнка',
# Штукатурные
'штукатурка', 'декоративная штукатурка', 'гипсовая штукатурка', 'цементная штукатурка',
'известковая штукатурка', 'полимерная штукатурка',
# Древесные
'дерево', 'древесина', 'фанера', 'ДСП', 'ОСП', 'МДФ', 'брус', 'доска', 'пиломатериалы',
'бревно', 'клееный брус', 'древесно-стружечная плита', 'древесно-волокнистая плита',
'ДВП', 'ЛДСП', 'ЛВЛ',
# Утеплители
'утеплитель', 'теплоизоляция', 'гидроизоляция', 'пароизоляция', 'звукоизоляция',
'теплоизоляционный материал', 'гидроизоляционный материал', 'пароизоляционный материал',
'вспененный полимер', 'эковата', 'пенофол', 'изолон', 'пеноизол',
# Покрытия
'краска', 'эмаль', 'лак', 'грунтовка', 'антикоррозийное покрытие',
'полимерное покрытие', 'порошковая краска', 'мастика', 'герметик',
'клей', 'силиконовый герметик', 'акриловый герметик', 'полиуретановый герметик',
# Прочие
'пенопластовые изделия', 'пенополиуретановые изделия', 'полистиролбетонные блоки',
'газосиликатные блоки', 'пеносиликатные блоки', 'перлитовый песок', 'вермикулитовый песок',
'шлак', 'зола', 'шлакопортландцемент', 'пуццолановый цемент', 'глинозёмистый цемент',
'магнезиальный цемент', 'гипс', 'гипсокартон', 'гипсоволокнистый лист', 'ГВЛ',
'стекломагниевый лист', 'СМЛ', 'целлюлозно-волокнистый утеплитель'
}
# Дополнительные фразы (биграммы и триграммы)
MATERIAL_PHRASES = {
'оцинкованная сталь', 'нержавеющая сталь', 'каменная вата', 'минеральная вата',
'базальтовое волокно', 'вспененный каучук', 'алюминиевая фольга',
'полиэтиленовая пленка', 'стекловолокнистое полотно', 'битумный лак',
'кэшированный материал', 'перлитоцементные плиты', 'совелитовые плиты',
'пенополиуретан', 'пенополистирол', 'пенополиэтилен', 'пенополимерминерал',
'антикоррозийное покрытие', 'теплоизоляционный материал', 'гидроизоляционный материал',
'пароизоляционный материал', 'полимерное покрытие', 'порошковая краска',
'асбестовые шнуры', 'асбестоцементные листы', 'минераловатные плиты',
'минераловатные маты', 'минераловатные цилиндры', 'рулонированные маты',
'прошивные маты', 'иглопробивные маты', 'древесно-стружечная плита',
'вспененный полиэтилен', 'экструзионный пенополистирол', 'гипсокартон',
'стекломагниевый лист', 'пенопластовые изделия'
}
Construction_terms = {
'материалы': MATERIALS,
'конструкции': {
'тоннель', 'теплоизоляционная конструкция', 'покровный слой',
'пароизоляционный слой', 'предохранительный слой', 'выравнивающий слой',
'теплоизоляционный слой', 'опорные элементы', 'разгружающие устройства',
'крепление', 'температурный шов', 'фланцевое соединение', 'компенсатор',
'трубопровод', 'оборудование', 'газоход', 'воздуховод', 'канал',
'эстакада', 'галерея', 'фундамент', 'стена', 'перекрытие', 'опорные конструкции',
'крепежные детали', 'изоляция', 'многослойная конструкция', 'съемная конструкция'
},
'параметры': {
'толщина стенок', 'плотность', 'теплопроводность', 'коэффициент теплопроводности',
'паропроницаемость', 'температуростойкость', 'уплотнение', 'коэффициент уплотнения',
'горючесть', 'группа горючести', 'температура применения', 'срок эксплуатации',
'толщина изоляции', 'потери', 'плотность теплового потока', 'термическое сопротивление'
},
'нормативы': {
'ГОСТ', 'СНиП', 'СП', 'ТУ', 'ГОСТ Р', 'СанПиН', 'ВСН', 'ТСН', 'СТО', 'ЕН', 'ISO'
}
}
# Создаем множества лемм для быстрого поиска
LEMMA_MATERIALS = {term.lower() for term in Construction_terms['материалы']}
LEMMA_STRUCTURES = {term.lower() for term in Construction_terms['конструкции']}
LEMMA_PARAMETERS = {term.lower() for term in Construction_terms['параметры']}
LEMMA_STANDARDS = Construction_terms['нормативы']
def read_docx(file_path):
"""Чтение DOCX файла"""
doc = Document(file_path)
return '\n'.join([paragraph.text for paragraph in doc.paragraphs])
def read_file(file_path):
"""Универсальное чтение файла"""
if file_path.endswith('.docx'):
return read_docx(file_path)
else:
with open(file_path, 'r', encoding='utf-8') as f:
return f.read()
def parse_construction_document(file_path):
"""Парсинг строительной документации"""
print(f"📖 Чтение файла: {file_path}")
# Читаем файл
text = read_file(file_path)
print(f"✅ Загружено {len(text)} символов")
# Ограничиваем текст для скорости
text_for_analysis = text[:500000]
# Обработка через spaCy
doc = nlp(text_for_analysis)
# Результаты
result = {
'file': file_path,
'full_text': text[:10000] + "..." if len(text) > 10000 else text,
'stats': {
'characters': len(text),
'words': len([t for t in doc if not t.is_space]),
'sentences': len(list(doc.sents))
},
'materials': set(),
'structures': set(),
'parameters': set(),
'materials_lemmas': set(),
'structures_lemmas': set(),
'parameters_lemmas': set(),
'standards': [],
'temperatures': [],
'thicknesses': [],
'densities': [],
'sections': {}
}
# 1. Поиск разделов документа
section_pattern = r'\n(\d+(?:\.\d+)*)\s+([А-ЯЁ][^\n]{5,100})'
for match in re.finditer(section_pattern, text[:50000]):
num = match.group(1)
title = match.group(2).strip()
result['sections'][num] = title[:80]
# 2. Поиск строительных терминов с использованием лемматизации
for token in doc:
if token.is_punct or token.is_space:
continue
lemma = token.lemma_.lower()
text_orig = token.text
# Поиск по леммам
if lemma in LEMMA_MATERIALS:
result['materials'].add(text_orig)
result['materials_lemmas'].add(lemma)
if lemma in LEMMA_STRUCTURES:
result['structures'].add(text_orig)
result['structures_lemmas'].add(lemma)
if lemma in LEMMA_PARAMETERS:
result['parameters'].add(text_orig)
result['parameters_lemmas'].add(lemma)
# Поиск по биграммам (два слова подряд)
if token.i + 1 < len(doc):
bigram = f"{token.text} {doc[token.i + 1].text}".lower()
if bigram in LEMMA_MATERIALS or bigram in MATERIAL_PHRASES:
result['materials'].add(bigram)
result['materials_lemmas'].add(bigram)
# Поиск по триграммам (три слова подряд)
if token.i + 2 < len(doc):
trigram = f"{token.text} {doc[token.i + 1].text} {doc[token.i + 2].text}".lower()
if trigram in MATERIAL_PHRASES:
result['materials'].add(trigram)
# Поиск нормативов
if token.text.upper() in LEMMA_STANDARDS:
if token.i + 1 < len(doc) and doc[token.i + 1].like_num:
result['standards'].append(f"{token.text} {doc[token.i + 1].text}")
else:
result['standards'].append(token.text)
# 1. Поиск по фразам из MATERIAL_PHRASES (только словарные)
for phrase in MATERIAL_PHRASES:
if phrase in text.lower():
result['materials'].add(phrase)
result['materials_lemmas'].add(phrase)
# 2. Прямой поиск материалов из MATERIALS (только словарные)
for material in MATERIALS:
if material in text.lower():
if material not in result['materials_lemmas']:
result['materials'].add(material)
result['materials_lemmas'].add(material)
# 3. Поиск биграмм из текста (только если биграмма из словаря)
words = text.lower().split()
for i in range(len(words) - 1):
bigram = f"{words[i]} {words[i + 1]}"
if bigram in LEMMA_MATERIALS or bigram in MATERIAL_PHRASES:
result['materials'].add(bigram)
result['materials_lemmas'].add(bigram)
# 4. Поиск по маркерам
markers = ['материал', 'покрытие', 'изоляция', 'утеплитель', 'плита', 'мат', 'слой']
for sent in doc.sents:
sent_text = sent.text.lower()
for marker in markers:
if marker in sent_text:
for token in sent:
if token.pos_ == 'NOUN' and len(token.text) > 3:
lemma = token.lemma_.lower()
# ДОБАВЛЯЕМ ТОЛЬКО ЕСЛИ УЖЕ ЕСТЬ В СЛОВАРЕ
if lemma in LEMMA_MATERIALS and lemma not in result['materials_lemmas']:
result['materials'].add(token.text)
result['materials_lemmas'].add(lemma)
print(f" 🔍 Добавлен из словаря по маркеру: {lemma}")
# 5. Поиск материалов в контексте (дополнительно)
for sent in doc.sents:
sent_text = sent.text.lower()
for material in MATERIALS:
if material in sent_text and material not in result['materials']:
result['materials'].add(material)
result['materials_lemmas'].add(material)
# 6. Числовые значения
temp_pattern = r'[−-]?\d+(?:[.,]\d+)?\s*°[CС]'
result['temperatures'] = list(set(re.findall(temp_pattern, text)))
thick_pattern = r'\d+(?:[.,]\d+)?\s*мм'
result['thicknesses'] = list(set(re.findall(thick_pattern, text)))
density_pattern = r'\d+(?:[.,]\d+)?\s*кг/м³'
result['densities'] = list(set(re.findall(density_pattern, text)))
# Преобразуем множества в списки
result['materials'] = list(result['materials'])
result['structures'] = list(result['structures'])
result['parameters'] = list(result['parameters'])
result['standards'] = list(set(result['standards']))
# Статистика найденного
print(f"📊 Найдено материалов (оригинальных форм): {len(result['materials'])}")
print(f"📊 Уникальных материалов (лемм): {len(result['materials_lemmas'])}")
print(f"📊 Найдено конструкций: {len(result['structures'])}")
print(f"📊 Найдено параметров: {len(result['parameters'])}")
print(f"📊 Найдено нормативов: {len(result['standards'])}")
return result
def save_to_json(result, output_file):
"""Сохраняет результат в JSON"""
with open(output_file, 'w', encoding='utf-8') as f:
json.dump(result, f, ensure_ascii=False, indent=2, default=str)
print(f"💾 Результат сохранен в {output_file}")
# Для тестирования
if __name__ == "__main__":
file_path = "data/raw/sp_61_13330_2012_27052024.docx"
if Path(file_path).exists():
result = parse_construction_document(file_path)
save_to_json(result, "construction_analysis.json")
print("\n✅ Анализ завершен!")
else:
print(f"❌ Файл {file_path} не найден!")