Spaces:
Sleeping
Sleeping
import logging | |
import os | |
from pathlib import Path | |
from bs4 import BeautifulSoup | |
from tqdm import tqdm | |
from components.parser.docx_to_xml import DocxToXml | |
from components.parser.xml.structures import ParsedXML, ParsedXMLs | |
from components.parser.xml.xml_info_parser import XMLInfoParser | |
from components.parser.xml.xml_table_parser import XMLTableParser | |
from components.parser.xml.xml_text_parser import XMLTextParser | |
logger = logging.getLogger(__name__) | |
class XMLParser: | |
""" | |
Класс для парсинга xml файлов. | |
""" | |
def parse_all( | |
cls, | |
filepath: os.PathLike, | |
encoding: str = 'cp866', | |
include_content: bool = False, | |
ignore_files_contains: list[str] = [], | |
use_tqdm: bool = False, | |
) -> ParsedXMLs: | |
""" | |
Парсинг всех xml файлов в директории. | |
Args: | |
filepath: os.PathLike - путь к директории с xml файлами | |
encoding: str - кодировка файлов | |
include_content: bool - включать ли содержимое файлов в результат | |
ignore_files_contains: list[str] - игнорировать файлы, содержащие эти строки в названии | |
use_tqdm: bool - использовать ли прогресс-бар | |
Returns: | |
ParsedXMLs - данные, полученные из всех xml файлов | |
""" | |
files = cls._get_recursive_files(filepath, ignore_files_contains) | |
logger.info(f"Found {len(files)} files to parse") | |
if use_tqdm: | |
files = tqdm(files, desc='Парсинг файлов') | |
parsed_xmls = [cls.parse(file, encoding, include_content) for file in files] | |
logger.info(f"Parsed {len(parsed_xmls)} files") | |
parsed_xmls = [ | |
xml | |
for xml in parsed_xmls | |
if ( | |
xml is not None | |
and not any( | |
ignore_file in xml.name for ignore_file in ignore_files_contains | |
) | |
) | |
] | |
logger.info(f"Filtered {len(parsed_xmls)} files") | |
return ParsedXMLs(parsed_xmls) | |
def parse( | |
cls, | |
filepath: os.PathLike, | |
encoding: str = 'utf-8', | |
include_content: bool = False, | |
) -> ParsedXML | None: | |
""" | |
Парсинг xml файла. | |
Args: | |
filepath: os.PathLike - путь к xml файлу | |
encoding: str - кодировка файла | |
include_content: bool - включать ли содержимое файла в результат | |
Returns: | |
ParsedXML - данные, полученные из xml файла | |
""" | |
if filepath.suffix in ['.docx', '.DOCX']: | |
logger.info(f"Parsing docx file {filepath}") | |
try: | |
xml_text = DocxToXml(filepath).extract_document_xml() | |
logger.info(f"Parsed docx file {filepath}") | |
except Exception as e: | |
logger.error(f"Error parsing docx file {filepath}: {e}") | |
return None | |
else: | |
with open(filepath, 'r', encoding=encoding) as file: | |
xml_text = file.read() | |
soup = BeautifulSoup(xml_text, features='xml') | |
# Создаем парсер информации и получаем базовые данные | |
info_parser = XMLInfoParser(soup, filepath) | |
parsed_xml = info_parser.parse() | |
logger.debug(f"Parsed info for {filepath}") | |
if not parsed_xml: | |
logger.warning(f"Failed to parse info for {filepath}") | |
return None | |
if not include_content: | |
logger.debug(f"Skipping content for {filepath}") | |
return parsed_xml | |
# Парсим таблицы и текст, сохраняя структурированные данные | |
table_parser = XMLTableParser(soup) | |
text_parser = XMLTextParser(soup) | |
# Сохраняем структурированные данные вместо текста | |
parsed_xml.tables = table_parser.parse() | |
logger.debug(f"Parsed table content for {filepath}") | |
parsed_xml.text = text_parser.parse() | |
logger.debug(f"Parsed text content for {filepath}") | |
# Собираем аббревиатуры из таблиц и текста | |
abbreviations = [] | |
# Получаем аббревиатуры из таблиц | |
table_abbreviations = table_parser.get_abbreviations() | |
if table_abbreviations: | |
logger.debug(f"Got {len(table_abbreviations)} abbreviations from tables") | |
abbreviations.extend(table_abbreviations) | |
# Получаем аббревиатуры из текста | |
text_abbreviations = text_parser.get_abbreviations() | |
if text_abbreviations: | |
logger.debug(f"Got {len(text_abbreviations)} abbreviations from text") | |
abbreviations.extend(text_abbreviations) | |
# Сохраняем все аббревиатуры в ParsedXML | |
if abbreviations: | |
logger.info(f"Total abbreviations extracted: {len(abbreviations)}") | |
parsed_xml.abbreviations = abbreviations | |
# Применяем аббревиатуры к содержимому документа | |
parsed_xml.apply_document_abbreviations() | |
logger.debug(f"Applied abbreviations to document content") | |
return parsed_xml | |
def _get_recursive_files( | |
cls, | |
path_to_dir: os.PathLike, | |
ignore_files_contains: list[str] = [], | |
) -> list[os.PathLike]: | |
""" | |
Получение всех xml файлов в директории любой вложенности. | |
""" | |
path_to_dir = Path(path_to_dir) | |
relative_paths = [ | |
path.relative_to(path_to_dir) | |
for path in path_to_dir.glob('**/*.xml') | |
if not any( | |
ignore_file in path.name for ignore_file in ignore_files_contains | |
) | |
] | |
return [Path(path_to_dir) / path for path in relative_paths] | |