Понадобилось мне проанализировать сайт на предмет попадания его под фильтр Гугла, причем с историческими данными.
Первым делом начал гуглить — чекер фильтров Гугла и вот это вот все. Естественно — в интернетах куча решений, но есть небольшое но… Они либо платные, либо условно‑бесплатные, либо вообще непонятно как работают.
Ну что делать — надо реализовать собственное решение, что я и сделал.
Палю годноту, так как использование этого решения никак не повлияет на мою деятельность, а кому то может и полезно будет.
Суть решения достаточно проста есть код (он будет ниже), написанный на 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 нужно будет добавить его руками в коде, если хотите сравнить повлиял ли он на вас.
Можно конечно все это делать и по-другому, но я вот сдела такой мини дашбоард и работаю с ним. Пользуйтесь, если надо.
ссылка на оригинал статьи https://habr.com/ru/articles/900482/
Добавить комментарий