Come uso Search Console + Python per trovare quick wins SEO in 10 minuti
Non serve un tool a pagamento. Basta la GSC API, un service account e 50 righe di Python. Ti mostro lo script esatto che uso ogni settimana — con i dati reali del mio sito.
Il principio del quick win SEO è semplice: le keyword in posizione 6-20 sono già state notate da Google. Il sito ha dimostrato rilevanza. Serve un piccolo spintone — un title tag più preciso, un paragrafo in più, un link interno — per salire sopra la soglia del clic.
Il problema è trovarlo questo spintone. Google Search Console mostra tutti i dati, ma navigarli manualmente per ogni sito è lento e impreciso. Io ho automatizzato il processo. Ogni lunedì mattina, uno script Python si collega alla GSC API, scarica le performance degli ultimi 28 giorni, filtra le keyword nel range di interesse, e mi restituisce una lista ordinata di opportunità con la stima di traffico guadagnabile.
Il tutto in meno di 10 minuti, incluso il tempo di lettura del report.
Perché la posizione 6-20 è il tesoro nascosto
Il CTR medio per posizione in Google segue una curva esponenziale. Secondo i dati aggregati del settore, la posizione 1 cattura circa il 28% dei clic, la posizione 3 l'11%, la posizione 5 il 7%. Scendi a posizione 6 e il CTR crolla sotto il 5%. Posizione 10: circa il 2%. Sotto la prima pagina: quasi zero.
CTR atteso per posizione
Pos 1 → ~28% CTR Pos 2 → ~15% CTR Pos 3 → ~11% CTR Pos 5 → ~7% CTR Pos 10 → ~2% CTR ← soglia di visibilità Pos 20 → ~0.5% CTR ← quasi invisibile
Una keyword con 1000 impressioni mensili in posizione 8 genera circa 20 clic. Se sale a posizione 3, diventa 110 clic. Stessa keyword, stesso contenuto — solo migliorato — e il traffico aumenta di 5 volte.
Ecco perché mi concentro su quel range. Non sto cercando di entrare in una SERP nuova partendo da zero. Sto ottimizzando contenuti già esistenti che Google ha già validato come rilevanti.
Setup: collegare la GSC API con un service account
Prima di scrivere una riga di Python, serve configurare l'autenticazione. Il modo più solido è il service account — si autentica silenziosamente senza bisogno di interazione umana, ideale per script automatizzati.
1. Crea il progetto Google Cloud
setup — passo 1
# Vai su console.cloud.google.com
# 1. Nuovo progetto → nome: "seo-tools"
# 2. API e servizi → Libreria → cerca "Google Search Console API" → Abilita
# 3. Credenziali → Crea credenziali → Account di servizio
# Nome: seo-agent
# Clicca sull'account → tab Chiavi → Aggiungi chiave → JSON
# Scarica il file service-account.json
# Installa le dipendenze
pip install google-auth google-auth-httplib2 google-api-python-client
2. Aggiungi il service account in GSC
Vai su Search Console → Impostazioni → Utenti e autorizzazioni → aggiungi l'email del service account (tipo seo-agent@seo-tools.iam.gserviceaccount.com) con permesso Proprietario.
Senza questo passaggio, la API restituisce un oggetto vuoto anche se le credenziali sono valide. Google distingue tra "autenticazione riuscita" e "autorizzazione per questa proprietà".
Lo script completo: quick wins in un colpo solo
Ecco lo script che uso. Fa quattro cose: si autentica, scarica le query degli ultimi 28 giorni, filtra quelle nel range 6-20 con impressioni sopra soglia, e stampa una lista ordinata per potenziale di traffico guadagnabile.
gsc_quick_wins.py
from google.oauth2 import service_account
from googleapiclient.discovery import build
from datetime import date, timedelta
import json
# --- CONFIG ---
KEY_FILE = 'service-account.json'
SITE_URL = 'https://tuosito.com/'
SCOPES = ['https://www.googleapis.com/auth/webmasters.readonly']
# Soglie filtro
MIN_IMPRESSIONS = 3 # ignora keyword con pochissima visibilità
POS_MIN = 6.0 # posizione minima (inclusa)
POS_MAX = 20.0 # posizione massima (inclusa)
DAYS = 28 # finestra temporale
# CTR atteso per posizione (da usare per stima guadagno)
EXPECTED_CTR = {
1: 0.28, 2: 0.15, 3: 0.11, 4: 0.08, 5: 0.07,
6: 0.05, 7: 0.04, 8: 0.03, 9: 0.025, 10: 0.02
}
def get_service():
creds = service_account.Credentials.from_service_account_file(
KEY_FILE, scopes=SCOPES)
return build('searchconsole', 'v1', credentials=creds)
def fetch_queries(service, start_date, end_date):
"""Scarica tutte le query con dimensioni query+page."""
response = service.searchanalytics().query(
siteUrl=SITE_URL,
body={
'startDate': str(start_date),
'endDate': str(end_date),
'dimensions': ['query', 'page'],
'rowLimit': 5000,
'orderBy': [{'fieldName': 'impressions', 'sortOrder': 'DESCENDING'}]
}
).execute()
return response.get('rows', [])
def find_quick_wins(rows, pos_min=POS_MIN, pos_max=POS_MAX, min_imp=MIN_IMPRESSIONS):
"""Filtra e scoreizza le opportunità quick win."""
opportunities = []
for row in rows:
query = row['keys'][0]
page = row['keys'][1]
clicks = row['clicks']
imps = row['impressions']
ctr = row['ctr']
pos = row['position']
# Filtro principale: range posizione + soglia impressioni
if not (pos_min <= pos <= pos_max) or imps < min_imp:
continue
# Stima CTR se arrivasse a top 3
target_pos = 3
target_ctr = EXPECTED_CTR.get(target_pos, 0.11)
estimated_gain = round((target_ctr - ctr) * imps * (28 / 28))
opportunities.append({
'query': query,
'page': page.replace(SITE_URL, '/'),
'position': round(pos, 1),
'impressions': int(imps),
'clicks': int(clicks),
'ctr_actual': f"{ctr*100:.1f}%",
'estimated_gain': max(0, estimated_gain)
})
# Ordina per traffico guadagnabile (decrescente)
return sorted(opportunities, key=lambda x: x['estimated_gain'], reverse=True)
def print_report(opportunities):
print(f"\n{'='*70}")
print(f" QUICK WINS SEO — {date.today()}")
print(f" Sito: {SITE_URL} | Finestra: {DAYS} giorni")
print(f" Filtro: pos {POS_MIN}-{POS_MAX} | min {MIN_IMPRESSIONS} imp")
print(f"{'='*70}\n")
if not opportunities:
print(" Nessun quick win trovato con i filtri attuali.")
return
print(f" {'#':<4} {'QUERY':<40} {'POS':>5} {'IMP':>6} {'CTR':>6} {'GAIN':>6} PAGINA")
print(f" {'-'*80}")
for i, opp in enumerate(opportunities[:20], 1):
query_short = opp['query'][:38] + '..' if len(opp['query']) > 38 else opp['query']
print(f" {i:<4} {query_short:<40} {opp['position']:>5} "
f"{opp['impressions']:>6} {opp['ctr_actual']:>6} "
f"{opp['estimated_gain']:>5}+ {opp['page']}")
print(f"\n Totale opportunità trovate: {len(opportunities)}")
print(f" Traffico aggiuntivo stimato (top 20): "
f"+{sum(o['estimated_gain'] for o in opportunities[:20])} clic/mese\n")
def main():
end = date.today()
start = end - timedelta(days=DAYS)
print(f"Connessione a GSC per {SITE_URL}...")
service = get_service()
rows = fetch_queries(service, start, end)
print(f"Query scaricate: {len(rows)}")
opportunities = find_quick_wins(rows)
print_report(opportunities)
# Salva anche in JSON per uso downstream
with open('quick_wins.json', 'w') as f:
json.dump(opportunities, f, ensure_ascii=False, indent=2)
print(" Risultati salvati in quick_wins.json")
if __name__ == '__main__':
main()
Output reale: smartweb-media.com
Ecco l'output dello script girato su questo sito (smartweb-media.com) il 29 marzo 2026, finestra 90 giorni:
Output — smartweb-media.com — 29/03/2026
====================================================================== QUICK WINS SEO — 2026-03-29 Sito: https://smartweb-media.com/ | Finestra: 28 giorni Filtro: pos 6.0-20.0 | min 3 imp ====================================================================== # QUERY POS IMP CTR GAIN PAGINA -------------------------------------------------------------------------------- 1 seo smart 10.5 8 0.0% 8+ / 2 smartweb seo 13.4 5 0.0% 4+ / 3 smart web seo 5.6 5 0.0% 3+ /blog/seo-audit-.. Totale opportunità trovate: 3 Traffico aggiuntivo stimato (top 20): +15 clic/mese
Il sito è giovane — i volumi sono piccoli. Ma la logica è identica su un sito con 10.000 impressioni al mese: lo stesso script trova le stesse opportunità, solo con numeri più grandi.
Nota la keyword "seo smart": 8 impressioni, posizione 10.5, 0 clic. Se salisse a posizione 3, stima +8 clic/mese. Non sembra tanto, ma su un sito con questa keyword a 500 impressioni il gain sarebbe +400 clic. La formula scala.
Come trasformare ogni quick win in azione concreta
Trovare la lista è la metà del lavoro. L'altra metà è sapere cosa fare con ogni opportunità. Ecco il mio processo:
1. Keyword in pos 6-10 con impressioni alte
Azione: ottimizza title tag e meta description
Sono sulla prima pagina ma il CTR è basso. Il problema è quasi sempre il title — non abbastanza specifico, non abbastanza orientato all'intent. Riscrivi il title includendo la keyword target e un numero o promessa concreta. Poi aggiorna la meta description con una call to action esplicita.
2. Keyword in pos 11-15 con buone impressioni
Azione: espandi il contenuto e aggiungi link interni
Sei in seconda pagina. Google ti vede come rilevante ma non abbastanza autorevole su quel topic. Aggiungi 300-500 parole di contenuto di qualità sulla pagina — esempi pratici, FAQ, approfondimenti. Poi aggiungi 2-3 link interni da pagine autorevoli del sito che puntino a questa pagina con anchor text pertinente.
3. Keyword in pos 16-20
Azione: valuta se creare una pagina dedicata
Potresti essere in posizione 16-20 perché stai usando una pagina generica per una keyword che meriterebbe contenuto dedicato. Controlla se la keyword è sufficientemente specifica da giustificare un articolo o una landing page a sé. Se sì, crea il contenuto dedicato e redirect o consolida quello vecchio.
4. Keyword con CTR 0% nonostante buona posizione
Azione: controlla il title tag dalla SERP reale
Se sei in posizione 7 con 20 impressioni e 0 clic, qualcosa non va nel snippet. Cerca la keyword su Google e guarda come appare il tuo risultato. Spesso il problema è che Google sta riscrivendo il title con qualcosa di meno attraente. Fix: riscrivi il tag title in modo che Google preferisca usare quello originale.
Automatizza: report settimanale con cron
Girare lo script manualmente ogni settimana è già utile. Ma puoi automatizzarlo completamente con un cron job Linux o un GitHub Action. Ecco entrambi:
crontab — ogni lunedì alle 8:00
# Aggiungi con: crontab -e
0 8 * * 1 cd /path/to/scripts && python3 gsc_quick_wins.py >> logs/quick_wins.log 2>&1
.github/workflows/weekly-gsc.yml
name: Weekly GSC Quick Wins
on:
schedule:
- cron: '0 8 * * 1' # ogni lunedì alle 8:00 UTC
workflow_dispatch: # permette anche esecuzione manuale
jobs:
quick-wins:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.11'
- run: pip install google-auth google-auth-httplib2 google-api-python-client
- name: Esegui script
env:
GSC_SERVICE_ACCOUNT: ${{ secrets.GSC_SERVICE_ACCOUNT }}
run: |
echo "$GSC_SERVICE_ACCOUNT" > service-account.json
python3 gsc_quick_wins.py
- name: Salva artefatti
uses: actions/upload-artifact@v4
with:
name: quick-wins-${{ github.run_id }}
path: quick_wins.json
Con GitHub Actions il report viene salvato come artefatto scaricabile. Aggiungi una notifica Slack o email e ogni lunedì mattina hai la lista delle opportunità della settimana nel tuo inbox — senza aprire un browser.
Varianti avanzate dello script
Analisi per pagina (non per query)
Invece di analizzare le singole keyword, puoi raggruppare per pagina e trovare quali URL hanno il maggior potenziale aggregato:
analisi per pagina
def quick_wins_by_page(rows):
"""Raggruppa quick wins per pagina — mostra quali URL ottimizzare."""
from collections import defaultdict
pages = defaultdict(lambda: {'queries': [], 'total_imp': 0, 'total_gain': 0})
for row in rows:
query = row['keys'][0]
page = row['keys'][1].replace(SITE_URL, '/')
pos = row['position']
imps = row['impressions']
if not (POS_MIN <= pos <= POS_MAX) or imps < MIN_IMPRESSIONS:
continue
target_ctr = 0.11 # pos 3
gain = max(0, (target_ctr - row['ctr']) * imps)
pages[page]['queries'].append(query)
pages[page]['total_imp'] += imps
pages[page]['total_gain'] += gain
# Ordina per gain totale
sorted_pages = sorted(pages.items(),
key=lambda x: x[1]['total_gain'], reverse=True)
for url, data in sorted_pages[:10]:
print(f"\n {url}")
print(f" Impressioni totali: {int(data['total_imp'])}")
print(f" Gain stimato: +{int(data['total_gain'])} clic/mese")
print(f" Query ({len(data['queries'])}): {', '.join(data['queries'][:5])}")
Confronto periodi (trend)
Confronta i quick wins del mese corrente con quelli del mese precedente per vedere se le keyword stanno salendo o scendendo:
confronto trend
def compare_periods(service):
end_curr = date.today()
start_curr = end_curr - timedelta(days=28)
end_prev = start_curr - timedelta(days=1)
start_prev = end_prev - timedelta(days=28)
rows_curr = fetch_queries(service, start_curr, end_curr)
rows_prev = fetch_queries(service, start_prev, end_prev)
pos_curr = {r['keys'][0]: r['position'] for r in rows_curr}
pos_prev = {r['keys'][0]: r['position'] for r in rows_prev}
movements = []
for query, pos in pos_curr.items():
if query in pos_prev:
delta = pos_prev[query] - pos # positivo = salita
movements.append({'query': query, 'pos_now': pos,
'pos_prev': pos_prev[query], 'delta': delta})
rising = sorted([m for m in movements if m['delta'] > 1],
key=lambda x: x['delta'], reverse=True)
falling = sorted([m for m in movements if m['delta'] < -1],
key=lambda x: x['delta'])
print("\n🟢 In salita:")
for m in rising[:5]:
print(f" '{m['query']}' {m['pos_prev']:.1f} → {m['pos_now']:.1f} ({m['delta']:+.1f})")
print("\n🔴 In discesa:")
for m in falling[:5]:
print(f" '{m['query']}' {m['pos_prev']:.1f} → {m['pos_now']:.1f} ({m['delta']:+.1f})")
Il punto
Il valore di questo approccio non è la sofisticatezza tecnica — lo script è relativamente semplice. È la sistematicità. La maggior parte dei siti ha keyword in posizione 6-20 che non vengono mai ottimizzate perché nessuno le guarda con sufficiente frequenza.
Automatizzando il processo di identificazione, puoi concentrare il tempo sulla parte che richiede giudizio umano: decidere quale azione intraprendere, come riscrivere il title, cosa aggiungere al contenuto. La macchina trova le opportunità; tu le trasformi in risultati.
Se vuoi approfondire la pipeline completa — dall'audit tecnico al piano editoriale — leggi come uso Claude per fare audit SEO in 20 minuti.
Vuoi che implementi questo per il tuo sito?
Configuro la pipeline GSC, identifico i quick wins esistenti e ti consegno una lista di azioni prioritizzate con stima di traffico. Senza tool a pagamento aggiuntivi.
Richiedi Audit Gratuito