Come correggere e pulire lo storico dei dati e-commerce con Python attraverso l’Item Data Import di GA4
Nel marketing, i dati ci aiutano a comprendere come le persone interagiscono con prodotti e servizi, sia online che offline, vivendo e arricchendo le loro esperienze. Ed è anche grazie ai numeri che riusciamo quotidianamente a mappare e conoscere in modo sempre più approfondito i loro bisogni. Fare un’attenta analisi dei dati significa quindi restituire ai progetti il cuore della loro esistenza, ovvero i vissuti delle persone, senza i quali obiettivi e risultati non riuscirebbero a parlarsi.
Ecco perché ogni giorno ci impegniamo per conoscere a fondo i pattern comportamentali delle persone che passano attraverso prodotti e servizi dei brand con cui collaboriamo, analizzando e ottimizzando sempre più a fondo i dati da cui ciascun progetto nasce e si sviluppa.
In questo articolo, esploreremo una particolare funzionalità di Google Analytics 4 che ci aiuta nel rendere sempre più puliti i dati su cui lavoriamo, per scremare criticità e ottimizzare i processi di lavoro.
Google Analytics 4 (GA4) offre agli utenti una vasta gamma di funzionalità avanzate per analizzare e monitorare il comportamento degli utenti sul proprio sito web. Una di queste funzionalità è proprio la “Data Import” o “Importazione Dati”, che permette di caricare dati da fonti esterne (CRM o punti vendita offline, ad esempio) in modo da combinarli con i dati di traffico degli utenti online. In questo modo è possibile ottenere informazioni più precise e consultarle più facilmente tramite un unico strumento.
Per utilizzare la funzione di Data Import è sufficiente disporre di un file CSV che contiene i dati esterni, che verranno collegati ai dati raccolti da GA4 una volta caricati all’interno della proprietà. Ci sono quattro diversi tipi di Data Import:
“Cost-data” o “Dati di Costo”: utilizzato per importare dati relativi a clic, costi e impressioni da annunci di terze parti.
“Item Data” o “Dati Elemento”: copre i metadati di prodotto come taglia, colore, categoria, nome e brand.
“User Data” o “Dati Utenti”: serve per importare i metadati relativi agli utenti come l’indice di fedeltà che può essere raccolto da CRM.
“Offline Events” o “Evento Offline”: eventi personalizzati offline che provengono da sorgenti che non sono collegate a internet.
Nel caso dell’Item Data Import (importazione dei dati di elemento), questa funzione diventa ancor più interessante: questo tipo di Data Import permette infatti non solo di modificare i dati che verranno raccolti in futuro, ma agisce retroattivamente anche sullo storico per correggere eventuali errori che sono stati registrati in passato.
Un esempio potrebbe essere quello di un e-commerce multilingua in cui i dati dei prodotti vengono raccolti in diverse lingue. In questo caso sarebbe più difficile analizzare le performance dei prodotti, in quanto per quello che è nei fatti un unico prodotto (ad esempio “T-shirt bianca”) non troveremmo un’unica voce “Nome elemento”, ma tante quante sono le lingue del sito (“T-shirt bianca”, “White t-shirt”, “T-shirt blanc” e così via), rendendo il dato meno fruibile.
Ecco che la funzione di Data Import di GA4 può essere di supporto per aggregare il dato e presentarlo in maniera più ordinata. Per questo processo però è necessario ottenere un file che contenga i dati di prodotto come SKU, nome di prodotto e nome di categoria, un processo che è possibile automatizzare grazie a Python.
Prima di tutto è necessario assicurarsi che le informazioni che vogliamo raccogliere siano effettivamente presenti nelle pagine del sito e-commerce, come lo SKU, il nome e la categoria di prodotto. È possibile fare delle prove con un paio di pagine di prodotto e aprire DevTools (Ctrl+Shift+I), aprire la tab “Elements” e cercare (Ctrl+F) gli elementi come il nome di un prodotto, il suo sku e la sua categoria nel codice: se saranno presenti allora sarà possibile estrarli.
Bisogna poi assicurarsi di aver installato Python, ed è consigliato creare un ambiente virtuale in modo da avere un’installazione di Python isolata e che contenga esclusivamente le librerie necessarie per il corretto funzionamento di una data applicazione (nel nostro caso, il crawler dell’e-commerce).
In alternativa è possibile sfruttare Anaconda, una distribuzione open source dei linguaggi di programmazione Python e R, per una più intuitiva e semplificata gestione di pacchetti e ambienti virtuali. Una volta aperto Anaconda è sufficiente cliccare su “Environments”, poi “Create” assegnandogli un nome e cliccare sull’ambiente appena creato per attivarlo.
L’ultimo passaggio preliminare è l’installazione delle librerie necessarie per il corretto funzionamento dell’applicazione, in particolare Scrapy per gestire il processo di crawling dell’e-commerce, BeautifulSoup, che permette di estrarre determinate informazioni dalle pagine individuate dal crawler e Pandas per la manipolazione e organizzazione dei dati raccolti. Per installarle si può utilizzare questo script all’interno del prompt di comandi o terminale:
Su Windows:
<code>
py -m pip install scrapy beautifulsoup4 pandas
</code>
Su macOS/Linux:
<code>
python3 -m pip install scrapy beautifulsoup4 pandas
</code>
Dopo aver creato un nuovo file .py, si potrà importare le librerie appena scaricate con questo script:
<code>
import urllib
import pandas as pd
from scrapy.crawler import CrawlerProcess
from bs4 import BeautifulSoup
from scrapy.spiders import SitemapSpider
</code>
Non verranno utilizzate solo Scrapy, Pandas e BeautifulSoup ma anche la libreria “urllib”, un pacchetto che contiene diversi moduli per lavorare con le URL (Uniform Resource Locator).
Il prossimo passaggio dopo l’importazione delle librerie è il crawling dell’e-commerce, eseguibile tramite questo script:
<code>
# Funzione per ottenere il contenuto di una pagina
def get_page(url):
response = urllib.request.urlopen(urllib.request.Request(url, headers={‘User-Agent’: ‘Mozilla’}))
soup = BeautifulSoup(response, ‘html.parser’, from_encoding=response.info().get_param(‘charset’))
return soup
# Funzione per ottenere le sitemap dal file robots.txt
def get_sitemaps(robots):
sitemapList = []
lines = str(robots).splitlines()
for line in lines:
if line.startswith(‘Sitemap:’):
split = line.split(‘:’, maxsplit=1)
if ‘/en/’ in split[1]:
sitemapList.append(split[1].strip())
return sitemapList
robots = get_page(‘https://www.example-e-commerce.com/robots.txt’)
sitemaps = get_sitemaps(robots)
# Definizione dello spider per il crawling dei prodotti
class ProductsSpider(SitemapSpider):
name = “products”
allowed_domains = [“example-e-commerce.com”]
custom_settings = {
‘USER_AGENT’: ‘Mozilla/5.0’
}
sitemap_urls = sitemaps
def parse(self, response):
data = {
‘item_name’: response.selector.xpath(“//h1[@class=’page-title’]/span/text()”).get(),
‘item_cat1′: response.selector.xpath(“//*[@class=’items’]/li[2]/a[@title]/text()”).get(),
‘sku’: response.selector.xpath(“//*[@data-product-sku]”).css(“::attr(data-product-sku)”).get()
}
yield data
# Funzione per eseguire lo spider, che al termine crea un file JSON
def run_spider():
process = CrawlerProcess(settings={“FEEDS”: {“products.json”: {“format”: “json”}}})
process.crawl(ProductsSpider)
process.start()
if __name__ == “__main__”:
run_spider()
</code>
Nel codice riportato sopra, tramite la funzione “get_sitemaps(robots)” si raccolgono all’interno del file robots.txt le sitemap che contengono le pagine del sito da scansionare. L’obiettivo è raccogliere solo i nomi in inglese dei vari prodotti, quindi, vengono presi in considerazione solo gli URL che contengono la cartella linguistica “/en/” tramite un filtro e salvati in una lista “sitemapList”.
Viene poi configurato lo spider che effettuerà il crawling delle sitemap raccolte e nella funzione “parse(self, response)” vengono specificati gli elementi che andranno raccolti da ogni singola pagina come il nome del prodotto, la categoria (o le categorie) e lo SKU. Questi elementi possono essere identificati ed estratti con un selettore XPath, un linguaggio utilizzato per selezionare specifiche parti di un documento XML o HTML. Ad esempio nel codice per identificare il nome di prodotto viene sfruttato l’elemento h1 con il seguente selettore: “//h1[@class=’page-title’]/span/text()” dove si seleziona il testo all’interno del tag “span” contenuto nel tag h1 che ha come classe “page-title” (il “//” all’inizio serve per prendere in considerazione tutti gli elementi della pagina). Lo stesso principio è utilizzato per la categoria e il codice SKU.
Visto che ogni sito ha struttura, tag e classi proprie per le sue pagine, sarà probabilmente necessario adattare di conseguenza i selettori per estrarre correttamente le informazioni di prodotto. Si può testare il corretto funzionamento dei selettori XPath usando DevTools di Chrome (Ctrl+Shift+I), selezionando la tab “Elements” e immettendo nella barra di ricerca (Ctrl+F) il testo del selettore, e se verrà trovato solo e unicamente l’elemento che stiamo effettivamente cercando di estrarre, vorrà dire che il selettore è corretto.
La funzione “run_spider()” fa effettivamente partire lo spider, e al termine del crawling verrà prodotto un file JSON che conterrà le informazioni di prodotto raccolte. Questo sarà utile nel prossimo passaggio, ovvero la pulizia dei dati.
Dopo aver raccolto i dati con lo script precedente, possiamo procedere con la pulizia e la formattazione dei dati in un file CSV, pronto per l’importazione in GA4. Sarà necessario aggiungere lo script seguente:
<code>
df = pd.read_json(‘products.json’)
# Rimozione delle righe che hanno degli item_name vuoti (NaN) e eliminazione degli SKU duplicati
df.dropna(axis=0, subset=[‘item_name’], inplace=True)
df.drop_duplicates(subset=[‘sku’], keep=’first’, inplace=True)
# Per ogni riga del dataframe, elimina gli spazi aggiuntivi e gli a capo all’interno dei valori “h1”
for i, row in df.iterrows():
df.at[i, ‘item_name’] = ‘ ‘.join(df.at[i, ‘item_name’].splitlines()).replace(‘ ‘, ‘ ‘)
df = df.replace(‘”‘, ”, regex=True)
df[‘item_name’] = df[‘item_name’].str.replace(‘,’, ‘.’)
# Salvataggio dei dati puliti
df.to_csv(‘ProductData.csv’, index=False)
</code>
In questo modo a partire dal file JSON viene creato un dataframe Pandas, ovvero una tabella, che contiene i dati grezzi e pronti per essere ripuliti.
Una prima correzione da apportare è l’eliminazione di quelle righe che hanno “NaN” come valore “item_name” (ovvero sono vuote), visto che con ogni probabilità lo spider avrà esaminato anche pagine che non sono di prodotto e che non sono di nostro interesse. Altra pulizia consiste nell’eliminare le righe che hanno lo stesso valore per SKU, nel caso in cui lo spider avesse effettuato il crawling della stessa pagina di prodotto ma per diverse country (ad esempio all’inizio venivano incluse solo le sitemap di partenza del crawling che avessero nell’URL la cartella linguistica /en/, ma potrebbero esserci pagine in inglese per diverse country, come “it/en”, “uk/en” o “us/en”, e verrebbero raccolti dei prodotti duplicati se la pulizia non considerasse anche questo aspetto). Queste operazioni sono utili per assicurare che i dati nel dataframe siano completi (senza valori mancanti in colonne critiche) e unici (senza duplicati indesiderati).
Si può passare quindi al controllo delle informazioni che sono stati raccolte: può capitare infatti che i valori di nome di prodotto contengano errori come spazi in più; quindi, è necessario normalizzare la stringa di testo nella colonna ‘item_name’, rimuovendo i ritorni a capo e sostituendo eventuali spazi doppi con uno spazio singolo.
Altra accortezza inclusa nel codice riguarda la rimozione delle doppie virgolette (“) dai valori della tabella e la sostituzione delle virgole (,) con punti (.), questo perché potrebbero causare problemi una volta creato il file CSV (in questo caso chiamato “ProductData.csv”), che fa appunto utilizzo di virgole per separare i dati. Dopo un ulteriore controllo del file, come ad esempio la verifica che effettivamente gli SKU siano univoci, si può procedere a importare i dati su GA4.
Con i dati pronti in formato CSV, è possibile procedere con l’importazione in GA4, creando una nuova origine di dati attraverso i seguenti passaggi:
1 – Accedere a GA4 e andare alla sezione “Amministratore” della apposita proprietà
2 – Nel riquadro intitolato “Raccolta e modifica dei dati“, selezionare “Importazione dati”
3 – Cliccare sul pulsante “Crea origine dati”
4 – Come tipo di dati selezionare “Elemento”, e come origine dell’importazione “Caricamento di file CSV manuale”
5 – Cliccare su “Carica CSV” e selezionare il file creato dallo script Python, poi cliccare su “Avanti”
6 – Si deve procedere qui a mappare i valori del CSV con le dimensioni utilizzate in GA4. È già selezionata di default la voce “id”, che con il menù a tendina di fianco dovrà essere associata a “sku”, e di conseguenza “Nome” a “item_name” e “Categoria 1” a “item_cat1”. Verrà mostrata anche un’anteprima dei valori, in modo da essere sicuri di star selezionando la voce del file CSV adatta per ogni dimensione GA4.
7 – Per finire, cliccare su “Importa”
Inizierà il processo di importazione, che verrà portato a termine una volta che comparirà un’icona di spunta verde sotto la voce “Stato”. Si può quindi procedere a verificare i dati nei rapporti di GA4 che riguardano i prodotti, per assicurarsi che siano stati caricati correttamente. Se tutto è andato a buon fine, ora sarà possibile consultare i dati in maniera aggregata e più ordinata, come ad esempio avere un unico nome di prodotto in una sola lingua per ogni SKU.
I passaggi delineati finora permettono un’estrazione una-tantum dei dati, ma con qualche linea di codice in più è possibile prendere in considerazione un’estrazione periodica, in modo da mantenere i dati di GA4 aggiornati nel tempo.
Sarà necessario aggiungere tra le librerie importate os, che fornisce una maniera di interfacciarsi con il sistema operativo, e shutil, che offre funzioni per la gestione di operazioni ad alto livello sui file e sulle directory, utilizzando questo script:
<code>
import os, shutil
from datetime import datetime
</code>
Subito dopo le righe di importazione delle librerie, andrà aggiunto lo script seguente:
<code>
if os.path.exists(‘ProductDataNew.csv’):
os.rename(‘ProductDataNew.csv’, ‘ProductDataOld.csv’)
if os.path.exists(‘products.json’):
os.remove(‘products.json’)
</code>
Lo script cerca se nella stessa cartella in cui è presente il file .py è presente il file “ProductData.csv”, e se è presente lo rinomina in “ProductDataOld.csv”. Controlla anche se è presente un file “products.json” e in caso affermativo lo elimina. Questo è per pulire la cartella dai file che possono essere rimasti da una precedente esecuzione dell’applicazione.
Un altro script da aggiungere subito dopo la creazione della pulizia dei dati e della creazione del file CSV è il seguente:
<code>
if os.path.exists(‘ProductData.csv’) and os.path.exists(‘ProductDataOld.csv’):
dfCurr=pd.read_csv(‘ProductData.csv’)
dfPrev=pd.read_csv(‘ProductDataOld.csv’)
dfPrevRowCount=len(dfPrev)
dfMerge=pd.concat([dfPrev, dfCurr])
dfMerge.drop_duplicates(subset=[‘sku’], keep=’first’, inplace=True)
dfMergeRowCount=len(dfMerge)
numAddedProducts=dfMergeRowCount-dfPrevRowCount
dfMerge.to_csv(‘ProductDataNew.csv’,index=False)
if numAddedProducts>0:
print(‘Sono stati trovati e aggiunti al file ‘+str(numAddedProducts)+’ nuovi articoli.’)
lastProductsAdded=dfMerge.tail(numAddedProducts)
print(lastProductsAdded)
print(‘Procedere al caricamento su GA4 del file “ProductDataNew.csv”.’)
else:
print(‘Non sono stati trovati nuovi articoli. Non è necessario caricare su GA4 il file “ProductDataNew.csv”.’)
os.remove(‘ProductDataOld.csv’)
os.remove(‘ProductData.csv’)
if os.path.exists(‘NomeSito_Storico-Dati-Prodotti/’) == False:
os.mkdir(‘NomeSito_Storico-Dati-Prodotti’)
shutil.copy2(‘./ProductDataNew.csv’, ‘./NomeSito_Storico-Dati-Prodotti’)
currentMonth=datetime.now().month
currentYear=datetime.now().year
currentDay=datetime.now().day
os.rename(‘NomeSito_Storico-Dati-Prodotti/ProductDataNew.csv’,’NomeSito_Storico-Dati-Prodotti/NomeSito-ProductInfo_’+str(currentYear)+’-‘+str(currentMonth)+’-‘+str(currentDay)+’.csv’)
</code>
Quest’ultimo script fa in modo che, se è presente un file generato da un precedente avvio dell’applicazione (“ProductDataOld.csv”), questo venga unito al nuovo CSV appena creato per creare un terzo file, in cui vengono eliminati i valori duplicati e in cui viene verificato se ci sono state delle aggiunte. In caso affermativo verrà notificato, altrimenti un messaggio avviserà che non è necessario caricare il nuovo file CSV su GA4. Questo poi verrà copiato anche in una cartella, chiamata “NomeSito_Storico-Dati-Prodotti”, e nel nome conterrà i valori di giorno, mese e anno di creazione, in modo da avere un backup nel caso in cui dovessero sorgere problemi in futuro.
In questo modo si potrà attivare lo script periodicamente in modo da aggiornare la lista dei prodotti nel corso del tempo, e quindi ottenere sempre dei report GA4 ordinati e di più facile consultazione, anche dopo l’introduzione di nuovi prodotti all’interno dell’e-commerce in esame.