Genn9508 commited on
Commit
35b3328
·
verified ·
1 Parent(s): b69da7e

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +15 -288
app.py CHANGED
@@ -1,303 +1,30 @@
1
  import sys
2
  import os
3
- import pandas as pd
4
- from datetime import datetime, timedelta
5
  try:
6
  from bs4 import BeautifulSoup
7
- except ImportError:
8
- print("Ошибка: модуль bs4 не найден. Проверьте файл requirements.txt")
 
 
 
 
 
9
  sys.exit(1)
10
- import requests
11
- import re
12
- import time
13
- import feedparser
14
- import gradio as gr
15
 
16
  # Конфигурация
17
  RSS_URL = 'https://vecherka.su/rss/'
18
  CSV_FILE_PATH = 'bd.csv'
19
 
20
- def parse_article_date(published_date_str):
21
- """Парсит дату публикации статьи из RSS в объект datetime."""
22
- date_formats = [
23
- '%a, %d %b %Y %H:%M:%S %z',
24
- '%a, %d %b %Y %H:%M:%S %Z',
25
- '%d %b %Y' # запасной вариант
26
- ]
27
- for fmt in date_formats:
28
- try:
29
- return datetime.strptime(published_date_str, fmt)
30
- except ValueError:
31
- continue
32
- print(f"Could not parse date: '{published_date_str}'")
33
- return None
34
-
35
-
36
-
37
-
38
-
39
-
40
- def parse_article_date(published_date_str):
41
- """Парсит дату публикации статьи из RSS в объект datetime."""
42
- date_formats = [
43
- '%a, %d %b %Y %H:%M:%S %z',
44
- '%a, %d %b %Y %H:%M:%S %Z',
45
- '%d %b %Y' # запасной вариант
46
- ]
47
- for fmt in date_formats:
48
- try:
49
- return datetime.strptime(published_date_str, fmt)
50
- except ValueError:
51
- continue
52
- print(f"Could not parse date: '{published_date_str}'")
53
- return None
54
-
55
- def is_recent_article(parsed_date):
56
- """Проверяет, опубликована ли статья сегодня или вчера."""
57
- today = datetime.now()
58
- yesterday = today - timedelta(days=1)
59
- article_date_str = parsed_date.strftime('%d-%m-%Y')
60
- today_str = today.strftime('%d-%m-%Y')
61
- yesterday_str = yesterday.strftime('%d-%m-%Y')
62
- return article_date_str == today_str or article_date_str == yesterday_str
63
-
64
- def check_for_new_articles_init():
65
- """Инициализация дат для проверки статей."""
66
- today_date = datetime.now()
67
- yesterday_date = today_date - timedelta(days=1)
68
- today_str = today_date.strftime('%d-%m-%Y')
69
- yesterday_str = yesterday_date.strftime('%d-%m-%Y')
70
- return today_str, yesterday_str
71
-
72
-
73
-
74
- def extract_images_from_entry(entry):
75
- """Извлекает URL изображений из RSS‑записи (media_content, links, HTML‑контент)."""
76
- image_urls = []
77
-
78
- # Из media_content
79
- if 'media_content' in entry and len(image_urls) < 3:
80
- for media in entry.media_content:
81
- if (media.get('type', '').startswith('image/') and
82
- media.get('url') and media.get('url') not in image_urls):
83
- image_urls.append(media['url'])
84
- if len(image_urls) == 3: break
85
-
86
- # Из links с rel='enclosure'
87
- if 'links' in entry and len(image_urls) < 3:
88
- for link_entry in entry.links:
89
- if (link_entry.get('rel') == 'enclosure' and
90
- link_entry.get('type', '').startswith('image/') and
91
- link_entry.get('href') and link_entry.get('href') not in image_urls):
92
- image_urls.append(link_entry['href'])
93
- if len(image_urls) == 3: break
94
-
95
- # Из HTML‑контента (summary или content)
96
- html_content = entry.get('summary', '') or (
97
- entry.get('content', [{}])[0].get('value', '') if entry.get('content') else '')
98
- if html_content and len(image_urls) < 3:
99
- soup = BeautifulSoup(html_content, 'html.parser')
100
- img_tags = soup.find_all('img')
101
- for img in img_tags:
102
- if img.get('src') and img.get('src') not in image_urls:
103
- image_urls.append(img['src'])
104
- if len(image_urls) == 3: break
105
- return image_urls
106
-
107
- def fetch_article_text(news_link):
108
- """Загружает и извлекает текст статьи по ссылке."""
109
- try:
110
- response = requests.get(news_link, timeout=10)
111
- response.raise_for_status()
112
- article_soup = BeautifulSoup(response.text, 'html.parser')
113
- detail_text_div = article_soup.find('div', class_='detail-text')
114
-
115
- if detail_text_div:
116
- full_text = detail_text_div.get_text(separator=' ', strip=True)
117
- # Удаляем предложения с «подписывайтесь»
118
- full_text = re.sub(r'[^.!?]*\bподписывайтесь\b[^.!?]*[?.!]', '', full_text, flags=re.IGNORECASE)
119
- full_text = re.sub(r'\s+', ' ', full_text).strip()
120
-
121
- # Пропускаем статьи с «Реклама»
122
- if re.search(r'\bРеклама\b', full_text, re.IGNORECASE):
123
- print(f"Skipping article due to 'Реклама' in full text.")
124
- return None
125
- return full_text
126
- else:
127
- print(f"Could not find 'detail-text' div for article: {news_link}")
128
- return None
129
- except requests.exceptions.RequestException as e:
130
- print(f"Error fetching content for {news_link}: {e}")
131
- return None
132
- except Exception as e:
133
- print(f"Error parsing content for {news_link}: {e}")
134
- return None
135
-
136
-
137
-
138
- def process_single_article(entry, today_str, yesterday_str, processed_links):
139
- """Обрабатывает одну статью: проверяет дату, извлекает данные, возвращает словарь с данными или None."""
140
- title = getattr(entry, 'title', 'No Title')
141
- news_link = getattr(entry, 'link', None)
142
-
143
- # Пропускаем, если нет ссылки или статья уже обработана
144
- if not news_link or news_link in processed_links:
145
- return None
146
-
147
- published_date_str = getattr(entry, 'published', None)
148
- if not published_date_str:
149
- print(f"Skipping entry '{title}' due to missing publication date.")
150
- return None
151
-
152
- # Парсим дату публикации
153
- parsed_date = parse_article_date(published_date_str)
154
- if not parsed_date:
155
- return None # Пропускаем статью, если дату не удалось распарсить
156
-
157
- article_date_str = parsed_date.strftime('%d-%m-%Y')
158
-
159
- # Фильтруем статьи, опубликованные сегодня или вчера
160
- if article_date_str != today_str and article_date_str != yesterday_str:
161
- return None
162
-
163
- # Извлекаем URL изображений
164
- image_urls = extract_images_from_entry(entry)
165
-
166
- # Получаем полный текст статьи
167
- full_text = fetch_article_text(news_link)
168
- if not full_text:
169
- return None
170
-
171
- # Создаём короткий текст (первые 200 символов)
172
- short_text = full_text[:200] if len(full_text) > 200 else full_text
173
-
174
- return {
175
- 'title': title,
176
- 'published': article_date_str,
177
- 'image_urls': image_urls,
178
- 'link': news_link,
179
- 'full_text': full_text,
180
- 'Status': 'Off',
181
- 'short_text': short_text,
182
- 'Constant': ''
183
- }
184
-
185
- def save_new_articles(new_articles_data, existing_df):
186
- """Сохраняет новые статьи в CSV‑файл."""
187
- if not new_articles_data:
188
- print("No new articles found to add.")
189
- return 0
190
-
191
- new_df = pd.DataFrame(new_articles_data)
192
- # Преобразуем список URL изображений в строку через запятую
193
- new_df['image_urls'] = new_df['image_urls'].apply(lambda x: ', '.join(x))
194
-
195
- if existing_df is not None and not existing_df.empty:
196
- # Дописываем в существующий файл без заголовка
197
- new_df.to_csv(
198
- CSV_FILE_PATH,
199
- mode='a',
200
- header=False,
201
- index=False,
202
- encoding='utf-8-sig',
203
- sep=';'
204
- )
205
- else:
206
- # Создаём новый файл с заголовком
207
- new_df.to_csv(
208
- CSV_FILE_PATH,
209
- mode='w',
210
- header=True,
211
- index=False,
212
- encoding='utf-8-sig',
213
- sep=';'
214
- )
215
-
216
- articles_added_count = len(new_articles_data)
217
- print(f"Added {articles_added_count} new articles to {CSV_FILE_PATH}.")
218
- return articles_added_count
219
-
220
- def check_for_new_articles():
221
- """Основная функция: проверяет RSS‑ленту на новые статьи и сохраняет их в CSV. Возвращает количество добавленных статей."""
222
- # Шаг 1: инициализация
223
- today_str, yesterday_str = check_for_new_articles_init()
224
-
225
- # Шаг 2: загрузка существующих статей
226
- processed_links, existing_df = load_existing_articles()
227
-
228
- # Шаг 3: получение RSS
229
- entries = fetch_and_parse_rss()
230
- if not entries:
231
- return 0
232
-
233
- # Шаг 4: обработка каждой статьи
234
- new_articles_data = []
235
- for entry in entries:
236
- article_data = process_single_article(entry, today_str, yesterday_str, processed_links)
237
- if article_data:
238
- new_articles_data.append(article_data)
239
-
240
- # Шаг 5: сохранение результатов
241
- return save_new_articles(new_articles_data, existing_df)
242
-
243
-
244
-
245
- # Интерфейс Gradio
246
- with gr.Blocks() as demo:
247
- gr.Markdown("# RSS Article Monitor — Мониторинг RSS‑ленты")
248
-
249
- with gr.Row():
250
- with gr.Column():
251
- check_btn = gr.Button("🔎 Проверить новые статьи")
252
- status_output = gr.Textbox(label="Статус выполн��ния", lines=6)
253
- rss_url_input = gr.Textbox(
254
- label="URL RSS‑ленты",
255
- value=RSS_URL,
256
- placeholder="Введите URL RSS‑ленты..."
257
- )
258
-
259
- with gr.Column():
260
- stats_output = gr.Textbox(label="Статистика", lines=4)
261
- preview_output = gr.Dataframe(
262
- label="Предпросмотр новых статей",
263
- headers=["Заголовок", "Дата", "Ссылка"],
264
- max_rows=5
265
- )
266
-
267
- def check_and_update():
268
- """Обработчик кнопки: запускает проверку и обновляет интерфейс."""
269
- try:
270
- # Обновляем URL, если пользователь его изменил
271
- global RSS_URL
272
- RSS_URL = rss_url_input.value
273
-
274
- count = check_for_new_articles()
275
- stats = f"✅ Успешно обработано: {count} новых статей\n"
276
- stats += f"📊 Всего статей в базе: {len(load_existing_articles()[1]) if load_existing_articles()[1] is not None else 0}"
277
-
278
- preview_data = []
279
- if count > 0:
280
- # Загружаем последние добавленные статьи для предпросмотра
281
- df = pd.read_csv(CSV_FILE_PATH, encoding='utf-8-sig', sep=';')
282
- latest = df.tail(min(5, count))
283
- preview_data = latest[['title', 'published', 'link']].values.tolist()
284
-
285
- return f"Результат проверки:\n{stats}", stats, preview_data
286
- except Exception as e:
287
- error_msg = f"❌ Ошибка во время обработки: {str(e)}"
288
- print(error_msg)
289
- return error_msg, error_msg, []
290
-
291
- check_btn.click(
292
- fn=check_and_update,
293
- outputs=[status_output, stats_output, preview_output]
294
- )
295
-
296
- # Точка входа приложения
297
  if __name__ == "__main__":
298
- print("Запуск RSS‑монитора...")
 
 
 
 
299
  demo.launch(
300
- share=False, # Не создавать публичную ссылку
301
  server_name="0.0.0.0",
302
  server_port=7860
303
  )
 
1
  import sys
2
  import os
3
+
4
+ # Тест зависимостей
5
  try:
6
  from bs4 import BeautifulSoup
7
+ import requests
8
+ import feedparser
9
+ import pandas as pd
10
+ import gradio as gr
11
+ print("✅ Все зависимости успешно загружены!")
12
+ except ImportError as e:
13
+ print(f"❌ Ошибка импорта: {e}")
14
  sys.exit(1)
 
 
 
 
 
15
 
16
  # Конфигурация
17
  RSS_URL = 'https://vecherka.su/rss/'
18
  CSV_FILE_PATH = 'bd.csv'
19
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
20
  if __name__ == "__main__":
21
+ print("Запуск приложения...")
22
+ # Создаём простой интерфейс Gradio
23
+ with gr.Blocks() as demo:
24
+ gr.Markdown("# Тест зависимостей")
25
+ gr.Textbox(value="Все пакеты успешно загружены!", label="Статус")
26
  demo.launch(
27
+ share=False,
28
  server_name="0.0.0.0",
29
  server_port=7860
30
  )