Corso
Esegui e modifica il codice da questo tutorial online
Esegui codiceCome data scientist, capire l'XML è molto utile sia per il web scraping che per esercitarsi a fare il parsing di documenti strutturati.
- Imparerai di più su XML e farai conoscenza con il pacchetto Python
ElementTree. - Scoprirai poi come esplorare gli alberi XML per capire meglio i dati con cui stai lavorando grazie alle funzioni di
ElementTree, ai for loop e alle espressioni XPath. - Successivamente, vedrai come modificare un file XML.
- Userai anche espressioni xpath per popolare file XML.
Che cos'è l'XML?
XML sta per "Extensible Markup Language". È usato principalmente nelle pagine web, dove i dati hanno una struttura specifica e vengono interpretati dinamicamente dal framework XML.
XML crea una struttura ad albero facile da interpretare e che supporta una gerarchia. Ogni volta che una pagina segue l'XML, può essere chiamata documento XML.
- I documenti XML hanno sezioni chiamate elementi, che sono definite da un tag di apertura e uno di chiusura. Un tag è una costruzione di markup che inizia con
<e termina con>. I caratteri tra il tag di apertura e quello di chiusura, se presenti, sono il contenuto dell'elemento. Gli elementi possono contenere markup, inclusi altri elementi, chiamati "elementi figli". - L'elemento più grande, di livello superiore, è chiamato root (radice) e contiene tutti gli altri elementi.
- Gli attributi sono coppie nome–valore che esistono all'interno di un tag di apertura o di un tag di elemento vuoto. Un attributo XML può avere un solo valore e ogni attributo può comparire al massimo una volta per ciascun elemento.
Per capirlo meglio, dai un'occhiata al seguente file XML (ridotto):
<?xml version="1.0"?>
<collection>
<genre category="Action">
<decade years="1980s">
<movie favorite="True" title="Indiana Jones: The raiders of the lost Ark">
<format multiple="No">DVD</format>
<year>1981</year>
<rating>PG</rating>
<description>
'Archaeologist and adventurer Indiana Jones
is hired by the U.S. government to find the Ark of the
Covenant before the Nazis.'
</description>
</movie>
<movie favorite="True" title="THE KARATE KID">
<format multiple="Yes">DVD,Online</format>
<year>1984</year>
<rating>PG</rating>
<description>None provided.</description>
</movie>
<movie favorite="False" title="Back 2 the Future">
<format multiple="False">Blu-ray</format>
<year>1985</year>
<rating>PG</rating>
<description>Marty McFly</description>
</movie>
</decade>
<decade years="1990s">
<movie favorite="False" title="X-Men">
<format multiple="Yes">dvd, digital</format>
<year>2000</year>
<rating>PG-13</rating>
<description>Two mutants come to a private academy for their kind whose resident superhero team must
oppose a terrorist organization with similar powers.</description>
</movie>
<movie favorite="True" title="Batman Returns">
<format multiple="No">VHS</format>
<year>1992</year>
<rating>PG13</rating>
<description>NA.</description>
</movie>
<movie favorite="False" title="Reservoir Dogs">
<format multiple="No">Online</format>
<year>1992</year>
<rating>R</rating>
<description>WhAtEvER I Want!!!?!</description>
</movie>
</decade>
</genre>
<genre category="Thriller">
<decade years="1970s">
<movie favorite="False" title="ALIEN">
<format multiple="Yes">DVD</format>
<year>1979</year>
<rating>R</rating>
<description>"""""""""</description>
</movie>
</decade>
<decade years="1980s">
<movie favorite="True" title="Ferris Bueller's Day Off">
<format multiple="No">DVD</format>
<year>1986</year>
<rating>PG13</rating>
<description>Funny movie about a funny guy</description>
</movie>
<movie favorite="FALSE" title="American Psycho">
<format multiple="No">blue-ray</format>
<year>2000</year>
<rating>Unrated</rating>
<description>psychopathic Bateman</description>
</movie>
</decade>
</genre>
Da quanto hai letto sopra, vedi che
<collection>è l'unico elemento root: contiene tutti gli altri elementi, come<genre>o<movie>, che sono gli elementi figli o sottoelementi. Come vedi, questi elementi sono annidati.
Nota che questi elementi figli possono anche fungere da genitori e contenere propri elementi figli, che vengono chiamati "sotto-figli".
- Vedrai che, ad esempio, l'elemento
<movie>contiene un paio di "attributi", comefavoritetitle, che forniscono ancora più informazioni!
Con questa breve introduzione ai file XML, sei pronto per saperne di più su ElementTree!
Introduzione a ElementTree
La struttura ad albero dell'XML rende relativamente semplice, a livello di codice, la navigazione, la modifica e la rimozione. Python ha una libreria integrata, ElementTree, che offre funzioni per leggere e manipolare XML (e altri file con struttura simile).
Per prima cosa, importa ElementTree. È pratica comune usare l'alias ET:
import xml.etree.ElementTree as ET
Parsing di dati XML
Il file XML fornito descrive una raccolta di film di base. L'unico problema è che i dati sono un disastro! Ci sono stati molti curatori diversi di questa collezione, e ognuno ha il proprio modo di inserire i dati nel file. L'obiettivo principale di questo tutorial sarà leggere e capire il file con Python e poi correggere i problemi.
Per prima cosa, devi leggere il file con ElementTree.
tree = ET.parse('movies.xml')
root = tree.getroot()
Ora che hai inizializzato l'albero, dovresti esaminare l'XML e stampare i valori per capire come è strutturato.
Ogni parte di un albero (root inclusa) ha un tag che descrive l'elemento. Inoltre, come hai visto nell'introduzione, gli elementi possono avere attributi, ovvero descrittori aggiuntivi usati soprattutto quando un tag si ripete. Gli attributi aiutano anche a convalidare i valori inseriti per quel tag, contribuendo ancora una volta al formato strutturato di un XML.
Vedrai più avanti in questo tutorial che gli attributi possono essere piuttosto potenti quando sono inclusi in un XML!
root.tag
'collection'
Al livello più alto, vedi che questo XML ha come root il tag collection.
root.attrib
{}
Quindi, la root non ha attributi.
For loop
Puoi iterare facilmente sui sottoelementi (comunemente chiamati "figli") della root usando un semplice for loop.
for child in root:
print(child.tag, child.attrib)
genre {'category': 'Action'}
genre {'category': 'Thriller'}
genre {'category': 'Comedy'}
Ora sai che i figli della root collection sono tutti genre. Per indicare il genere, l'XML usa l'attributo category. In base all'elemento genre, ci sono film Action, Thriller e Comedy.
Di solito è utile conoscere tutti gli elementi nell'intero albero. Una funzione utile per farlo è root.iter(). Puoi usare questa funzione in un for loop e scorrerà l'intero albero.
[elem.tag for elem in root.iter()]
['collection',
'genre',
'decade',
'movie',
'format',
'year',
'rating',
'description',
'movie',
'format',
'year',
'rating',
'description',
'movie',
'format',
'year',
'rating',
'description',
'decade',
'movie',
'format',
'year',
'rating',
'description',
'movie',
'format',
'year',
'rating',
'description',
'movie',
'format',
'year',
'rating',
'description',
'genre',
'decade',
'movie',
'format',
'year',
'rating',
'description',
'decade',
'movie',
'format',
'year',
'rating',
'description',
'movie',
'format',
'year',
'rating',
'description',
'genre',
'decade',
'movie',
'format',
'year',
'rating',
'description',
'decade',
'movie',
'format',
'year',
'rating',
'description',
'movie',
'format',
'year',
'rating',
'description',
'decade',
'movie',
'format',
'year',
'rating',
'description',
'decade',
'movie',
'format',
'year',
'rating',
'description']
Questo dà un'idea generale di quanti elementi hai, ma non mostra gli attributi o i livelli nell'albero.
C'è un modo utile per vedere l'intero documento. Qualsiasi elemento ha un metodo .tostring(). Se passi la root a .tostring(), puoi restituire l'intero documento. All'interno di ElementTree (ricorda l'alias ET), .tostring() ha una forma leggermente particolare.
Poiché ElementTree è una libreria potente che può interpretare più del solo XML, devi specificare sia la codifica che la decodifica del documento che stai visualizzando come stringa. Per gli XML, usa 'utf8' - è il formato tipico di un documento XML.
print(ET.tostring(root, encoding='utf8').decode('utf8'))
<?xml version='1.0' encoding='utf8'?>
<collection>
<genre category="Action">
<decade years="1980s">
<movie favorite="True" title="Indiana Jones: The raiders of the lost Ark">
<format multiple="No">DVD</format>
<year>1981</year>
<rating>PG</rating>
<description>
'Archaeologist and adventurer Indiana Jones
is hired by the U.S. government to find the Ark of the
Covenant before the Nazis.'
</description>
</movie>
<movie favorite="True" title="THE KARATE KID">
<format multiple="Yes">DVD,Online</format>
<year>1984</year>
<rating>PG</rating>
<description>None provided.</description>
</movie>
<movie favorite="False" title="Back 2 the Future">
<format multiple="False">Blu-ray</format>
<year>1985</year>
<rating>PG</rating>
<description>Marty McFly</description>
</movie>
</decade>
<decade years="1990s">
<movie favorite="False" title="X-Men">
<format multiple="Yes">dvd, digital</format>
<year>2000</year>
<rating>PG-13</rating>
<description>Two mutants come to a private academy for their kind whose resident superhero team must
oppose a terrorist organization with similar powers.</description>
</movie>
<movie favorite="True" title="Batman Returns">
<format multiple="No">VHS</format>
<year>1992</year>
<rating>PG13</rating>
<description>NA.</description>
</movie>
<movie favorite="False" title="Reservoir Dogs">
<format multiple="No">Online</format>
<year>1992</year>
<rating>R</rating>
<description>WhAtEvER I Want!!!?!</description>
</movie>
</decade>
</genre>
<genre category="Thriller">
<decade years="1970s">
<movie favorite="False" title="ALIEN">
<format multiple="Yes">DVD</format>
<year>1979</year>
<rating>R</rating>
<description>"""""""""</description>
</movie>
</decade>
<decade years="1980s">
<movie favorite="True" title="Ferris Bueller's Day Off">
<format multiple="No">DVD</format>
<year>1986</year>
<rating>PG13</rating>
<description>Funny movie about a funny guy</description>
</movie>
<movie favorite="FALSE" title="American Psycho">
<format multiple="No">blue-ray</format>
<year>2000</year>
<rating>Unrated</rating>
<description>psychopathic Bateman</description>
</movie>
</decade>
</genre>
<genre category="Comedy">
<decade years="1960s">
<movie favorite="False" title="Batman: The Movie">
<format multiple="Yes">DVD,VHS</format>
<year>1966</year>
<rating>PG</rating>
<description>What a joke!</description>
</movie>
</decade>
<decade years="2010s">
<movie favorite="True" title="Easy A">
<format multiple="No">DVD</format>
<year>2010</year>
<rating>PG--13</rating>
<description>Emma Stone = Hester Prynne</description>
</movie>
<movie favorite="True" title="Dinner for SCHMUCKS">
<format multiple="Yes">DVD,digital,Netflix</format>
<year>2011</year>
<rating>Unrated</rating>
<description>Tim (Rudd) is a rising executive
who “succeeds” in finding the perfect guest,
IRS employee Barry (Carell), for his boss’ monthly event,
a so-called “dinner for idiots,” which offers certain
advantages to the exec who shows up with the biggest buffoon.
</description>
</movie>
</decade>
<decade years="1980s">
<movie favorite="False" title="Ghostbusters">
<format multiple="No">Online,VHS</format>
<year>1984</year>
<rating>PG</rating>
<description>Who ya gonna call?</description>
</movie>
</decade>
<decade years="1990s">
<movie favorite="True" title="Robin Hood: Prince of Thieves">
<format multiple="No">Blu_Ray</format>
<year>1991</year>
<rating>Unknown</rating>
<description>Robin Hood slaying</description>
</movie>
</decade>
</genre>
</collection>
Puoi ampliare l'uso della funzione iter() per trovare elementi particolari di interesse. root.iter() elencherà tutti i sottoelementi sotto la root che corrispondono all'elemento specificato. Qui elencherai tutti gli attributi dell'elemento movie nell'albero:
for movie in root.iter('movie'):
print(movie.attrib)
{'favorite': 'True', 'title': 'Indiana Jones: The raiders of the lost Ark'}
{'favorite': 'True', 'title': 'THE KARATE KID'}
{'favorite': 'False', 'title': 'Back 2 the Future'}
{'favorite': 'False', 'title': 'X-Men'}
{'favorite': 'True', 'title': 'Batman Returns'}
{'favorite': 'False', 'title': 'Reservoir Dogs'}
{'favorite': 'False', 'title': 'ALIEN'}
{'favorite': 'True', 'title': "Ferris Bueller's Day Off"}
{'favorite': 'FALSE', 'title': 'American Psycho'}
{'favorite': 'False', 'title': 'Batman: The Movie'}
{'favorite': 'True', 'title': 'Easy A'}
{'favorite': 'True', 'title': 'Dinner for SCHMUCKS'}
{'favorite': 'False', 'title': 'Ghostbusters'}
{'favorite': 'True', 'title': 'Robin Hood: Prince of Thieves'}
Vedi già come i movies sono stati inseriti in modi diversi. Non preoccuparti per ora. Avrai modo di correggere uno degli errori più avanti nel tutorial.
Espressioni XPath
Spesso gli elementi non hanno attributi, ma solo contenuto testuale. Usando l'attributo .text, puoi stampare questo contenuto.
Ora, stampa tutte le descrizioni dei film.
for description in root.iter('description'):
print(description.text)
'Archaeologist and adventurer Indiana Jones
is hired by the U.S. government to find the Ark of the
Covenant before the Nazis.'
None provided.
Marty McFly
Two mutants come to a private academy for their kind whose resident superhero team must
oppose a terrorist organization with similar powers.
NA.
WhAtEvER I Want!!!?!
"""""""""
Funny movie about a funny guy
psychopathic Bateman
What a joke!
Emma Stone = Hester Prynne
Tim (Rudd) is a rising executive
who “succeeds” in finding the perfect guest,
IRS employee Barry (Carell), for his boss’ monthly event,
a so-called “dinner for idiots,” which offers certain
advantages to the exec who shows up with the biggest buffoon.
Who ya gonna call?
Robin Hood slaying
Stampare l'XML è utile, ma XPath è un linguaggio di query usato per cercare in un XML in modo rapido e semplice. XPath sta per XML Path Language e usa, come suggerisce il nome, una sintassi "simile a un percorso" per identificare e navigare i nodi in un documento XML.
Capire XPath è fondamentale per scansionare e popolare XML. ElementTree ha una funzione .findall() che attraversa i figli immediati dell'elemento di riferimento. Puoi usare espressioni XPath per specificare ricerche più utili.
Qui cercherai nell'albero i film usciti nel 1992:
for movie in root.findall("./genre/decade/movie/[year='1992']"):
print(movie.attrib)
{'favorite': 'True', 'title': 'Batman Returns'}
{'favorite': 'False', 'title': 'Reservoir Dogs'}
La funzione .findall() parte sempre dall'elemento specificato. Questo tipo di funzione è estremamente potente per i "trova e sostituisci". Puoi persino cercare sugli attributi!
Ora, stampa solo i film disponibili in più formati (un attributo).
for movie in root.findall("./genre/decade/movie/format/[@multiple='Yes']"):
print(movie.attrib)
{'multiple': 'Yes'}
{'multiple': 'Yes'}
{'multiple': 'Yes'}
{'multiple': 'Yes'}
{'multiple': 'Yes'}
Rifletti sul perché, in questo caso, la print restituisce i valori "Yes" di multiple. Pensa a come è definito il for loop. Potresti riscrivere questo loop per stampare invece i titoli dei film? Prova qui sotto:
Suggerimento: usa '...' dentro XPath per tornare all'elemento genitore di quello corrente.
for movie in root.findall("./genre/decade/movie/format[@multiple='Yes']..."):
print(movie.attrib)
{'favorite': 'True', 'title': 'THE KARATE KID'}
{'favorite': 'False', 'title': 'X-Men'}
{'favorite': 'False', 'title': 'ALIEN'}
{'favorite': 'False', 'title': 'Batman: The Movie'}
{'favorite': 'True', 'title': 'Dinner for SCHMUCKS'}
Modificare un XML
Prima i titoli dei film erano un vero caos. Ora stampali di nuovo:
for movie in root.iter('movie'):
print(movie.attrib)
{'favorite': 'True', 'title': 'Indiana Jones: The raiders of the lost Ark'}
{'favorite': 'True', 'title': 'THE KARATE KID'}
{'favorite': 'False', 'title': 'Back 2 the Future'}
{'favorite': 'False', 'title': 'X-Men'}
{'favorite': 'True', 'title': 'Batman Returns'}
{'favorite': 'False', 'title': 'Reservoir Dogs'}
{'favorite': 'False', 'title': 'ALIEN'}
{'favorite': 'True', 'title': "Ferris Bueller's Day Off"}
{'favorite': 'FALSE', 'title': 'American Psycho'}
{'favorite': 'False', 'title': 'Batman: The Movie'}
{'favorite': 'True', 'title': 'Easy A'}
{'favorite': 'True', 'title': 'Dinner for SCHMUCKS'}
{'favorite': 'False', 'title': 'Ghostbusters'}
{'favorite': 'True', 'title': 'Robin Hood: Prince of Thieves'}
Correggi il "2" in Back 2 the Future. Dovrebbe essere un problema di trova-e-sostituisci. Scrivi del codice per trovare il titolo 'Back 2 the Future' e salvalo in una variabile:
b2tf = root.find("./genre/decade/movie[@title='Back 2 the Future']")
print(b2tf)
<Element 'movie' at 0x10ce00ef8>
Nota che usare il metodo .find() restituisce un elemento dell'albero. Molto spesso è più utile modificare il contenuto all'interno di un elemento.
Modifica l'attributo title dell'elemento Back 2 the Future in "Back to the Future". Poi stampa gli attributi della variabile per vedere il cambiamento. Puoi farlo facilmente accedendo all'attributo di un elemento e assegnandogli un nuovo valore:
b2tf.attrib["title"] = "Back to the Future"
print(b2tf.attrib)
{'favorite': 'False', 'title': 'Back to the Future'}
Scrivi di nuovo le modifiche nell'XML in modo che siano corrette in modo permanente nel documento. Stampa di nuovo gli attributi dei film per assicurarti che le modifiche abbiano funzionato. Usa il metodo .write() per farlo:
tree.write("movies.xml")
tree = ET.parse('movies.xml')
root = tree.getroot()
for movie in root.iter('movie'):
print(movie.attrib)
{'favorite': 'True', 'title': 'Indiana Jones: The raiders of the lost Ark'}
{'favorite': 'True', 'title': 'THE KARATE KID'}
{'favorite': 'False', 'title': 'Back to the Future'}
{'favorite': 'False', 'title': 'X-Men'}
{'favorite': 'True', 'title': 'Batman Returns'}
{'favorite': 'False', 'title': 'Reservoir Dogs'}
{'favorite': 'False', 'title': 'ALIEN'}
{'favorite': 'True', 'title': "Ferris Bueller's Day Off"}
{'favorite': 'FALSE', 'title': 'American Psycho'}
{'favorite': 'False', 'title': 'Batman: The Movie'}
{'favorite': 'True', 'title': 'Easy A'}
{'favorite': 'True', 'title': 'Dinner for SCHMUCKS'}
{'favorite': 'False', 'title': 'Ghostbusters'}
{'favorite': 'True', 'title': 'Robin Hood: Prince of Thieves'}
Correggere gli attributi
L'attributo multiple è errato in alcuni punti. Usa ElementTree per correggere l'indicatore in base a quanti formati ha il film. Prima, stampa l'attributo e il testo di format per vedere cosa va sistemato.
for form in root.findall("./genre/decade/movie/format"):
print(form.attrib, form.text)
{'multiple': 'No'} DVD
{'multiple': 'Yes'} DVD,Online
{'multiple': 'False'} Blu-ray
{'multiple': 'Yes'} dvd, digital
{'multiple': 'No'} VHS
{'multiple': 'No'} Online
{'multiple': 'Yes'} DVD
{'multiple': 'No'} DVD
{'multiple': 'No'} blue-ray
{'multiple': 'Yes'} DVD,VHS
{'multiple': 'No'} DVD
{'multiple': 'Yes'} DVD,digital,Netflix
{'multiple': 'No'} Online,VHS
{'multiple': 'No'} Blu_Ray
C'è un po' di lavoro da fare su questo tag.
Puoi usare le regex per trovare le virgole: questo ti dirà se l'attributo multiple deve essere "Yes" o "No". Aggiungere e modificare attributi è semplice con il metodo .set().
Nota: re è l'interprete regex standard per Python. Se vuoi saperne di più sulle espressioni regolari, dai un'occhiata a questo tutorial.
import re
for form in root.findall("./genre/decade/movie/format"):
# Search for the commas in the format text
match = re.search(',',form.text)
if match:
form.set('multiple','Yes')
else:
form.set('multiple','No')
# Write out the tree to the file again
tree.write("movies.xml")
tree = ET.parse('movies.xml')
root = tree.getroot()
for form in root.findall("./genre/decade/movie/format"):
print(form.attrib, form.text)
{'multiple': 'No'} DVD
{'multiple': 'Yes'} DVD,Online
{'multiple': 'No'} Blu-ray
{'multiple': 'Yes'} dvd, digital
{'multiple': 'No'} VHS
{'multiple': 'No'} Online
{'multiple': 'No'} DVD
{'multiple': 'No'} DVD
{'multiple': 'No'} blue-ray
{'multiple': 'Yes'} DVD,VHS
{'multiple': 'No'} DVD
{'multiple': 'Yes'} DVD,digital,Netflix
{'multiple': 'Yes'} Online,VHS
{'multiple': 'No'} Blu_Ray
Spostare elementi
Alcuni dati sono stati inseriti nel decennio sbagliato. Usa ciò che hai imparato su XML e ElementTree per trovare e correggere gli errori sui decenni.
Sarà utile stampare sia i tag decade che i tag year in tutto il documento.
for decade in root.findall("./genre/decade"):
print(decade.attrib)
for year in decade.findall("./movie/year"):
print(year.text, '\n')
{'years': '1980s'}
1981
1984
1985
{'years': '1990s'}
2000
1992
1992
{'years': '1970s'}
1979
{'years': '1980s'}
1986
2000
{'years': '1960s'}
1966
{'years': '2010s'}
2010
2011
{'years': '1980s'}
1984
{'years': '1990s'}
1991
I due anni che sono nel decennio sbagliato sono i film degli anni 2000. Scopri quali film sono usando un'espressione XPath.
for movie in root.findall("./genre/decade/movie/[year='2000']"):
print(movie.attrib)
{'favorite': 'False', 'title': 'X-Men'}
{'favorite': 'FALSE', 'title': 'American Psycho'}
Devi aggiungere un nuovo tag decade, gli anni 2000, al genere Action per spostare i dati di X-Men. Il metodo .SubElement() può essere usato per aggiungere questo tag alla fine dell'XML.
action = root.find("./genre[@category='Action']")
new_dec = ET.SubElement(action, 'decade')
new_dec.attrib["years"] = '2000s'
print(ET.tostring(action, encoding='utf8').decode('utf8'))
<?xml version='1.0' encoding='utf8'?>
<genre category="Action">
<decade years="1980s">
<movie favorite="True" title="Indiana Jones: The raiders of the lost Ark">
<format multiple="No">DVD</format>
<year>1981</year>
<rating>PG</rating>
<description>
'Archaeologist and adventurer Indiana Jones
is hired by the U.S. government to find the Ark of the
Covenant before the Nazis.'
</description>
</movie>
<movie favorite="True" title="THE KARATE KID">
<format multiple="Yes">DVD,Online</format>
<year>1984</year>
<rating>PG</rating>
<description>None provided.</description>
</movie>
<movie favorite="False" title="Back to the Future">
<format multiple="No">Blu-ray</format>
<year>1985</year>
<rating>PG</rating>
<description>Marty McFly</description>
</movie>
</decade>
<decade years="1990s">
<movie favorite="False" title="X-Men">
<format multiple="Yes">dvd, digital</format>
<year>2000</year>
<rating>PG-13</rating>
<description>Two mutants come to a private academy for their kind whose resident superhero team must
oppose a terrorist organization with similar powers.</description>
</movie>
<movie favorite="True" title="Batman Returns">
<format multiple="No">VHS</format>
<year>1992</year>
<rating>PG13</rating>
<description>NA.</description>
</movie>
<movie favorite="False" title="Reservoir Dogs">
<format multiple="No">Online</format>
<year>1992</year>
<rating>R</rating>
<description>WhAtEvER I Want!!!?!</description>
</movie>
</decade>
<decade years="2000s" /></genre>
Ora aggiungi il film X-Men agli anni 2000 e rimuovilo dagli anni '90 usando rispettivamente .append() e .remove().
xmen = root.find("./genre/decade/movie[@title='X-Men']")
dec2000s = root.find("./genre[@category='Action']/decade[@years='2000s']")
dec2000s.append(xmen)
dec1990s = root.find("./genre[@category='Action']/decade[@years='1990s']")
dec1990s.remove(xmen)
print(ET.tostring(action, encoding='utf8').decode('utf8'))
<?xml version='1.0' encoding='utf8'?>
<genre category="Action">
<decade years="1980s">
<movie favorite="True" title="Indiana Jones: The raiders of the lost Ark">
<format multiple="No">DVD</format>
<year>1981</year>
<rating>PG</rating>
<description>
'Archaeologist and adventurer Indiana Jones
is hired by the U.S. government to find the Ark of the
Covenant before the Nazis.'
</description>
</movie>
<movie favorite="True" title="THE KARATE KID">
<format multiple="Yes">DVD,Online</format>
<year>1984</year>
<rating>PG</rating>
<description>None provided.</description>
</movie>
<movie favorite="False" title="Back to the Future">
<format multiple="No">Blu-ray</format>
<year>1985</year>
<rating>PG</rating>
<description>Marty McFly</description>
</movie>
</decade>
<decade years="1990s">
<movie favorite="True" title="Batman Returns">
<format multiple="No">VHS</format>
<year>1992</year>
<rating>PG13</rating>
<description>NA.</description>
</movie>
<movie favorite="False" title="Reservoir Dogs">
<format multiple="No">Online</format>
<year>1992</year>
<rating>R</rating>
<description>WhAtEvER I Want!!!?!</description>
</movie>
</decade>
<decade years="2000s"><movie favorite="False" title="X-Men">
<format multiple="Yes">dvd, digital</format>
<year>2000</year>
<rating>PG-13</rating>
<description>Two mutants come to a private academy for their kind whose resident superhero team must
oppose a terrorist organization with similar powers.</description>
</movie>
</decade></genre>
Creare documenti XML
Ottimo, sei riuscito a spostare un intero film in un nuovo decennio. Salva le modifiche nell'XML.
tree.write("movies.xml")
tree = ET.parse('movies.xml')
root = tree.getroot()
print(ET.tostring(root, encoding='utf8').decode('utf8'))
<?xml version='1.0' encoding='utf8'?>
<collection>
<genre category="Action">
<decade years="1980s">
<movie favorite="True" title="Indiana Jones: The raiders of the lost Ark">
<format multiple="No">DVD</format>
<year>1981</year>
<rating>PG</rating>
<description>
'Archaeologist and adventurer Indiana Jones
is hired by the U.S. government to find the Ark of the
Covenant before the Nazis.'
</description>
</movie>
<movie favorite="True" title="THE KARATE KID">
<format multiple="Yes">DVD,Online</format>
<year>1984</year>
<rating>PG</rating>
<description>None provided.</description>
</movie>
<movie favorite="False" title="Back to the Future">
<format multiple="No">Blu-ray</format>
<year>1985</year>
<rating>PG</rating>
<description>Marty McFly</description>
</movie>
</decade>
<decade years="1990s">
<movie favorite="True" title="Batman Returns">
<format multiple="No">VHS</format>
<year>1992</year>
<rating>PG13</rating>
<description>NA.</description>
</movie>
<movie favorite="False" title="Reservoir Dogs">
<format multiple="No">Online</format>
<year>1992</year>
<rating>R</rating>
<description>WhAtEvER I Want!!!?!</description>
</movie>
</decade>
<decade years="2000s"><movie favorite="False" title="X-Men">
<format multiple="Yes">dvd, digital</format>
<year>2000</year>
<rating>PG-13</rating>
<description>Two mutants come to a private academy for their kind whose resident superhero team must
oppose a terrorist organization with similar powers.</description>
</movie>
</decade></genre>
<genre category="Thriller">
<decade years="1970s">
<movie favorite="False" title="ALIEN">
<format multiple="No">DVD</format>
<year>1979</year>
<rating>R</rating>
<description>"""""""""</description>
</movie>
</decade>
<decade years="1980s">
<movie favorite="True" title="Ferris Bueller's Day Off">
<format multiple="No">DVD</format>
<year>1986</year>
<rating>PG13</rating>
<description>Funny movie about a funny guy</description>
</movie>
<movie favorite="FALSE" title="American Psycho">
<format multiple="No">blue-ray</format>
<year>2000</year>
<rating>Unrated</rating>
<description>psychopathic Bateman</description>
</movie>
</decade>
</genre>
<genre category="Comedy">
<decade years="1960s">
<movie favorite="False" title="Batman: The Movie">
<format multiple="Yes">DVD,VHS</format>
<year>1966</year>
<rating>PG</rating>
<description>What a joke!</description>
</movie>
</decade>
<decade years="2010s">
<movie favorite="True" title="Easy A">
<format multiple="No">DVD</format>
<year>2010</year>
<rating>PG--13</rating>
<description>Emma Stone = Hester Prynne</description>
</movie>
<movie favorite="True" title="Dinner for SCHMUCKS">
<format multiple="Yes">DVD,digital,Netflix</format>
<year>2011</year>
<rating>Unrated</rating>
<description>Tim (Rudd) is a rising executive
who “succeeds” in finding the perfect guest,
IRS employee Barry (Carell), for his boss’ monthly event,
a so-called “dinner for idiots,” which offers certain
advantages to the exec who shows up with the biggest buffoon.
</description>
</movie>
</decade>
<decade years="1980s">
<movie favorite="False" title="Ghostbusters">
<format multiple="Yes">Online,VHS</format>
<year>1984</year>
<rating>PG</rating>
<description>Who ya gonna call?</description>
</movie>
</decade>
<decade years="1990s">
<movie favorite="True" title="Robin Hood: Prince of Thieves">
<format multiple="No">Blu_Ray</format>
<year>1991</year>
<rating>Unknown</rating>
<description>Robin Hood slaying</description>
</movie>
</decade>
</genre>
</collection>
Novità in ElementTree
Ecco una panoramica delle nuove funzionalità e dei miglioramenti della libreria ElementTree nelle versioni più recenti di Python:
1. Supporto a XPath 1.0 (Python 3.8): A partire da Python 3.8, ElementTree include il supporto completo a XPath 1.0 con i metodi find() e findall(), consentendo query XML più ricche e complesse. Esempio:
# Finding all movies with a specific attribute using XPath
for movie in root.findall(".//movie[@favorite='True']"):
print(movie.attrib)
2. Miglioramenti ai namespace (Python 3.8+): Supporto avanzato per i namespace XML, che permette un'interazione più semplice con file XML che usano namespace con prefisso o predefiniti. Esempio:
# Register a namespace and find elements using it
ET.register_namespace('', 'http://example.com/namespace')
movies = root.findall(".//{http://example.com/namespace}movie")
3. Miglioramenti al parser (Python 3.9): Messaggi di errore di parsing più chiari che facilitano il debug di file XML malformati.
4. Nuova funzione indent() (Python 3.9): È stata aggiunta la funzione xml.etree.ElementTree.indent() per stampare in modo leggibile i documenti XML rientrando i loro elementi. Esempio:
ET.indent(root, space=" ", level=0)
ET.dump(root)
5. Parsing efficiente con iterparse (Python 3.10): Ottimizzato per l'efficienza in memoria, particolarmente utile con file XML di grandi dimensioni.
6. Documentazione ampliata (aggiornamenti continui): La documentazione Python di ElementTree è ora più completa, con best practice e casi d'uso avanzati.
Funzionalità deprecate in ElementTree e alternative
1. write() con xml_declaration in Python 3.8+: Il parametro xml_declaration del metodo write() è deprecato quando la codifica è impostata su 'unicode'.
- Alternativa: Usa
xml_declarationsolo quando la codifica è definita esplicitamente come diversa da'unicode'.
tree.write("output.xml", encoding="utf-8", xml_declaration=True)
2. parser HTML: Anche se non ufficialmente deprecato, l'uso di ElementTree per il parsing HTML è sconsigliato perché limitato nella gestione di HTML non ben formato.
- Alternativa: Usa librerie progettate specificamente per il parsing HTML, come
BeautifulSoupdel pacchettobs4.
from bs4 import BeautifulSoup soup = BeautifulSoup(html_content, 'html.parser')
3. Soluzioni alternative per la gestione dei namespace: I metodi più vecchi per gestire manualmente i namespace (ad esempio concatenare gli URI dei namespace con i tag degli elementi) sono meno consigliati con l'introduzione del supporto robusto ai namespace nelle versioni più recenti.
- Alternativa: Usa i metodi e le funzioni integrate e compatibili con i namespace.
ET.register_namespace('', 'http://example.com/namespace') movies = root.findall(".//{http://example.com/namespace}movie")
4. Pretty-printing manuale: Tecniche manuali per rientrare e formattare l'XML sono state superate dalla nuova funzione indent() (Python 3.9).
- Alternativa: Usa
ET.indent()per una formattazione automatica dell'XML.
ET.indent(root, space=" ")
5. Uso diretto di _ElementInterface: Classi interne come _ElementInterface non sono pensate per l'uso diretto e potrebbero cambiare in versioni future.
- Alternativa: Interagisci sempre con le API pubbliche documentate della libreria ElementTree.
Conclusione
Ci sono alcuni punti chiave da ricordare sugli XML e sull'uso di ElementTree.
I tag costruiscono la struttura ad albero e indicano quali valori devono essere delineati lì. Un'organizzazione intelligente può rendere facile leggere e scrivere un XML. I tag hanno sempre bisogno di parentesi di apertura e chiusura per mostrare le relazioni tra genitori e figli.
Gli attributi descrivono ulteriormente come convalidare un tag o consentono designazioni booleane. Gli attributi in genere assumono valori molto specifici, così che il parser XML (e l'utente) possano usare gli attributi per controllare i valori dei tag.
ElementTree è una libreria Python importante che ti permette di analizzare e navigare un documento XML. Usare ElementTree scompone il documento XML in una struttura ad albero facile da gestire. Nel dubbio, stampa tutto (print(ET.tostring(root, encoding='utf8').decode('utf8'))) - usa questa comoda istruzione di stampa per visualizzare l'intero documento XML in una volta sola. Aiuta a controllare quando modifichi, aggiungi o rimuovi da un XML.
Ora hai gli strumenti per capire l'XML e iniziare a fare parsing!

FAQ
Quali sono alcuni casi d'uso comuni di XML nel data science?
XML è spesso usato nel data science per lo scambio di dati tra sistemi, il web scraping, i file di configurazione e la gestione di dati con una struttura complessa e gerarchica. È particolarmente utile quando si lavora con API che restituiscono dati XML.
ElementTree può gestire in modo efficiente file XML di grandi dimensioni?
ElementTree è adatto alla gestione di file XML di dimensioni moderate, ma per file molto grandi potresti considerare librerie come lxml o xml.sax che sono più efficienti in memoria e possono gestire lo streaming di file di grandi dimensioni.
Come si confronta ElementTree con altre librerie di parsing XML come lxml o minidom?
ElementTree fa parte della libreria standard di Python ed è facile da usare per attività di parsing XML di base. lxml è più potente e veloce, e offre funzionalità aggiuntive come il supporto a XPath 2.0. minidom, un'altra libreria standard, si basa sul Document Object Model (DOM) ed è meno efficiente per documenti di grandi dimensioni.
Cosa sono le espressioni XPath e in che modo sono utili nel parsing XML?
Le espressioni XPath sono linguaggi di query per selezionare nodi da un documento XML. Sono utili per navigare tra elementi e attributi nei documenti XML, consentendo un recupero e una manipolazione dei dati precisi.
Come posso convalidare un documento XML prima di analizzarlo con ElementTree?
I documenti XML possono essere convalidati usando una XML Schema Definition (XSD) o una Document Type Definition (DTD). Librerie Python come lxml offrono supporto integrato per la validazione rispetto a questi standard.
Quali sono alcune best practice per modificare dati XML con ElementTree?
Le best practice includono lavorare su una copia dei dati XML per evitare perdite accidentali, usare espressioni XPath per una navigazione precisa e assicurarsi che tutte le modifiche mantengano la correttezza formale (well-formedness) del documento XML.
Si può usare ElementTree per convertire dati XML in JSON?
Sebbene ElementTree non fornisca una conversione diretta da XML a JSON, puoi analizzare i dati XML in un dizionario Python usando ElementTree e poi convertire il dizionario in JSON usando il modulo json.
Come gestisci i namespace XML con ElementTree?
ElementTree può gestire i namespace XML usando il prefisso {namespace} nei nomi dei tag. Puoi anche registrare i namespace con ET.register_namespace() per una gestione più semplice degli XML con namespace.
Cosa dovrei fare se incontro un errore di parsing XML in ElementTree?
Controlla problemi comuni come XML malformati, codifiche non supportate o percorsi file errati. Usa i meccanismi di gestione errori di Python (try-except ) per diagnosticare e gestire con grazia gli errori di parsing.
È possibile fare il pretty-print di un documento XML usando ElementTree?
ElementTree non supporta direttamente il pretty-printing, ma puoi usare xml.dom.minidom per analizzare la stringa XML e poi usare il metodo toprettyxml() per formattare l'XML in modo leggibile.

