Leggere, interrogare e trasformare file XML da riga di comando
Intro
Il comune di Palermo ha pubblicato i dati pubblici e aperti sulle elezioni comunali dell’11 giugno 2017 in formato XML
, con uno schema descritto in questo file.
Qui sotto ad esempio la struttura di uno degli oltre 200 file pubblicati.
L’XML
è uno dei formati classici di pubblicazione di dati aperti, ma non è un formato per tutti. Molti utenti infatti non sono in grado di esaminarli.
Per la sua natura è facilmente leggibile da un calcolatore e ci sono varie modalità per farlo.
E con un’utility specializzata (XMLStarlet) e con un piccolo comando come questo di sotto, è possibile trasformare questo file XML in una “piatta” tabella con tutti i dati sui voti dei candidati di una lista al consiglio comunale, per ogni sezione elettorale (40 candidati per 600 sezioni, quindi 24000 record).
Qui sotto lo vedete in azione:
I dati sui risultati delle liste elettorali hanno questa struttura:
SV
è la sezione elettorale con il suo numero identificativo (e altri attributi), che contiene al suo interno i dati su V0
che rappresenta la lista (in questo caso la 12, quella del “Movimento 5 stelle”), che contiene al suo interno V1
, ovvero i dati sui candidati al consiglio comunale. Con questa struttura gerarchica SV>V0>V1
.
Il comando di sopra nel dettaglio:
sel --net -t -m
, abilito la selezione (sel
) su un file remoto (--net
), impostando un template (-t
) per “mappare” gli elementi che corrispondono (“matchano”-m
) alla seguente query XPATH;"//SV/V0/V1"
, i candidati al consiglio;"@NUMERO"
, l’attributo con il numero identificativo del candidato;-o ","
, per inserire un separatore di testo;-v "@VOTIVALIDI_C1"
, l’attributo con i voti validi del candidato;-o ","
, per inserire un separatore di testo;-v "../@NUMERO"
, mi muovo verso l’alto nella gerarchia dell’XML
di un gradino con..
, quindi vado inV0
(la lista) e recupero l’identificativo numerico della lista;-o ","
, per inserire un separatore di testo;-v "../../@NUMERO"
, mi muovo verso l’alto nella gerarchia dell’XML
di due gradini con../..
, quindi vado inSV
(il seggio) e recupero l’identificativo numerico del seggio;-n
, per inserire un’andata a capo per ogni risultato ottenuto;- http://…/SEZ_3_82053_L12.xml è l’URL del file XML.
In output nella shell avrò:
Ovvero
numeroCandidato | voti | numeroLista | sezione |
---|---|---|---|
1 | 5 | 12 | 1 |
2 | 9 | 12 | 1 |
3 | 2 | 12 | 1 |
… | … | … | … |
Creare dei file CSV con i dati per tutte le liste
Ho pensato che possa essere molto interessante fare un esempio più ricco e completo e creare uno script bash
per:
- scaricare tutti i file
XML
delle 18 liste; - estrarre da ognuno l’anagrafica dei candidati consiglieri;
- estrarre da ognuno il numero di voti, per ogni sezione, di ogni candidato al consiglio;
- fare il join – unire – le info sul numero di voti, con l’anagrafica dei candidati consiglieri, e creare un file
CSV
per ogni lista; - unire tutti i file
CSV
e produrre anche un unico file con il numero di voti di ogni candidato, per ogni lista, per ogni sezione.
Requisiti
Il prodotto finale è uno script BASH
, quindi bisogna avere a disposizione un sistema compatibile con questo linguaggio (lo sono essenzialmente tutti).
Richiede tre utility:
- l’immancabile cURL, per scaricare i file;
- XMLStarlet per interrogare i file
XML
e trasformarli in fileCSV
; - csvkit per fare il join e il merge dei
CSV
scaricati.
Richiede una conoscenza di base (e/o la volontà/possibità di farserla) su:
- XPATH, per estrarre i dati (per interrogare) i file XML;
- la linea di comando, perché è un po’ il campo di gioco di queste modalità di accesso e modifica di file;
- BASH, che è il linguaggio dello script finale;
- aprire i file XML del comune con un buon editor di testo, guardarli un po’ e comprenderne la struttura.
Lo script
Lo script per intero è più in basso. A seguire un esploso delle varie parti che lo compongono.
La prima cosa che viene eseguita nello script è il download dei file delle liste. Queste sono 18 ed è comodo scaricarle con un ciclo for ... loop
che lo fa 18 volte per noi.
Poi da ognuno dei 18 file XML
vengono estratti i dati anagrafici e i dati per sezione, sempre con un ciclo for
.
A ogni file viene aggiunta anche un’intestazione di colonne.
I file di anagrafica hanno questa struttura:
numeroCandidato | nomeCandidato | numeroLista | nomeLista |
---|---|---|---|
1 | GELARDA IGOR DETTO GERARDA DETTO GERALDA | 12 | MOVIMENTO 5 STELLE |
2 | ARGIROFFI GIULIA | 12 | MOVIMENTO 5 STELLE |
3 | CAPARROTTA GIANCARLO DETTO CAPAROTTA | 12 | MOVIMENTO 5 STELLE |
Mentre quelli con i dati per sezione:
numeroCandidato | voti | numeroLista | sezione |
---|---|---|---|
1 | 5 | 12 | 1 |
2 | 9 | 12 | 1 |
3 | 2 | 12 | 1 |
Poi viene fatto il join tra anagrafica e dati per sezione:
Per ogni lista viene prodotto un file con nome lista_NumeroLista.csv
, con questa struttura (ci sono delle colonne duplicate, che potrei rimuovere in fase di join):
numeroCandidato | voti | numeroLista | sezione | numeroCandidato | nomeCandidato | numeroLista | nomeLista |
---|---|---|---|---|---|---|---|
1 | 5 | 12 | 1 | 1 | GELARDA IGOR DETTO GERARDA DETTO GERALDA | 12 | MOVIMENTO 5 STELLE |
2 | 9 | 12 | 1 | 2 | ARGIROFFI GIULIA | 12 | MOVIMENTO 5 STELLE |
3 | 2 | 12 | 1 | 3 | CAPARROTTA GIANCARLO DETTO CAPAROTTA | 12 | MOVIMENTO 5 STELLE |
E infine viene creato anche un unico file CSV
di insieme (scaricabile da qui), con i dati per tutti i consiglieri di tutte le liste, per ogni sezione (senza le colonne duplicate). Sono 638 candidati per 600 sezioni per un totale di 382800 record.
Quindi avrò in output 1 file CSV
con i dati per ogni lista e quello soprastante, per totale di 19 file CSV
(encoding UTF-8
e come separatore la ,
).
Lo script di poche righe (al netto dei commenti) è quello di sotto, tutto realizzato con oggetti free e open-source.
Libro consigliato
Per entrare nel mondo divertentissimo ed efficiente della “riga di comando” mi sento di consigliare il bel “Data Science at the Command Line” . È un libro per tutti, di facile lettura e pieno di esempi utili.