tmp: код etpgrf перенесен внутрь проекта...

This commit is contained in:
2026-01-05 22:43:57 +03:00
parent 0a4fcb44be
commit 9740887359
14 changed files with 2416 additions and 0 deletions

View File

@@ -0,0 +1,76 @@
# etpgrf/sanitizer.py
# Модуль для очистки и нормализации HTML-кода перед типографикой.
import logging
from bs4 import BeautifulSoup
from .config import (SANITIZE_ALL_HTML, SANITIZE_ETPGRF, SANITIZE_NONE,
HANGING_PUNCTUATION_CLASSES, PROTECTED_HTML_TAGS)
logger = logging.getLogger(__name__)
class SanitizerProcessor:
"""
Выполняет очистку HTML-кода в соответствии с заданным режимом.
"""
def __init__(self, mode: str | bool | None = SANITIZE_NONE):
"""
:param mode: Режим очистки:
- 'etp' (SANITIZE_ETPGRF): удаляет только разметку висячей пунктуации.
- 'html' (SANITIZE_ALL_HTML): удаляет все HTML-теги.
- None или False: ничего не делает.
"""
if mode is False:
mode = SANITIZE_NONE
self.mode = mode
# Оптимизация: заранее готовим CSS-селектор для поиска висячей пунктуации
if self.mode == SANITIZE_ETPGRF:
# Собираем уникальные классы
unique_classes = sorted(list(frozenset(HANGING_PUNCTUATION_CLASSES.values())))
# Формируем селектор вида: span.class1, span.class2, ...
# Это позволяет использовать нативный парсер (lxml) для поиска, что намного быстрее python-лямбд.
self._etp_selector = ", ".join(f"span.{cls}" for cls in unique_classes)
else:
self._etp_selector = None
logger.debug(f"SanitizerProcessor `__init__`. Mode: {self.mode}")
def process(self, soup: BeautifulSoup) -> BeautifulSoup | str:
"""
Применяет правила очистки к `soup`-объекту.
:param soup: Объект BeautifulSoup для обработки.
:return: Обработанный объект BeautifulSoup или строка (в режиме 'html').
"""
if self.mode == SANITIZE_ETPGRF:
if not self._etp_selector:
return soup
# Используем CSS-селектор для быстрого поиска всех нужных элементов
spans_to_clean = soup.select(self._etp_selector)
# "Агрессивная" очистка: просто "разворачиваем" все найденные теги,
# заменяя их своим содержимым.
for span in spans_to_clean:
span.unwrap()
return soup
elif self.mode == SANITIZE_ALL_HTML:
# Оптимизированный подход:
# 1. Удаляем защищенные теги (script, style и т.д.) вместе с содержимым.
# Используем select для поиска, так как это обычно быстрее.
if PROTECTED_HTML_TAGS:
# Формируем селектор: script, style, pre, ...
protected_selector = ", ".join(PROTECTED_HTML_TAGS)
for tag in soup.select(protected_selector):
tag.decompose() # Полное удаление тега из дерева
# 2. Извлекаем чистый текст из оставшегося дерева.
# get_text() работает на уровне C (в lxml) и намного быстрее ручного обхода.
return soup.get_text()
# Если режим не задан, ничего не делаем
return soup