parse_saby/main.py

203 lines
8.7 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

import requests
from bs4 import BeautifulSoup
import re
# Используется два раза, создал отдельную переменную
URL_FORMATS = 'https://formats.saby.ru'
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()
# Ищем все ссылки <a> с 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:
# Получаем имя тайтла через парсинг
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()