«[Figlio dell'uomo] Porgi l'orecchio e ascolta le parole di KGB
e applica la tua mente alla SUA istruzione
» Pv. 22,17

Qui si straparla di vari argomenti:
1. Il genere dei pezzi è segnalato da varie immagini, vedi Legenda
2. Per contattarmi e istruzioni per i nuovi lettori (occasionali e non) qui
3. L'ultimo corto è questo
4. Molti articoli di questo blog fanno riferimento a definizioni e concetti che ho enunciato nella mia Epitome gratuitamente scaricabile QUI. Tali riferimenti sono identificati da una “E” fra parentesi quadre e uno o più capitoli. Per esempio: ([E] 5.1 e 5.4)

venerdì 22 febbraio 2019

Esperimento effettivo

Come anticipato in Introduzione esperimento oggi voglio raccontare passo passo i miei sforzi orientati sull’obiettivo finale di ottenere una rete neurale che scriva testi basati sul modello dei miei pezzi.

Il primo passo che devo portare a termine per raggiungere tale obiettivo è quello di acquisire in Python i miei testi.
Fortunatamente come amministratore del mio ghiribizzo posso ottenere automaticamente in un unico archivio in formato XML tutto quanto ho pubblicato.
Dovrò quindi estrarre dall’archivio XML solo i dati, ovvero i testi, che mi interessano.
Gli approcci possibili che mi vengono in mente sono due: il primo, forse inizialmente più rapido e semplice è quello si scorrere l’archivio XML come fosse un unico testo, riconoscere i marcatori che individuano i miei pezzi ed estrarli. Il secondo approccio è quello di “parserizzare” opportunamente tale archivio usando una qualche apposita libreria che non conosco ma che di sicuro esisterà.
Questo secondo metodo, forse inizialmente più complicato, ha però il vantaggio di darmi poi, se necessario, maggiore flessibilità: ad esempio potrei decidere di eliminare dall’allenamento della rete neurale i pezzi marcati come “Corti” oppure “Poesia”, o magari basarmi sulla data di creazione…
Inoltre mi pare più educativo anche per i miei lettori usare questo approccio… Osinga invece userebbe il primo, compattando in due linee di codice quello per cui a me ne serviranno venti!

Vabbè… adesso mi scarico l’archivio…
Ecco, ora ho un archivio XML di 20,7Mb che mi copio (rinominandolo “TestiKGB.xml”) in una cartella di lavoro.
E ora mi cerco la libreria che faccia al caso mio: la mia paura è che ci sia troppa scelta o che trovi qualcosa complicatissimo da usare… vabbè, vedremo…

Cerco su Google “Python XML”.
Il mio occhio è stato attirato da: How do I parse XML in Python?

Problema inatteso: mentre sfogliavo l’articolo precedente ho deciso di dare un’occhiata alla struttura del mio archivio XML. E ho problemi ad aprirlo perché è troppo grosso!
Di solito apro i back-up del mio ghiribizzo usando Chrome che però mi nasconde la struttura sottostante dell’XML che invece è quella che mi interessa adesso.
Ho provato quindi a usare un semplice editore di testi (pluma) ma l’archivio è troppo grande è il programma non risponde più…
Ora cercherò di usare qualche altro programma oppure mi scaricherò un editore per XML..

Ok. Cerco su Google “XML editor”.
In XML editor for linux ho trovato un accenno a usare “less”: si tratta di un comando da CLI che permette di scorrere testi fornito di sistema.
Beh, è un incubo perché l’archivio XML non ha ritorni a capo e mi riempie quindi l’intero schermo senza soluzione di continuità…
Comunque sforzandomi gli occhi ho capito che quello che mi interessa sono i nodi “content” dentro i nodi “entry” (il problema è che ci sono molti altri metadati e che non è facile leggere il contenuto dell’archivio con less proprio a livello ottico).

Torno alle librerie XML per Python…
Come immaginavo ci sono varie possibilità. Scelgo quella con l’esempio che usa “minidom” (mi rassicura il prefisso “mini”!).
Copio modificando e commentando il frammento di esempio fornito:
1:  from xml.dom import minidom  
2:  xmldoc = minidom.parse('TestiKGB.xml')  
3:  itemlist = xmldoc.getElementsByTagName('entry')  
4:  print(len(itemlist))  
5:  #print(itemlist[0].attributes['name'].value)  
6:  #for s in itemlist:  
7:    #print(s.attributes['name'].value)  

Con mio stupore, dopo pochi secondi, mi viene stampato il risultato 3005 che dovrebbe corrispondere al numero totale dei miei pezzi.
Ora controllo…
No (sigh!) i miei pezzi sono 2405 comprese 6 bozze non pubblicate. Il problema sicuramente è che il nome del nodo “entry” non è usato solo per identificare i miei articoli ma anche altre entità. Così come i nodi “content” sono usati per il testo vero e proprio e per una miriade di altre cose.
Adesso devo scorrere dolorosamente (per i miei occhi) l’archivio XML e trovare come si chiama il nodo che contiene tutti i nodi “entry” del ghiribizzo. Poi dovrò vedere di capire come usare minidom per scorrere solo il contenuto di un nodo: ma facciamo un passo per volta…

Ora vado a mangiare!

Contrordine: credo che sia più utile scoprire come leggere il padre di un nodo usando minidom e poi filtrare in base ad esso.
Cerco su Google “Python minidom parent”.
Primo risultato: xml python parsing get parent node name : minidom.

Spizzico dall’esempio il frammento di codice che mi interessa…
Bene: print(listItem[0].parentNode.nodeName) stampa “feed”.
Adesso voglio però trovare l’elenco completo di tutti i nodi genitori dei 3005 nodi “entry” in maniera da capire quali filtrare via.
Potrei semplicemente stamparli tutti e, manualmente, identificare come si chiamano i nodi genitori dei nodi “entry” riferiti ai pezzi del ghiribizzo.
A scopo educativo voglio però usare un dizionario Python che mi indichi per ciascun nodo genitore il numero delle sue occorrenze. Ovviamente dei dizionari Python non ricordo niente se non che esistono, quindi: cerco “python dictionary keys”

Trovo How to use dictionaries in Python.
Aggiorno il mio programmino in questa maniera:
1:  from xml.dom import minidom  
2:  xmldoc = minidom.parse('TestiKGB.xml')  
3:  itemlist = xmldoc.getElementsByTagName('entry')  
4:  print(len(itemlist))  
5:  print(itemlist[0].parentNode.nodeName)  
6:  #print(itemlist[0].attributes['name'].value)  
7:  dizio={}  
8:  for s in itemlist:  
9:    dizio[s.parentNode.nodeName]=dizio[s.parentNode.nodeName]+1  
10:    #print(s.attributes['name'].value)  
11:  print(dizio)  

Ma…
L’istruzione 9 mi dà l’errore «The debugged program raised the exception unhandled KeyError
"'feed'"». Evidentemente la mia sintassi è sbagliata.
Come al solito cerco su Google: “Python unhandled KeyError”
Trovo: Wiky.Python.org KeyError

In pratica fallisce il membro destro dell’assegnamento: dizio[s.parentNode.nodeName] non è definito mentre io speravo che ritornasse 0. Vabbè, devo solo scoprire come testare se una chiave è già presente nel dizionario…

ah! è facile: era già spiegato in How to use dictionaries in Python.
Aggiorno opportunamente il mio codice…
1:  from xml.dom import minidom  
2:  xmldoc = minidom.parse('TestiKGB.xml')  
3:  itemlist = xmldoc.getElementsByTagName('entry')  
4:  print(len(itemlist))  
5:  print(itemlist[0].parentNode.nodeName)  
6:  #print(itemlist[0].attributes['name'].value)  
7:  dizio={}  
8:  for s in itemlist:  
9:    if(s.parentNode.nodeName in dizio):  
10:      dizio[s.parentNode.nodeName]=dizio[s.parentNode.nodeName]+1  
11:    else:  
12:      dizio[s.parentNode.nodeName]=1  
13:    #print(s.attributes['name'].value)  
14:  print(dizio)  

Funziona!
Ma il risultato è un frustrante: {'feed': 3005}
Cioè tutti i nodi “entry” hanno un nodo genitore che si chiama “feed” mentre io speravo in qualcosa di diverso che mi permettesse di distinguire gli “entry” che contengono gli articoli del mio ghiribizzo dalle altre entità…
Ora, al volo provo a vedere il nome del “nonno” (s.parentNode.parentNode.nodeName) altrimenti devo riscorrere l’archivio XML e vedere se l’informazione che mi interessa è nascosta in qualche tag interno…

Sfortunatamente il tentativo “al volo” sui “nonni” mi ha restituito 3005 “#document” che suona un po’ come qualcosa di convenzionale per indicare che sono già al livello base dell’archivio XML.

Uffa… Mi tocca ritornare a sbirciare l’archivio XML…
Sbirciando la struttura dell’archivio ho scoperto che all’interno di un nodo “entry” ci sono vari nodi “Category” che contengono svariate informazioni all’attributo “term” come, ad esempio, i marcatori dell’articolo e se il relativo nodo “content” sia un pezzo o no.
Quindi: adesso devo scoprire come trovare i figli di un nodo in minidom e leggerne gli attributi…
Cerco su Google: “Python minidom child”

...e leggo Python minidom: list childnode attributes per parent tag
Trovo le parole chiave che mi interessano: childNodes e getAttribute('id')

Quindi rimodifico il mio codice per ottenere un dizionario con tutti gli attributi “term” dei figli “category” di ogni nodo “entry”. Il risultato dovrebbe essere interessante.
1:  from xml.dom import minidom  
2:  xmldoc = minidom.parse('TestiKGB.xml')  
3:  itemlist = xmldoc.getElementsByTagName('entry')  
4:  print(len(itemlist))  
5:  #print(itemlist[0].parentNode.nodeName)  
6:  #print(itemlist[0].attributes['name'].value)  
7:  dizio={}  
8:  for s in itemlist:  
9:    for c in s.childnodes:  
10:      if(c.nodeName=="category"):  
11:        if(c.getAttribute("term") in dizio):  
12:          dizio[c.getAttribute("term")]=dizio[c.getAttribute("term")]+1  
13:        else:  
14:          dizio[c.getAttribute("term")]=1  
15:  print(dizio)  

Come temevo ho un nuovo errore ma non del genere che mi aspettavo:
«The debugged program raised the exception unhandled AttributeError "'Element' object has no attribute 'childnodes'"»
Cioè se ha un genitore perché non ha figli?

Googlo “Python minidom Element object child”
Leggo: docs.python.org  Element Objects
L’oggetto Element è una sottoclasse di Node e quindi dovrebbe averne tutti gli attributi/metodi. Vado a controllare la documentazione per Node e childNodes è presente…
Uhm… ho capito: avevo sbagliato a scrivere, mi ero dimenticato la “N” maiuscola in “chilnodes” alla linea 9!
Corretto l’errore ottengo questo risultato (ne riporto solo la parte iniziale):
3005
{'http://schemas.google.com/blogger/2008/kind#template': 1, 'http://schemas.google.com/blogger/2008/kind#settings': 57, 'esperimento': 10, 'http://schemas.google.com/blogger/2008/kind#post': 2405, 'Python': 5, 'reti neurali': 5, 'codice': 21, 'problema': 97, 'scienza': 11, 'libro': 259, 'epitome': 88, 'amore': 34, 'amico': 52, 'aneddoto': 57, 'storia': 87, 'frasi': 139,


Dove 3005 è il numero di nodi “Entry” mentre, fra parentesi graffe, è riportato il contenuto dell’attributo “term” dei vari nodi “category” figli dei diversi “Entry”.
Come previsto vi sono le occorrenze dei vari marcatori e, soprattutto, esattamente 2405 (BINGO!), nodi “Category” con attributo “term” uguale a “http://schemas.google.com/blogger/2008/kind#post”.
Quindi adesso posso trovare facilmente i nodi “Content” contenenti il testo dei miei articoli “facilmente”.
1. carico tutti i nodi “Entry”
2. scarto tutti i nodi “Entry” che NON abbiano un figlio “Category” con attributo “term” pari a “http://schemas.google.com/blogger/2008/kind#post” (*1)
3. Dei nodi Entry rimasti mi salvo in un archivio di testo il contenuto del nodo figlio “content”.

Poi questo nuovo archivio andrà ulteriormente elaborato eliminando o sostituendo le varie entità HTML che non ci interessano. Quella sarà probabilmente l’opportunità per usare le espressioni regolari in Python…

Ma per adesso, siccome devo uscire, ne approfitto per smettere qui…

Conclusione: bo… ancora devo decidere se è valsa la pena di fare questo doppio lavoro… però dà una buona idea del mestiere dell’informatico: ovviamente io sono molto scarso, qualcuno più esperto di me avrebbe saputo la maggior parte delle informazioni senza doverle cercare tutte su Google… ma, insomma, qualche ricerca l’avrebbero fatta tutti, forse eccetto Osinga che, invece delle mie 15, avrebbe usato solo 2 o 3 linee di codice!

Nota (*1): volendo qui potrei anche facilmente eliminare tutti i pezzi con marcatore “Corto” o “Poesia”...

Nessun commento:

Posta un commento