#!/usr/bin/env python3 """ Проверка отелей в реестре операторов персональных данных Роскомнадзора Проверяет только отели с сайтами """ import asyncio import psycopg2 from psycopg2.extras import RealDictCursor from playwright.async_api import async_playwright from urllib.parse import unquote from datetime import datetime import logging import re # Настройка логирования logging.basicConfig( level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s', handlers=[ logging.FileHandler(f'rkn_check_{datetime.now().strftime("%Y%m%d_%H%M%S")}.log'), logging.StreamHandler() ] ) logger = logging.getLogger(__name__) # Конфигурация БД DB_CONFIG = { 'host': "147.45.189.234", 'port': 5432, 'database': "default_db", 'user': "gen_user", 'password': unquote("2~~9_%5EkVsU%3F2%5CS") } # Конфигурация REQUEST_DELAY = 3 # Задержка между запросами (секунды) PAGE_TIMEOUT = 30000 class RKNChecker: """Проверка в реестре РКН""" def __init__(self): self.db_conn = None self.browser = None self.page = None def connect_db(self): """Подключение к БД""" try: self.db_conn = psycopg2.connect(**DB_CONFIG) logger.info("✓ Подключено к PostgreSQL") except Exception as e: logger.error(f"✗ Ошибка подключения к БД: {e}") raise def close_db(self): """Закрытие соединения с БД""" if self.db_conn: self.db_conn.close() async def init_browser(self): """Инициализация браузера""" playwright = await async_playwright().start() self.browser = await playwright.chromium.launch(headless=True) self.page = await self.browser.new_page() await self.page.set_viewport_size({"width": 1920, "height": 1080}) await self.page.set_extra_http_headers({ 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36' }) logger.info("✓ Браузер инициализирован") async def close_browser(self): """Закрытие браузера""" if self.browser: await self.browser.close() async def check_inn_in_registry(self, inn: str) -> dict: """Проверка ИНН в реестре РКН""" if not inn or inn == '-': return { 'found': False, 'status': 'no_inn', 'message': 'ИНН не указан' } try: # Формируем URL url = f'https://pd.rkn.gov.ru/operators-registry/operators-list/?act=search&inn={inn}' logger.info(f" 🔍 Проверка ИНН: {inn}") # Задержка перед запросом await asyncio.sleep(REQUEST_DELAY) # Загружаем страницу response = await self.page.goto(url, timeout=PAGE_TIMEOUT, wait_until='networkidle') if response.status != 200: return { 'found': False, 'status': 'error', 'message': f'HTTP {response.status}' } # Ждем загрузки await asyncio.sleep(1) # Получаем текст страницы text = await self.page.evaluate('() => document.body.innerText') # Проверяем наличие результатов if 'Не найдено' in text or 'не найдено' in text.lower(): logger.info(f" ❌ Не найден в реестре") return { 'found': False, 'status': 'not_found', 'message': 'Не найден в реестре РКН' } # Пытаемся извлечь данные # Ищем регистрационный номер (разные форматы: 41-14-000746 или 10-0107355) reg_number_match = re.search(r'(\d{2}-\d{2,4}-\d{6,7})', text) reg_number = reg_number_match.group(1) if reg_number_match else None # Ищем дату регистрации date_match = re.search(r'Приказ.*?(\d{2}\.\d{2}\.\d{4})', text) reg_date = date_match.group(1) if date_match else None # Ищем название организации org_match = re.search(r'(?:Общество|Индивидуальный предприниматель|Акционерное общество).*?(?=ИНН:|$)', text, re.IGNORECASE) org_name = org_match.group(0).strip() if org_match else None if reg_number: logger.info(f" ✅ Найден: {reg_number} ({reg_date})") return { 'found': True, 'status': 'found', 'reg_number': reg_number, 'reg_date': reg_date, 'org_name': org_name, 'message': f'Зарегистрирован: {reg_number}' } else: logger.info(f" ⚠️ Страница загружена, но данные не распознаны") return { 'found': None, 'status': 'unclear', 'message': 'Результат неясен' } except Exception as e: logger.error(f" ✗ Ошибка проверки: {e}") return { 'found': False, 'status': 'error', 'message': str(e) } def save_result(self, hotel_id: str, result: dict): """Сохранение результата в БД""" try: cur = self.db_conn.cursor() cur.execute(''' UPDATE hotel_main SET rkn_registry_status = %s, rkn_registry_number = %s, rkn_registry_date = %s, rkn_checked_at = %s WHERE id = %s ''', ( result['status'], result.get('reg_number'), result.get('reg_date'), datetime.now(), hotel_id )) self.db_conn.commit() cur.close() except Exception as e: logger.error(f" ✗ Ошибка сохранения в БД: {e}") self.db_conn.rollback() async def process_hotels(self, region_name=None): """Обработка отелей""" # Получаем отели с сайтами cur = self.db_conn.cursor(cursor_factory=RealDictCursor) where_clause = "" params = [] if region_name: where_clause = "AND h.region_name ILIKE %s" params = [f'%{region_name}%'] query = f''' SELECT DISTINCT h.id, h.full_name, h.owner_inn, h.website_address, h.region_name FROM hotel_main h WHERE h.owner_inn IS NOT NULL AND h.owner_inn != '' AND h.owner_inn != '-' AND (h.rkn_checked_at IS NULL OR h.rkn_checked_at < NOW() - INTERVAL '30 days') {where_clause} ORDER BY h.region_name, h.full_name ''' cur.execute(query, params) hotels = cur.fetchall() cur.close() logger.info(f"\n{'='*70}") logger.info(f"🏨 Отелей для проверки: {len(hotels)}") logger.info(f"⏱️ Примерное время: {len(hotels) * REQUEST_DELAY / 60:.1f} минут") logger.info(f"{'='*70}\n") # Обрабатываем отели results = { 'found': 0, 'not_found': 0, 'error': 0, 'unclear': 0, 'no_inn': 0 } for i, hotel in enumerate(hotels, 1): logger.info(f"\n[{i}/{len(hotels)}] {'='*50}") logger.info(f"🏨 {hotel['full_name']}") logger.info(f"📍 {hotel['region_name']}") logger.info(f"🌐 {hotel['website_address']}") logger.info(f"🔢 ИНН: {hotel['owner_inn']}") # Проверяем в реестре result = await self.check_inn_in_registry(hotel['owner_inn']) # Сохраняем результат self.save_result(hotel['id'], result) # Обновляем статистику if result['found'] == True: results['found'] += 1 elif result['found'] == False: if result['status'] == 'no_inn': results['no_inn'] += 1 elif result['status'] == 'not_found': results['not_found'] += 1 else: results['error'] += 1 else: results['unclear'] += 1 # Итоги logger.info(f"\n{'='*70}") logger.info("📊 ИТОГИ ПРОВЕРКИ:") logger.info(f" ✅ Найдено в реестре: {results['found']}") logger.info(f" ❌ Не найдено в реестре: {results['not_found']}") logger.info(f" ⚠️ Ошибки: {results['error']}") logger.info(f" ❓ Неясно: {results['unclear']}") logger.info(f" 🔢 Нет ИНН: {results['no_inn']}") logger.info(f"{'='*70}") return results async def main(): """Основная функция""" import sys region = sys.argv[1] if len(sys.argv) > 1 else None checker = RKNChecker() try: # Подключаемся к БД checker.connect_db() # Добавляем колонки для РКН (если их нет) cur = checker.db_conn.cursor() cur.execute(''' ALTER TABLE hotel_main ADD COLUMN IF NOT EXISTS rkn_registry_status VARCHAR(50); ''') cur.execute(''' ALTER TABLE hotel_main ADD COLUMN IF NOT EXISTS rkn_registry_number VARCHAR(50); ''') cur.execute(''' ALTER TABLE hotel_main ADD COLUMN IF NOT EXISTS rkn_registry_date VARCHAR(20); ''') cur.execute(''' ALTER TABLE hotel_main ADD COLUMN IF NOT EXISTS rkn_checked_at TIMESTAMP; ''') checker.db_conn.commit() cur.close() logger.info("✓ Колонки для РКН добавлены") # Инициализируем браузер await checker.init_browser() # Обрабатываем отели await checker.process_hotels(region) except Exception as e: logger.error(f"❌ Критическая ошибка: {e}") finally: await checker.close_browser() checker.close_db() if __name__ == "__main__": asyncio.run(main())