TGViewer
Записки м̶о̶л̶о̶д̶о̶г̶о̶ вебмастера Записки м̶о̶л̶о̶д̶о̶г̶о̶ вебмастера @yawebmaster · 976 subscribers
Post #2338 439


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}"
))
More from @yawebmaster
  1. Aug 5, 2026С появлением ИИ я очень плотно с ним начал взаимодействовать, но не на уровне - "эт самое,…
  2. Jul 24, 2026Ох давненько я ничего не писал тут)) Аж скулы сводит)) Короче, прикольная штука приключила…
  3. Oct 23, 2025video post
  4. Oct 23, 2025Я в последнее время часто работаю с прокси, и обратил внимание, как изменились мобильные п…
  5. Oct 22, 2025video post
  6. Oct 22, 2025Какой же трэш, Индусы дорвались до Кворка. Подыскиваю чувака на крауд, и полез на Кворк, а…
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →