hy / app.py
boompack's picture
Update app.py
2c5fbe9 verified
raw
history blame
10.1 kB
from transformers import pipeline
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
}
# Инициализация модели Hugging Face для анализа настроений
self.sentiment_analyzer = pipeline("sentiment-analysis")
def analyze_sentiment(self, text: str) -> str:
"""
Анализ настроений в комментарии с использованием модели Hugging Face.
Args:
text: Текст комментария
Returns:
Строка с меткой настроения ('POSITIVE' или 'NEGATIVE')
"""
result = self.sentiment_analyzer(text)
return result[0]['label']
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] + "..."
# Добавление анализа настроений
comment.sentiment = self.analyze_sentiment(comment.content)
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}" "Настроение {comment.sentiment}"'
)
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():
"""
Пример использования анализатора.
"""
# Пример входного текста
example_text = """
user1 2 нед. This is a positive comment! Отметки "Нравится": 25
user2 3 нед. This is a negative comment! Отметки "Нравится": 5
"""
analyzer = InstagramCommentAnalyzer()
formatted_comments = analyzer.process_comments(example_text)
for formatted_comment in formatted_comments:
print(formatted_comment)
if __name__ == "__main__":
main()