hy / app.py
boompack's picture
Update app.py
60e12de verified
raw
history blame
9.77 kB
from dataclasses import dataclass, field
from typing import List, Optional, Dict, Any
import re
from datetime import datetime
import logging
import html
from uuid import uuid4
# Настройка логирования
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger(__name__)
@dataclass
class Comment:
"""
Представляет комментарий Instagram со всеми метаданными и вложенной структурой.
Attributes:
id: Уникальный идентификатор комментария
username: Имя пользователя
time: Временная метка
content: Текст комментария
likes: Количество лайков
level: Уровень вложенности
parent_id: ID родительского комментария
replies: Список ответов
is_verified: Верифицированный аккаунт
mentions: Упоминания пользователей
hashtags: Хэштеги
is_deleted: Флаг удаленного комментария
"""
id: str = field(default_factory=lambda: str(uuid4()))
username: str = ""
time: str = ""
content: str = ""
likes: int = 0
level: int = 0
parent_id: Optional[str] = None
replies: List['Comment'] = field(default_factory=list)
is_verified: bool = False
mentions: List[str] = field(default_factory=list)
hashtags: List[str] = field(default_factory=list)
is_deleted: bool = False
def __post_init__(self):
"""Валидация после инициализации"""
if len(self.content) > 2200:
logger.warning(f"Comment content exceeds 2200 characters for user {self.username}")
self.content = self.content[:2200] + "..."
class InstagramCommentAnalyzer:
"""
Основной класс для обработки и анализа комментариев Instagram.
Обрабатывает парсинг комментариев, вложенную структуру и особые случаи.
"""
# Регулярное выражение для извлечения комментариев
COMMENT_PATTERN = r'''
(?P<username>[\w.-]+)\s+
(?P<time>\d+\s+нед\.)
(?P<content>.*?)
(?:Отметки\s*"Нравится":\s*(?P<likes>\d+))?
(?:Ответить)?(?:Показать\sперевод)?(?:Нравится)?
'''
def __init__(self, max_depth: int = 10, max_comment_length: int = 2200):
"""
Инициализация анализатора с настраиваемыми параметрами.
Args:
max_depth: Максимальная глубина вложенности комментариев
max_comment_length: Максимальная длина комментария
"""
self.max_depth = max_depth
self.max_comment_length = max_comment_length
self.pattern = re.compile(self.COMMENT_PATTERN, re.VERBOSE | re.DOTALL)
self.comments: List[Comment] = []
self.stats: Dict[str, int] = {
'total_comments': 0,
'deleted_comments': 0,
'empty_comments': 0,
'max_depth_reached': 0,
'truncated_comments': 0,
'processed_mentions': 0,
'processed_hashtags': 0
}
def normalize_text(self, text: str) -> str:
"""
Нормализация входного текста.
Args:
text: Исходный текст
Returns:
Нормализованный текст
"""
# Декодирование HTML-сущностей
text = html.unescape(text)
# Нормализация пробелов
text = ' '.join(text.split())
# Удаление невидимых символов
text = re.sub(r'[\u200b\ufeff\u200c]', '', text)
return text
def extract_metadata(self, comment: Comment) -> None:
"""
Извлечение метаданных из комментария.
Args:
comment: Объект комментария
"""
# Извлечение @упоминаний
comment.mentions = re.findall(r'@(\w+)', comment.content)
self.stats['processed_mentions'] += len(comment.mentions)
# Извлечение #хэштегов
comment.hashtags = re.findall(r'#(\w+)', comment.content)
self.stats['processed_hashtags'] += len(comment.hashtags)
# Проверка верификации
comment.is_verified = bool(re.search(r'✓|Подтвержденный', comment.username))
def process_comment(self, text: str, parent_id: Optional[str] = None, level: int = 0) -> Optional[Comment]:
"""
Обработка отдельного комментария.
Args:
text: Текст комментария
parent_id: ID родительского комментария
level: Уровень вложенности
Returns:
Обработанный объект Comment или None
"""
if level > self.max_depth:
logger.warning(f"Maximum depth {self.max_depth} exceeded")
self.stats['max_depth_reached'] += 1
return None
if not text.strip():
self.stats['empty_comments'] += 1
return None
try:
match = self.pattern.match(text)
if not match:
raise ValueError(f"Could not parse comment: {text[:100]}...")
data = match.groupdict()
comment = Comment(
username=data['username'],
time=data['time'],
content=data['content'].strip(),
likes=int(data['likes'] or 0),
level=level,
parent_id=parent_id
)
if len(comment.content) > self.max_comment_length:
self.stats['truncated_comments'] += 1
comment.content = comment.content[:self.max_comment_length] + "..."
self.extract_metadata(comment)
self.stats['total_comments'] += 1
return comment
except Exception as e:
logger.error(f"Error processing comment: {str(e)}")
comment = Comment(
username="[damaged]",
time="",
content="[Поврежденные данные]",
is_deleted=True
)
self.stats['deleted_comments'] += 1
return comment
def format_comment(self, comment: Comment, index: int) -> str:
"""
Форматирование комментария для вывода.
Args:
comment: Объект комментария
index: Номер комментария
Returns:
Отформатированная строка комментария
"""
if comment.is_deleted:
return f'{index}. "[УДАЛЕНО]" "" "" "Нравится 0"'
return (
f'{index}. "{comment.username}" "{comment.time}" '
f'"{comment.content}" "Нравится {comment.likes}"'
)
def process_comments(self, text: str) -> List[str]:
"""
Обработка всех комментариев в тексте.
Args:
text: Исходный текст с комментариями
Returns:
Список отформатированных комментариев
"""
# Сброс статистики
self.stats = {key: 0 for key in self.stats}
# Нормализация текста
text = self.normalize_text(text)
# Разделение на отдельные комментарии
raw_comments = text.split('ОтветитьНравится')
# Обработка комментариев
formatted_comments = []
for i, raw_comment in enumerate(raw_comments, 1):
if not raw_comment.strip():
continue
comment = self.process_comment(raw_comment)
if comment:
formatted_comments.append(self.format_comment(comment, i))
return formatted_comments
def main():
"""
Пример использования анализатора.
"""
# Пример входного текста
input_text = """
0001.minakov 53 нед.А что такое для,Вас Василина,волшебство?Отметки "Нравится": 3ОтветитьНравится
aliyllmn 54 нед.seni seviyorum:)Отметки "Нравится": 2ОтветитьПоказать переводНравится
"""
# Создание экземпляра анализатора
analyzer = InstagramCommentAnalyzer(max_depth=10)
# Обработка комментариев
results = analyzer.process_comments(input_text)
# Вывод результатов
print("\nОбработанные комментарии:")
print("-" * 50)
for result in results:
print(result)
# Вывод статистики
print("\nСтатистика обработки:")
print("-" * 50)
for key, value in analyzer.stats.items():
print(f"{key}: {value}")
if __name__ == "__main__":
main()