From b3c7d9e9e95a6723f6fc9d5e110397aba35177bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9B=D0=B5=D0=B2?= Date: Mon, 25 Aug 2025 09:47:29 +0500 Subject: [PATCH] =?UTF-8?q?=D0=9F=D1=80=D0=BE=D0=B1=D1=83=D1=8E=20=D1=80?= =?UTF-8?q?=D0=B0=D0=B7=D0=B2=D0=B5=D1=80=D0=BD=D1=83=D1=82=D1=8C=20=D0=BA?= =?UTF-8?q?=D0=BE=D0=BD=D1=82=D1=80=D0=B5=D0=B9=D0=BD=D0=B5=D1=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Dockerfile | 27 +++++++ main.py | 5 -- parse_saby.py | 202 -------------------------------------------------- 3 files changed, 27 insertions(+), 207 deletions(-) create mode 100644 Dockerfile delete mode 100644 main.py delete mode 100644 parse_saby.py diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..e02a171 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,27 @@ +FROM python:3.11-slim-bookworm +ENV PYTHONUNBUFFERED=1 + +RUN apt-get update +RUN apt-get -y install wget libaio1 unzip alien + +RUN cd /home && wget -q https://download.oracle.com/otn_software/linux/instantclient/2112000/oracle-instantclient-basic-21.12.0.0.0-1.el8.x86_64.rpm &&\ + alien -i --scripts /home/oracle-instantclient-basic-21.12.0.0.0-1.el8.x86_64.rpm &&\ + rm /home/oracle-instantclient-basic-21.12.0.0.0-1.el8.x86_64.rpm &&\ + export LD_LIBRARY_PATH=/usr/lib/oracle/21.12/client64/lib/${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH} + +RUN cd /home/ && wget -q https://download.oracle.com/otn_software/linux/instantclient/2112000/oracle-instantclient-sqlplus-21.12.0.0.0-1.el8.x86_64.rpm &&\ + alien -i --scripts /home/oracle-instantclient-sqlplus-21.12.0.0.0-1.el8.x86_64.rpm &&\ + rm /home/oracle-instantclient-sqlplus-21.12.0.0.0-1.el8.x86_64.rpm + +RUN cd /home/ && wget -q https://download.oracle.com/otn_software/linux/instantclient/2112000/oracle-instantclient-devel-21.12.0.0.0-1.el8.x86_64.rpm && \ + alien -i --scripts /home/oracle-instantclient-devel-21.12.0.0.0-1.el8.x86_64.rpm &&\ + rm /home/oracle-instantclient-devel-21.12.0.0.0-1.el8.x86_64.rpm &&\ + ldconfig + +CMD ["sqlplus", "-v"] +EXPOSE 8000 + +WORKDIR /code/app +COPY ./requirements.txt /code/requirements.txt +RUN python3.11 -m pip install --upgrade pip +RUN echo Y | python3.11 -m pip install --no-cache-dir --upgrade -r /code/requirements.txt \ No newline at end of file diff --git a/main.py b/main.py deleted file mode 100644 index b89d6d4..0000000 --- a/main.py +++ /dev/null @@ -1,5 +0,0 @@ -import working_database -import parse_saby - -working_database.connect_hvac() -parse_saby.process_reports_parse() \ No newline at end of file diff --git a/parse_saby.py b/parse_saby.py deleted file mode 100644 index 2e51c64..0000000 --- a/parse_saby.py +++ /dev/null @@ -1,202 +0,0 @@ -import requests -from bs4 import BeautifulSoup -import re - -def parse_html(url: str): - """ - Фунция принимает строку URL, выполняет запрос. - Создает и возращает объект BeautifulSoup(HTML). - """ - # Запрос страницы - response = requests.get(url) - # Проверка статуса в ответе на запрос - if not(200 <= response.status_code <= 299): - # Повторный запрос - response = requests.get(url) - print("Ошибка при запросе: ", response.status_code) - return response.status_code - # Создание обьекта BeautifulSoup(HTML страница) - soup = BeautifulSoup(response.text, 'html.parser') - return soup - -def parse_date_report(url: str): - """ - Функия ожидает list из URL ввида: - url = https://formats.saby.ru/report/fns/128513 - Заходит по URL, запоминает дату. - Возвращает две строки ввида: - 'date = 01.07.22' - 'id_date = 5.01' - """ - # HTML report - soup = parse_html(url) - # Если не удалось отправить запрос, ворзвращаем URL и код ошибки. - # Они будут занесы в файл. - if soup == int: - return url, soup - # Поиск в HTML строки ввида: 'Действующий формат (с 10.01.23) 5.01' - div_element = soup.find('div', class_='controls-Dropdown__text') - # Извлекаем текст из элемента - text = div_element.get_text() - # Парсим нужные данные - # Уловия ловит: text = 'Действующий формат 02-04-2025 _2025_001' - if '_' in text: - regex = r'\w+\s\w+\s(\S+) (\S+)' - date, id_date = re.search(regex, text).groups() - return date, id_date - # Все остальные - else: - regex = r'(\d{2}\D\d{2}\D\d{2})(?:(?:.+)?\))? #?(\d+(?:\D\d+)?)' - date, id_date = re.search(regex, text).groups() - return date, id_date - -def parse_reports(soup=BeautifulSoup, # HTML объект - report_title = str, # строка ввида: 'report/fns' - url_formats = str, # Строка ввида: 'https://formats.saby.ru' - name_title = str): # имя тайтла: 'fns' - """ - Достаются все необходимые данные, возвращаются в ввиде словаря: - {106538: ('fns', 'НД по косвенным налогам', '01.08.23', '5.04')} - """ - - result_dict_data = {} - # Перебарает все URL, ищутся по тегу 'a' - for link in soup.find_all('a'): - # Ищет по тегу: href - href = link.get('href') - if f'{report_title}/' in href: - # id report - id = href.rstrip('/').split('/')[-1] - #URL report - url_report = f'{url_formats}{href}' - link = soup.find('a', href=href) - # Name report - span = link.find('span', class_="ProxySbisRu__registry-BrowserItem_typeName") - # Данные получены из url после парсинга - date, id_date = parse_date_report(url_report) - # Добавление всех данных в итоговый словарь - result_dict_data.update({id: (name_title, span.text, date, id_date)}) - - return result_dict_data - -def write_report_data(filename, dict_name = dict, name_title = str): - """ - Сохраняем запись, каждая запись с новой строки: - 'ключ: значение' - 'ключ: значение' - ... - """ - #Блок для красивого офорлмения файла - - # Вычисляем количество подчёркиваний слева и справа - def center_text(): - """ - Функия парсит сколько нужно подчеркивания, - что бы центролизовать текст в строке с этими подчеркиваниями - Возвращает две строки одна для начало блока записей тайтал, вторая конца блока. - """ - start_str = f'START_{name_title}' - end_str = f'END_{name_title}' - dash = 100 - list_result = [] - # сначал вернеться dash_start, потом end_start. - for text in start_str, end_str: - remaining_space = dash - len(text) - left = remaining_space // 2 - right = remaining_space - left # Чтобы учесть нечётную разницу - list_result.append('_' * left + text + '_' * right) - return list_result[:2] - dash_start, dash_end = center_text() - - #Конец блока - #Запись в файл с красивым офрмление в виде нижнего подчеркивания - with open(filename, 'a', encoding='utf-8') as f: - f.write(f'\n{dash_start}\n') - for key, value in dict_name.items(): - str_k_v = f'{key}: {value}\n' - f.write(str_k_v) - f.write(f'{dash_end}\n') - -def search_title(): - """ - Функция ищет все тайтлы на странице formats.saby.ru/report. - - Возвращает: - Список URL-путей, например: ['/report/fns', '/report/example', ...] - - Исключения: - ValueError: Если запрос к странице завершился с ошибкой (неверный статус). - """ - # url по которому в котором будет происходить поиск, - url_format_report = 'https://formats.saby.ru/report' - - # Получаем HTML-cтраницу - html = parse_html(url_format_report) - - # Проверяем, что html не является кодом ошибки (int) - if isinstance(html, int): - error_message = f'Ошибка при запросе {url_format_report}: {html}' - raise Exception(error_message) - - # < в который будут заноситься тайтлы - report_urls = set() - - # Ищем все ссылки с href, соответствующие шаблону /report/{title} - for link in html.find_all('a', href=True): - # Ищем по тегу: href - href = link['href'] - # Проверям что href содержит: /report/{title} - if re.search(r'\/report\/(\w+)$', href): - report_urls .add(href) - return report_urls - -# Имя файла в который будет записан результат кода -filename_save = 'ReportData.txt' -def clear_report_data_file(): - """ - Удаляем старые записи, что бы записать актульные - """ - with open(filename_save, 'w') as f: - pass - -def process_reports_parse(): - """ - Функция пробегается по каждому тайтлу. - Для всех записей(reports) выполняется запрос HTML страницы, - которая парситься в объект BeautifulSoup(HTML страница). - Из это обьекта достаются не обходимые данные, - которые записываются в текстовый файл. - """ - # Очищаем файл перед записью - clear_report_data_file() - - # Лист имеет вид: ['/report/fns', '/report/sfr'...] - list_title = search_title() - - for report_title in list_title: - try: - # используется для создание полных URL - url_formats = 'https://formats.saby.ru' - # Получаем имя тайтла через парсинг - name_title = report_title.rstrip('/').split('/')[-1] - - # URL на конкретный title - url_title = f'{url_formats}{report_title}' - - # Объект HTML, конкретного title - soup = parse_html(url_title) - - # Словарь с нужными данными - dict_result = parse_reports(soup, report_title, url_formats, name_title) - - # Запись данных в текстовый файл - write_report_data(filename_save, dict_result, name_title) - - except Exception as e: - print(f"Ошибка при обработке отчета {report_title}: {str(e)}") - continue - -if __name__ == '__main__': - process_reports_parse() - -