Spaces:
Sleeping
Sleeping
File size: 6,401 Bytes
57cf043 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 |
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 файлов.
"""
@classmethod
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)
@classmethod
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
@classmethod
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]
|