Created
April 27, 2026 16:33
-
-
Save MikyPo/7b96db94b407f7e367521f4aa68ec87c to your computer and use it in GitHub Desktop.
File upload statistics in YaMetrics
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # Импорт и инициализация | |
| import locale | |
| import os | |
| import pandas as pd | |
| import plotly.express as px | |
| import plotly.graph_objects as go | |
| import requests | |
| import time | |
| from requests.exceptions import RequestException, Timeout, ConnectionError | |
| from io import StringIO | |
| from datetime import datetime, timedelta | |
| from dotenv import load_dotenv | |
| # Переключаем локаль на русскую | |
| print('Кодировка:') | |
| locale.setlocale(locale.LC_ALL, 'ru_RU.utf8') | |
| # Настройки | |
| load_dotenv('../.env') | |
| COUNTER_ID = os.getenv('COUNTER_ID') | |
| API_TOKEN = os.getenv('API_TOKEN') | |
| DATE_1 = '2026-01-01' | |
| DATE_2 = '2026-03-31' | |
| LIMIT = 10000 | |
| API_URL = 'https://api-metrika.yandex.net/stat/v1/data.csv' | |
| output_dir = 'loads' | |
| FILE_URL = 'полный путь к файлу' | |
| os.makedirs(output_dir, exist_ok=True) | |
| start_date = datetime.strptime(DATE_1, '%Y-%m-%d') | |
| end_date = datetime.strptime(DATE_2, '%Y-%m-%d') | |
| date_range = [start_date + timedelta(days=x) for x in range((end_date - start_date).days + 1)] | |
| headers = {'Authorization': f'OAuth {API_TOKEN}'} | |
| # Цикл выгрузки данных | |
| print("🚀 Начинаем выгрузку данных...") | |
| # Настройки повторов | |
| MAX_RETRIES = 3 # Максимальное число попыток | |
| RETRY_DELAY = 5 # Пауза перед первым повтором (сек) | |
| BACKOFF_MULTIPLIER = 2 # Множитель увеличения задержки | |
| # Проходим по каждой дате из указанного периода | |
| for current_date in date_range: | |
| current_date_str = current_date.strftime('%Y-%m-%d') | |
| filename = f"{current_date_str}.csv" | |
| filepath = os.path.join(output_dir, filename) | |
| if os.path.exists(filepath): | |
| print(f"⏭️ Файл {filename} уже существует, пропускаем {current_date_str}") | |
| continue | |
| print(f"📥 Загружаем данные за {current_date_str}...") | |
| params = { | |
| 'ids': COUNTER_ID, | |
| 'date1': current_date_str, | |
| 'date2': current_date_str, | |
| 'metrics': 'ym:dl:pageviews', | |
| 'dimensions': 'ym:dl:date,ym:dl:URL', | |
| 'limit': LIMIT, | |
| 'accuracy': 'full', | |
| 'include_undefined': True, | |
| 'filters': f"ym:dl:URL=='{FILE_URL}'" | |
| } | |
| all_rows_day = [] | |
| offset = 1 | |
| day_failed = False # 🔑 Флаг: была ли критическая ошибка в этот день | |
| while True: | |
| params['offset'] = offset | |
| attempt = 0 | |
| delay = RETRY_DELAY | |
| success = False | |
| # 🔄 Цикл повторов для текущего offset | |
| while attempt < MAX_RETRIES: | |
| try: | |
| print(f" 🔹 Попытка №{attempt + 1}/{MAX_RETRIES} (offset={offset})...") | |
| response = requests.get(API_URL, params=params, headers=headers, timeout=30) | |
| if response.status_code == 200: | |
| success = True | |
| print(f" ✅ Успешно получено (offset={offset})") | |
| break | |
| elif response.status_code in [429, 500, 502, 503, 504]: | |
| print(f" ⚠️ Сервер вернул {response.status_code}, жду {delay}с...") | |
| time.sleep(delay) | |
| delay *= BACKOFF_MULTIPLIER | |
| attempt += 1 | |
| else: | |
| print(f"❌ Критическая ошибка {response.status_code}: {response.text[:150]}") | |
| break # Не повторяем клиентские ошибки | |
| except RequestException as e: | |
| print(f"⚠️ Сетевая ошибка: {type(e).__name__}, жду {delay}с...") | |
| time.sleep(delay) | |
| delay *= BACKOFF_MULTIPLIER | |
| attempt += 1 | |
| except Exception as e: | |
| print(f"❌ Неожиданная ошибка: {type(e).__name__}: {e}") | |
| break | |
| # ❌ Если ни одна попытка не увенчалась успехом | |
| if not success: | |
| print(f"❌ Не удалось загрузить offset={offset} за {current_date_str} после {MAX_RETRIES} попыток.") | |
| day_failed = True | |
| break # Выходим из while True, переходим к финальному сохранению | |
| # 📄 Парсим успешный ответ | |
| try: | |
| df_page = pd.read_csv(StringIO(response.text), sep=',') | |
| except Exception as e: | |
| print(f"❌ Ошибка парсинга CSV: {e}") | |
| day_failed = True | |
| break | |
| if df_page.empty: | |
| print(f"✅ Все данные за {current_date_str} загружены.") | |
| break | |
| all_rows_day.append(df_page) | |
| offset += LIMIT | |
| # 📊 Формируем итоговый DataFrame | |
| if all_rows_day and not day_failed: | |
| df_day = pd.concat(all_rows_day, ignore_index=True) | |
| else: | |
| # Принудительно нули при любой ошибке или пустоте | |
| print(f"⚠️ Создаю файл с нулями (данные не получены или ошибка)") | |
| df_day = pd.DataFrame({ | |
| 'ym:dl:date': [current_date_str], | |
| 'ym:dl:URL': [FILE_URL], | |
| 'ym:dl:pageviews': [0] | |
| }) | |
| # 🏷 Приводим имена колонок к нужному виду | |
| df_output = df_day.rename(columns={ | |
| 'ym:dl:date': 'Page view date', | |
| 'ym:dl:URL': 'URL', | |
| 'ym:dl:pageviews': 'Pageviews' | |
| }) | |
| # 💾 Сохраняем файл (всегда, без исключений) | |
| df_output.to_csv(filepath, index=False, encoding='utf-8') | |
| print(f"💾 Сохранено: {filepath}\n") | |
| print("\n🔄 Объединяем все CSV-файлы в один DataFrame...") | |
| # Собираем все файлы из папки | |
| csv_files = [f for f in os.listdir(output_dir) if f.endswith('.csv')] | |
| all_dfs = [] | |
| for file in sorted(csv_files): # сортируем по дате | |
| filepath = os.path.join(output_dir, file) | |
| df = pd.read_csv(filepath) | |
| all_dfs.append(df) | |
| if all_dfs: | |
| df_final = pd.concat(all_dfs, ignore_index=True) | |
| print(f"✅ Всего загружено строк: {len(df_final)}") | |
| else: | |
| print("❌ Не найдено ни одного CSV-файла.") | |
| df_final = pd.DataFrame() | |
| df_final = df_final[df_final['Page view date'] != 'Totals and averages'] | |
| df_final['Page view date'] = pd.to_datetime(df_final['Page view date'], format='%Y-%m-%d') | |
| # Линейный график | |
| fig = px.line( | |
| df_final, | |
| x='Page view date', | |
| y='Pageviews', | |
| title='📊 Динамика просмотров файла', | |
| labels={ | |
| 'Page view date': 'Дата', | |
| 'Pageviews': 'Количество просмотров' | |
| }, | |
| markers=True, # 🔵 Показать точки на линии | |
| line_shape='linear' # Тип линии: 'linear', 'spline', 'hv', 'vh' | |
| ) | |
| # 🎨 Дополнительные настройки оформления | |
| fig.update_traces( | |
| line=dict(width=3, color='#4fbbff'), # Толщина и цвет линии | |
| marker=dict(size=4, color='#3ea2e0') # Размер и цвет маркеров | |
| ) | |
| # 🧭 Настройка осей и сетки | |
| fig.update_layout( | |
| hovermode='x unified', | |
| xaxis=dict( | |
| tickformat='%d.%m.%Y', # Формат дат на оси | |
| tickangle=45, # Поворот подписей | |
| showgrid=True | |
| ), | |
| yaxis=dict( | |
| showgrid=True, | |
| zeroline=True, | |
| zerolinewidth=1, | |
| zerolinecolor='gray' | |
| ), | |
| plot_bgcolor='white', | |
| height=500 | |
| ) | |
| # 🖥 Показать график | |
| fig.show() | |
| # 💾 сохранить в HTML | |
| fig.write_html('pageviews.html') |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment