import logging import re from bs4 import BeautifulSoup from components.parser.abbreviations import AbbreviationExtractor from components.parser.xml.structures import ParsedText logger = logging.getLogger(__name__) class XMLTextParser: """ Класс для парсинга текста из xml файлов. """ def __init__(self, soup: BeautifulSoup): """ Инициализация парсера. Args: soup: BeautifulSoup - суп, содержащий весь xml документ """ self.soup = soup self.abbreviation_extractor = AbbreviationExtractor() self.abbreviations = [] def parse(self) -> ParsedText: """ Парсинг текстовой информации из xml файла. Returns: ParsedText - структура с текстом, полученным из xml файла """ parsed_text = self._extract_text() # Извлекаем аббревиатуры из текста if parsed_text and parsed_text.content: text_content = parsed_text.to_text() self.abbreviations = self.abbreviation_extractor.extract_abbreviations_from_text(text_content) logger.debug(f"Extracted {len(self.abbreviations)} abbreviations from text") return parsed_text def get_abbreviations(self) -> list: """ Возвращает список аббревиатур, извлеченных из текста. Returns: list: Список аббревиатур """ return self.abbreviations def _extract_text(self) -> ParsedText: """ Извлечение и очистка текста из XML. Returns: ParsedText - структура, содержащая очищенный текст """ # Удаляем все таблицы for table in self.soup.find_all('w:tbl'): table.decompose() # Удаляем бинарные данные for bindata in self.soup.find_all('w:bindata'): bindata.decompose() # Удаляем элементы v:shape (изображения) for shape in self.soup.find_all('v:shape'): shape.decompose() # Удаляем заголовки документа doc_props = self.soup.find('o:documentproperties') if doc_props: doc_props.decompose() # Извлекаем абзацы (теги w:p) paragraphs = [] for p_tag in self.soup.find_all('w:p'): # Собираем все текстовые элементы в этом абзаце paragraph_text_elements = [] for text_tag in p_tag.find_all('w:t'): content = text_tag.get_text() # Пропускаем специальные элементы в фигурных скобках if content and '{' in content and '}' in content: if '{КСС}' in content or content.startswith('{СС_'): continue if content: paragraph_text_elements.append(content) if paragraph_text_elements: # Объединяем текст этого абзаца paragraph_text = ' '.join(paragraph_text_elements) # Очистка текста абзаца paragraph_text = paragraph_text.replace('&', '&') paragraph_text = paragraph_text.replace('<', '<') paragraph_text = paragraph_text.replace('>', '>') paragraph_text = paragraph_text.replace('MS-Word', '') paragraph_text = paragraph_text.replace('См. документ в ', '') paragraph_text = paragraph_text.replace( '------------------------------------------------------------------', '' ) # Удаление фигурных скобок и их содержимого paragraph_text = re.sub( r'\{[\.\,\#\:\=A-Za-zа-яА-Я\d\/\s\"\-\/\?\%\_\.\&\$]+\}', '', paragraph_text ) # Форматирование текста абзаца paragraph_text = re.sub(r'[\t ]+', ' ', paragraph_text) paragraph_text = paragraph_text.strip() if paragraph_text: # Добавляем только непустые абзацы paragraphs.append(paragraph_text) return ParsedText(content=paragraphs)