boompack commited on
Commit
8c8dfe8
·
verified ·
1 Parent(s): 6bdcb96

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +228 -317
app.py CHANGED
@@ -1,108 +1,87 @@
1
- import gradio as gr
2
  import re
3
- from collections import Counter
4
- from datetime import datetime
5
  import emoji
6
- import logging
7
- from typing import Tuple, List, Optional
8
  import statistics
9
- import csv
 
 
10
  from io import StringIO
 
11
 
12
- # Настройка логирования
13
  logging.basicConfig(level=logging.INFO)
14
  logger = logging.getLogger(__name__)
15
 
16
- def clean_text(text):
17
- """Очищает текст от лишних пробелов и переносов строк"""
18
- text = re.sub(r'\s+', ' ', text)
19
- return text.strip()
20
-
21
- def count_emojis(text):
22
- """Подсчитывает количество эмодзи в тексте"""
23
- return len([c for c in text if c in emoji.EMOJI_DATA])
24
-
25
- def extract_mentions(text):
26
- """Извлекает упоминания пользователей из текста"""
27
- return re.findall(r'@[\w\.]+', text)
28
-
29
- def get_comment_words(text):
30
- """Получает список слов из комментария для анализа"""
31
- words = re.findall(r'\w+', text.lower())
32
- return [w for w in words if len(w) > 2]
33
-
34
- def analyze_sentiment(text):
35
- """Расширенный анализ тональности по эмодзи и ключевым словам"""
36
- positive_indicators = ['🔥', '❤️', '👍', '😊', '💪', '👏', '🎉', '♥️', '😍', '🙏',
37
- 'круто', 'супер', 'класс', 'огонь', 'пушка', 'отлично', 'здорово',
38
- 'прекрасно', 'молодец', 'красота', 'спасибо', 'топ', 'лучший',
39
- 'amazing', 'wonderful', 'great', 'perfect', 'love', 'beautiful']
40
 
41
- negative_indicators = ['👎', '😢', '😞', '😠', '😡', '💔', '😕', '😑',
42
- 'плохо', 'ужас', 'отстой', 'фу', 'жесть', 'ужасно',
43
- 'разочарован', 'печаль', 'грустно', 'bad', 'worst',
44
- 'terrible', 'awful', 'sad', 'disappointed']
45
 
46
- text_lower = text.lower()
 
 
47
 
48
- # Подсчет индикаторов настроения
49
- positive_count = sum(1 for ind in positive_indicators if ind in text_lower)
50
- negative_count = sum(1 for ind in negative_indicators if ind in text_lower)
 
 
 
 
 
 
 
 
 
51
 
52
- # Учет восклицательных знаков
53
- exclamation_count = text.count('!')
54
- if positive_count > negative_count:
55
- positive_count += exclamation_count * 0.5
56
- elif negative_count > positive_count:
57
- negative_count += exclamation_count * 0.5
58
 
59
- # Определение итогового настроения
60
- if positive_count > negative_count:
61
- return 'positive'
62
- elif negative_count > positive_count:
63
- return 'negative'
64
- return 'neutral'
 
 
 
 
 
 
 
 
 
65
 
66
- def extract_comment_data(comment_text):
67
- """Извлекает данные из отдельного комментария с поддержкой различных форматов"""
68
- try:
69
- # Паттерны для извлечения данных
70
- username_patterns = [
71
  r"Фото профиля ([^\n]+)",
72
  r"^([^\s]+)\s+",
73
- r"@([^\s]+)\s+",
74
- ]
75
-
76
- time_patterns = [
77
  r"(\d+)\s*(?:ч|нед)\.",
78
  r"(\d+)\s*(?:h|w)",
79
- r"(\d+)\s*(?:час|hour|week)",
80
- ]
81
-
82
- likes_patterns = [
83
  r"(\d+) отметк[аи] \"Нравится\"",
84
  r"Нравится: (\d+)",
85
  r"(\d+) отметка \"Нравится\"",
86
  r"\"Нравится\": (\d+)",
87
- r"likes?: (\d+)",
88
- ]
89
-
90
- # Поиск имени пользователя
91
- username = None
92
- for pattern in username_patterns:
93
- username_match = re.search(pattern, comment_text)
94
- if username_match:
95
- username = username_match.group(1).strip()
96
- break
97
-
98
- if not username:
99
- return None, None, 0, 0
100
-
101
- # Извлечение комментария
102
- comment = comment_text
103
-
104
- # Удаление метаданных
105
- metadata_patterns = [
106
  r"Фото профиля [^\n]+\n",
107
  r"\d+\s*(?:ч|нед|h|w|час|hour|week)\.",
108
  r"Нравится:?\s*\d+",
@@ -110,266 +89,198 @@ def extract_comment_data(comment_text):
110
  r"Ответить",
111
  r"Показать перевод",
112
  r"Скрыть все ответы",
113
- r"Смотреть все ответы \(\d+\)",
114
- username
115
  ]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
116
 
117
- for pattern in metadata_patterns:
118
- comment = re.sub(pattern, '', comment)
 
 
 
 
119
 
120
- comment = clean_text(comment)
121
-
122
- # Определение времени публикации
123
- weeks = 0
124
- for pattern in time_patterns:
125
- time_match = re.search(pattern, comment_text)
126
- if time_match:
127
- time_value = int(time_match.group(1))
128
- if any(unit in comment_text.lower() for unit in ['нед', 'w', 'week']):
129
- weeks = time_value
130
- else:
131
- weeks = time_value / (24 * 7) # конвертация часов в недели
132
- break
133
-
134
- # Подсчет лайков
135
- likes = 0
136
- for pattern in likes_patterns:
137
- likes_match = re.search(pattern, comment_text)
138
- if likes_match:
139
- likes = int(likes_match.group(1))
140
- break
141
-
142
- return username, comment.strip(), likes, weeks
143
 
144
- except Exception as e:
145
- logger.error(f"Error extracting comment data: {e}")
146
- return None, None, 0, 0
 
 
 
 
 
147
 
148
  def analyze_post(content_type: str, link_to_post: str, post_likes: int, post_date: str,
149
  description: str, comment_count: int, all_comments: str) -> Tuple[str, str, str, str, str]:
150
- """
151
- Анализирует пост Instagram и его комментарии
152
-
153
- Args:
154
- content_type: Тип контента (фото/видео)
155
- link_to_post: Ссылка на пост
156
- post_likes: Количество лайков поста
157
- post_date: Дата публикации
158
- description: Описание поста
159
- comment_count: Ожидаемое количество комментариев
160
- all_comments: Текст всех комментариев
161
-
162
- Returns:
163
- Tuple[str, str, str, str, str]: Кортеж с результатами анализа
164
- """
165
  try:
166
- # Разделение на блоки комментариев
167
- comment_patterns = [
168
  r"(?=Фото профиля)",
169
  r"(?=\n\s*[a-zA-Z0-9._]+\s+[^\n]+\n)",
170
  r"(?=^[a-zA-Z0-9._]+\s+[^\n]+\n)",
171
  r"(?=@[a-zA-Z0-9._]+\s+[^\n]+\n)"
172
- ]
 
 
173
 
174
- split_pattern = '|'.join(comment_patterns)
175
- comments_blocks = re.split(split_pattern, all_comments)
176
- comments_blocks = [block.strip() for block in comments_blocks if block and block.strip()]
177
 
178
- # Инициализация переменных для анализа
179
- usernames = []
180
- comments = []
181
- likes = []
182
- weeks = []
183
- total_emojis = 0
184
- mentions = []
185
- sentiments = []
186
- comment_lengths = []
187
- words_per_comment = []
188
- all_words = []
189
- user_engagement = {}
190
-
191
- # Обработка комментариев
192
- for block in comments_blocks:
193
- if 'Скрыто алгоритмами Instagram' in block:
194
- continue
195
-
196
- username, comment, like_count, week_number = extract_comment_data(block)
197
- if username and comment:
198
- usernames.append(username)
199
- comments.append(comment)
200
- likes.append(str(like_count))
201
- weeks.append(week_number)
202
-
203
- # Сбор статистики
204
- total_emojis += count_emojis(comment)
205
- mentions.extend(extract_mentions(comment))
206
- sentiment = analyze_sentiment(comment)
207
- sentiments.append(sentiment)
208
- comment_lengths.append(len(comment))
209
-
210
- words = get_comment_words(comment)
211
- words_per_comment.append(len(words))
212
- all_words.extend(words)
213
-
214
- # Обновление статистики пользователя
215
- if username not in user_engagement:
216
- user_engagement[username] = {
217
- 'comments': 0,
218
- 'total_likes': 0,
219
- 'emoji_usage': 0,
220
- 'avg_length': 0,
221
- 'sentiments': [],
222
- 'weeks': []
223
- }
224
-
225
- user_stats = user_engagement[username]
226
- user_stats['comments'] += 1
227
- user_stats['total_likes'] += like_count
228
- user_stats['emoji_usage'] += count_emojis(comment)
229
- user_stats['avg_length'] += len(comment)
230
- user_stats['sentiments'].append(sentiment)
231
- user_stats['weeks'].append(week_number)
232
-
233
- # Расчет статистики
234
- total_comments = len(comments)
235
- if total_comments == 0:
236
  return "No comments found", "", "", "", "0"
237
 
238
- # Обновление статистики пользователей
239
- for username in user_engagement:
240
- stats = user_engagement[username]
241
- stats['avg_length'] /= stats['comments']
242
- stats['engagement_rate'] = stats['total_likes'] / stats['comments']
243
- stats['sentiment_ratio'] = sum(1 for s in stats['sentiments'] if s == 'positive') / len(stats['sentiments'])
244
- stats['activity_period'] = max(stats['weeks']) - min(stats['weeks']) if stats['weeks'] else 0
245
-
246
- # Базовая статистика
247
- avg_comment_length = sum(comment_lengths) / total_comments
248
- sentiment_distribution = Counter(sentiments)
249
- most_active_users = Counter(usernames).most_common(5)
250
- most_mentioned = Counter(mentions).most_common(5)
251
- avg_likes = sum(map(int, likes)) / len(likes) if likes else 0
252
 
253
- # Временной анализ
254
- if weeks:
255
- earliest_week = max(weeks)
256
- latest_week = min(weeks)
257
- week_range = earliest_week - latest_week
258
-
259
- # Разделение на периоды
260
- period_length = week_range / 3 if week_range > 0 else 1
261
- engagement_periods = {
262
- 'early': [],
263
- 'middle': [],
264
- 'late': []
265
- }
266
-
267
- for i, week in enumerate(weeks):
268
- if week >= earliest_week - period_length:
269
- engagement_periods['early'].append(i)
270
- elif week >= earliest_week - 2 * period_length:
271
- engagement_periods['middle'].append(i)
272
- else:
273
- engagement_periods['late'].append(i)
274
-
275
- period_stats = {
276
- period: {
277
- 'comments': len(indices),
278
- 'avg_likes': sum(int(likes[i]) for i in indices) / len(indices) if indices else 0,
279
- 'sentiment_ratio': sum(1 for i in indices if sentiments[i] == 'positive') / len(indices) if indices else 0
280
- }
281
- for period, indices in engagement_periods.items()
282
- }
283
- else:
284
- period_stats = {}
285
- earliest_week = 0
286
- latest_week = 0
287
 
288
- # Подготовка CSV
289
- csv_data = {
290
- 'metadata': {
291
- 'content_type': content_type,
292
- 'link': link_to_post,
293
- 'post_likes': post_likes,
294
- 'post_date': post_date,
295
- 'total_comments': total_comments,
296
- 'expected_comments': comment_count
297
- },
298
- 'basic_stats': {
299
- 'avg_comment_length': round(avg_comment_length, 2),
300
- 'median_comment_length': statistics.median(comment_lengths),
301
- 'avg_words': round(sum(words_per_comment) / total_comments, 2),
302
- 'total_emojis': total_emojis,
303
- 'avg_likes': round(avg_likes, 2)
304
- },
305
- 'sentiment_stats': dict(Counter(sentiments)),
306
- 'period_analysis': period_stats,
307
- 'top_users': dict(most_active_users),
308
- 'top_mentioned': dict(most_mentioned)
309
  }
310
 
311
- # Создание CSV строки
312
- output = StringIO()
313
- writer = csv.writer(output)
314
- for section, data in csv_data.items():
315
- writer.writerow([section])
316
- for key, value in data.items():
317
- writer.writerow([key, value])
318
- writer.writerow([])
319
- csv_output = output.getvalue()
320
 
321
- # Формирование отчета
322
- analytics_summary = (
323
- f"CSV DATA:\n{csv_output}\n\n"
324
- f"ДЕТАЛЬНЫЙ АНАЛИЗ:\n"
325
- f"Контент: {content_type}\n"
326
- f"Ссылка: {link_to_post}\n\n"
327
- f"СТАТИСТИКА:\n"
328
- f"- Всего комментариев: {total_comments} (ожидалось: {comment_count})\n"
329
- f"- Всего лайков на комментариях: {sum(map(int, likes))}\n"
330
- f"- Среднее лайков на комментарий: {avg_likes:.1f}\n"
331
- f"- Период активности: {earliest_week}-{latest_week} недель\n\n"
332
- f"АНАЛИЗ КОНТЕНТА:\n"
333
- f"- Средняя длина комментария: {avg_comment_length:.1f} символов\n"
334
- f"- Медиана длины: {statistics.median(comment_lengths)} символов\n"
335
- f"- Среднее количество слов: {sum(words_per_comment) / total_comments:.1f}\n"
336
- f"- Всего эмодзи: {total_emojis}\n"
337
- f"- Тональность:\n"
338
- f" * Позитивных: {sentiment_distribution['positive']}\n"
339
- f" * Нейтральных: {sentiment_distribution['neutral']}\n"
340
- f" * Негативных: {sentiment_distribution['negative']}\n\n"
341
- f"АНАЛИЗ КОНТЕНТА:\n"
342
- f"- Средняя длина: {avg_comment_length:.1f} символов\n"
343
- f"- Медиана длины: {median_comment_length} символов\n"
344
- f"- Среднее слов: {avg_words_per_comment:.1f}\n"
345
- f"- Эмодзи: {total_emojis}\n"
346
- f"- Тональность:\n"
347
- f" * Позитив: {sentiment_distribution['positive']}\n"
348
- f" * Нейтрально: {sentiment_distribution['neutral']}\n"
349
- f" * Негатив: {sentiment_distribution['negative']}\n\n"
350
- f"ПОПУЛЯРНЫЕ СЛОВА:\n"
351
- + "\n".join([f"- {word}: {count}" for word, count in common_words]) + "\n\n"
352
- f"АКТИВНЫЕ ПОЛЬЗОВАТЕЛИ:\n"
353
- + "\n".join([f"- {user}: {count}" for user, count in most_active_users]) + "\n\n"
354
- f"УПОМИНАНИЯ:\n"
355
- + "\n".join([f"- {user}: {count}" for user, count in most_mentioned if user]) + "\n\n"
356
- f"АНАЛИЗ ПО ПЕРИОДАМ:\n"
357
- + "\n".join([f"- {period}: {stats['comments']} комментариев, {stats['avg_likes']:.1f} лайков/коммент, "
358
- f"{stats['sentiment_ratio']*100:.1f}% позитивных"
359
- for period, stats in period_stats.items()]) + "\n\n"
360
- f"ЭКСПЕРИМЕНТАЛЬНАЯ АНАЛИТИКА:\n"
361
- f"- Самый активный период: {max(period_stats.items(), key=lambda x: x[1]['comments'])[0]}\n"
362
- f"- Наиболее позитивный период: {max(period_stats.items(), key=lambda x: x[1]['sentiment_ratio'])[0]}\n"
363
- f"- Период с макс. вовлеченностью: {max(period_stats.items(), key=lambda x: x[1]['avg_likes'])[0]}"
364
  )
365
 
366
- return analytics_summary, "\n".join(usernames), "\n".join(comments), "\n".join(likes), str(sum(map(int, likes)))
367
-
368
  except Exception as e:
369
  logger.error(f"Error in analyze_post: {e}", exc_info=True)
370
  return f"Error: {str(e)}", "", "", "", "0"
371
 
372
- # Создаем интерфейс Gradio
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
373
  iface = gr.Interface(
374
  fn=analyze_post,
375
  inputs=[
@@ -389,7 +300,7 @@ iface = gr.Interface(
389
  gr.Textbox(label="Total Likes on Comments")
390
  ],
391
  title="Enhanced Instagram Comment Analyzer",
392
- description="Анализатор комментариев Instagram с расширенной аналитикой и CSV-форматированием"
393
  )
394
 
395
  if __name__ == "__main__":
 
1
+ # analyzers.py
2
  import re
 
 
3
  import emoji
 
 
4
  import statistics
5
+ from collections import Counter
6
+ from typing import Dict, List, Tuple, Optional
7
+ import logging
8
  from io import StringIO
9
+ import csv
10
 
 
11
  logging.basicConfig(level=logging.INFO)
12
  logger = logging.getLogger(__name__)
13
 
14
+ class TextAnalyzer:
15
+ """Класс для базового анализа текста"""
16
+ @staticmethod
17
+ def clean_text(text: str) -> str:
18
+ return re.sub(r'\s+', ' ', text).strip()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
19
 
20
+ @staticmethod
21
+ def count_emojis(text: str) -> int:
22
+ return len([c for c in text if c in emoji.EMOJI_DATA])
 
23
 
24
+ @staticmethod
25
+ def extract_mentions(text: str) -> List[str]:
26
+ return re.findall(r'@[\w\.]+', text)
27
 
28
+ @staticmethod
29
+ def get_words(text: str) -> List[str]:
30
+ return [w for w in re.findall(r'\w+', text.lower()) if len(w) > 2]
31
+
32
+ class SentimentAnalyzer:
33
+ """Класс для анализа тональности"""
34
+ POSITIVE_INDICATORS = {
35
+ 'emoji': ['🔥', '❤️', '👍', '😊', '💪', '👏', '🎉', '♥️', '😍', '🙏'],
36
+ 'words': ['круто', 'супер', 'класс', 'огонь', 'пушка', 'отлично', 'здорово',
37
+ 'прекрасно', 'молодец', 'красота', 'спасибо', 'топ', 'лучший',
38
+ 'amazing', 'wonderful', 'great', 'perfect', 'love', 'beautiful']
39
+ }
40
 
41
+ NEGATIVE_INDICATORS = {
42
+ 'emoji': ['👎', '😢', '😞', '😠', '😡', '💔', '😕', '😑'],
43
+ 'words': ['плохо', 'ужас', 'отстой', 'фу', 'жесть', 'ужасно',
44
+ 'разочарован', 'печаль', 'грустно', 'bad', 'worst',
45
+ 'terrible', 'awful', 'sad', 'disappointed']
46
+ }
47
 
48
+ @classmethod
49
+ def analyze(cls, text: str) -> str:
50
+ text_lower = text.lower()
51
+ pos_count = sum(1 for ind in cls.POSITIVE_INDICATORS['emoji'] + cls.POSITIVE_INDICATORS['words']
52
+ if ind in text_lower)
53
+ neg_count = sum(1 for ind in cls.NEGATIVE_INDICATORS['emoji'] + cls.NEGATIVE_INDICATORS['words']
54
+ if ind in text_lower)
55
+
56
+ exclamation_boost = text.count('!') * 0.5
57
+ if pos_count > neg_count:
58
+ pos_count += exclamation_boost
59
+ elif neg_count > pos_count:
60
+ neg_count += exclamation_boost
61
+
62
+ return 'positive' if pos_count > neg_count else 'negative' if neg_count > pos_count else 'neutral'
63
 
64
+ class CommentExtractor:
65
+ """Класс для извлечения данных из комментариев"""
66
+ PATTERNS = {
67
+ 'username': [
 
68
  r"Фото профиля ([^\n]+)",
69
  r"^([^\s]+)\s+",
70
+ r"@([^\s]+)\s+"
71
+ ],
72
+ 'time': [
 
73
  r"(\d+)\s*(?:ч|нед)\.",
74
  r"(\d+)\s*(?:h|w)",
75
+ r"(\d+)\s*(?:час|hour|week)"
76
+ ],
77
+ 'likes': [
 
78
  r"(\d+) отметк[аи] \"Нравится\"",
79
  r"Нравится: (\d+)",
80
  r"(\d+) отметка \"Нравится\"",
81
  r"\"Нравится\": (\d+)",
82
+ r"likes?: (\d+)"
83
+ ],
84
+ 'metadata': [
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
85
  r"Фото профиля [^\n]+\n",
86
  r"\d+\s*(?:ч|нед|h|w|час|hour|week)\.",
87
  r"Нравится:?\s*\d+",
 
89
  r"Ответить",
90
  r"Показать перевод",
91
  r"Скрыть все ответы",
92
+ r"Смотреть все ответы \(\d+\)"
 
93
  ]
94
+ }
95
+
96
+ @classmethod
97
+ def extract_data(cls, comment_text: str) -> Tuple[Optional[str], Optional[str], int, float]:
98
+ try:
99
+ # Извлечение имени пользователя
100
+ username = None
101
+ for pattern in cls.PATTERNS['username']:
102
+ if match := re.search(pattern, comment_text):
103
+ username = match.group(1).strip()
104
+ break
105
+
106
+ if not username:
107
+ return None, None, 0, 0
108
+
109
+ # Очистка комментария
110
+ comment = comment_text
111
+ for pattern in cls.PATTERNS['metadata'] + [username]:
112
+ comment = re.sub(pattern, '', comment)
113
+ comment = TextAnalyzer.clean_text(comment)
114
+
115
+ # Извлечение времени
116
+ weeks = 0
117
+ for pattern in cls.PATTERNS['time']:
118
+ if match := re.search(pattern, comment_text):
119
+ time_value = int(match.group(1))
120
+ if any(unit in comment_text.lower() for unit in ['нед', 'w', 'week']):
121
+ weeks = time_value
122
+ else:
123
+ weeks = time_value / (24 * 7)
124
+ break
125
+
126
+ # Извлечение лайков
127
+ likes = 0
128
+ for pattern in cls.PATTERNS['likes']:
129
+ if match := re.search(pattern, comment_text):
130
+ likes = int(match.group(1))
131
+ break
132
+
133
+ return username, comment, likes, weeks
134
+
135
+ except Exception as e:
136
+ logger.error(f"Error extracting comment data: {e}")
137
+ return None, None, 0, 0
138
+
139
+ class StatsCalculator:
140
+ """Класс для расчета статистики"""
141
+ @staticmethod
142
+ def calculate_period_stats(weeks: List[float], likes: List[str], sentiments: List[str]) -> Dict:
143
+ if not weeks:
144
+ return {}
145
+
146
+ earliest_week = max(weeks)
147
+ latest_week = min(weeks)
148
+ week_range = earliest_week - latest_week
149
 
150
+ period_length = week_range / 3 if week_range > 0 else 1
151
+ engagement_periods = {
152
+ 'early': [],
153
+ 'middle': [],
154
+ 'late': []
155
+ }
156
 
157
+ for i, week in enumerate(weeks):
158
+ if week >= earliest_week - period_length:
159
+ engagement_periods['early'].append(i)
160
+ elif week >= earliest_week - 2 * period_length:
161
+ engagement_periods['middle'].append(i)
162
+ else:
163
+ engagement_periods['late'].append(i)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
164
 
165
+ return {
166
+ period: {
167
+ 'comments': len(indices),
168
+ 'avg_likes': sum(int(likes[i]) for i in indices) / len(indices) if indices else 0,
169
+ 'sentiment_ratio': sum(1 for i in indices if sentiments[i] == 'positive') / len(indices) if indices else 0
170
+ }
171
+ for period, indices in engagement_periods.items()
172
+ }
173
 
174
  def analyze_post(content_type: str, link_to_post: str, post_likes: int, post_date: str,
175
  description: str, comment_count: int, all_comments: str) -> Tuple[str, str, str, str, str]:
176
+ """Основная функция анализа поста"""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
177
  try:
178
+ # Разделение на комментарии
179
+ comment_patterns = '|'.join([
180
  r"(?=Фото профиля)",
181
  r"(?=\n\s*[a-zA-Z0-9._]+\s+[^\n]+\n)",
182
  r"(?=^[a-zA-Z0-9._]+\s+[^\n]+\n)",
183
  r"(?=@[a-zA-Z0-9._]+\s+[^\n]+\n)"
184
+ ])
185
+ comments_blocks = [block.strip() for block in re.split(comment_patterns, all_comments)
186
+ if block and block.strip() and 'Скрыто алгоритмами Instagram' not in block]
187
 
188
+ # Извлечение данных
189
+ data = [CommentExtractor.extract_data(block) for block in comments_blocks]
190
+ valid_data = [(u, c, l, w) for u, c, l, w in data if all((u, c))]
191
 
192
+ if not valid_data:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
193
  return "No comments found", "", "", "", "0"
194
 
195
+ usernames, comments, likes, weeks = zip(*valid_data)
196
+ likes = [str(l) for l in likes]
 
 
 
 
 
 
 
 
 
 
 
 
197
 
198
+ # Анализ комментариев
199
+ comment_stats = {
200
+ 'lengths': [len(c) for c in comments],
201
+ 'words': [len(TextAnalyzer.get_words(c)) for c in comments],
202
+ 'emojis': sum(TextAnalyzer.count_emojis(c) for c in comments),
203
+ 'mentions': [m for c in comments for m in TextAnalyzer.extract_mentions(c)],
204
+ 'sentiments': [SentimentAnalyzer.analyze(c) for c in comments]
205
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
206
 
207
+ # Расчет базовой статистики
208
+ basic_stats = {
209
+ 'total_comments': len(comments),
210
+ 'avg_length': statistics.mean(comment_stats['lengths']),
211
+ 'median_length': statistics.median(comment_stats['lengths']),
212
+ 'avg_words': statistics.mean(comment_stats['words']),
213
+ 'total_likes': sum(map(int, likes)),
214
+ 'avg_likes': statistics.mean(map(int, likes))
 
 
 
 
 
 
 
 
 
 
 
 
 
215
  }
216
 
217
+ # Расчет периодов
218
+ period_stats = StatsCalculator.calculate_period_stats(weeks, likes, comment_stats['sentiments'])
 
 
 
 
 
 
 
219
 
220
+ # Создание отчета
221
+ csv_data = create_csv_report(content_type, link_to_post, post_likes, basic_stats,
222
+ comment_stats, period_stats, usernames, comment_stats['mentions'])
223
+
224
+ analytics_summary = create_text_report(basic_stats, comment_stats, period_stats, csv_data)
225
+
226
+ return (
227
+ analytics_summary,
228
+ "\n".join(usernames),
229
+ "\n".join(comments),
230
+ "\n".join(likes),
231
+ str(basic_stats['total_likes'])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
232
  )
233
 
 
 
234
  except Exception as e:
235
  logger.error(f"Error in analyze_post: {e}", exc_info=True)
236
  return f"Error: {str(e)}", "", "", "", "0"
237
 
238
+ def create_csv_report(content_type, link, post_likes, basic_stats, comment_stats, period_stats, usernames, mentions):
239
+ """Создание CSV отчета"""
240
+ csv_data = {
241
+ 'metadata': {
242
+ 'content_type': content_type,
243
+ 'link': link,
244
+ 'post_likes': post_likes
245
+ },
246
+ 'basic_stats': basic_stats,
247
+ 'sentiment_stats': dict(Counter(comment_stats['sentiments'])),
248
+ 'period_analysis': period_stats,
249
+ 'top_users': dict(Counter(usernames).most_common(5)),
250
+ 'top_mentioned': dict(Counter(mentions).most_common(5))
251
+ }
252
+
253
+ output = StringIO()
254
+ writer = csv.writer(output)
255
+ for section, data in csv_data.items():
256
+ writer.writerow([section])
257
+ for key, value in data.items():
258
+ writer.writerow([key, value])
259
+ writer.writerow([])
260
+ return output.getvalue()
261
+
262
+ def create_text_report(basic_stats, comment_stats, period_stats, csv_data):
263
+ """Создание текстового отчета"""
264
+ sentiment_dist = Counter(comment_stats['sentiments'])
265
+ return (
266
+ f"CSV DATA:\n{csv_data}\n\n"
267
+ f"СТАТИСТИКА:\n"
268
+ f"- Всего комментариев: {basic_stats['total_comments']}\n"
269
+ f"- Среднее лайков: {basic_stats['avg_likes']:.1f}\n"
270
+ f"АНАЛИЗ КОНТЕНТА:\n"
271
+ f"- Средняя длина: {basic_stats['avg_length']:.1f}\n"
272
+ f"- Медиана длины: {basic_stats['median_length']}\n"
273
+ f"- Среднее слов: {basic_stats['avg_words']:.1f}\n"
274
+ f"- Эмодзи: {comment_stats['emojis']}\n"
275
+ f"ТОНАЛЬНОСТЬ:\n"
276
+ f"- Позитив: {sentiment_dist['positive']}\n"
277
+ f"- Нейтрально: {sentiment_dist['neutral']}\n"
278
+ f"- Негатив: {sentiment_dist['negative']}\n"
279
+ )
280
+
281
+ # Создание интерфейса Gradio
282
+ import gradio as gr
283
+
284
  iface = gr.Interface(
285
  fn=analyze_post,
286
  inputs=[
 
300
  gr.Textbox(label="Total Likes on Comments")
301
  ],
302
  title="Enhanced Instagram Comment Analyzer",
303
+ description="Анализатор комментариев Instagram с расширенной аналитикой"
304
  )
305
 
306
  if __name__ == "__main__":