Skip to content

Instantly share code, notes, and snippets.

@MikyPo
Created April 27, 2026 16:33
Show Gist options
  • Select an option

  • Save MikyPo/7b96db94b407f7e367521f4aa68ec87c to your computer and use it in GitHub Desktop.

Select an option

Save MikyPo/7b96db94b407f7e367521f4aa68ec87c to your computer and use it in GitHub Desktop.
File upload statistics in YaMetrics
# Импорт и инициализация
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