Понадобилось мне проанализировать сайт на предмет попадания его под фильтр Гугла, причем с историческими данными.
Первым делом начал гуглить — чекер фильтров Гугла и вот это вот все. Естественно — в интернетах куча решений, но есть небольшое но... Они либо платные, либо условно‑бесплатные, либо вообще непонятно как работают.
Ну что делать — надо реализовать собственное решение, что я и сделал.
Палю годноту, так как использование этого решения никак не повлияет на мою деятельность, а кому то может и полезно будет.
Суть решения достаточно проста есть код (он будет ниже), написанный на Python. Он реализует интерактивный дашборд для визуализации данных, полученных из Google Search Console, с использованием библиотеки Dash (на базе Plotly) для построения графиков.

Фактически скрипт подключается по АПИ к Гугл Серч Консоль, забирает оттуда данные по показам, кликам и позициям, после чего выводит их на графике. В самом скрипте добавлены все апы Гугла за последние 2 года (информацию брал тут), ну и можно вручную добавить значимые события, которые вы внедряли на сайте (ниже покажу где это меняется в коде).
Немного теории - что это за скрипт и как он настраивается и работает
Настройка параметров и констант в коде
В разделе с параметрами задаются:Список сайтов – определяются доменные или URL‑свойства, для которых будут запрашиваться данные.
Дата начала и окончания – задается временной интервал, за который нужно получать данные.
Списки обновлений (updates) и событий (events) – массивы с информацией о значимых изменениях (обновлениях алгоритмов и событиях на сайте). Для каждого обновления указаны его название, дата начала и окончания, а для событий – дата и название.
Запрос данных из Google Search Console
Функцияfetch_gsc_data
формирует запрос к API Google Search Console, используя сервисный аккаунт (авторизационные данные считываются из JSON‑файла - который вам также понадобится для работы скрипта). В запросе задается диапазон дат и необходимые измерения (в данном случае – «date»). Ответ API преобразуется в DataFrame, где хранится дата и основные метрики (клики, показы, позиция).Агрегация данных по сайтам
Функцияload_all_data
итеративно вызываетfetch_gsc_data
для каждого сайта из заданного списка и объединяет полученные данные в один общий DataFrame. Каждая выборка дополняется информацией о соответствующем сайте.Создание интерактивного дашборда с Dash
Интерфейс пользователя:
В Layout приложения создаются элементы управления:Чекбоксы для выбора сайтов, метрик, обновлений и событий.
Графический компонент, в котором отображаются данные.
Обновление графика:
Callback-функция отслеживает изменения состояния элементов управления (выбор сайтов, метрик, обновлений и событий, а также изменения зума на графике). При каждом изменении происходит следующее:Данные фильтруются по выбранным сайтам.
Если выбраны метрики, по каждому сайту строятся линии (с использованием Plotly Scatter), где каждая линия отражает динамику конкретной метрики.
Дополнительно, в графике добавляются:
Прямоугольники (vrect) – для обозначения периодов обновлений (например, обновлений поискового алгоритма), с отображением названия обновления.
Вертикальные линии (vline) и аннотации – для отображения событий, таких как изменения или важные действия на сайте.
Применяется диапазонный слайдер (range slider) для оси X, позволяющий пользователю интерактивно изменять временной интервал.
При изменении зума по оси X происходит динамический пересчет диапазона оси Y для более точного отображения изменений в данных.
Свойство
uirevision='fixed'
используется для того, чтобы сохранить настройки зума и панорамирования между обновлениями графика.
От теории к практике - как работать со скриптом
Итак, для начала вам потребуется:
1. Создать проект в Google Cloud Console
Переходим на https://console.cloud.google.com/
Создаем новый проект
Переходим в APIs & Services → Library
Находим и включаем API:
✅ Google Search Console API
2. Создать Service Account (сервисный аккаунт)
Переходим в IAM & Admin → Service Accounts
Нажимаем Create Service Account и создаем его
Далее создаем и скачаем JSON-файл с ключом (
credentials.json
)Сохраняем этот файл – он понадобится в корне проекта
В файле будет вот такие данные
{
"type": "service_account",
"project_id": "Имя вашего проекта",
"private_key_id": "Ваш Private Key ID",
"private_key": "Ваш Private Key",
"client_email": "project@project.iam.gserviceaccount.com",
"client_id": "Ваш номер клиента",
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
"token_uri": "https://oauth2.googleapis.com/token",
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
"client_x509_cert_url": "Ваши данные",
"universe_domain": "googleapis.com"
}
Сразу оговорюсь - в вашем JSON-файле будут ваши данные, не такие как в примере.
Почту из строки client_email необходимо будет добавить в качестве админа в вашей Серч Консоли (для нужного сайта).
Далее в той же папке, где у вас лежит JSON-файл создаем файл с расширением .py, я делаю всегда так - main.py
В файле вот такой код
import os
import json
import pandas as pd
import plotly.graph_objs as go
import dash
from dash import dcc, html
from datetime import datetime
from dash.dependencies import Input, Output
from google.oauth2 import service_account
from googleapiclient.discovery import build
# Ваши константы
SITES = [
"sc-domain:Ваш домен",
"sc-domain:Ваш домен",
]
# Даты, за которые берем данные
START_DATE = "2024-01-01"
END_DATE = "2025-04-12"
# ============================================================
# Обновленный список Google Updates с дополнительными данными
# ============================================================
GOOGLE_UPDATES = [
{"name": "March 2025 core update", "start": "2025-03-13", "end": "2025-03-26"},
{"name": "December 2024 spam update", "start": "2024-12-19", "end": "2024-12-26"},
{"name": "December 2024 core update", "start": "2024-12-12", "end": "2024-12-18"},
{"name": "November 2024 core update", "start": "2024-11-11", "end": "2024-12-04"},
{"name": "Ranking is experiencing an ongoing issue (Aug 2024)", "start": "2024-08-15", "end": "2024-08-19"},
{"name": "August 2024 core update", "start": "2024-08-15", "end": "2024-09-03"},
{"name": "June 2024 spam update", "start": "2024-06-20", "end": "2024-06-27"},
{"name": "March 2024 spam update", "start": "2024-03-05", "end": "2024-03-19"},
{"name": "March 2024 core update", "start": "2024-03-05", "end": "2024-04-19"},
{"name": "November 2023 reviews update", "start": "2023-11-08", "end": "2023-12-07"},
{"name": "November 2023 core update", "start": "2023-11-02", "end": "2023-11-27"},
{"name": "October 2023 core update", "start": "2023-10-05", "end": "2023-10-18"},
{"name": "Ranking is experiencing an ongoing issue (Oct 2023)", "start": "2023-10-05", "end": "2023-10-31"},
{"name": "October 2023 spam update", "start": "2023-10-04", "end": "2023-10-19"},
{"name": "September 2023 helpful content update", "start": "2023-09-14", "end": "2023-09-27"},
{"name": "August 2023 core update", "start": "2023-08-22", "end": "2023-09-07"},
{"name": "April 2023 reviews update", "start": "2023-04-12", "end": "2023-04-25"},
{"name": "March 2023 core update", "start": "2023-03-15", "end": "2023-03-28"},
{"name": "February 2023 product reviews update", "start": "2023-02-21", "end": "2023-03-07"},
]
# События на сайте
EVENTS = [
{"name": "Ваше событие", "date": "2024-02-12"},
{"name": "Ваше событие", "date": "2024-02-16"},
{"name": "Ваше событие", "date": "2024-02-27"},
{"name": "Ваше событие", "date": "2024-03-20"},
{"name": "Ваше событие", "date": "2024-03-21"},
]
def fetch_gsc_data(property_uri, start_date, end_date, creds):
service = build('searchconsole', 'v1', credentials=creds)
body = {
'startDate': start_date,
'endDate': end_date,
'dimensions': ['date'],
'rowLimit': 10000
}
response = service.searchanalytics().query(siteUrl=property_uri, body=body).execute()
rows = response.get('rows', [])
data = []
for r in rows:
d = r['keys'][0]
data.append({
'date': d,
'clicks': r.get('clicks', 0),
'impressions': r.get('impressions', 0),
'position': r.get('position', 0)
})
df = pd.DataFrame(data)
df['date'] = pd.to_datetime(df['date'])
return df
def load_all_data(
start_date=START_DATE,
end_date=END_DATE,
sites=SITES,
creds_file="Название вашего JSON-файла"
):
creds = service_account.Credentials.from_service_account_file(
creds_file,
scopes=["https://www.googleapis.com/auth/webmasters.readonly"]
)
all_df = []
for s in sites:
df_s = fetch_gsc_data(s, start_date, end_date, creds)
df_s['domain'] = s
all_df.append(df_s)
big_df = pd.concat(all_df, ignore_index=True)
return big_df
app = dash.Dash(__name__)
GLOBAL_DF = None # кешируем загруженные данные
app.layout = html.Div([
html.H1("GSC Дашборд - Клики, Показы, Позиция"),
html.Div([
html.Label("Выберите сайт(-ы):"),
dcc.Checklist(
id='site-selector',
options=[{'label': site, 'value': site} for site in SITES],
value=SITES,
inline=True
),
]),
html.Div([
html.Label("Метрики:"),
dcc.Checklist(
id='metric-selector',
options=[
{'label': 'Клики', 'value': 'clicks'},
{'label': 'Показы', 'value': 'impressions'},
{'label': 'Позиция', 'value': 'position'},
],
# Если тут пусто, у нас нет рядов данных => ось X может стать [0..1]
value=[],
inline=True
),
]),
html.Div([
html.Label("Показать Google Updates:"),
dcc.Checklist(
id='updates-toggle',
options=[{'label': u['name'], 'value': u['name']} for u in GOOGLE_UPDATES],
value=[],
inline=True
),
]),
html.Div([
html.Label("Показать события:"),
dcc.Checklist(
id='events-toggle',
options=[{'label': e['name'], 'value': e['name']} for e in EVENTS],
value=[],
inline=True
),
]),
dcc.Graph(id='gsc-graph')
])
@app.callback(
Output('gsc-graph', 'figure'),
[
Input('site-selector', 'value'),
Input('metric-selector', 'value'),
Input('updates-toggle', 'value'),
Input('events-toggle', 'value'),
Input('gsc-graph', 'relayoutData')
]
)
def update_graph(selected_sites, selected_metrics, selected_updates, selected_events, relayoutData):
global GLOBAL_DF
if GLOBAL_DF is None:
GLOBAL_DF = load_all_data()
print("=== CALLBACK ===")
print("Selected events: ", selected_events) # Проверяем в консоли, что выбрано
fig = go.Figure()
# Фильтруем общий DF по выбранным доменам
filtered_df = GLOBAL_DF[GLOBAL_DF['domain'].isin(selected_sites)]
# Если выбраны метрики, строим линии
if selected_metrics:
for site in selected_sites:
df_site = filtered_df[filtered_df['domain'] == site].sort_values('date')
for metric in selected_metrics:
fig.add_trace(go.Scatter(
x=df_site['date'],
y=df_site[metric],
mode='lines',
name=f"{site} - {metric}"
))
# Добавляем прямоугольники (vrect) для Google Updates
for upd in GOOGLE_UPDATES:
if upd['name'] in selected_updates:
fig.add_vrect(
x0=upd['start'], x1=upd['end'],
fillcolor='red', opacity=0.1, layer='below', line_width=0,
annotation_text=upd['name'],
annotation_position="top left"
)
# Добавляем события (вертикальные линии) для выбранных ev
for ev in EVENTS:
if ev['name'] in selected_events:
date_str = pd.to_datetime(ev['date']).strftime('%Y-%m-%d')
print(f"Adding event line: {ev['name']} at {date_str}") # Диагностика
fig.add_vline(
x=date_str,
line_color='blue',
opacity=1.0,
line_width=2,
layer='above'
)
fig.add_annotation(
x=date_str,
y=1,
xref='x',
yref='paper',
text=ev['name'],
showarrow=True,
arrowhead=2,
ax=0,
ay=-40,
font=dict(color='blue', size=12)
)
# Настройка осей
fig.update_layout(
title="GSC Трафик",
xaxis_title="Дата",
yaxis_title="Значение",
legend_title="Сайты и метрики",
xaxis=dict(
tickmode='linear',
tick0=pd.to_datetime(START_DATE),
dtick=86400000.0, # один день (в мс)
tickformat="%d-%m-%Y",
rangeslider=dict(visible=True),
)
)
# Если метрик нет => вручную задаём X-ось (иначе range может оказаться [0..1])
if not selected_metrics:
print("No metrics selected -> Forcing x-range to START..END")
fig.update_xaxes(range=[START_DATE, END_DATE])
# Обработка зума (для Y-оси)
if relayoutData:
if "xaxis.range[0]" in relayoutData and "xaxis.range[1]" in relayoutData and selected_metrics:
x_start = pd.to_datetime(relayoutData["xaxis.range[0]"])
x_end = pd.to_datetime(relayoutData["xaxis.range[1]"])
mask = (filtered_df['date'] >= x_start) & (filtered_df['date'] <= x_end)
zoomed_df = filtered_df[mask]
if not zoomed_df.empty:
y_min = zoomed_df[selected_metrics].min().min()
y_max = zoomed_df[selected_metrics].max().max()
y_padding = (y_max - y_min) * 0.05
fig.update_yaxes(range=[y_min - y_padding, y_max + y_padding])
# Чтобы при переключении чекбоксов не сбрасывался зум или аннотации
fig.update_layout(uirevision='fixed')
return fig
if __name__ == '__main__':
app.run(debug=True)
В данном коде необходимо изменить следующее:
Строка 14 - вписать ваши домены, которые нужно вывести на графике
Строка 19 - указываем период, за который нужны данные (если проектов много, рекомендую не брать слишком большой период - будет долго подгружать)
Строка - 25-46 - тут я вписал апы Гугла - вы можете добавить если что то не указано
Строка 48 - тут вписываете события, которые хотите видеть на графике (они будут показаны вертикальной линией, как на скрине выше)
Строка 84 - название вашего JSON - файла (который расположен в той же папке, что и сам скрипт)
Все - вы почти готовы начинать...
Пока писал статью, показалось что уж проще было заплатить какому-нибудь сервису
Запуск и работа с дашбордом
Перед тем как запускать скрипт - устанавливаем необходимые зависимости, для чего в терминале (я через VS Code ставлю) вводим эту команду:
pip install dash plotly pandas google-auth google-auth-oauthlib google-auth-httplib2 google-api-python-client
Ждем когда все установится и запускаем скрипт простой командой
python main.py
Когда скрипт запустится вы увидите в терминале, что дашборд доступен по локальному адресу, в моем случае это - http://127.0.0.1:8050
Думаю у вас будет так же.
По умолчанию чтобы увидеть график нужно будет установить чекбокс на показы, клики или позиции и выбрать какой ап гугла показать, далее можно масштабировать график, там в принципе все интуитивно понятно.
Самое главное, что несколько минут настройки и у вас свой мини-сервис по анализу - попали ли вы под очередной ап Гугла или нет.
Кстати, когда будет новый update нужно будет добавить его руками в коде, если хотите сравнить повлиял ли он на вас.
Можно конечно все это делать и по-другому, но я вот сдела такой мини дашбоард и работаю с ним. Пользуйтесь, если надо.