feat(browser): auto-commit con 178 cambios
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,109 @@
|
||||
---
|
||||
name: pull_gsc_search_analytics
|
||||
kind: function
|
||||
lang: py
|
||||
domain: datascience
|
||||
version: "1.0.0"
|
||||
purity: impure
|
||||
signature: "def pull_gsc_search_analytics(service: object, site_url: str, start_date: str, end_date: str, dimensions: list = None, row_limit: int = 25000, max_total_rows: int = 0, search_type: str = 'web') -> list"
|
||||
description: "Extrae datos de la Search Analytics API de Google Search Console (GSC): impresiones, clicks, CTR y posicion por las dimensiones pedidas (query, page, date, country, device, searchAppearance). Recibe un objeto service GSC ya autenticado (el que devuelve gsc_auth, inyectado) y llama a service.searchanalytics().query(siteUrl, body).execute(). Pagina automaticamente con startRow en pasos de row_limit (tope duro 25000 filas/request) hasta que una pagina devuelve menos de row_limit filas o se alcanza max_total_rows. Aplana cada fila mapeando el array keys posicionalmente a los nombres de dimensions y añade clicks, impressions, ctr y position. Si la API no devuelve filas (rows ausente), retorna lista vacia sin error. Es el extractor principal de datos SEO para alimentar un pipeline hacia DuckDB/Postgres."
|
||||
tags: [seo, gsc, datascience, search-console, google, extractor]
|
||||
uses_functions: []
|
||||
uses_types: []
|
||||
returns: []
|
||||
returns_optional: false
|
||||
error_type: "error_go_core"
|
||||
imports: [typing.Any]
|
||||
params:
|
||||
- name: service
|
||||
desc: "objeto service autenticado de la Google Search Console API (el que devuelve gsc_auth_py_infra). Se inyecta ya construido; esta funcion NO lo crea ni llama a gsc_auth internamente. Debe exponer .searchanalytics().query(siteUrl=..., body=...).execute()."
|
||||
- name: site_url
|
||||
desc: "propiedad de Search Console. Formato 'sc-domain:ejemplo.com' para propiedad de dominio, o URL completa 'https://ejemplo.com/' para propiedad de prefijo. El formato importa: usar el que coincida con como la propiedad esta dada de alta en GSC."
|
||||
- name: start_date
|
||||
desc: "fecha inicial inclusiva en formato YYYY-MM-DD."
|
||||
- name: end_date
|
||||
desc: "fecha final inclusiva en formato YYYY-MM-DD. La API tiene ~2-3 dias de lag; el caller deberia pedir hasta hoy-3 para datos completos."
|
||||
- name: dimensions
|
||||
desc: "lista de dimensiones a desglosar. Por defecto ['query', 'page']. Otras validas: 'date', 'country', 'device', 'searchAppearance'. El orden define el orden de las keys en cada fila."
|
||||
- name: row_limit
|
||||
desc: "filas por request y tamaño de paso de la paginacion. Rango 1..25000 (se clampa al tope duro de la API). Por defecto 25000."
|
||||
- name: max_total_rows
|
||||
desc: "tope total de filas acumuladas en todas las paginas. 0 = sin tope (trae todo lo disponible). Si >0, recorta el resultado al llegar a ese numero."
|
||||
- name: search_type
|
||||
desc: "tipo de busqueda: 'web' | 'image' | 'video' | 'news' | 'discover' | 'googleNews'. Va en el body de la API como 'type'. Por defecto 'web'."
|
||||
output: "list de dicts aplanados. Cada dict tiene una clave por cada dimension (con su nombre real, ej. query, page) mas clicks, impressions, ctr y position. Ejemplo con dimensions=['query','page']: {'query': '...', 'page': '...', 'clicks': 5, 'impressions': 100, 'ctr': 0.05, 'position': 12.3}. Lista vacia si la API no devuelve filas."
|
||||
tested: true
|
||||
tests:
|
||||
- "test_aplanado_mapea_keys_a_nombres_de_dimension"
|
||||
- "test_paginacion_recorre_varias_paginas_y_para_en_pagina_corta"
|
||||
- "test_max_total_rows_recorta"
|
||||
- "test_rows_ausente_retorna_lista_vacia"
|
||||
- "test_dimension_unica_date"
|
||||
test_file_path: "python/functions/datascience/pull_gsc_search_analytics_test.py"
|
||||
file_path: "python/functions/datascience/pull_gsc_search_analytics.py"
|
||||
---
|
||||
|
||||
## Ejemplo
|
||||
|
||||
```python
|
||||
import sys, os
|
||||
sys.path.insert(0, os.path.join("python", "functions"))
|
||||
from infra import gsc_auth
|
||||
from datascience import pull_gsc_search_analytics
|
||||
|
||||
# 1. Autenticar (service account JSON via env var GSC_SA_JSON o ruta explicita)
|
||||
service = gsc_auth() # o gsc_auth("/ruta/fuera/del/repo/sa.json")
|
||||
|
||||
# 2. Extraer datos SEO por query + page de los ultimos dias (hasta hoy-3 por el lag)
|
||||
rows = pull_gsc_search_analytics(
|
||||
service,
|
||||
site_url="sc-domain:ejemplo.com", # propiedad de dominio
|
||||
# site_url="https://ejemplo.com/", # alternativa: propiedad de prefijo
|
||||
start_date="2026-06-01",
|
||||
end_date="2026-06-17",
|
||||
dimensions=["query", "page"],
|
||||
)
|
||||
|
||||
print(len(rows), "filas")
|
||||
# rows[0] -> {'query': 'comprar zapatillas', 'page': 'https://ejemplo.com/zapatillas',
|
||||
# 'clicks': 5, 'impressions': 100, 'ctr': 0.05, 'position': 12.3}
|
||||
|
||||
# Desglose temporal (1 fila por dia) con tope de filas:
|
||||
serie = pull_gsc_search_analytics(
|
||||
service, "sc-domain:ejemplo.com", "2026-06-01", "2026-06-17",
|
||||
dimensions=["date"], max_total_rows=1000,
|
||||
)
|
||||
```
|
||||
|
||||
## Cuando usarla
|
||||
|
||||
Cuando necesites ingerir datos SEO de Google Search Console (impresiones, clicks,
|
||||
CTR, posicion por query/pagina/fecha/pais/dispositivo) para volcarlos a DuckDB o
|
||||
Postgres. Es el paso de extraccion del pipeline SEO: primero `gsc_auth` para
|
||||
construir el `service`, luego esta funcion para traer las filas paginadas y
|
||||
aplanadas, listas para upsert en una tabla.
|
||||
|
||||
## Gotchas
|
||||
|
||||
- **Lag de 2-3 dias**: GSC no tiene los datos del dia actual ni los 1-2 previos
|
||||
completos. Pide `end_date` = hoy-3 para evitar dias parciales que luego cambian.
|
||||
- **Privacy threshold (anonimizacion)**: las queries de baja frecuencia se ocultan
|
||||
por privacidad de Google. La suma de clicks/impressions por `query` NO cuadra con
|
||||
el total agregado sin dimension `query` — falta la "cola" anonimizada. Para totales
|
||||
exactos, pide tambien una consulta sin la dimension `query` (ej. solo `["date"]`).
|
||||
- **Formato de site_url**: `sc-domain:ejemplo.com` para propiedad de dominio; URL
|
||||
completa con esquema y barra final `https://ejemplo.com/` para propiedad de prefijo.
|
||||
Si no coincide con como esta dada de alta la propiedad, la API devuelve 403/permission.
|
||||
- **Tope 25000 filas/request**: `row_limit` se clampa a 25000. Para propiedades grandes
|
||||
la paginacion puede dar muchas requests; vigila los rate limits de la API (la funcion
|
||||
no reintenta — el error de quota se propaga al caller).
|
||||
- **Permisos**: la service account debe estar añadida como usuario (al menos lectura)
|
||||
en la propiedad de GSC; si no, error de permisos al ejecutar el query.
|
||||
|
||||
## Notas
|
||||
|
||||
`service` se inyecta ya construido (separacion auth/extraccion), por eso esta funcion
|
||||
no aparece acoplada a `gsc_auth` en `uses_functions`: no la importa ni la llama. El
|
||||
test ejercita la logica de paginado y aplanado con un service mock, sin red ni
|
||||
credenciales. Funcion impura: hace I/O de red contra la API de Google; cualquier error
|
||||
HTTP (auth, permisos, quota) se propaga.
|
||||
Reference in New Issue
Block a user