mirror of
https://github.com/ada-dmitry/osint_parser_ada.git
synced 2026-09-24 01:10:20 +00:00
- 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.
175 lines
6.4 KiB
Python
175 lines
6.4 KiB
Python
"""
|
|
Парсер реестра аудиторов и индивидуальных аудиторов
|
|
"""
|
|
|
|
from typing import List, Dict, Any
|
|
from bs4 import BeautifulSoup
|
|
from urllib.parse import urljoin
|
|
import re
|
|
|
|
from parsers.base_parser import BaseParser
|
|
from models.auditor import Auditor
|
|
from config import BASE_URL
|
|
|
|
|
|
class AuditorsParser(BaseParser):
|
|
"""
|
|
Парсер для реестра аудиторов и индивидуальных аудиторов
|
|
https://sroaas.ru/reestr/auditory/
|
|
"""
|
|
|
|
def __init__(self):
|
|
from config import REGISTRIES
|
|
|
|
registry = REGISTRIES["auditors"]
|
|
super().__init__(registry["url"], registry["name"])
|
|
|
|
def parse_list_page(self, soup: BeautifulSoup) -> List[Dict[str, Any]]:
|
|
"""
|
|
Парсинг страницы со списком аудиторов
|
|
|
|
Args:
|
|
soup: Parsed HTML страницы
|
|
|
|
Returns:
|
|
Список словарей с данными аудиторов
|
|
"""
|
|
auditors = []
|
|
|
|
# Поиск таблицы с данными
|
|
table = soup.find("table")
|
|
if not table:
|
|
self.logger.warning("Таблица с данными не найдена на странице")
|
|
return auditors
|
|
|
|
# Парсинг строк таблицы
|
|
rows = table.find_all("tr")[1:] # Пропускаем заголовок
|
|
|
|
for row in rows:
|
|
try:
|
|
cols = row.find_all("td")
|
|
if len(cols) < 4:
|
|
continue
|
|
|
|
# Извлечение данных
|
|
full_name = cols[0].get_text(strip=True)
|
|
ornz = cols[1].get_text(strip=True)
|
|
certificate_number = cols[2].get_text(strip=True)
|
|
region = cols[3].get_text(strip=True)
|
|
|
|
# Статус (если есть)
|
|
status = cols[4].get_text(strip=True) if len(cols) > 4 else None
|
|
|
|
# URL детальной страницы
|
|
detail_url = None
|
|
link = row.find("a")
|
|
if link:
|
|
href = link.get("href")
|
|
# Проверяем, что href - строка, а не список
|
|
if isinstance(href, list):
|
|
href = href[0] if href else None
|
|
if href:
|
|
detail_url = urljoin(BASE_URL, href)
|
|
|
|
auditor_data = {
|
|
"full_name": full_name,
|
|
"ornz": ornz,
|
|
"certificate_number": certificate_number,
|
|
"region": region,
|
|
"status": status,
|
|
"detail_url": detail_url,
|
|
}
|
|
|
|
auditors.append(auditor_data)
|
|
|
|
except Exception as e:
|
|
self.logger.error(f"Ошибка при парсинге строки таблицы: {e}")
|
|
continue
|
|
|
|
return auditors
|
|
|
|
def parse_detail_page(self, url: str, soup: BeautifulSoup) -> Dict[str, Any]:
|
|
"""
|
|
Парсинг детальной страницы аудитора
|
|
|
|
Args:
|
|
url: URL детальной страницы
|
|
soup: Parsed HTML страницы
|
|
|
|
Returns:
|
|
Словарь с детальными данными
|
|
"""
|
|
detail_data = {}
|
|
|
|
try:
|
|
info_blocks = soup.find_all("div", class_=["info-block", "detail-info"])
|
|
|
|
for block in info_blocks:
|
|
labels = block.find_all(["dt", "div"], class_=["label", "info-label"])
|
|
values = block.find_all(["dd", "div"], class_=["value", "info-value"])
|
|
|
|
for label, value in zip(labels, values):
|
|
key = label.get_text(strip=True).lower()
|
|
val = value.get_text(strip=True)
|
|
|
|
if "инн" in key:
|
|
detail_data["inn"] = val
|
|
elif "снилс" in key:
|
|
detail_data["snils"] = val
|
|
elif "квалификация" in key:
|
|
detail_data["qualification"] = val
|
|
elif "организация" in key and "название" in key:
|
|
detail_data["organization_name"] = val
|
|
elif "организация" in key and "инн" in key:
|
|
detail_data["organization_inn"] = val
|
|
elif "образование" in key:
|
|
detail_data["education"] = val
|
|
elif "стаж" in key:
|
|
match = re.search(r"\d+", val)
|
|
if match:
|
|
detail_data["experience_years"] = int(match.group())
|
|
|
|
detail_data["source_url"] = url
|
|
|
|
except Exception as e:
|
|
self.logger.error(f"Ошибка при парсинге детальной страницы {url}: {e}")
|
|
|
|
return detail_data
|
|
|
|
def parse_to_objects(self, detailed: bool = False) -> List[Auditor]:
|
|
"""
|
|
Парсинг реестра с преобразованием в объекты Auditor
|
|
|
|
Args:
|
|
detailed: Парсить ли детальные страницы
|
|
|
|
Returns:
|
|
Список объектов Auditor
|
|
"""
|
|
data = self.parse_registry(detailed=detailed)
|
|
auditors = []
|
|
|
|
for item in data:
|
|
try:
|
|
auditor = Auditor(
|
|
full_name=item.get("full_name", ""),
|
|
ornz=item.get("ornz", ""),
|
|
certificate_number=item.get("certificate_number", ""),
|
|
region=item.get("region", ""),
|
|
status=item.get("status"),
|
|
inn=item.get("inn"),
|
|
snils=item.get("snils"),
|
|
qualification=item.get("qualification"),
|
|
organization_name=item.get("organization_name"),
|
|
organization_inn=item.get("organization_inn"),
|
|
education=item.get("education"),
|
|
experience_years=item.get("experience_years"),
|
|
source_url=item.get("source_url"),
|
|
)
|
|
auditors.append(auditor)
|
|
except Exception as e:
|
|
self.logger.error(f"Ошибка при создании объекта Auditor: {e}")
|
|
continue
|
|
|
|
return auditors
|