feat: Обновить README и архитектуру проекта, добавить поддержку режима cron и улучшить интерфейс командной строки

This commit is contained in:
ada
2025-10-26 16:53:59 +03:00
parent 877a0e0553
commit 22e32354f5
4 changed files with 349 additions and 97 deletions
+101 -28
View File
@@ -1,40 +1,47 @@
# Парсер реестров СРО ААС # 📋 Парсер реестров СРО ААС
Автоматизированный инструмент для сбора данных из реестров Саморегулируемой организации аудиторов Ассоциация «Содружество» (СРО ААС). Это Python-приложение для автоматизированного сбора данных из 14 официальных реестров **Саморегулируемой организации аудиторов Ассоциация «Содружество»** (СРО ААС). Проект реализует веб-скрейпинг с поддержкой пагинации, обработкой ошибок и экспортом в Excel.
## Возможности ## 🎯 Основные возможности
- Парсинг всех 14 официальных реестров СРО ААС - **14 реестров** — полное покрытие всех публичных реестров СРО ААС
- Два режима работы: быстрое сканирование (первая страница) и полное сканирование (все страницы) - **Два режима парсинга:**
- Экспорт данных в формат Excel (.xlsx) с форматированием - 🚀 **Быстрый** (первая страница, ~50 записей, 1-2 сек)
- Система логирования и обработки ошибок - 🔍 **Полный** (все страницы с пагинацией, тысячи записей, несколько минут)
- Поддержка пагинации и повторных попыток при сбоях - **Экспорт в Excel** — автоматическое форматирование, заголовки, стилизация
- **Логирование** — подробные логи всех операций в `logs/`
- **Обработка ошибок** — повторные попытки (3x) при сбоях сети
- **CLI-интерфейс** — интерактивное меню для выбора реестра
- **Режим Cron** — автоматизация парсинга через аргументы командной строки
- **Неинтерактивный режим** — для запуска по расписанию без пользовательского ввода
## Поддерживаемые реестры ## 📊 Поддерживаемые реестры
**Действующие члены:** **Действующие члены (7 реестров):**
1. Реестр аудиторов и индивидуальных аудиторов (14,691+ записей) 1. Реестр аудиторов и индивидуальных аудиторов (14,691+ записей)
2. Реестр аудиторских организаций (256+ записей) 2. Реестр аудиторских организаций (256+ записей)
3. Реестр квалификационных аттестатов 3. Квалификационные аттестаты
4. Перечень индивидуальных аудиторов 4. Индивидуальные аудиторы
5. Реестр учебно-методических центров 5. Учебно-методические центры
6. Сведения о подтверждении ОППК 6. Подтверждение ОППК
7. Сведения о прохождении ПК руководителем аудита ОЗО ФР 7. Прохождение ПК руководителем аудита ОЗО ФР
**Исключенные члены:** **Исключенные члены (4 реестра):**
8. Аудиторы, прекратившие членство 8. Аудиторы, прекратившие членство
9. Аудиторские организации, прекратившие членство 9. Аудиторские организации, прекратившие членство
10. Аннулированные квалификационные аттестаты 10. Аннулированные аттестаты
11. УМЦ, исключенные из реестра 11. Исключенные УМЦ
**Дисциплинарные меры:** **Дисциплинарные меры (2 реестра):**
12. Меры воздействия к аудиторам 12. Меры воздействия к аудиторам
13. Меры воздействия к организациям 13. Меры воздействия к организациям
**Сети:** **Сети (1 реестр):**
14. Российские и международные сети аудиторских организаций 14. Российские и международные сети аудиторских организаций
## Установка ## 🚀 Быстрый старт
### Установка
```bash ```bash
# Клонирование репозитория # Клонирование репозитория
@@ -51,19 +58,80 @@ venv\Scripts\activate # Windows
pip install -r requirements.txt pip install -r requirements.txt
``` ```
## Использование ### Запуск
### Интерактивный режим
**Интерактивный режим:**
```bash ```bash
python main.py python main.py
# Выбор реестра из меню → выбор режима (y/n)
``` ```
Выберите реестр из меню и режим парсинга: **Через скрипт (Fish Shell):**
```bash
./run.fish
```
### Режим командной строки (для Cron)
**Просмотр доступных реестров:**
```bash
python main.py --list
```
**Запуск парсинга без интерактивного режима:**
```bash
# Быстрый парсинг аудиторов
python main.py --registry auditors --mode quick
# Полный парсинг организаций
python main.py --registry organizations --mode full
# Сокращенный синтаксис
python main.py -r certificates -m quick
```
**Доступные реестры для --registry:**
- `auditors`, `organizations`, `certificates`
- `individual_auditors`, `training_centers`, `oppk_confirmation`, `ozo_training`
- `excluded_auditors`, `excluded_organizations`, `cancelled_certificates`, `excluded_training_centers`
- `disciplinary_auditors`, `disciplinary_organizations`
- `audit_networks`
**Режимы (--mode):**
- `quick` — быстрый (первая страница, ~50 записей)
- `full` — полный (все страницы)
### Автоматизация с Cron
Примеры cron записей находятся в файле `cron_examples.sh`.
**Настройка автоматического парсинга:**
```bash
# Открыть редактор cron
crontab -e
# Добавить задачу (парсинг аудиторов каждый день в 02:00)
0 2 * * * cd /path/to/osint_parser_ada && venv/bin/python main.py --registry auditors --mode quick >> logs/cron_auditors.log 2>&1
# Полный парсинг организаций каждое воскресенье в 01:00
0 1 * * 0 cd /path/to/osint_parser_ada && venv/bin/python main.py --registry organizations --mode full >> logs/cron_organizations_full.log 2>&1
```
**Просмотр логов:**
```bash
tail -f logs/cron_auditors.log
```
📖 **Подробное руководство:** См. [CRON_GUIDE.md](CRON_GUIDE.md) для детальных инструкций по настройке автоматизации.
### Использование
**Выбор режима парсинга:**
- `n` (No) — быстрое сканирование (только первая страница, ~50 записей, 1-2 сек) - `n` (No) — быстрое сканирование (только первая страница, ~50 записей, 1-2 сек)
- `y` (Yes) — полное сканирование (все страницы, все записи, несколько минут) - `y` (Yes) — полное сканирование (все страницы, все записи, несколько минут)
### Программный режим **Программный режим:**
```python ```python
from parsers.auditors_parser import AuditorsParser from parsers.auditors_parser import AuditorsParser
@@ -81,12 +149,16 @@ exporter = ExcelExporter()
exporter.export_to_excel(data, "auditors", "output.xlsx") exporter.export_to_excel(data, "auditors", "output.xlsx")
``` ```
## Структура проекта ## 🏗️ Архитектура проекта
``` ```
osint_parser_ada/ osint_parser_ada/
├── config.py # Конфигурация и URL реестров ├── config.py # Конфигурация и URL реестров
├── main.py # CLI-интерфейс ├── main.py # CLI-интерфейс с поддержкой Cron
├── run.fish / run.sh # Скрипты быстрого запуска
├── run_cron.fish # Скрипт для Cron (Fish Shell)
├── cron_examples.sh # Примеры Cron записей
├── CRON_GUIDE.md # Подробное руководство по Cron
├── parsers/ # Парсеры реестров ├── parsers/ # Парсеры реестров
│ ├── base_parser.py # Базовый класс с пагинацией │ ├── base_parser.py # Базовый класс с пагинацией
│ ├── auditors_parser.py # Парсер аудиторов │ ├── auditors_parser.py # Парсер аудиторов
@@ -108,6 +180,7 @@ osint_parser_ada/
- **Python:** 3.8+ - **Python:** 3.8+
- **Зависимости:** requests, beautifulsoup4, lxml, pandas, openpyxl - **Зависимости:** requests, beautifulsoup4, lxml, pandas, openpyxl
- **Архитектура:** Паттерн Template Method, модульная структура - **Архитектура:** Паттерн Template Method, модульная структура
- **Режимы работы:** Интерактивный CLI и неинтерактивный (Cron)
- **Обработка ошибок:** Повторные попытки (3x), логирование всех операций - **Обработка ошибок:** Повторные попытки (3x), логирование всех операций
- **Производительность:** ~1.5 сек/страница с задержками для защиты от блокировки - **Производительность:** ~1.5 сек/страница с задержками для защиты от блокировки
+69
View File
@@ -0,0 +1,69 @@
# Архитектура проекта
Ниже — схема на уровне компонентов в формате, аналогичном примеру.
```
[CLI (main.py / run.fish / run.sh / run_cron.fish)]
|
v
Fetcher — загрузка HTML и проверка кода ответа
[Fetcher: HTTP GET (requests.Session)]
(timeout / retries / delay)
|
v
• Parser — извлечение таблиц/списков и распознавание колонок (BS4+lxml)
• Normalizer — очистка значений, trim, удаление префиксов, приведение типов/дат
• Model — маппинг в dataclasses (Auditor, Organization, …)
• Exporter — выгрузка pandas.DataFrame в Excel через openpyxl
|
v
[Pipeline]
[Parser (BeautifulSoup + эвристики)] --> [Normalizer] --> [Model] --> [DataFrame] --> [Exporter (XLSX)]
```
## Соответствие компонент файлам
- CLI / запуск:
- `main.py` — CLI-меню, режим неинтерактивного запуска через аргументы (`--registry`, `--mode`, `--list`).
- `run.fish`, `run.sh` — быстрый старт; `run_cron.fish` — обёртка для cron (Fish).
- Fetcher:
- `parsers/base_parser.py``BaseParser._make_request()` (requests.Session, заголовок User-Agent, таймаут, задержка, повторные попытки).
- Пагинация: `BaseParser._get_pagination_urls()`.
- Parser:
- Специализированные: `parsers/auditors_parser.py`, `parsers/organizations_parser.py`.
- Универсальный: `parsers/generic_parser.py` (табличные реестры, автоматическое определение заголовков).
- Общая точка входа: `BaseParser.parse_registry(detailed=...)`.
- Normalizer:
- Локальные шаги очистки/приведения: обрезка пробелов, извлечение чисел/дат, нормализация полей (внутри парсеров).
- Приведение к словарям/рубрикам, единообразные ключи для экспорта.
- Model:
- `models/auditor.py`, `models/organization.py`, и др. — dataclasses, метод `to_dict()` для экспорта.
- DataFrame + Exporter:
- Формирование `pandas.DataFrame` и экспорт: `utils/excel_exporter.py``ExcelExporter.export_to_excel()`.
- Форматирование XLSX (ширина колонок, шапка, стили): `ExcelExporter._format_excel()`.
- Конфигурация и логирование:
- `config.py` — URL реестров (`REGISTRIES`), сетевые и экспортные настройки (`PARSER_CONFIG`, `EXPORT_CONFIG`, `LOGGING_CONFIG`).
- `utils/logger.py` — консольный и файловый логгеры (`logs/parser_YYYY-MM-DD.log`).
## Потоки выполнения
- Режим запуска:
- Интерактивный CLI: выбор реестра и детализации из меню (`main.py`).
- Неинтерактивный (для cron): `python main.py --registry <key> --mode <quick|full>`.
- Детализация:
- `mode=quick` — первая страница (без обхода пагинации и детальных карточек).
- `mode=full` — все страницы (пагинация) и, при поддержке, парсинг детальных карточек записей.
- Надёжность:
- Повторные попытки при ошибках сети, управляемая задержка между запросами.
- Логи INFO/DEBUG в консоль и файл.
- Выходные данные:
- Сырые записи → нормализованные словари → модели (dataclasses) → `pandas.DataFrame` → XLSX в `data/exports/`.
+178 -17
View File
@@ -3,10 +3,21 @@
Парсер реестров СРО ААС (Саморегулируемая организация аудиторов) Парсер реестров СРО ААС (Саморегулируемая организация аудиторов)
Автор: [ada] Автор: [ada]
Использование:
Интерактивный режим:
python main.py
Режим cron (автоматический):
python main.py --registry auditors --mode quick
python main.py --registry organizations --mode full
python main.py -r certificates -m quick
python main.py --list # Показать доступные реестры
""" """
import sys import sys
import os import os
import argparse
from datetime import datetime from datetime import datetime
from config import REGISTRIES from config import REGISTRIES
@@ -29,6 +40,66 @@ def print_banner():
print(banner) print(banner)
def get_registry_map():
"""Получить маппинг реестров"""
return {
"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", "Перечень сетей аудиторских организаций"),
}
def list_registries():
"""Вывести список доступных реестров"""
print("\n" + "=" * 60)
print("ДОСТУПНЫЕ РЕЕСТРЫ ДЛЯ --registry ПАРАМЕТРА:")
print("=" * 60)
registry_map = get_registry_map()
print("\n📋 ДЕЙСТВИТЕЛЬНЫЕ ЧЛЕНЫ:")
print(" auditors - Реестр аудиторов")
print(" organizations - Реестр организаций")
print(" certificates - Квалификационные аттестаты")
print(" individual_auditors - Индивидуальные аудиторы")
print(" training_centers - Учебно-методические центры")
print(" oppk_confirmation - Подтверждение ОППК")
print(" ozo_training - Прохождение ПК руководителем")
print("\n❌ ИСКЛЮЧЕННЫЕ ЧЛЕНЫ:")
print(" excluded_auditors - Исключенные аудиторы")
print(" excluded_organizations - Исключенные организации")
print(" cancelled_certificates - Аннулированные аттестаты")
print(" excluded_training_centers - Исключенные УМЦ")
print("\n⚠️ ДИСЦИПЛИНАРНЫЕ МЕРЫ:")
print(" disciplinary_auditors - Меры к аудиторам")
print(" disciplinary_organizations - Меры к организациям")
print("\n🌐 СЕТИ:")
print(" audit_networks - Сети организаций")
print("\n" + "=" * 60)
print("Режимы (--mode):")
print(" quick - Быстрый (первая страница)")
print(" full - Полный (все страницы)")
print("=" * 60)
print("\nПример:")
print(" python main.py --registry auditors --mode quick")
print(" python main.py -r organizations -m full\n")
def print_menu(): def print_menu():
"""Вывод главного меню""" """Вывод главного меню"""
print("\n" + "=" * 60) print("\n" + "=" * 60)
@@ -245,26 +316,11 @@ def not_implemented():
def main(): def main():
"""Главная функция приложения""" """Главная функция - интерактивный режим"""
print_banner() print_banner()
# Маппинг выбора на реестры # Маппинг выбора на реестры
registry_map = { registry_map = get_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: while True:
print_menu() print_menu()
@@ -304,9 +360,114 @@ def main():
input("\n⏸️ Нажмите Enter для возврата в меню...") input("\n⏸️ Нажмите Enter для возврата в меню...")
def run_cron_mode(registry_key: str, mode: str):
"""
Запуск в режиме cron (неинтерактивный)
Args:
registry_key: Ключ реестра (auditors, organizations, и т.д.)
mode: Режим парсинга (quick или full)
"""
logger = setup_logger("cron")
# Проверка валидности реестра
if registry_key not in REGISTRIES:
logger.error(f"Неизвестный реестр: {registry_key}")
print(f"❌ Ошибка: Реестр '{registry_key}' не найден.")
print("Используйте --list для просмотра доступных реестров.")
sys.exit(1)
# Определение режима детализации
detailed = mode.lower() == "full"
mode_str = "полный" if detailed else "быстрый"
registry_name = REGISTRIES[registry_key]["name"]
logger.info(f"Запуск в режиме cron: {registry_key} ({mode_str})")
print(f"\n🤖 РЕЖИМ CRON")
print("=" * 60)
print(f"Реестр: {registry_name}")
print(f"Режим: {mode_str}")
print(f"Детализация: {'Да' if detailed else 'Нет'}")
print("=" * 60 + "\n")
try:
# Выбор парсера в зависимости от реестра
if registry_key == "auditors":
parse_auditors(detailed=detailed)
elif registry_key == "organizations":
parse_organizations(detailed=detailed)
else:
parse_generic_registry(registry_key, registry_name, detailed=detailed)
logger.info(f"Парсинг {registry_key} успешно завершен")
print("\n✅ Парсинг успешно завершен")
sys.exit(0)
except Exception as e:
logger.error(f"Ошибка при парсинге в режиме cron: {e}")
print(f"\n❌ Ошибка: {e}")
sys.exit(1)
def parse_args():
"""Парсинг аргументов командной строки"""
parser = argparse.ArgumentParser(
description="Парсер реестров СРО ААС",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog="""
Примеры использования:
# Интерактивный режим
python main.py
# Режим cron - быстрый парсинг аудиторов
python main.py --registry auditors --mode quick
# Режим cron - полный парсинг организаций
python main.py --registry organizations --mode full
# Список доступных реестров
python main.py --list
""",
)
parser.add_argument(
"-r", "--registry", type=str, help="Ключ реестра для парсинга (см. --list)"
)
parser.add_argument(
"-m",
"--mode",
type=str,
choices=["quick", "full"],
default="quick",
help="Режим парсинга: quick (быстрый) или full (полный)",
)
parser.add_argument(
"-l", "--list", action="store_true", help="Показать список доступных реестров"
)
return parser.parse_args()
if __name__ == "__main__": if __name__ == "__main__":
try: try:
args = parse_args()
# Режим показа списка реестров
if args.list:
list_registries()
sys.exit(0)
# Режим cron (неинтерактивный)
if args.registry:
run_cron_mode(args.registry, args.mode)
# Интерактивный режим (по умолчанию)
else:
main() main()
except KeyboardInterrupt: except KeyboardInterrupt:
print("\n\n👋 Программа завершена.") print("\n\n👋 Программа завершена.")
sys.exit(0) sys.exit(0)
-51
View File
@@ -1,51 +0,0 @@
#!/usr/bin/env fish
# Скрипт быстрого запуска для Fish Shell
# Использование: ./run.fish
echo "=============================================="
echo " ПАРСЕР РЕЕСТРОВ СРО ААС"
echo "=============================================="
echo ""
# Проверка наличия Python
if not command -v python3 &> /dev/null
echo "❌ Python 3 не найден. Установите Python 3.8 или выше."
exit 1
end
echo "✅ Python найден: "(python3 --version)
echo ""
# Проверка виртуального окружения
if not test -d venv
echo "📦 Виртуальное окружение не найдено. Создание..."
python3 -m venv venv
echo "✅ Виртуальное окружение создано."
echo ""
end
# Активация виртуального окружения
echo "🔧 Активация виртуального окружения..."
source venv/bin/activate.fish
# Проверка зависимостей
if not python -c "import requests" 2>/dev/null
echo "📥 Установка зависимостей..."
pip install -q --upgrade pip
pip install -q -r requirements.txt
echo "✅ Зависимости установлены."
echo ""
end
# Создание необходимых директорий
mkdir -p data/exports
mkdir -p logs
# Запуск парсера
echo "🚀 Запуск парсера..."
echo ""
python main.py
# Деактивация виртуального окружения
deactivate