La verità è che, nonostante tutto, la politica italiana mi manca. E allora, in quanto studente di social data science espatriato, ho trovato un modo per interessarmene anche da lontano: contribuire, nel mio piccolo, a renderla più aperta. La trasparenza nella cosa pubblica, come ci insegnano i membri di OpenDataSicilia, è fondamentale per una governance efficace nel ventunesimo secolo. Il mio ultimo progetto cerca di rendere un minimo più accessibile un elemento della politica italiana di cui non si parla molto, magari non nobile quanto altre tipologie di dati ma comunque importante per comprendere certe dinamiche politiche: le campagne pubblicitarie su Facebook.
Cosa sono le Facebook Ads
Si parla molto spesso, ormai dappertutto, sulle strategie social dei leader e politici in generale. Un po’ meno si parla dell’aspetto di marketing delle stesse: quando un politico fa un post dove parla di x, oltre a suscitare clamore o interesse cosi detto organico, ovvero scaturito da normali interazioni – condivisioni, likes, commenti – degli utenti, lo stesso post potrebbe venire promosso dal comitato elettorale – tramite un pagamento al social media in questione – per essere mostrato a più persone potenzialmente interessate possibili, budget permettendo. O ancora, contenuti possono essere creati ad hoc a questo scopo per attirare interesse verso il politico o il partito in questione, per chiedere donazioni agli elettori o anche solo cercare di aumentare il numero di follower della propria pagina politica.
Facebook permette di creare ads per molti scopi pubblicitari diversi, tuttavia, a seguito dei fatti del 2016 che coinvolgevano quell’azienda che fa rima con psicanalitica, ha deciso di rendere pubbliche tutte le ad con contenuto politico a partire dal 2019. Per questa ragione, tutte le pubblicità politiche italiane mai finanziate su Fb negli ultimi anni sono consultabili sulla Facebook Ad Library.
Nelle ultime settimane, per via della campagna elettorale imperversante, mi sono deciso a implementare un idea che avevo da tempo: un bot che accedesse a questo archivio, ottenesse le ad create da o inerenti i principali partiti e leader politici italiani e ne estrapolasse le più importanti della giornata per poi twittarle automaticamente.
Come si traggono dati dalla Facebook Ad library
Il mio bot è composto quindi da uno script in python che fa quanto descritto: ogni sera viene avviato da un comando cron e inizia a analizzare le ad della giornata. Facebook, almeno in apparenza, cerca di essere trasparente, autorizzando l’uso di ampi datasets di post a scopo di ricerca e divulgazione. Tuttavia l’accesso non è totalmente libero: mentre le ad possono essere consultate liberamente dall’archivio, per accedere all’API sulle stesse è necessario completare diversi step burocratici, sempre per via di quei fatti dell’azienda che fa rima con psicanalitica. Bisogna purtroppo avere necessariamente un account FB, accedere a FB for developers, e, come requisito in più per accedere a dati politicamente sensibili, fornire un documento di identità come prova di essere residente in un paese le cui ad politiche sono parte dell’archivio. Una volta venduta l’anima al diavolo (Facebook giura che non venderà mai le informazioni dei documenti di identità forniti – se crederci o no è lasciato come esercizio al lettore), per scaricare grandi quantità di dati dall’archivio Facebook mette a disposizione un tool a riga di comando open source (qui la repository). Purtroppo la documentazione di questo strumento manca all’appello dal 2020, tuttavia leggendo la sorgente in python è possibile capire che cosa ci si può fare. Lo si può utilizzare per raccogliere le ad pubblicate in un certo periodo in un certo paese per poi salvarle in formato csv, ma bisogna sempre inserire una parola chiave per la ricerca, visto che è impossibile chiederle semplicemente di averle tutte. Oltre alla query e al timeframe, si può chiedere al tool quali informazioni richiedere a proposito delle ad raccolte. La lista delle informazioni che si possono chiedere è disponibile qui. Le più importanti sono sicuramente il nome dell’entità che ha finanziato il post (chiamato dall’api bylines), il link dell’ad, la spesa e le impressions. Le impressions sono un parametro che serve a esaminare l’andamento di un contenuto online: grosso modo indicano quante volte l’ad è apparsa davanti agli occhi di qualcuno.
Per il mio bot, ho semplicemente impostato di effettuare varie query con i nomi di tutti i partiti e leader. Successivamente, lo script apre tutti i file csv così ottenuti e li unisce in un singolo dataframe. Ovviamente si tratta di un dataframe pandas, la più importante library open source per Python nell’ambito della data science
import pandas as pd
import glob
import os
files = glob.glob("adslist/*.csv")
df = pd.concat(map(pd.read_csv, files), ignore_index=True).drop_duplicates()
Questo codice semplicemente trova tutti i csv nella cartella adslist – dove il tool di Fb salva i risultati delle query – per poi ricavarne un unico dataframe, eliminando i doppioni.
Tuttavia, l’API ritorna la colonna della spesa (spend) e quella delle impressions (impressions) in una maniera non pulita: visto che si tratta di una stima, nella stessa colonna vengono riportati sia il dato minimo che quello massimo, nel formato {lower_bound: 900, upper_bound: 999}
.
Per questo le righe successive dividono la colonna in due, estraendo il dato numerico dalla parte testuale.
df[["min_spend","max_spend"]]=df["spend"].str.split(',', expand=True)
df[["min_impressions", "max_impressions"]]=df["impressions"].str.split(',', expand=True)
df=df.drop(["impressions","spend"], axis=1)
df[["min_spend", "max_spend", "min_impressions", "max_impressions"]]= df[["min_spend", "max_spend", "min_impressions", "max_impressions"]].apply(
lambda x: x.str.extract('(\d+)', expand=False).fillna(0).astype("int")
)
Una volta fatto questo, basterà filtrare le ads per entità della colonna di spesa o di impressioni massime/minime. Al momento il bot seleziona le ad con un minimo di almeno 10000 visualizzazioni.
Un immagine vale più di mille parole
A questo punto, tutti i dati sono disponibili per essere twittati. Tuttavia, ho ritenuto che l’impatto visivo dell’ad e il suo contenuto fossero importanti quanto, se non più, dei puri dati della spesa e del reach , per questo ho deciso che il bot avrebbe dovuto mostrare nel tweet anche un immagine dell’inserzione. Senonché l’api non fornisce nessun immagine o link ad essa. Fornisce però un link per vedere come ogni ad sarebbe mostrata ad un utente. Ho deciso quindi di scrivere uno script per aprire ogni ad e effettuarne uno screenshot. Per farlo ho utilizzato Selenium, un progetto open source inizialmente concepito per effettuare testing automatizzati di siti internet.
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.chrome.options import Options
from selenium import webdriver
import uuid
import time
options = Options()
options.add_argument('--headless')
options.add_argument("window-size=1920,1080")
driver=webdriver.Chrome(options=options)
df["filename"]=None
for i in range(len(high_spend)):
df["filename"].iloc[i] = "ads/{}.png".format(str(uuid.uuid4()))
driver.get(df.ad_snapshot_url.iloc[i])
time.sleep(5)
ad=driver.find_element(By.CLASS_NAME, "_8n-d")
driver.execute_script('arguments[0].scrollIntoView({block: "center"});', ad)
ad.screenshot(df["filename"].iloc[i])
Questa è forse la parte più complessa del bot e sicuramente quella che ha richiesto più tempo per essere implementata. Di base, lo script apre un vero browser (in questo caso Chrome – cioè in realtà chromium – ma funzionerebbe benissimo anche con Firefox) in modalità headless – in maniera invisibile, ovvero senza aprire la parte grafica vera e propria – per poi, per ogni ad, andare all’url della stessa, cercare di metterla bene in vista, fare uno screen e salvarlo col nome di una stringa casuale.
Una volta fatto questo, lo script twitta gli screen correlati dalle stime massime di impressions e spesa delle ad selezionate, insieme al nome della pagina committente. Per interfacciarsi con l’API di Twitter lo script utilizza Tweepy, principale library open source di python per questo scopo. Anche in questo caso, per farlo è necessario ottenere l’accesso come sviluppatore. Io fortunatamente già ne avevo fatto richiesta in passato per un altro mio progetto.
Considerazioni finali
Per quanto i dati che il bot mostrerà non rappresentano un quadro completo delle campagne elettorali sui social, ma solo uno spaccato sulle piattaforme Meta, ne possono essere tratte comunque conclusioni interessanti.
Una di queste riguarda il discorso del silenzio elettorale. Per legge, il giorno prima delle elezioni è vietato fare propaganda elettorale tramite comizi, affissione di manifesti o messaggi televisivi. Tuttavia la normativa è vecchia e non è stata ancora approvata una modifica che estenda il divieto anche alle piattaforme digitali. L’Agcom però sostiene debba essere rispettato comunque, anche sui social.
Se il bot avrà materiale da postare il 24 settembre, quindi, evidenzierà violazioni delle linee guida dell’Agcom, anche se tecnicamente esse non dovrebbero costituire atti illegali. Ancora più interessante sarà vedere se ci saranno post per il 25 settembre, il che implicherebbe che qualcuno stia facendo innegabilmente campagna elettorale mentre i seggi sono aperti. Ho ragione di pensare che accadrà, poiché ho osservato questo fenomeno durante le elezioni amministrative del giugno scorso, quando alcuni candidati alle comunali hanno intrapreso ad campaigns perfino il giorno dell’apertura dei seggi. Non vi resta che seguire Political_ads_bot.
In ogni modo, ringrazio la community di Open Data Sicilia e Andrea Borruso per le loro immense disponibilità e supporto. Per chi volesse fornirmi suggerimenti o lamentele su questo progetto scriva liberamente al mio account twitter personale.
Per completezza, anche Google – che controlla la più grande piattaforma pubblicitaria online del mondo – gestisce un archivio simile. In futuro sarebbe possibile integrare anche questi dati nel bot per riportare le ad che i politici comprano per i siti internet al di fuori dei social, tuttavia richiederebbe interfacciarsi con Google Cloud.