boompack commited on
Commit
11ef3f4
·
verified ·
1 Parent(s): d9d543a

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +21 -330
app.py CHANGED
@@ -1,337 +1,28 @@
1
- from transformers import pipeline
2
- from dataclasses import dataclass, field
3
- from typing import List, Optional, Dict, Any
4
- import re
5
- from datetime import datetime
6
- import logging
7
- import html
8
- from uuid import uuid4
9
- import torch
10
  import gradio as gr
11
- import emoji
12
-
13
- # Настройка логирования
14
- logging.basicConfig(
15
- level=logging.INFO,
16
- format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
17
- )
18
- logger = logging.getLogger(__name__)
19
-
20
- @dataclass
21
- class Comment:
22
- """Представляет комментарий Instagram со всеми метаданными"""
23
- id: str = field(default_factory=lambda: str(uuid4()))
24
- username: str = ""
25
- time: str = ""
26
- content: str = ""
27
- likes: int = 0
28
- level: int = 0
29
- parent_id: Optional[str] = None
30
- replies: List['Comment'] = field(default_factory=list)
31
- is_verified: bool = False
32
- mentions: List[str] = field(default_factory=list)
33
- hashtags: List[str] = field(default_factory=list)
34
- is_deleted: bool = False
35
- sentiment: Optional[str] = None
36
- language: Optional[str] = None
37
- emojis: List[str] = field(default_factory=list)
38
-
39
- def __post_init__(self):
40
- if len(self.content) > 2200:
41
- logger.warning(f"Comment content exceeds 2200 characters for user {self.username}")
42
- self.content = self.content[:2200] + "..."
43
-
44
- class InstagramCommentAnalyzer:
45
- """Анализатор комментариев Instagram с расширенной функциональностью"""
46
-
47
- COMMENT_PATTERN = r'''
48
- (?P<username>[\w\u0400-\u04FF.-]+)\s*
49
- (?P<time>(?:\d+\s+(?:нед|мин|ч|д|мес|год|sec|min|h|d|w|mon|y)\.?))\s*
50
- (?P<content>.*?)
51
- (?:(?:Отметки|Likes)\s*"?Нравится"?:\s*(?P<likes>\d+))?
52
- (?:Ответить|Reply)?(?:Показать\sперевод|Show\stranslation)?(?:Нравится|Like)?
53
- '''
54
-
55
- TIME_MAPPING = {
56
- 'нед': 'week', 'мин': 'minute', 'ч': 'hour',
57
- 'д': 'day', 'мес': 'month', 'год': 'year',
58
- 'w': 'week', 'h': 'hour', 'd': 'day',
59
- 'mon': 'month', 'y': 'year'
60
- }
61
-
62
- def __init__(self, max_depth: int = 10, max_comment_length: int = 2200):
63
- """Инициализация анализатора"""
64
- self.check_dependencies()
65
- self.max_depth = max_depth
66
- self.max_comment_length = max_comment_length
67
- self.pattern = re.compile(self.COMMENT_PATTERN, re.VERBOSE | re.DOTALL)
68
- self.comments: List[Comment] = []
69
- self.stats = self.initialize_stats()
70
- self.sentiment_analyzer = self.load_sentiment_model()
71
-
72
- def initialize_stats(self) -> Dict[str, int]:
73
- """Инициализация статистики"""
74
- return {
75
- 'total_comments': 0,
76
- 'deleted_comments': 0,
77
- 'empty_comments': 0,
78
- 'max_depth_reached': 0,
79
- 'truncated_comments': 0,
80
- 'processed_mentions': 0,
81
- 'processed_hashtags': 0,
82
- 'processed_emojis': 0,
83
- 'failed_parses': 0
84
- }
85
-
86
- def check_dependencies(self):
87
- """Проверка зависимостей"""
88
- required_packages = ['torch', 'transformers', 'emoji']
89
- for package in required_packages:
90
- try:
91
- __import__(package)
92
- except ImportError:
93
- logger.error(f"Required package {package} is not installed")
94
- raise
95
-
96
- def load_sentiment_model(self):
97
- """Загрузка модели анализа тональности"""
98
- try:
99
- device = "cuda" if torch.cuda.is_available() else "cpu"
100
- logger.info(f"Using device: {device}")
101
- return pipeline(
102
- "sentiment-analysis",
103
- model="distilbert-base-uncased-finetuned-sst-2-english",
104
- device=device
105
- )
106
- except Exception as e:
107
- logger.error(f"Model loading failed: {str(e)}")
108
- raise
109
-
110
- def normalize_text(self, text: str) -> str:
111
- """Улучшенная нормализация текста"""
112
- text = html.unescape(text)
113
- text = ' '.join(text.split())
114
- text = re.sub(r'[\u200b\ufeff\u200c]', '', text)
115
- return text
116
-
117
- def extract_emojis(self, text: str) -> List[str]:
118
- """Извлечение эмодзи из текста"""
119
- return [c for c in text if c in emoji.EMOJI_DATA]
120
-
121
- def normalize_time(self, time_str: str) -> str:
122
- """Нормализация временных меток"""
123
- for rus, eng in self.TIME_MAPPING.items():
124
- if rus in time_str:
125
- return time_str.replace(rus, eng)
126
- return time_str
127
-
128
- def clean_content(self, content: str) -> str:
129
- """Очистка содержимого комментария"""
130
- content = content.strip()
131
- content = re.sub(r'\s+', ' ', content)
132
- if len(content) > self.max_comment_length:
133
- self.stats['truncated_comments'] += 1
134
- content = content[:self.max_comment_length] + "..."
135
- return content
136
-
137
- def extract_metadata(self, comment: Comment) -> None:
138
- """Извлечение метаданных из комментария"""
139
- try:
140
- # Извлечение упоминаний и хэштегов
141
- comment.mentions = re.findall(r'@(\w+)', comment.content)
142
- comment.hashtags = re.findall(r'#(\w+)', comment.content)
143
-
144
- # Извлечение эмодзи
145
- comment.emojis = self.extract_emojis(comment.content)
146
-
147
- # Обновление статистики
148
- self.stats['processed_mentions'] += len(comment.mentions)
149
- self.stats['processed_hashtags'] += len(comment.hashtags)
150
- self.stats['processed_emojis'] += len(comment.emojis)
151
-
152
- # Проверка верификации
153
- comment.is_verified = bool(re.search(r'✓|Подтвержденный', comment.username))
154
- except Exception as e:
155
- logger.error(f"Metadata extraction failed: {str(e)}")
156
-
157
- def analyze_sentiment(self, text: str) -> str:
158
- """Анализ тональности текста"""
159
- try:
160
- result = self.sentiment_analyzer(text)
161
- return result[0]['label']
162
- except Exception as e:
163
- logger.error(f"Sentiment analysis failed: {str(e)}")
164
- return "UNKNOWN"
165
- def process_comment(self, text: str, parent_id: Optional[str] = None, level: int = 0) -> Optional[Comment]:
166
- """Обработка отдельного комментария"""
167
- if not self.validate_input(text):
168
- return None
169
-
170
- if level > self.max_depth:
171
- logger.warning(f"Maximum depth {self.max_depth} exceeded")
172
- self.stats['max_depth_reached'] += 1
173
- return None
174
-
175
- try:
176
- text = self.normalize_text(text)
177
- match = self.pattern.match(text)
178
-
179
- if not match:
180
- alt_match = self.alternative_parse(text)
181
- if not alt_match:
182
- raise ValueError(f"Could not parse comment: {text[:100]}...")
183
- match = alt_match
184
-
185
- data = match.groupdict()
186
- comment = Comment(
187
- username=data['username'].strip(),
188
- time=self.normalize_time(data['time']),
189
- content=self.clean_content(data['content']),
190
- likes=self.parse_likes(data.get('likes', '0')),
191
- level=level,
192
- parent_id=parent_id
193
- )
194
-
195
- # Анализ тональности и метаданных
196
- comment.sentiment = self.analyze_sentiment(comment.content)
197
- self.extract_metadata(comment)
198
-
199
- self.stats['total_comments'] += 1
200
- return comment
201
-
202
- except Exception as e:
203
- logger.error(f"Error processing comment: {str(e)}", exc_info=True)
204
- self.stats['failed_parses'] += 1
205
- return self.create_damaged_comment()
206
-
207
- def alternative_parse(self, text: str) -> Optional[re.Match]:
208
- """Альтернативный метод парсинга для сложных случаев"""
209
- alternative_patterns = [
210
- # Более простой паттерн
211
- r'(?P<username>[\w\u0400-\u04FF.-]+)\s*(?P<content>.*?)(?P<time>\d+\s+\w+\.?)(?P<likes>\d+)?',
212
- # Паттерн для мобильной версии
213
- r'(?P<username>[\w\u0400-\u04FF.-]+)\s*(?P<content>.*?)(?P<time>\d+\s+\w+)(?:Like)?(?P<likes>\d+)?'
214
- ]
215
-
216
- for pattern in alternative_patterns:
217
- try:
218
- match = re.compile(pattern, re.VERBOSE | re.DOTALL).match(text)
219
- if match:
220
- return match
221
- except Exception:
222
- continue
223
- return None
224
-
225
- def parse_likes(self, likes_str: str) -> int:
226
- """Безопасный парсинг количества лайков"""
227
- try:
228
- return int(re.sub(r'\D', '', likes_str) or 0)
229
- except (ValueError, TypeError):
230
- return 0
231
-
232
- def create_damaged_comment(self) -> Comment:
233
- """Создание заглушки для поврежденного комментария"""
234
- return Comment(
235
- username="[damaged]",
236
- time="unknown",
237
- content="[Поврежденные данные]",
238
- is_deleted=True
239
- )
240
-
241
- def validate_input(self, text: str) -> bool:
242
- """Валидация входного текста"""
243
- if not text or not isinstance(text, str):
244
- logger.error("Invalid input: text must be non-empty string")
245
- return False
246
- if len(text) > 50000:
247
- logger.error("Input text too large")
248
- return False
249
- return True
250
-
251
- def format_comment(self, comment: Comment, index: int) -> str:
252
- """Форматирование комментария для вывода"""
253
- try:
254
- if comment.is_deleted:
255
- return f'{index}. "[УДАЛЕНО]"'
256
-
257
- emoji_str = ' '.join(comment.emojis) if comment.emojis else ''
258
- mentions_str = ', '.join(comment.mentions) if comment.mentions else ''
259
- hashtags_str = ', '.join(comment.hashtags) if comment.hashtags else ''
260
-
261
- return (
262
- f'{index}. "{comment.username}" "{comment.time}" '
263
- f'"{comment.content}" "Лайки: {comment.likes}" '
264
- f'"Настроение: {comment.sentiment}" '
265
- f'"Эмодзи: {emoji_str}" '
266
- f'"Упоминания: {mentions_str}" '
267
- f'"Хэштеги: {hashtags_str}"'
268
- )
269
-
270
- try-except Exception as e:
271
- logger.error(f"Error formatting comment: {str(e)}")
272
- return f'{index}. "[ОШИБКА ФОРМАТИРОВАНИЯ]"'
273
-
274
- def process_comments(self, text: str) -> List[str]:
275
- """Обработка всех комментариев"""
276
- try:
277
- self.stats = self.initialize_stats()
278
- text = self.normalize_text(text)
279
- raw_comments = text.split('ОтветитьНравится')
280
- formatted_comments = []
281
-
282
- for i, raw_comment in enumerate(raw_comments, 1):
283
- if not raw_comment.strip():
284
- continue
285
-
286
- comment = self.process_comment(raw_comment)
287
- if comment:
288
- formatted_comments.append(self.format_comment(comment, i))
289
 
290
- return formatted_comments
291
- except Exception as e:
292
- logger.error(f"Error processing comments: {str(e)}")
293
- return ["[ОШИБКА ОБРАБОТКИ КОММЕНТАРИЕВ]"]
294
 
295
- def create_interface():
296
- """Создание интерфейса Gradio"""
297
- analyzer = InstagramCommentAnalyzer()
298
 
299
- def analyze_text(text: str):
300
- formatted_comments = analyzer.process_comments(text)
301
- return "\n".join(formatted_comments)
 
 
302
 
303
- iface = gr.Interface(
304
- fn=analyze_text,
305
- inputs=gr.Textbox(
306
- lines=10,
307
- placeholder="Вставьте текст комментариев здесь...",
308
- label="Входной текст"
309
- ),
310
- outputs=gr.Textbox(
311
- lines=20,
312
- placeholder="Результаты анализа будут отображены здесь...",
313
- label="Результаты анализа"
314
- ),
315
- title="Instagram Comment Analyzer",
316
- description="Анализатор комментариев Instagram с поддержкой эмодзи и мультиязычности",
317
- theme="default",
318
- analytics_enabled=False,
319
- )
320
- return iface
321
 
322
- def main():
323
- """Основная функция запуска приложения"""
324
- try:
325
- interface = create_interface()
326
- interface.launch(
327
- server_name="0.0.0.0",
328
- server_port=7860,
329
- share=False,
330
- debug=True
331
- )
332
- except Exception as e:
333
- logger.error(f"Application failed to start: {str(e)}")
334
- raise
335
 
336
- if __name__ == "__main__":
337
- main()
 
 
 
 
 
 
 
 
 
 
1
  import gradio as gr
2
+ import re
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3
 
4
+ # Функция для обработки входного текста
5
+ def process_text(input_text):
6
+ # Регулярное выражение для извлечения данных
7
+ pattern = r"(\S+) (\d+ нед\.)\.([^.]+?)Отметки \"Нравится\": (\d+)"
8
 
9
+ # Поиск всех совпадений в тексте
10
+ matches = re.findall(pattern, input_text)
 
11
 
12
+ # Формируем вывод
13
+ output = []
14
+ for i, match in enumerate(matches, 1):
15
+ username, time, text, likes = match
16
+ output.append(f'{i}. "{username}" "{time}" "{text.strip()}" "Нравится {likes}"')
17
 
18
+ return "\n".join(output)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
19
 
20
+ # Настройка Gradio интерфейса
21
+ iface = gr.Interface(
22
+ fn=process_text, # функция для обработки текста
23
+ inputs=gr.Textbox(lines=10, placeholder="Введите текст сюда..."), # поле для ввода текста
24
+ outputs=gr.Textbox(lines=10, placeholder="Результат...") # поле для вывода результата
25
+ )
 
 
 
 
 
 
 
26
 
27
+ # Запуск интерфейса
28
+ iface.launch()