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 официальных реестров СРО ААС
- Два режима работы: быстрое сканирование (первая страница) и полное сканирование (все страницы)
- Экспорт данных в формат Excel (.xlsx) с форматированием
- Система логирования и обработки ошибок
- Поддержка пагинации и повторных попыток при сбоях
- **14 реестров** — полное покрытие всех публичных реестров СРО ААС
- **Два режима парсинга:**
- 🚀 **Быстрый** (первая страница, ~50 записей, 1-2 сек)
- 🔍 **Полный** (все страницы с пагинацией, тысячи записей, несколько минут)
- **Экспорт в Excel** — автоматическое форматирование, заголовки, стилизация
- **Логирование** — подробные логи всех операций в `logs/`
- **Обработка ошибок** — повторные попытки (3x) при сбоях сети
- **CLI-интерфейс** — интерактивное меню для выбора реестра
- **Режим Cron** — автоматизация парсинга через аргументы командной строки
- **Неинтерактивный режим** — для запуска по расписанию без пользовательского ввода
## Поддерживаемые реестры
## 📊 Поддерживаемые реестры
**Действующие члены:**
**Действующие члены (7 реестров):**
1. Реестр аудиторов и индивидуальных аудиторов (14,691+ записей)
2. Реестр аудиторских организаций (256+ записей)
3. Реестр квалификационных аттестатов
4. Перечень индивидуальных аудиторов
5. Реестр учебно-методических центров
6. Сведения о подтверждении ОППК
7. Сведения о прохождении ПК руководителем аудита ОЗО ФР
3. Квалификационные аттестаты
4. Индивидуальные аудиторы
5. Учебно-методические центры
6. Подтверждение ОППК
7. Прохождение ПК руководителем аудита ОЗО ФР
**Исключенные члены:**
**Исключенные члены (4 реестра):**
8. Аудиторы, прекратившие членство
9. Аудиторские организации, прекратившие членство
10. Аннулированные квалификационные аттестаты
11. УМЦ, исключенные из реестра
10. Аннулированные аттестаты
11. Исключенные УМЦ
**Дисциплинарные меры:**
**Дисциплинарные меры (2 реестра):**
12. Меры воздействия к аудиторам
13. Меры воздействия к организациям
**Сети:**
**Сети (1 реестр):**
14. Российские и международные сети аудиторских организаций
## Установка
## 🚀 Быстрый старт
### Установка
```bash
# Клонирование репозитория
@@ -51,19 +58,80 @@ venv\Scripts\activate # Windows
pip install -r requirements.txt
```
## Использование
### Интерактивный режим
### Запуск
**Интерактивный режим:**
```bash
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 сек)
- `y` (Yes) — полное сканирование (все страницы, все записи, несколько минут)
### Программный режим
**Программный режим:**
```python
from parsers.auditors_parser import AuditorsParser
@@ -81,12 +149,16 @@ exporter = ExcelExporter()
exporter.export_to_excel(data, "auditors", "output.xlsx")
```
## Структура проекта
## 🏗️ Архитектура проекта
```
osint_parser_ada/
├── 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/ # Парсеры реестров
│ ├── base_parser.py # Базовый класс с пагинацией
│ ├── auditors_parser.py # Парсер аудиторов
@@ -108,6 +180,7 @@ osint_parser_ada/
- **Python:** 3.8+
- **Зависимости:** requests, beautifulsoup4, lxml, pandas, openpyxl
- **Архитектура:** Паттерн Template Method, модульная структура
- **Режимы работы:** Интерактивный CLI и неинтерактивный (Cron)
- **Обработка ошибок:** Повторные попытки (3x), логирование всех операций
- **Производительность:** ~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]
Использование:
Интерактивный режим:
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 os
import argparse
from datetime import datetime
from config import REGISTRIES
@@ -29,6 +40,66 @@ def 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():
"""Вывод главного меню"""
print("\n" + "=" * 60)
@@ -245,26 +316,11 @@ def not_implemented():
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", "Перечень сетей аудиторских организаций"),
}
registry_map = get_registry_map()
while True:
print_menu()
@@ -304,9 +360,114 @@ def main():
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__":
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()
except KeyboardInterrupt:
print("\n\n👋 Программа завершена.")
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