202 lines
6.7 KiB
Python
202 lines
6.7 KiB
Python
|
|
#!/usr/bin/env python3
|
|||
|
|
"""
|
|||
|
|
Обновление статусов доступности сайтов на основе результатов краулинга
|
|||
|
|
"""
|
|||
|
|
|
|||
|
|
import psycopg2
|
|||
|
|
from urllib.parse import unquote
|
|||
|
|
import re
|
|||
|
|
|
|||
|
|
# Конфигурация БД
|
|||
|
|
DB_CONFIG = {
|
|||
|
|
'host': "147.45.189.234",
|
|||
|
|
'port': 5432,
|
|||
|
|
'database': "default_db",
|
|||
|
|
'user': "gen_user",
|
|||
|
|
'password': unquote("2~~9_%5EkVsU%3F2%5CS")
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
def update_website_statuses(log_file_path):
|
|||
|
|
"""Обновление статусов на основе лог-файла краулера"""
|
|||
|
|
|
|||
|
|
conn = psycopg2.connect(**DB_CONFIG)
|
|||
|
|
cur = conn.cursor()
|
|||
|
|
|
|||
|
|
# Паттерны для определения типов ошибок
|
|||
|
|
error_patterns = {
|
|||
|
|
'timeout': r'Timeout \d+ms exceeded',
|
|||
|
|
'connection_refused': r'ERR_CONNECTION_REFUSED',
|
|||
|
|
'dns_error': r'ERR_NAME_NOT_RESOLVED',
|
|||
|
|
'ssl_error': r'ERR_SSL_PROTOCOL_ERROR|ERR_CERT_DATE_INVALID',
|
|||
|
|
'http_error': r'Ошибка загрузки: (403|404|500)',
|
|||
|
|
'invalid_url': r'Cannot navigate to invalid URL'
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
# Читаем лог-файл
|
|||
|
|
with open(log_file_path, 'r', encoding='utf-8') as f:
|
|||
|
|
log_content = f.read()
|
|||
|
|
|
|||
|
|
# Находим все отели и их ошибки
|
|||
|
|
hotel_pattern = r'🏨 «(.+?)»\n.*?🌐 (.+?)\n'
|
|||
|
|
hotels = re.findall(hotel_pattern, log_content, re.DOTALL)
|
|||
|
|
|
|||
|
|
stats = {
|
|||
|
|
'accessible': 0,
|
|||
|
|
'timeout': 0,
|
|||
|
|
'connection_refused': 0,
|
|||
|
|
'dns_error': 0,
|
|||
|
|
'ssl_error': 0,
|
|||
|
|
'http_error': 0,
|
|||
|
|
'invalid_url': 0,
|
|||
|
|
'not_checked': 0
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
for hotel_name, website in hotels:
|
|||
|
|
# Нормализуем URL
|
|||
|
|
website = website.strip()
|
|||
|
|
if not website.startswith(('http://', 'https://')):
|
|||
|
|
website = 'https://' + website
|
|||
|
|
|
|||
|
|
# Ищем секцию этого отеля в логе
|
|||
|
|
hotel_section_pattern = f'🏨 «{re.escape(hotel_name)}».*?(?=🏨 «|======================================================================\\n📊 ИТОГИ:)'
|
|||
|
|
hotel_section_match = re.search(hotel_section_pattern, log_content, re.DOTALL)
|
|||
|
|
|
|||
|
|
if not hotel_section_match:
|
|||
|
|
continue
|
|||
|
|
|
|||
|
|
hotel_section = hotel_section_match.group(0)
|
|||
|
|
|
|||
|
|
# Определяем статус
|
|||
|
|
status = 'not_checked'
|
|||
|
|
|
|||
|
|
# Проверяем на успешность
|
|||
|
|
if '✓ Спарсено' in hotel_section and '✓ Сохранено' in hotel_section:
|
|||
|
|
status = 'accessible'
|
|||
|
|
stats['accessible'] += 1
|
|||
|
|
else:
|
|||
|
|
# Проверяем типы ошибок
|
|||
|
|
for error_type, pattern in error_patterns.items():
|
|||
|
|
if re.search(pattern, hotel_section):
|
|||
|
|
status = error_type
|
|||
|
|
stats[error_type] += 1
|
|||
|
|
break
|
|||
|
|
|
|||
|
|
if status == 'not_checked':
|
|||
|
|
stats['not_checked'] += 1
|
|||
|
|
|
|||
|
|
# Обновляем статус в БД
|
|||
|
|
try:
|
|||
|
|
cur.execute('''
|
|||
|
|
UPDATE hotel_main
|
|||
|
|
SET website_status = %s
|
|||
|
|
WHERE website_address LIKE %s
|
|||
|
|
OR website_address LIKE %s
|
|||
|
|
OR website_address LIKE %s
|
|||
|
|
''', (status, f'%{website.replace("https://", "").replace("http://", "").split("/")[0]}%',
|
|||
|
|
f'%{website}%',
|
|||
|
|
website.replace("https://", "").replace("http://", "").split("/")[0]))
|
|||
|
|
|
|||
|
|
except Exception as e:
|
|||
|
|
print(f"Ошибка обновления для {hotel_name}: {e}")
|
|||
|
|
|
|||
|
|
conn.commit()
|
|||
|
|
cur.close()
|
|||
|
|
conn.close()
|
|||
|
|
|
|||
|
|
return stats
|
|||
|
|
|
|||
|
|
|
|||
|
|
def generate_report(region_name='Камчатский край'):
|
|||
|
|
"""Генерация отчета по доступности сайтов"""
|
|||
|
|
|
|||
|
|
conn = psycopg2.connect(**DB_CONFIG)
|
|||
|
|
cur = conn.cursor()
|
|||
|
|
|
|||
|
|
# Получаем статистику
|
|||
|
|
cur.execute('''
|
|||
|
|
SELECT
|
|||
|
|
website_status,
|
|||
|
|
COUNT(*) as count,
|
|||
|
|
ROUND(COUNT(*) * 100.0 / SUM(COUNT(*)) OVER(), 2) as percentage
|
|||
|
|
FROM hotel_main
|
|||
|
|
WHERE region_name ILIKE %s
|
|||
|
|
GROUP BY website_status
|
|||
|
|
ORDER BY count DESC
|
|||
|
|
''', (f'%{region_name}%',))
|
|||
|
|
|
|||
|
|
stats = cur.fetchall()
|
|||
|
|
|
|||
|
|
print(f"\n{'='*80}")
|
|||
|
|
print(f"📊 ОТЧЕТ О ДОСТУПНОСТИ САЙТОВ: {region_name}")
|
|||
|
|
print(f"{'='*80}\n")
|
|||
|
|
|
|||
|
|
status_labels = {
|
|||
|
|
'accessible': '✅ Сайт доступен',
|
|||
|
|
'no_website': '❌ Сайт отсутствует',
|
|||
|
|
'timeout': '⏱️ Таймаут (сайт медленный)',
|
|||
|
|
'connection_refused': '🚫 Соединение отклонено',
|
|||
|
|
'dns_error': '🔍 DNS ошибка (домен не найден)',
|
|||
|
|
'ssl_error': '🔒 SSL ошибка (проблема с сертификатом)',
|
|||
|
|
'http_error': '⚠️ HTTP ошибка (403/404/500)',
|
|||
|
|
'invalid_url': '❓ Неверный URL',
|
|||
|
|
'not_checked': '⏳ Не проверено'
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
for status, count, percentage in stats:
|
|||
|
|
label = status_labels.get(status, status)
|
|||
|
|
print(f"{label:45} {count:5} ({percentage:5.2f}%)")
|
|||
|
|
|
|||
|
|
# Получаем список недоступных отелей
|
|||
|
|
print(f"\n{'='*80}")
|
|||
|
|
print("🔴 ОТЕЛИ С НЕДОСТУПНЫМИ САЙТАМИ:")
|
|||
|
|
print(f"{'='*80}\n")
|
|||
|
|
|
|||
|
|
cur.execute('''
|
|||
|
|
SELECT full_name, website_address, website_status
|
|||
|
|
FROM hotel_main
|
|||
|
|
WHERE region_name ILIKE %s
|
|||
|
|
AND website_status NOT IN ('accessible', 'no_website', 'not_checked')
|
|||
|
|
ORDER BY website_status, full_name
|
|||
|
|
LIMIT 20
|
|||
|
|
''', (f'%{region_name}%',))
|
|||
|
|
|
|||
|
|
problematic = cur.fetchall()
|
|||
|
|
|
|||
|
|
for name, website, status in problematic:
|
|||
|
|
status_icon = {
|
|||
|
|
'timeout': '⏱️',
|
|||
|
|
'connection_refused': '🚫',
|
|||
|
|
'dns_error': '🔍',
|
|||
|
|
'ssl_error': '🔒',
|
|||
|
|
'http_error': '⚠️',
|
|||
|
|
'invalid_url': '❓'
|
|||
|
|
}.get(status, '❓')
|
|||
|
|
|
|||
|
|
print(f"{status_icon} {name}")
|
|||
|
|
print(f" 🌐 {website}")
|
|||
|
|
print(f" 📋 Статус: {status}")
|
|||
|
|
print()
|
|||
|
|
|
|||
|
|
cur.close()
|
|||
|
|
conn.close()
|
|||
|
|
|
|||
|
|
|
|||
|
|
if __name__ == "__main__":
|
|||
|
|
import sys
|
|||
|
|
|
|||
|
|
if len(sys.argv) > 1:
|
|||
|
|
log_file = sys.argv[1]
|
|||
|
|
print(f"📖 Обработка лог-файла: {log_file}")
|
|||
|
|
stats = update_website_statuses(log_file)
|
|||
|
|
|
|||
|
|
print("\n📊 СТАТИСТИКА ОБНОВЛЕНИЯ:")
|
|||
|
|
for status, count in stats.items():
|
|||
|
|
print(f" {status}: {count}")
|
|||
|
|
|
|||
|
|
# Генерируем отчет
|
|||
|
|
generate_report('Камчатский край')
|
|||
|
|
|
|||
|
|
|
|||
|
|
|
|||
|
|
|