Files
osint_parser_ada/main.py
T
ada 12d5c6eaff feat: Add data models for disciplinary actions, organizations, and training centers
- Implemented DisciplinaryAction model for tracking disciplinary measures.
- Created Organization model to represent auditing organizations with relevant details.
- Developed TrainingCenter model for educational and methodological centers.
- Added methods to convert models to dictionaries for easy export.

feat: Implement parsers for auditors and organizations

- Developed AuditorsParser to scrape and parse auditor registry data.
- Created OrganizationsParser for scraping auditing organizations' registry.
- Implemented GenericRegistryParser for handling generic tabular data.

chore: Set up base parser functionality

- Established BaseParser class with common methods for making requests and parsing HTML.
- Added pagination handling and detailed page parsing capabilities.

feat: Implement Excel export functionality

- Created ExcelExporter class for exporting data to Excel files with formatting options.
- Added support for exporting multiple sheets and organization data specifically.

chore: Add logging utilities

- Implemented setup_logger function for consistent logging across the application.
- Configured logging to output to both console and file.

chore: Update requirements and scripts

- Added necessary dependencies for parsing, Excel handling, and logging.
- Created run.sh and run.fish scripts for easy execution of the parser.
2025-10-25 20:31:45 +03:00

316 lines
14 KiB
Python
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env python3
"""
Парсер реестров СРО ААС (Саморегулируемая организация аудиторов)
Автор: [ada]
"""
import sys
import os
from datetime import datetime
from config import REGISTRIES
from parsers.organizations_parser import OrganizationsParser
from parsers.auditors_parser import AuditorsParser
from parsers.generic_parser import GenericRegistryParser
from utils.excel_exporter import ExcelExporter
from utils.logger import setup_logger
def print_banner():
"""Вывод баннера приложения"""
banner = """
╔═══════════════════════════════════════════════════════════╗
║ ПАРСЕР РЕЕСТРОВ СРО ААС ║
║ Саморегулируемая организация аудиторов ║
║ Ассоциация «Содружество» ║
╚═══════════════════════════════════════════════════════════╝
"""
print(banner)
def print_menu():
"""Вывод главного меню"""
print("\n" + "=" * 60)
print("ДОСТУПНЫЕ РЕЕСТРЫ:")
print("=" * 60)
print("\n📋 ДЕЙСТВИТЕЛЬНЫЕ ЧЛЕНЫ И ВЫДАННЫЕ АТТЕСТАТЫ:")
print(" 1. Реестр членов — аудиторов и индивидуальных аудиторов ⭐")
print(" 2. Реестр членов — аудиторских организаций ⭐")
print(" 3. Реестр выданных СРО ААС квалификационных аттестатов ⭐")
print(" 4. Перечень индивидуальных аудиторов ⭐")
print(" 5. Реестр учебно-методических центров ⭐")
print(" 6. Сведения о подтверждении ОППК аудиторами ⭐")
print(" 7. Сведения о прохождении ПК руководителем аудита ОЗО ФР ⭐")
print("\n❌ ИСКЛЮЧЕННЫЕ ЧЛЕНЫ И АННУЛИРОВАННЫЕ АТТЕСТАТЫ:")
print(" 8. Реестр аудиторов, прекративших членство в СРО ААС ⭐")
print(" 9. Реестр аудиторских организаций, прекративших членство ⭐")
print(" 10. Реестр аннулированных квалификационных аттестатов ⭐")
print(" 11. УМЦ, исключенные из реестра СРО ААС ⭐")
print("\n⚠️ ДИСЦИПЛИНАРНЫЕ МЕРЫ:")
print(" 12. Меры дисциплинарного воздействия к аудиторам ⭐")
print(" 13. Меры дисциплинарного воздействия к организациям ⭐")
print("\n🌐 СЕТИ АУДИТОРСКИХ ОРГАНИЗАЦИЙ:")
print(" 14. Перечень российских и международных сетей ⭐")
print("\n" + "=" * 60)
print(" 0. Выход")
print("=" * 60)
print("\n⭐ - Полностью реализованный парсер")
def parse_organizations(detailed=False):
"""
Парсинг реестра аудиторских организаций
Args:
detailed: Парсить ли детальные страницы
"""
logger = setup_logger("main")
print("\n" + "=" * 60)
print("ПАРСИНГ РЕЕСТРА АУДИТОРСКИХ ОРГАНИЗАЦИЙ")
print("=" * 60)
# Подтверждение пользователя
if detailed:
print("\n⚠️ ВНИМАНИЕ: Детальный парсинг может занять продолжительное время!")
choice = input("Продолжить? (y/n): ").lower()
if choice != "y":
print("Операция отменена.")
return
try:
# Создание парсера
parser = OrganizationsParser()
# Парсинг данных
print("\n🔄 Начало парсинга...")
organizations = parser.parse_to_objects(detailed=detailed)
if not organizations:
print("\n❌ Не удалось получить данные из реестра.")
return
print(f"\n✅ Успешно собрано записей: {len(organizations)}")
# Экспорт в Excel
print("\n📊 Экспорт данных в Excel...")
exporter = ExcelExporter()
timestamp = datetime.now().strftime("%Y-%m-%d_%H-%M-%S")
filename = f"organizations_{timestamp}"
filepath = exporter.export_organizations(organizations, filename=filename)
if filepath:
print(f"\n✅ Данные успешно экспортированы!")
print(f"📁 Файл: {os.path.abspath(filepath)}")
print(f"📊 Количество записей: {len(organizations)}")
else:
print("\n❌ Ошибка при экспорте данных.")
except KeyboardInterrupt:
print("\n\n⚠️ Операция прервана пользователем.")
except Exception as e:
logger.error(f"Ошибка при парсинге: {e}")
print(f"\n❌ Произошла ошибка: {e}")
def parse_generic_registry(registry_key: str, registry_name: str, detailed=False):
"""
Универсальная функция парсинга любого реестра
Args:
registry_key: Ключ реестра из config.REGISTRIES
registry_name: Название реестра для отображения
detailed: Парсить ли детальные страницы
"""
logger = setup_logger("main")
print("\n" + "=" * 60)
print(f"ПАРСИНГ: {registry_name.upper()}")
print("=" * 60)
# Подтверждение пользователя
if detailed:
print("\n⚠️ ВНИМАНИЕ: Детальный парсинг может занять продолжительное время!")
choice = input("Продолжить? (y/n): ").lower()
if choice != "y":
print("Операция отменена.")
return
try:
# Создание парсера
parser = GenericRegistryParser(registry_key)
# Парсинг данных
print("\n🔄 Начало парсинга...")
data = parser.parse_registry(detailed=detailed)
if not data:
print("\n❌ Не удалось получить данные из реестра.")
return
print(f"\n✅ Успешно собрано записей: {len(data)}")
# Экспорт в Excel
print("\n📊 Экспорт данных в Excel...")
exporter = ExcelExporter()
timestamp = datetime.now().strftime("%Y-%m-%d_%H-%M-%S")
filename = f"{registry_key}_{timestamp}"
filepath = exporter.export_to_excel(
data=data,
filename=filename,
sheet_name=registry_name[:30], # Ограничение длины для Excel
)
if filepath:
print(f"\n✅ Данные успешно экспортированы!")
print(f"📁 Файл: {os.path.abspath(filepath)}")
print(f"📊 Количество записей: {len(data)}")
else:
print("\n❌ Ошибка при экспорте данных.")
except KeyboardInterrupt:
print("\n\n⚠️ Операция прервана пользователем.")
except Exception as e:
logger.error(f"Ошибка при парсинге: {e}")
print(f"\n❌ Произошла ошибка: {e}")
def parse_auditors(detailed=False):
"""Парсинг реестра аудиторов"""
logger = setup_logger("main")
print("\n" + "=" * 60)
print("ПАРСИНГ РЕЕСТРА АУДИТОРОВ")
print("=" * 60)
if detailed:
print("\n⚠️ ВНИМАНИЕ: Детальный парсинг может занять продолжительное время!")
choice = input("Продолжить? (y/n): ").lower()
if choice != "y":
print("Операция отменена.")
return
try:
parser = AuditorsParser()
print("\n🔄 Начало парсинга...")
auditors = parser.parse_to_objects(detailed=detailed)
if not auditors:
print("\n❌ Не удалось получить данные из реестра.")
return
print(f"\n✅ Успешно собрано записей: {len(auditors)}")
# Экспорт в Excel
print("\n📊 Экспорт данных в Excel...")
exporter = ExcelExporter()
timestamp = datetime.now().strftime("%Y-%m-%d_%H-%M-%S")
filename = f"auditors_{timestamp}"
# Преобразуем в словари
data = [auditor.to_dict() for auditor in auditors]
filepath = exporter.export_to_excel(
data=data, filename=filename, sheet_name="Аудиторы"
)
if filepath:
print(f"\n✅ Данные успешно экспортированы!")
print(f"📁 Файл: {os.path.abspath(filepath)}")
print(f"📊 Количество записей: {len(auditors)}")
else:
print("\n❌ Ошибка при экспорте данных.")
except KeyboardInterrupt:
print("\n\n⚠️ Операция прервана пользователем.")
except Exception as e:
logger.error(f"Ошибка при парсинге: {e}")
print(f"\n❌ Произошла ошибка: {e}")
def not_implemented():
"""Заглушка для нереализованных функций"""
print("\n⚠️ Данный парсер находится в разработке.")
print("💡 Вы можете реализовать его по аналогии с OrganizationsParser.")
def main():
"""Главная функция приложения"""
print_banner()
# Маппинг выбора на реестры
registry_map = {
"1": ("auditors", "Реестр аудиторов и индивидуальных аудиторов"),
"2": ("organizations", "Реестр аудиторских организаций"),
"3": ("certificates", "Реестр квалификационных аттестатов"),
"4": ("individual_auditors", "Перечень индивидуальных аудиторов"),
"5": ("training_centers", "Реестр учебно-методических центров"),
"6": ("oppk_confirmation", "Сведения о подтверждении ОППК"),
"7": ("ozo_training", "Сведения о прохождении ПК руководителем"),
"8": ("excluded_auditors", "Реестр исключенных аудиторов"),
"9": ("excluded_organizations", "Реестр исключенных организаций"),
"10": ("cancelled_certificates", "Реестр аннулированных аттестатов"),
"11": ("excluded_training_centers", "УМЦ исключенные из реестра"),
"12": ("disciplinary_auditors", "Дисциплинарные меры к аудиторам"),
"13": ("disciplinary_organizations", "Дисциплинарные меры к организациям"),
"14": ("audit_networks", "Перечень сетей аудиторских организаций"),
}
while True:
print_menu()
choice = input("\n👉 Выберите реестр (0-14): ").strip()
if choice == "0":
print("\n👋 До свидания!")
sys.exit(0)
elif choice == "1":
# Реестр аудиторов - специализированный парсер
print(f"\n📋 Выбран: {registry_map[choice][1]}")
detail_choice = input("Парсить детальные страницы? (y/n): ").lower()
detailed = detail_choice == "y"
parse_auditors(detailed=detailed)
elif choice == "2":
# Реестр аудиторских организаций - специализированный парсер
print(f"\n📋 Выбран: {registry_map[choice][1]}")
detail_choice = input("Парсить детальные страницы? (y/n): ").lower()
detailed = detail_choice == "y"
parse_organizations(detailed=detailed)
elif choice in registry_map:
# Все остальные реестры - универсальный парсер
registry_key, registry_name = registry_map[choice]
print(f"\n📋 Выбран: {registry_name}")
detail_choice = input("Парсить детальные страницы? (y/n): ").lower()
detailed = detail_choice == "y"
parse_generic_registry(registry_key, registry_name, detailed=detailed)
else:
print("\n❌ Неверный выбор. Попробуйте снова.")
# Пауза перед возвратом в меню
input("\n⏸️ Нажмите Enter для возврата в меню...")
if __name__ == "__main__":
try:
main()
except KeyboardInterrupt:
print("\n\n👋 Программа завершена.")
sys.exit(0)
except Exception as e:
print(f"\n❌ Критическая ошибка: {e}")
sys.exit(1)