-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathxml.html
678 lines (574 loc) · 66.7 KB
/
xml.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
<!DOCTYPE html>
<meta charset=utf-8>
<title>XML - Immersione in Python 3</title>
<!--[if IE]><script src=j/html5.js></script><![endif]-->
<link rel=stylesheet href=dip3.css>
<style>
body{counter-reset:h1 12}
mark{display:inline}
</style>
<link rel=stylesheet media='only screen and (max-device-width: 480px)' href=mobile.css>
<link rel=stylesheet media=print href=print.css>
<meta name=viewport content='initial-scale=1.0'>
<form action=http://www.google.com/cse><div><input type=hidden name=cx value=014021643941856155761:l5eihuescdw><input type=hidden name=ie value=UTF-8> <input type=search name=q size=25 placeholder="powered by Google™"> <input type=submit name=root value=Search></div></form>
<p>Voi siete qui: <a href=index.html>Inizio</a> <span class=u>‣</span> <a href=indice.html#xml>Immersione in Python 3</a> <span class=u>‣</span>
<p id=level>Livello di difficoltà: <span class=u title=avanzato>♦♦♦♦♢</span>
<h1>XML</h1>
<blockquote class=q>
<p><span class=u>❝</span> Sotto l’arcontato di Aristecmo, Dracone stabilì le sue leggi. <span class=u>❞</span><br>— <a href='http://www.perseus.tufts.edu/cgi-bin/ptext?doc=Perseus:text:1999.01.0046;query=chapter%3D%235;layout=;loc=3.1'>Aristotele</a>
</blockquote>
<p id=toc>
<h2 id=divingin>Immersione!</h2>
<p class=f>Per la maggior parte, i capitoli di questo libro sono stati costruiti attorno al codice di un programma di esempio. Ma <abbr>XML</abbr> non ha a che fare con il codice; ha a che fare con i dati. Uno degli usi più comuni di <abbr>XML</abbr> è il “syndication feed” che elenca gli ultimi articoli di un blog, un forum, o un altro sito web frequentemente aggiornato. La maggior parte dei software più popolari per la gestione di contenuti web è in grado di produrre un feed e aggiornarlo ogni volta che vengono pubblicati nuovi messaggi, discussioni, o articoli. Potete seguire un singolo blog “abbonandovi” al suo feed, e potete seguire più di un blog alla volta utilizzando un “<a href=http://en.wikipedia.org/wiki/List_of_feed_aggregators>aggregatore di feed</a>” dedicato come <a href=http://www.google.com/reader/>Google Reader</a>.
<p>Qui di seguito, dunque, trovate il documento <abbr>XML</abbr> che contiene i dati con cui lavoreremo in questo capitolo. È un feed — nello specifico, un <a href=http://atompub.org/rfc4287.html>syndication feed in formato Atom</a>.
<p class=d>[<a href=esempi/feed.xml>scarica <code>feed.xml</code></a>]
<pre class=pp><code><?xml version='1.0' encoding='utf-8'?>
<feed xmlns='http://www.w3.org/2005/Atom' xml:lang='it'>
<title>dive into mark</title>
<subtitle>attualmente tra una dipendenza e l'altra</subtitle>
<id>tag:diveintomark.org,2001-07-29:/</id>
<updated>2009-03-27T21:56:07Z</updated>
<link rel='alternate' type='text/html' href='http://diveintomark.org/'/>
<link rel='self' type='application/atom+xml' href='http://diveintomark.org/feed/'/>
<entry>
<author>
<name>Mark</name>
<uri>http://diveintomark.org/</uri>
</author>
<title>Immersione nella storia, edizione 2009</title>
<link rel='alternate' type='text/html'
href='http://diveintomark.org/archives/2009/03/27/dive-into-history-2009-edition'/>
<id>tag:diveintomark.org,2009-03-27:/archives/20090327172042</id>
<updated>2009-03-27T21:56:07Z</updated>
<published>2009-03-27T17:20:42Z</published>
<category scheme='http://diveintomark.org' term='diveintopython'/>
<category scheme='http://diveintomark.org' term='docbook'/>
<category scheme='http://diveintomark.org' term='html'/>
<summary type='html'>Mettere un intero capitolo in una sola pagina
sembra eccessivo, ma considerate questo &amp;mdash; finora il mio
capitolo più lungo equivarrebbe a 75 pagine stampate, e si carica
in meno di 5 secondi&amp;hellip; Su una connessione in
dial-up.</summary>
</entry>
<entry>
<author>
<name>Mark</name>
<uri>http://diveintomark.org/</uri>
</author>
<title>L'accessibilità è una padrona inflessibile</title>
<link rel='alternate' type='text/html'
href='http://diveintomark.org/archives/2009/03/21/accessibility-is-a-harsh-mistress'/>
<id>tag:diveintomark.org,2009-03-21:/archives/20090321200928</id>
<updated>2009-03-22T01:05:37Z</updated>
<published>2009-03-21T20:09:28Z</published>
<category scheme='http://diveintomark.org' term='accessibility'/>
<summary type='html'>L'ortodossia dell'accessibilità non permette a
nessuno di mettere in discussione il valore di caratteristiche che
sono raramente utili e raramente usate.</summary>
</entry>
<entry>
<author>
<name>Mark</name>
</author>
<title>Una introduzione graduale alla codifica video, parte 1: i formati dei contenitori</title>
<link rel='alternate' type='text/html'
href='http://diveintomark.org/archives/2008/12/18/give-part-1-container-formats'/>
<id>tag:diveintomark.org,2008-12-18:/archives/20081218155422</id>
<updated>2009-01-11T19:39:22Z</updated>
<published>2008-12-18T15:54:22Z</published>
<category scheme='http://diveintomark.org' term='asf'/>
<category scheme='http://diveintomark.org' term='avi'/>
<category scheme='http://diveintomark.org' term='encoding'/>
<category scheme='http://diveintomark.org' term='flv'/>
<category scheme='http://diveintomark.org' term='GIVE'/>
<category scheme='http://diveintomark.org' term='mp4'/>
<category scheme='http://diveintomark.org' term='ogg'/>
<category scheme='http://diveintomark.org' term='video'/>
<summary type='html'>Alla fine queste note diventeranno parte di
una presentazione sulla tecnologia della codifica video.</summary>
</entry>
</feed></code></pre>
<p class=a>⁂
<h2 id=xml-intro>Un corso accelerato di XML in 5 minuti</h2>
<p>Se conoscete già <abbr>XML</abbr>, potete saltare questa sezione.
<p><abbr>XML</abbr> è un modo generalizzato di descrivere strutture dati gerarchiche. Un <i>documento</i> <abbr>XML</abbr> contiene uno o più <i>elementi</i> che sono delimitati da <i>tag di apertura e chiusura</i>. Questo è un documento <abbr>XML</abbr> completo (anche se noioso):
<pre class='nd pp'><code><a><foo> <span class=u>①</span></a>
<a></foo> <span class=u>②</span></a></code></pre>
<ol>
<li>Questo è il <i>tag di apertura</i> dell’elemento <code>foo</code>.
<li>Questo è il corrispondente <i>tag di chiusura</i> dell’elemento <code>foo</code>. Allo stesso modo in cui si bilanciano le parentesi nella scrittura, in matematica, o in programmazione, ogni tag di apertura deve essere <i>terminato</i> da un corrispondente tag di chiusura.
</ol>
<p>Gli elementi possono essere <i>annidati</i> senza alcun limite di profondità. Un elemento <code>bar</code> all’interno di un elemento <code>foo</code> si definisce come un <i>sottoelemento</i> o un elemento <i>figlio</i> di <code>foo</code>.
<pre class='nd pp'><code><foo>
<mark><bar></bar></mark>
</foo>
</code></pre>
<p>Il primo elemento di ogni documento <abbr>XML</abbr> si chiama <i>elemento radice</i>. Un documento <abbr>XML</abbr> può avere un solo elemento radice. Il documento che segue <strong>non è un documento <abbr>XML</abbr></strong> perché possiede due elementi radice:
<pre class='nd pp'><code><foo></foo>
<bar></bar></code></pre>
<p>Gli elementi possono essere dotati di <i>attributi</i>, che sono coppie nome-valore. Gli attributi sono elencati all’interno del tag di apertura di un elemento e separati da spazi bianchi. I <i>nomi degli attributi</i> non possono essere ripetuti nell’ambito di uno stesso elemento. I <i>valori degli attributi</i> devono essere racchiusi tra apici o virgolette.
<pre class='nd pp'><code><a><foo <mark>lang='en'</mark>> <span class=u>①</span></a>
<a> <bar id='papayawhip' <mark>lang="fr"</mark>></bar> <span class=u>②</span></a>
</foo>
</code></pre>
<ol>
<li>L’elemento <code>foo</code> possiede un attributo chiamato <code>lang</code>. Il valore del suo attributo <code>lang</code> è <code>en</code>.
<li>L’elemento <code>bar</code> possiede due attributi chiamati <code>id</code> e <code>lang</code>. Il valore del suo attributo <code>lang</code> è <code>fr</code>. Questo non crea conflitti in alcun modo con l’elemento <code>foo</code>. Ogni elemento possiede il proprio insieme di attributi.
</ol>
<p>Nel caso un elemento sia dotato di più di un attributo, l’ordine degli attributi non è significativo. Gli attributi di un elemento formano un insieme non ordinato di chiavi e valori, proprio come un dizionario Python. Non c’è alcun limite sul numero degli attributi che potete definire per ogni elemento.
<p>Gli elementi possono includere <i>contenuto testuale</i>.
<pre class='nd pp'><code><foo lang='en'>
<bar lang='fr'><mark>PapayaWhip</mark></bar>
</foo>
</code></pre>
<p>Gli elementi che non contengono testo e non hanno figli si dicono <i>vuoti</i>.
<pre class='nd pp'><code><foo></foo></code></pre>
<p>Gli elementi vuoti si possono scrivere in maniera abbreviata. Inserendo un carattere <code>/</code> nel tag di apertura, potete omettere completamente il tag di chiusura. Il documento <abbr>XML</abbr> nell’esempio precedente potrebbe anche essere scritto in questo modo:
<pre class='nd pp'><code><foo<mark>/</mark>></code></pre>
<p>Così come le funzioni Python possono essere dichiarate in <i>moduli</i> differenti, gli elementi <abbr>XML</abbr> possono essere dichiarati in <i>spazi di nomi</i> differenti. Di solito, gli spazi di nomi sono identificati da un URL. Potete usare una dichiarazione <code>xmlns</code> per definire uno <i>spazio di nomi predefinito</i>. Una dichiarazione di spazio di nomi ha un aspetto simile a un attributo, ma viene impiegata con uno scopo differente.
<pre class='nd pp'><code><a><feed <mark>xmlns='http://www.w3.org/2005/Atom'</mark>> <span class=u>①</span></a>
<a> <title>dive into mark</title> <span class=u>②</span></a>
</feed>
</code></pre>
<ol>
<li>L’elemento <code>feed</code> è nello spazio di nomi <code>http://www.w3.org/2005/Atom</code>.
<li>Anche l’elemento <code>title</code> si trova nello spazio di nomi <code>http://www.w3.org/2005/Atom</code>. La dichiarazione dello spazio di nomi ha effetto sull’elemento che la contiene e su tutti i suoi elementi figli.
</ol>
<p>Potete anche usare una dichiarazione <code>xmlns:<var>prefisso</var></code> per definire uno spazio di nomi e associarlo a un <i>prefisso</i>. Dopodiché ogni elemento in quello spazio di nomi dovrà essere esplicitamente dichiarato con il prefisso.
<pre class='nd pp'><code><a><atom:feed <mark>xmlns:atom='http://www.w3.org/2005/Atom'</mark>> <span class=u>①</span></a>
<a> <atom:title>dive into mark</atom:title> <span class=u>②</span></a>
</atom:feed></code></pre>
<ol>
<li>L’elemento <code>feed</code> è nello spazio di nomi <code>http://www.w3.org/2005/Atom</code>.
<li>Anche l’elemento <code>title</code> si trova nello spazio di nomi <code>http://www.w3.org/2005/Atom</code>.
</ol>
<p>Un qualsiasi riconoscitore <abbr>XML</abbr> considera <em>identici</em> i due documenti <abbr>XML</abbr> precedenti. Spazio di nomi + nome dell’elemento = identità <abbr>XML</abbr>. I prefissi esistono solamente per fare riferimento agli spazi di nomi, così l’effettivo nome del prefisso (<code>atom</code>) è irrilevante. Gli spazi di nomi corrispondono, i nomi degli elementi corrispondono, gli attributi (o il fatto che siano assenti) corrispondono e il contenuto testuale di ogni elemento corrisponde, quindi i documenti <abbr>XML</abbr> sono uguali.
<p>Infine, i documenti <abbr>XML</abbr> possono contentere <a href=stringhe.html#one-ring-to-rule-them-all>informazioni sulla codifica di carattere</a> nella prima riga, prima dell’elemento radice. (Se siete curiosi di sapere come fa un documento a contenere informazioni che è necessario conoscere prima di poterlo leggere, la <a href=http://www.w3.org/TR/REC-xml/#sec-guessing-no-ext-info>Sezione F della specifica <abbr>XML</abbr></a> descrive in dettaglio come risolvere questo Comma 22.)
<pre class='nd pp'><code><?xml version='1.0' <mark>encoding='utf-8'</mark>?></code></pre>
<p>E ora conoscete quel tanto di <abbr>XML</abbr> che vi basta per essere pericolosi!
<p class=a>⁂
<h2 id=xml-structure>La struttura di un feed Atom</h2>
<p>Pensate a un weblog, o in effetti a qualsiasi sito web il cui contenuto venga frequentemente aggiornato, come <a href=http://www.cnn.com/>CNN.com</a>. Il sito ha un titolo (“CNN.com”), un sottotitolo (“Ultime notizie dagli Stati Uniti e dal mondo, meteo, intrattenimento <i class=baa>&</i> servizi filmati”), una data per l’ultimo aggiornamento (“aggiornato alle 12:43 p.m. EDT, sabato 16 maggio 2009”) e una lista di articoli pubblicati a orari differenti. Anche ogni articolo ha un titolo, una data di pubblicazione (e magari anche una data per l’ultimo aggiornamento, se ne è stata pubblicata una modifica oppure è stato corretto un errore di battitura) e un URL unico.
<p>Il formato di syndication Atom è stato progettato per catturare tutte queste informazioni in un formato standard. Il mio weblog e CNN.com hanno un aspetto, un ambito e un pubblico largamente differente, ma entrambi hanno la stessa struttura di base. CNN.com ha un titolo, il mio blog ha un titolo; CNN.com pubblica articoli, io pubblico articoli.
<p>Al livello più alto si trova l’<i>elemento radice</i>, che è lo stesso per ogni feed Atom: l’elemento <code>feed</code> nello spazio di nomi <code>http://www.w3.org/2005/Atom</code>.
<pre class=pp><code><a><feed xmlns='http://www.w3.org/2005/Atom' <span class=u>①</span></a>
<a> xml:lang='it'> <span class=u>②</span></a></code></pre>
<ol>
<li><code>http://www.w3.org/2005/Atom</code> è lo spazio di nomi Atom.
<li>Qualsiasi elemento può contenere un attributo <code>xml:lang</code> che dichiara la lingua di quell’elemento e dei suoi figli. In questo caso, l’attributo <code>xml:lang</code> è dichiarato una sola volta nell’elemento radice, per indicare che l’intero feed è in italiano.
</ol>
<p>Un feed Atom contiene diversi elementi di informazione sul feed stesso. Questi sono dichiarati come figli dell’elemento radice <code>feed</code>.
<pre class=pp><code><feed xmlns='http://www.w3.org/2005/Atom' xml:lang='it'>
<a> <title>dive into mark</title> <span class=u>①</span></a>
<a> <subtitle>attualmente tra una dipendenza e l'altra</subtitle> <span class=u>②</span></a>
<a> <id>tag:diveintomark.org,2001-07-29:/</id> <span class=u>③</span></a>
<a> <updated>2009-03-27T21:56:07Z</updated> <span class=u>④</span></a>
<a> <link rel='alternate' type='text/html' href='http://diveintomark.org/'/> <span class=u>⑤</span></a></code></pre>
<ol>
<li>Il titolo di questo feed è <code>dive into mark</code>.
<li>Il sottotitolo di questo feed è <code>attualmente tra una dipendenza e l'altra</code>.
<li>Ogni feed necessita di un identificatore unico. Leggete la <a href=http://www.ietf.org/rfc/rfc4151.txt>RFC 4151</a> per sapere come crearne uno.
<li>Questo feed è stata aggiornato per l’ultima volta il 27 marzo 2009, alle 21:56 GMT. Di solito, questa data è uguale alla data di ultima modifica dell’articolo più recente.
<li>Ora le cose cominciano a diventare interessanti. Questo elemento <code>link</code> non ha contenuto testuale, ma possiede tre attributi: <code>rel</code>, <code>type</code> e <code>href</code>. Il valore di <code>rel</code> vi dice che tipo di collegamento è questo; <code>rel='alternate'</code> significa che è un collegamento a una rappresentazione alternativa per questo feed. L’attributo <code>type='text/html'</code> significa che questo è un collegamento a una pagina <abbr>HTML</abbr>. E la destinazione del collegamento viene fornita nell’attributo <code>href</code>.
</ol>
<p>Ora sappiamo che questo è un feed per un sito chiamato “dive into mark” che è disponibile all’indirizzo <a href=http://diveintomark.org/><code>http://diveintomark.org/</code></a> e il cui aggiornamento più recente risale al 27 marzo 2009.
<blockquote class=note>
<p><span class=u>☞</span>L’ordine degli elementi in un feed Atom non è rilevante, sebbene possa esserlo in alcuni documenti <abbr>XML</abbr>.
</blockquote>
<p>Dopo i metadati a livello di feed troviamo la lista degli articoli più recenti. Un articolo ha un aspetto simile a questo:
<pre class=pp><code><entry>
<a> <author> <span class=u>①</span></a>
<name>Mark</name>
<uri>http://diveintomark.org/</uri>
</author>
<a> <title>Immersione nella storia, edizione 2009</title> <span class=u>②</span></a>
<a> <link rel='alternate' type='text/html' <span class=u>③</span></a>
href='http://diveintomark.org/archives/2009/03/27/dive-into-history-2009-edition'/>
<a> <id>tag:diveintomark.org,2009-03-27:/archives/20090327172042</id> <span class=u>④</span></a>
<a> <updated>2009-03-27T21:56:07Z</updated> <span class=u>⑤</span></a>
<published>2009-03-27T17:20:42Z</published>
<a> <category scheme='http://diveintomark.org' term='diveintopython'/> <span class=u>⑥</span></a>
<category scheme='http://diveintomark.org' term='docbook'/>
<category scheme='http://diveintomark.org' term='html'/>
<a> <summary type='html'>Mettere un intero capitolo in una sola pagina <span class=u>⑦</span></a>
sembra eccessivo, ma considerate questo &amp;mdash; finora il mio
capitolo più lungo equivarrebbe a 75 pagine stampate, e si carica
in meno di 5 secondi&amp;hellip; Su una connessione in
dial-up.</summary>
<a></entry> <span class=u>⑧</span></a></code></pre>
<ol>
<li>L’elemento <code>author</code> vi dice chi ha scritto questo articolo: un certo tizio di nome Mark, che potete trovare a bighellonare all’indirizzo <code>http://diveintomark.org/</code>. (Questo è lo stesso indirizzo del collegamento alternativo contenuto nei metadati del feed, ma non è necessario che lo sia. Molti weblog hanno più di un autore, ognuno col proprio sito personale.)
<li>L’elemento <code>title</code> vi dà il titolo dell’articolo, “Immersione nella storia, edizione 2009”.
<li>Come per il collegamento alternativo a livello di feed, questo elemento <code>link</code> vi dà l’indirizzo della versione <abbr>HTML</abbr> di questo articolo.
<li>Ogni voce, come ogni feed, necessita di un identificatore unico.
<li>Ogni voce ha due date: la data di pubblicazione (<code>published</code>) e la data di ultima modifica (<code>updated</code>).
<li>Ogni voce può avere un numero arbitrario di categorie. Questo articolo è classificato sotto le categorie <code>diveintopython</code>, <code>docbook</code> e <code>html</code>.
<li>L’elemento <code>summary</code> fornisce un breve riepilogo dell’articolo. (Esiste anche un elemento <code>content</code>, che qui non viene mostrato, da utilizzare se volete includere il testo completo dell’articolo nel vostro feed.) Questo elemento <code>summary</code> usa l’attributo <code>type='html'</code> specifico di Atom per indicare che questo riepilogo non è in formato di testo semplice, ma è un frammento di <abbr>HTML</abbr>. Questo dettaglio è importante, dato che il riepilogo contiene entità specifiche di <abbr>HTML</abbr> (<code>&mdash;</code> e <code>&hellip;</code>) che dovrebbero essere rappresentate come “—” e “…” piuttosto che visualizzate direttamente.
<li>Infine, il tag di chiusura per l’elemento <code>entry</code> segnala la fine dei metadati per questo articolo.
</ol>
<p class=a>⁂
<h2 id=xml-parse>Riconoscere XML</h2>
<p>Python può riconoscere documenti XML in molti modi. Dispone di riconoscitori tradizionali di tipo <a href=http://en.wikipedia.org/wiki/XML#DOM><abbr>DOM</abbr></a> e <a href=http://en.wikipedia.org/wiki/Simple_API_for_XML><abbr>SAX</abbr></a>, ma io mi concentrerò su una diversa libreria chiamata ElementTree.
<p class=d>[<a href=esempi/feed.xml>scarica <code>feed.xml</code></a>]
<pre class=screen>
<a><samp class=p>>>> </samp><kbd class=pp>import xml.etree.ElementTree as etree</kbd> <span class=u>①</span></a>
<a><samp class=p>>>> </samp><kbd class=pp>tree = etree.parse('esempi/feed.xml')</kbd> <span class=u>②</span></a>
<a><samp class=p>>>> </samp><kbd class=pp>root = tree.getroot()</kbd> <span class=u>③</span></a>
<a><samp class=p>>>> </samp><kbd class=pp>root</kbd> <span class=u>④</span></a>
<samp><Element {http://www.w3.org/2005/Atom}feed at cd1eb0></samp></pre>
<ol>
<li>La libreria ElementTree fa parte della libreria standard di Python e si trova nel modulo <code>xml.etree.ElementTree</code>.
<li>Il punto d’ingresso principale della libreria ElementTree è la funzione <code>parse()</code>, che può prendere come argomento un nome di file o un <a href=file.html#file-like-objects>oggetto simile a un file</a>. Questa funzione riconosce l’intero documento tutto in una volta. Se la memoria disponibile è scarsa, esistono modi per <a href=http://effbot.org/zone/element-iterparse.htm>riconoscere un documento <abbr>XML</abbr> in maniera incrementale</a>.
<li>La funzione <code>parse()</code> restituisce un oggetto che rappresenta l’intero documento. Questo oggetto <em>non</em> è l’elemento radice. Per ottenere un riferimento all’elemento radice dovete chiamare il metodo <code>getroot()</code>.
<li>Come vi sareste aspettati, l’elemento radice è l’elemento <code>feed</code> nello spazio di nomi <code>http://www.w3.org/2005/Atom</code>. La rappresentazione sotto forma di stringa di questo oggetto rafforza un concetto importante: un elemento <abbr>XML</abbr> è la combinazione del proprio spazio di nomi e del nome del proprio tag (anche chiamato <i>nome locale</i>). Ogni elemento in questo documento si trova nello spazio di nomi Atom, quindi l’elemento radice viene rappresentato come <code>{http://www.w3.org/2005/Atom}feed</code>.
</ol>
<blockquote class=note>
<p><span class=u>☞</span>ElementTree rappresenta gli elementi <abbr>XML</abbr> come <code>{<var>spaziodinomi</var>}<var>nomelocale</var></code>. Vedrete e userete questo formato in più punti nella <abbr>API</abbr> di ElementTree.
</blockquote>
<h3 id=xml-elements>Gli elementi sono liste</h3>
<p>Nella API di ElementTree, un elemento <abbr>XML</abbr> si comporta come una lista. Gli elementi della lista sono i figli dell’elemento <abbr>XML</abbr>.
<pre class=screen>
# continua dall'esempio precedente
<a><samp class=p>>>> </samp><kbd class=pp>root.tag</kbd> <span class=u>①</span></a>
<samp>'{http://www.w3.org/2005/Atom}feed'</samp>
<a><samp class=p>>>> </samp><kbd class=pp>len(root)</kbd> <span class=u>②</span></a>
<samp class=pp>8</samp>
<a><samp class=p>>>> </samp><kbd class=pp>for child in root:</kbd> <span class=u>③</span></a>
<a><samp class=p>... </samp><kbd class=pp> print(child)</kbd> <span class=u>④</span></a>
<samp class=p>... </samp>
<samp><Element {http://www.w3.org/2005/Atom}title at e2b5d0>
<Element {http://www.w3.org/2005/Atom}subtitle at e2b4e0>
<Element {http://www.w3.org/2005/Atom}id at e2b6c0>
<Element {http://www.w3.org/2005/Atom}updated at e2b6f0>
<Element {http://www.w3.org/2005/Atom}link at e2b4b0>
<Element {http://www.w3.org/2005/Atom}entry at e2b720>
<Element {http://www.w3.org/2005/Atom}entry at e2b510>
<Element {http://www.w3.org/2005/Atom}entry at e2b750></samp></pre>
<ol>
<li>Proseguendo l’esempio precedente, l’elemento radice è <code>{http://www.w3.org/2005/Atom}feed</code>.
<li>La “lunghezza” dell’elemento radice è il numero dei suoi elementi figli.
<li>Potete usare l’elemento stesso come un iteratore per attraversare tutti i suoi elementi figli.
<li>Come potete vedere, ci sono effettivamente 8 elementi figli: tutti quelli che contengono metadati a livello di feed (<code>title</code>, <code>subtitle</code>, <code>id</code>, <code>updated</code> e <code>link</code>) seguiti dai tre elementi <code>entry</code>.
</ol>
<p>Potreste averlo già indovinato, ma voglio sottolinearlo esplicitamente: la lista degli elementi figli include solamente i figli <em>diretti</em>. Ogni elemento <code>entry</code> contiene i propri figli, ma questi non sono inclusi nella lista. Sarebbero inclusi nella lista dei figli di ogni elemento <code>entry</code>, ma non sono inclusi nella lista dei figli dell’elemento <code>feed</code>. Esistono modi di trovare elementi a prescindere da quanto profondamente siano annidati; vedremo due di queste tecniche più avanti in questo capitolo.
<h3 id=xml-attributes>Gli attributi sono dizionari</h3>
<p><abbr>XML</abbr> non è semplicemente una collezione di elementi; ogni elemento può anche avere il proprio insieme di attributi. Una volta che avete un riferimento a uno specifico elemento, potete facilmente ottenere i suoi attributi sotto forma di un dizionario Python.
<pre class=screen>
# continua dall'esempio precedente
<a><samp class=p>>>> </samp><kbd class=pp>root.attrib</kbd> <span class=u>①</span></a>
<samp class=pp>{'{http://www.w3.org/XML/1998/namespace}lang': 'it'}</samp>
<a><samp class=p>>>> </samp><kbd class=pp>root[4]</kbd> <span class=u>②</span></a>
<samp><Element {http://www.w3.org/2005/Atom}link at e181b0></samp>
<a><samp class=p>>>> </samp><kbd class=pp>root[4].attrib</kbd> <span class=u>③</span></a>
<samp class=pp>{'href': 'http://diveintomark.org/',
'type': 'text/html',
'rel': 'alternate'}</samp>
<a><samp class=p>>>> </samp><kbd class=pp>root[3]</kbd> <span class=u>④</span></a>
<samp><Element {http://www.w3.org/2005/Atom}updated at e2b4e0></samp>
<a><samp class=p>>>> </samp><kbd class=pp>root[3].attrib</kbd> <span class=u>⑤</span></a>
<samp class=pp>{}</samp></pre>
<ol>
<li>La proprietà <code>attrib</code> è un dizionario degli attributi dell’elemento. Il markup originale qui era <code><feed xmlns='http://www.w3.org/2005/Atom' xml:lang='it'></code>. Il prefisso <code>xml:</code> si riferisce a uno spazio di nomi predefinito che ogni documento <abbr>XML</abbr> può usare senza doverlo dichiarare.
<li>Il quinto figlio — <code>[4]</code> in una lista i cui indici partono da 0 — è l’elemento <code>link</code>.
<li>L’elemento <code>link</code> ha tre attributi: <code>href</code>, <code>type</code> e <code>rel</code>.
<li>Il quarto figlio — <code>[3]</code> in una lista i cui indici partono da 0 — è l’elemento <code>updated</code>.
<li>L’elemento <code>updated</code> non ha attributi, quindi <code>attrib</code> in questo caso è semplicemente un dizionario vuoto.
</ol>
<p class=a>⁂
<h2 id=xml-find>Cercare nodi all’interno di un documento XML</h2>
<p>Finora abbiamo lavorato con questo documento <abbr>XML</abbr> in maniera “top down”, dall’alto verso il basso, partendo dall’elemento radice, recuperando i suoi elementi figli, e così via attraverso tutto il documento. Ma molti usi di <abbr>XML</abbr> vi richiedono di trovare elementi specifici. ElementTree può fare anche questo.
<pre class=screen>
<samp class=p>>>> </samp><kbd class=pp>import xml.etree.ElementTree as etree</kbd>
<samp class=p>>>> </samp><kbd class=pp>tree = etree.parse('esempi/feed.xml')</kbd>
<samp class=p>>>> </samp><kbd class=pp>root = tree.getroot()</kbd>
<a><samp class=p>>>> </samp><kbd class=pp>root.findall('{http://www.w3.org/2005/Atom}entry')</kbd> <span class=u>①</span></a>
<samp>[<Element {http://www.w3.org/2005/Atom}entry at e2b4e0>,
<Element {http://www.w3.org/2005/Atom}entry at e2b510>,
<Element {http://www.w3.org/2005/Atom}entry at e2b540>]</samp>
<samp class=p>>>> </samp><kbd class=pp>root.tag</kbd>
<samp class=pp>'{http://www.w3.org/2005/Atom}feed'</samp>
<a><samp class=p>>>> </samp><kbd class=pp>root.findall('{http://www.w3.org/2005/Atom}feed')</kbd> <span class=u>②</span></a>
<samp class=pp>[]</samp>
<a><samp class=p>>>> </samp><kbd class=pp>root.findall('{http://www.w3.org/2005/Atom}author')</kbd> <span class=u>③</span></a>
<samp class=pp>[]</samp></pre>
<ol>
<li>Il metodo <code>findall()</code> trova elementi figli che corrispondono a una richiesta specifica. (I dettagli sul formato della richiesta arriveranno in un minuto.)
<li>Tutti gli elementi — incluso l’elemento radice, ma anche gli elementi figli — hanno un metodo <code>findall()</code>. Il metodo trova tutti gli elementi che corrispondono alla richiesta cercando tra gli elementi figli. Ma perché in questo caso non viene trovato alcun risultato? Sebbene possa non apparire immediato, questa particolare richiesta cerca solo tra i figli di un elemento. Dato che l’elemento radice <code>feed</code> non ha figli chiamati <code>feed</code>, questa richiesta restituisce una lista vuota.
<li>Anche questo risultato potrebbe sorprendervi. <a href=#divingin>C’è un elemento <code>author</code></a> in questo documento, anzi, in effetti ce ne sono tre (uno per ogni <code>entry</code>). Ma quegli elementi <code>author</code> non sono <em>figli diretti</em> dell’elemento radice, bensì “nipoti” (letteralmente, un elemento figlio di un elemento figlio). Se volete cercare elementi <code>author</code> a qualsiasi livello di profondità potete farlo, ma la forma della richiesta è leggermente differente.
</ol>
<pre class=screen>
<a><samp class=p>>>> </samp><kbd class=pp>tree.findall('{http://www.w3.org/2005/Atom}entry')</kbd> <span class=u>①</span></a>
<samp>[<Element {http://www.w3.org/2005/Atom}entry at e2b4e0>,
<Element {http://www.w3.org/2005/Atom}entry at e2b510>,
<Element {http://www.w3.org/2005/Atom}entry at e2b540>]</samp>
<a><samp class=p>>>> </samp><kbd class=pp>tree.findall('{http://www.w3.org/2005/Atom}author')</kbd> <span class=u>②</span></a>
<samp class=pp>[]</samp>
</pre>
<ol>
<li>Per convenienza, l’oggetto <code>tree</code> (restituito dalla funzione <code>etree.parse()</code>) possiede diversi metodi speculari a quelli dell’elemento radice. I risultati di questi metodi sono gli stessi che avreste ottenuto invocando il metodo <code>tree.getroot().findall()</code>.
<li>In maniera forse sorprendente, questa richiesta non trova gli elementi <code>author</code> in questo documento. Perché no? Perché questa è solo una scorciatoia per <code>tree.getroot().findall('{http://www.w3.org/2005/Atom}author')</code>, che significa “trova tutti gli elementi <code>author</code> che sono figli dell’elemento radice”. Gli elementi <code>author</code> non sono figli dell’elemento radice, ma sono figli degli elementi <code>entry</code>. Quindi la richiesta non restituisce alcuna corrispondenza.
</ol>
<p>Esiste anche un metodo <code>find()</code> che restituisce il primo elemento corrispondente. Questo è utile per situazioni in cui vi aspettate una sola corrispondenza o vi interessa solo la prima corrispondenza nel caso ce ne sia più di una.
<pre class=screen>
<a><samp class=p>>>> </samp><kbd class=pp>entries = tree.findall('{http://www.w3.org/2005/Atom}entry')</kbd> <span class=u>①</span></a>
<samp class=p>>>> </samp><kbd class=pp>len(entries)</kbd>
<samp class=p>3</samp>
<a><samp class=p>>>> </samp><kbd class=pp>title_element = entries[0].find('{http://www.w3.org/2005/Atom}title')</kbd> <span class=u>②</span></a>
<samp class=p>>>> </samp><kbd class=pp>title_element.text</kbd>
<samp class=pp>'Immersione nella storia, edizione 2009'</samp>
<a><samp class=p>>>> </samp><kbd class=pp>foo_element = entries[0].find('{http://www.w3.org/2005/Atom}foo')</kbd> <span class=u>③</span></a>
<samp class=p>>>> </samp><kbd class=pp>foo_element</kbd>
<samp class=p>>>> </samp><kbd class=pp>type(foo_element)</kbd>
<samp><class 'NoneType'></samp>
</pre>
<ol>
<li>Avete visto questo metodo in azione nell’esempio precedente. Trova tutti gli elementi <code>atom:entry</code>.
<li>Il metodo <code>find()</code> accetta una richiesta ElementTree e restituisce il primo elemento corrispondente.
<li>In questa voce non ci sono elementi chiamati <code>foo</code>, quindi questa richiesta restituisce <code>None</code>.
</ol>
<blockquote class=note>
<p><span class=u>☞</span>C’è una sgradita “sorpresa” che il metodo <code>find()</code> finirà per riservarvi. In un contesto logico, gli oggetti elemento di ElementTree verranno valutati come <code>False</code> se non contengono figli (cioè se <code>len(element)</code> vale 0). Questo significa che <code>if element.find('...')</code> non sta verificando che il metodo <code>find()</code> abbia trovato un elemento corrispondente, ma sta verificando che quell’elemento corrispondente abbia elementi figli! Per verificare che il metodo <code>find()</code> abbia restituito un elemento usate <code>if element.find('...') is not None</code>.
</blockquote>
<p><em>Esiste</em> un modo per cercare gli elementi <em>discendenti</em>, cioè figli, nipoti e qualsiasi elemento a qualsiasi livello di profondità.
<pre class=screen>
<a><samp class=p>>>> </samp><kbd class=pp>all_links = tree.findall('//{http://www.w3.org/2005/Atom}link')</kbd> <span class=u>①</span></a>
<samp class=p>>>> </samp><kbd class=pp>all_links</kbd>
<samp>[<Element {http://www.w3.org/2005/Atom}link at e181b0>,
<Element {http://www.w3.org/2005/Atom}link at e2b570>,
<Element {http://www.w3.org/2005/Atom}link at e2b480>,
<Element {http://www.w3.org/2005/Atom}link at e2b5a0>]</samp>
<a><samp class=p>>>> </samp><kbd class=pp>all_links[0].attrib</kbd> <span class=u>②</span></a>
<samp class=pp>{'href': 'http://diveintomark.org/',
'type': 'text/html',
'rel': 'alternate'}</samp>
<a><samp class=p>>>> </samp><kbd class=pp>all_links[1].attrib</kbd> <span class=u>③</span></a>
<samp class=pp>{'href': 'http://diveintomark.org/archives/2009/03/27/dive-into-history-2009-edition',
'type': 'text/html',
'rel': 'alternate'}</samp>
<samp class=p>>>> </samp><kbd class=pp>all_links[2].attrib</kbd>
<samp class=pp>{'href': 'http://diveintomark.org/archives/2009/03/21/accessibility-is-a-harsh-mistress',
'type': 'text/html',
'rel': 'alternate'}</samp>
<samp class=p>>>> </samp><kbd class=pp>all_links[3].attrib</kbd>
<samp class=pp>{'href': 'http://diveintomark.org/archives/2008/12/18/give-part-1-container-formats',
'type': 'text/html',
'rel': 'alternate'}</samp></pre>
<ol>
<li>Questa richiesta — <code>//{http://www.w3.org/2005/Atom}link</code> — è molto simile agli esempi precedenti, tranne per i due caratteri di slash che si trovano all’inizio della richiesta. Quei due slash significano “non cercare solo tra i figli diretti, voglio <em>qualsiasi</em> elemento a prescindere dal livello di profondità”. Quindi il risultato è una lista che contiene quattro elementi <code>link</code>, non uno solo.
<li>Il primo risultato <em>è</em> un figlio diretto dell’elemento radice. Come potete vedere dai suoi attributi, quello è il collegamento alternativo a livello di feed che punta alla versione <abbr>HTML</abbr> del sito web descritto dal feed.
<li>Gli altri tre risultati sono i collegamenti alternativi a livello di singola voce. Ogni elemento <code>entry</code> ha un singolo elemento figlio <code>link</code> e, a causa del doppio slash all’inizio della richiesta, questa richiesta li trova tutti e tre.
</ol>
<!--
<p>Cosa dite? Volete la potenza del metodo <code>findall()</code>, ma preferireste lavorare con un iteratore invece che costruire una lista completa? ElementTree può fare anche questo.
<pre class=screen>
# continua dall'esempio precedente
<a><samp class=p>>>> </samp><kbd class=pp>it = tree.getiterator('{http://www.w3.org/2005/Atom}link')</kbd> <span class=u>①</span></a>
<a><samp class=p>>>> </samp><kbd class=pp>next(it)</kbd> <span class=u>②</span></a>
<samp><Element {http://www.w3.org/2005/Atom}link at 122f1b0></samp>
<samp class=p>>>> </samp><kbd class=pp>next(it)</kbd>
<samp><Element {http://www.w3.org/2005/Atom}link at 122f1e0></samp>
<samp class=p>>>> </samp><kbd class=pp>next(it)</kbd>
<samp><Element {http://www.w3.org/2005/Atom}link at 122f210></samp>
<samp class=p>>>> </samp><kbd class=pp>next(it)</kbd>
<samp><Element {http://www.w3.org/2005/Atom}link at 122f1b0></samp>
<samp class=p>>>> </samp><kbd class=pp>next(it)</kbd>
<samp class=traceback>Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration</samp></pre>
<ol>
<li>Il metodo <code>getiterator()</code> può prendere zero o più argomenti. Se viene chiamato senza argomenti, restituisce un iteratore che produce ogni elemento ed elemento figlio nell’intero documento. Oppure, come viene mostrato qui, potete invocarlo con il nome di un elemento nel formato standard di ElementTree. In questo caso, il metodo restituisce un iteratore che produce solo gli elementi con quel nome.
<li>Invocare ripetutamente la funzione <code>next()</code> con questo iteratore finirà per restituire tutti gli elementi del documento corrispondenti alla richiesta che avete passato al metodo <code>getiterator()</code>.
</ol>
-->
<p>Nel complesso, il metodo <code>findall()</code> è una caratteristica molto potente di ElementTree, ma la sintassi utilizzata nelle sue richieste può risultare un po’ sorprendente. Viene ufficialmente descritta come un “<a href=http://effbot.org/zone/element-xpath.htm>sottoinsieme ristretto delle espressioni XPath</a>”. <a href=http://www.w3.org/TR/xpath>XPath</a> è uno standard W3C per effettuare ricerche nei documenti <abbr>XML</abbr>. La sintassi delle richieste di ElementTree è abbastanza simile a XPath da consentire ricerche di base, ma abbastanza diversa da potervi infastidire se conoscete già XPath. Ora diamo un’occhiata a una libreria <abbr>XML</abbr> di terze parti che estende la <abbr>API</abbr> di ElementTree con un supporto completo per XPath.
<p class=a>⁂
<h2 id=xml-lxml>Proseguire con lxml</h2>
<p><a href=http://codespeak.net/lxml/><code>lxml</code></a> è una libreria open source di terze parti basata sul popolare <a href=http://www.xmlsoft.org/>riconoscitore libxml2</a>. Fornisce una <abbr>API</abbr> compatibile al 100% con ElementTree, poi la estende con un supporto completo per XPath 1.0 e alcune altre raffinatezze. Ne esistono <a href=http://pypi.python.org/pypi/lxml/>pacchetti di installazione disponibili per Windows</a>, mentre gli utenti Linux dovrebbero sempre provare a usare strumenti specifici per la loro distribuzione, come <code>yum</code> o <code>apt-get</code>, che permettono di installare librerie precompilate scaricandole dagli archivi della distribuzione. Altrimenti, dovrete <a href=http://codespeak.net/lxml/installation.html>installare <code>lxml</code> manualmente</a>.
<pre class=screen>
<a><samp class=p>>>> </samp><kbd class=pp>from lxml import etree</kbd> <span class=u>①</span></a>
<a><samp class=p>>>> </samp><kbd class=pp>tree = etree.parse('esempi/feed.xml')</kbd> <span class=u>②</span></a>
<a><samp class=p>>>> </samp><kbd class=pp>root = tree.getroot()</kbd> <span class=u>③</span></a>
<a><samp class=p>>>> </samp><kbd class=pp>root.findall('{http://www.w3.org/2005/Atom}entry')</kbd> <span class=u>④</span></a>
<samp>[<Element {http://www.w3.org/2005/Atom}entry at e2b4e0>,
<Element {http://www.w3.org/2005/Atom}entry at e2b510>,
<Element {http://www.w3.org/2005/Atom}entry at e2b540>]</samp></pre>
<ol>
<li>Una volta importata, <code>lxml</code> fornisce la stessa <abbr>API</abbr> della libreria built-in ElementTree.
<li>La funzione <code>parse()</code>: la stessa di ElementTree.
<li>Il metodo <code>getroot()</code>: anche questo è lo stesso.
<li>Il metodo <code>findall()</code>: esattamente identico.
</ol>
<p>Per documenti <abbr>XML</abbr> di grandi dimensioni, <code>lxml</code> è significativamente più veloce rispetto alla libreria built-in ElementTree. Se state usando solo la <abbr>API</abbr> di ElementTree e volete sfruttare l’implementazione più veloce disponibile, potete provare a importare <code>lxml</code> e ricorrere all’alternativa built-in ElementTree solo nel caso in cui <code>lxml</code> non sia presente.
<pre class='nd pp'><code>try:
from lxml import etree
except ImportError:
import xml.etree.ElementTree as etree</code></pre>
<p>Ma <code>lxml</code> è molto più di una ElementTree più veloce. Il suo metodo <code>findall()</code> include il supporto per espressioni più complicate.
<pre class=screen>
<a><samp class=p>>>> </samp><kbd class=pp>import lxml.etree</kbd> <span class=u>①</span></a>
<samp class=p>>>> </samp><kbd class=pp>tree = lxml.etree.parse('esempi/feed.xml')</kbd>
<a><samp class=p>>>> </samp><kbd class=pp>tree.findall('//{http://www.w3.org/2005/Atom}*[@href]')</kbd> <span class=u>②</span></a>
<samp>[<Element {http://www.w3.org/2005/Atom}link at eeb8a0>,
<Element {http://www.w3.org/2005/Atom}link at eeb990>,
<Element {http://www.w3.org/2005/Atom}link at eeb960>,
<Element {http://www.w3.org/2005/Atom}link at eeb9c0>]</samp>
<a><samp class=p>>>> </samp><kbd class=pp>tree.findall("//{http://www.w3.org/2005/Atom}*[@href='http://diveintomark.org/']")</kbd> <span class=u>③</span></a>
<samp>[<Element {http://www.w3.org/2005/Atom}link at eeb930>]</samp>
<samp class=p>>>> </samp><kbd class=pp>NS = '{http://www.w3.org/2005/Atom}'</kbd>
<a><samp class=p>>>> </samp><kbd class=pp>tree.findall('//{NS}author[{NS}uri]'.format(NS=NS))</kbd> <span class=u>④</span></a>
<samp>[<Element {http://www.w3.org/2005/Atom}author at eeba80>,
<Element {http://www.w3.org/2005/Atom}author at eebba0>]</samp></pre>
<ol>
<li>In questo esempio, scriverò <code>import lxml.etree</code> (invece di scrivere, diciamo, <code>from lxml import etree</code>) per enfatizzare che queste caratteristiche sono specifiche di <code>lxml</code>.
<li>Questa richiesta trova tutti gli elementi nello spazio di nomi Atom, ovunque nel documento, che abbiano un attributo <code>href</code>. I caratteri <code>//</code> all’inizio della richiesta significano “elementi ovunque (non solo come figli dell’elemento radice)”. <code>{http://www.w3.org/2005/Atom}</code> significa “solo elementi nello spazio di nomi Atom”. <code>*</code> significa “elementi con un qualsiasi nome locale”. E <code>[@href]</code> significa “ha un attributo <code>href</code>”.
<li>La richiesta trova tutti gli elementi Atom con un attributo <code>href</code> il cui valore sia <code>http://diveintomark.org/</code>.
<li>Dopo aver eseguito alcune rapide <a href=stringhe.html#formatting-strings>formattazioni di stringhe</a> (perché altrimenti le richieste composte diventano ridicolmente lunghe), questa richiesta cerca gli elementi Atom <code>author</code> che hanno un elemento Atom <code>uri</code> come figlio. Questa richiesta restituisce solo due elementi <code>author</code>, quelli nella prima e nella seconda <code>entry</code>. L’elemento <code>author</code> nell’ultima <code>entry</code> contiene solo un figlio <code>name</code>, non un figlio <code>uri</code>.
</ol>
<p>Non vi basta? <code>lxml</code> include anche il supporto per espressioni XPath 1.0 arbitrarie. Non cercherò di approfondire la sintassi XPath (ci si potrebbe scrivere un intero libro!) ma vi mostrerò come si integra in <code>lxml</code>.
<pre class=screen>
<samp class=p>>>> </samp><kbd class=pp>import lxml.etree</kbd>
<samp class=p>>>> </samp><kbd class=pp>tree = lxml.etree.parse('esempi/feed.xml')</kbd>
<a><samp class=p>>>> </samp><kbd class=pp>NSMAP = {'atom': 'http://www.w3.org/2005/Atom'}</kbd> <span class=u>①</span></a>
<a><samp class=p>>>> </samp><kbd class=pp>entries = tree.xpath("//atom:category[@term='accessibility']/..",</kbd> <span class=u>②</span></a>
<samp class=p>... </samp><kbd class=pp> namespaces=NSMAP)</kbd>
<a><samp class=p>>>> </samp><kbd class=pp>entries</kbd> <span class=u>③</span></a>
<samp>[<Element {http://www.w3.org/2005/Atom}entry at e2b630>]</samp>
<samp class=p>>>> </samp><kbd class=pp>entry = entries[0]</kbd>
<a><samp class=p>>>> </samp><kbd class=pp>entry.xpath('./atom:title/text()', namespaces=NSMAP)</kbd> <span class=u>④</span></a>
<samp class=pp>["L'accessibilità è una padrona inflessibile"]</samp></pre>
<ol>
<li>Per effettuare richieste XPath su elementi in uno spazio di nomi, avete bisogno di definire una corrispondenza tra prefissi e spazi di nomi sotto la semplice forma di un dizionario Python.
<li>Ecco una richiesta XPath. L’espressione XPath cerca gli elementi <code>category</code> (nello spazio di nomi Atom) che contengono un attributo <code>term</code> con il valore <code>accessibility</code>. Ma questo non è l’effettivo risultato della richiesta. Guardate alla fine della stringa di richiesta: avete notato quel <code>/..</code>? Questo significa “e poi restituisci l’elemento genitore dell’elemento <code>category</code> che hai appena trovato”. Così questa singola richiesta XPath troverà tutte le voci con un elemento figlio <code><category term='accessibility'></code>.
<li>La funzione <code>xpath()</code> restituisce una lista di oggetti ElementTree. In questo documento c’è solo una voce con un elemento <code>category</code> il cui attributo <code>term</code> abbia il valore <code>accessibility</code>.
<li>Le espressioni XPath non restituiscono sempre una lista di elementi. Tecnicamente, la rappresentazione <abbr>DOM</abbr> di un documento <abbr>XML</abbr> non contiene elementi, ma contiene <i>nodi</i>. A seconda del loro tipo, i nodi possono essere elementi, attributi, o anche contenuti testuali. Il risultato di una richiesta XPath è una lista di nodi. Questa richiesta restituisce una lista di nodi di testo: il contenuto testuale (<code>text()</code>) dell’elemento <code>title</code> (<code>atom:title</code>) che è un figlio dell’elemento corrente (<code>./</code>).
</ol>
<p class=a>⁂
<h2 id=xml-generate>Generare XML</h2>
<p>Il supporto di Python per <abbr>XML</abbr> non si limita al riconoscimento di documenti esistenti. Potete anche creare documenti <abbr>XML</abbr> da zero.
<pre class=screen>
<samp class=p>>>> </samp><kbd class=pp>import xml.etree.ElementTree as etree</kbd>
<a><samp class=p>>>> </samp><kbd class=pp>new_feed = etree.Element('{http://www.w3.org/2005/Atom}feed',</kbd> <span class=u>①</span></a>
<a><samp class=p>... </samp><kbd class=pp> attrib={'{http://www.w3.org/XML/1998/namespace}lang': 'it'})</kbd> <span class=u>②</span></a>
<a><samp class=p>>>> </samp><kbd class=pp>print(etree.tostring(new_feed))</kbd> <span class=u>③</span></a>
<samp class=pp><ns0:feed xmlns:ns0='http://www.w3.org/2005/Atom' xml:lang='it'/></samp></pre>
<ol>
<li>Per creare un nuovo elemento dovete istanziare la classe <code>Element</code>. Passate il nome dell’elemento (spazio di nomi + nome locale) come primo argomento. Questa istruzione crea un elemento <code>feed</code> nello spazio di nomi Atom. Questo sarà l’elemento radice del nostro nuovo documento.
<li>Per aggiungere attributi all’elemento appena creato, passate un dizionario di nomi e valori di attributi come argomento <var>attrib</var>. Notate che i nomi di attributi dovrebbero essere nel formato standard di ElementTree <code>{<var>spaziodinomi</var>}<var>nomelocale</var></code>.
<li>Potete serializzare qualsiasi elemento (e i suoi figli) in ogni momento utilizzando la funzione <code>tostring()</code> di ElementTree.
</ol>
<p>Quella serializzazione vi ha sorpreso? Il modo in cui ElementTree serializza gli spazi di nomi <abbr>XML</abbr> è tecnicamente accurato ma non ottimale. Il documento <abbr>XML</abbr> di esempio all’inizio di questo capitolo definiva uno <i>spazio di nomi predefinito</i> (<code>xmlns='http://www.w3.org/2005/Atom'</code>). Definire uno spazio di nomi predefinito è utile per documenti — come i feed Atom — dove tutti gli elementi sono nello stesso spazio di nomi, perché potete dichiarare lo spazio di nomi una volta sola e dichiarare ogni elemento utilizzando semplicemente il suo nome locale (<code><feed></code>, <code><link></code>, <code><entry></code>). Non c’è alcun bisogno di usare un prefisso a meno che non vogliate dichiarare elementi appartenenti a un altro spazio di nomi.
<p>Un riconoscitore <abbr>XML</abbr> non sarà in grado di “vedere” alcuna differenza tra un documento <abbr>XML</abbr> con uno spazio di nomi predefinito e un documento <abbr>XML</abbr> con uno spazio di nomi che utilizza un prefisso. La rappresentazione <abbr>DOM</abbr> che risulta da questa serializzazione:
<pre class='nd pp'><code><ns0:feed xmlns:ns0='http://www.w3.org/2005/Atom' xml:lang='it'/></code></pre>
<p>è identica alla rappresentazione <abbr>DOM</abbr> di questa serializzazione:
<pre class='nd pp'><code><feed xmlns='http://www.w3.org/2005/Atom' xml:lang='it'/></code></pre>
<p>L’unica differenza concreta è che la seconda serializzazione è più corta di alcuni caratteri. Se dovessimo riscrivere interamente il nostro feed di esempio con un prefisso <code>ns0:</code> in ogni tag di apertura e chiusura, aggiungeremmo 4 caratteri per tag di apertura × 79 tag + 4 caratteri per la dichiarazione di spazio di nomi, per un totale di 320 caratteri. Assumendo una <a href=stringhe.html#byte-arrays>codifica <abbr>UTF-8</abbr></a>, il totale corrisponde a 320 byte aggiuntivi. (Dopo aver compresso il documento tramite gzip, la differenza scende a 21 byte, ciò nondimeno 21 byte sono 21 byte.) Forse per voi non ha importanza, ma per qualcosa come un feed Atom, che potrebbe essere scaricato diverse migliaia di volte a ogni cambiamento, il risparmio di alcuni byte per ogni richiesta può accumularsi velocemente.
<p>La libreria built-in ElementTree non offre un controllo così accurato sulla serializzazione di elementi in uno spazio di nomi, ma <code>lxml</code> lo fa.
<pre class=screen>
<samp class=p>>>> </samp><kbd class=pp>import lxml.etree</kbd>
<a><samp class=p>>>> </samp><kbd class=pp>NSMAP = {None: 'http://www.w3.org/2005/Atom'}</kbd> <span class=u>①</span></a>
<a><samp class=p>>>> </samp><kbd class=pp>new_feed = lxml.etree.Element('feed', nsmap=NSMAP)</kbd> <span class=u>②</span></a>
<a><samp class=p>>>> </samp><kbd class=pp>print(lxml.etree.tounicode(new_feed))</kbd> <span class=u>③</span></a>
<samp class=pp><feed xmlns='http://www.w3.org/2005/Atom'/></samp>
<a><samp class=p>>>> </samp><kbd class=pp>new_feed.set('{http://www.w3.org/XML/1998/namespace}lang', 'it')</kbd> <span class=u>④</span></a>
<samp class=p>>>> </samp><kbd class=pp>print(lxml.etree.tounicode(new_feed))</kbd>
<samp class=pp><feed xmlns='http://www.w3.org/2005/Atom' xml:lang='it'/></samp></pre>
<ol>
<li>Per cominciare, definite una corrispondenza di spazi di nomi sotto forma di un dizionario in cui i valori siano gli spazi di nomi e le chiavi siano i prefissi desiderati. Usate <code>None</code> come prefisso per dichiarare uno spazio di nomi predefinito.
<li>Ora potete passare l’argomento <var>nsmap</var> specifico per <code>lxml</code> quando create un elemento, così <code>lxml</code> rispetterà i prefissi per gli spazi di nomi che avete definito.
<li>Come vi sareste aspettati, questa serializzazione definisce lo spazio di nomi Atom come spazio di nomi predefinito e dichiara l’elemento <code>feed</code> senza un prefisso per lo spazio di nomi.
<li>Oops, abbiamo dimenticato di aggiungere l’attributo <code>xml:lang</code>. Potete sempre aggiungere attributi a qualsiasi elemento tramite il metodo <code>set()</code>. Questo metodo prende due argomenti: il nome dell’attributo nel formato standard di ElementTree, poi il valore dell’attributo. (Questo metodo non è specifico per <code>lxml</code>. L’unica parte specifica per <code>lxml</code> in questo esempio era l’argomento <var>nsmap</var> usato per controllare i prefissi degli spazi di nomi nell’uscita serializzata.)
</ol>
<p>I documenti <abbr>XML</abbr> si limitano ad avere un elemento per documento? No, naturalmente no. Potete facilmente creare anche elementi figli.
<pre class=screen>
<a><samp class=p>>>> </samp><kbd class=pp>title = lxml.etree.SubElement(new_feed, 'title',</kbd> <span class=u>①</span></a>
<a><samp class=p>... </samp><kbd class=pp> attrib={'type':'html'})</kbd> <span class=u>②</span></a>
<a><samp class=p>>>> </samp><kbd class=pp>print(lxml.etree.tounicode(new_feed))</kbd> <span class=u>③</span></a>
<samp class=pp><feed xmlns='http://www.w3.org/2005/Atom' xml:lang='it'><title type='html'/></feed></samp>
<a><samp class=p>>>> </samp><kbd class=pp>title.text = 'dive into &hellip;'</kbd> <span class=u>④</span></a>
<a><samp class=p>>>> </samp><kbd class=pp>print(lxml.etree.tounicode(new_feed))</kbd> <span class=u>⑤</span></a>
<samp class=pp><feed xmlns='http://www.w3.org/2005/Atom' xml:lang='it'><title type='html'>dive into &amp;hellip;</title></feed></samp>
<a><samp class=p>>>> </samp><kbd class=pp>print(lxml.etree.tounicode(new_feed, pretty_print=True))</kbd> <span class=u>⑥</span></a>
<samp class=pp><feed xmlns='http://www.w3.org/2005/Atom' xml:lang='it'>
<title type='html'>dive into &amp;hellip;</title>
</feed></samp></pre>
<ol>
<li>Per creare un elemento figlio di un elemento esistente, istanziate la classe <code>SubElement</code>. Gli unici argomenti richiesti sono l’elemento genitore (<var>new_feed</var> in questo caso) e il nome del nuovo elemento. Dato che questo elemento figlio erediterà la corrispondenza di spazi di nomi del suo genitore, qui non c’è bisogno di dichiarare nuovamente lo spazio di nomi o il prefisso.
<li>Potete anche passare in ingresso un dizionario di attributi in cui le chiavi siano i nomi degli attributi e i valori siano i valori degli attributi.
<li>Come vi sareste aspettati, il nuovo elemento <code>title</code> è stato creato nello spazio di nomi Atom e inserito come figlio dell’elemento <code>feed</code>. Dato che l’elemento <code>title</code> non ha contenuto testuale e non ha figli propri, <code>lxml</code> lo serializza come un elemento vuoto (utilizzando l’abbreviazione <code>/></code>).
<li>Per impostare il contenuto testuale di un elemento, impostate semplicemente la sua proprietà <code>text</code>.
<li>Ora l’elemento <code>title</code> viene serializzato insieme al suo contenuto testuale. Durante la serializzazione è necessario effettuare l’escape del contenuto testuale che contiene segni di minore oppure caratteri di E commerciale. <code>lxml</code> gestisce questo processo in maniera automatica.
<li>Potete anche utilizzare l’argomento con nome <var>pretty_print</var> per ottenere una stampa formattata del testo serializzato, nella quale viene inserito un ritorno a capo dopo i tag di chiusura e prima dei tag di apertura di elementi che contengono elementi figli ma non includono contenuto testuale. In termini tecnici, <code>lxml</code> aggiunge “spazio bianco non significativo” per rendere il risultato più leggibile.
</ol>
<blockquote class=note>
<p><span class=u>☞</span>Potreste anche voler provare <a href=http://github.com/galvez/xmlwitch/tree/master>xmlwitch</a>, un’altra libreria di terze parti per generare <abbr>XML</abbr> che fa largo uso della <a href=nomi-dei-metodi-speciali.html#context-managers>istruzione <code>with</code></a> per rendere più leggibile il codice di generazione di documenti <abbr>XML</abbr>.
</blockquote>
<p class=a>⁂
<h2 id=xml-custom-parser>Riconoscere documenti XML contenenti errori di malformazione</h2>
<p>La specifica <abbr>XML</abbr> obbliga tutti i riconoscitori <abbr>XML</abbr> conformanti a impiegare una “gestione degli errori draconiana”. Vale a dire che i riconoscitori si devono “fermare e prendere fuoco” non appena scoprono un qualsiasi tipo di errore di malformazione nel documento <abbr>XML</abbr>. Gli errori di malformazione includono tag di apertura e chiusura che non corrispondono, entità non definite, caratteri Unicode illegali e un certo numero di altre regole esoteriche. Questo è in netto contrasto con altri formati comuni come <abbr>HTML</abbr> — il vostro browser non smette di visualizzare una pagina web se dimenticate di chiudere un tag <abbr>HTML</abbr> o di effettuare l’escape di una E commerciale nel valore di un attributo. (È un equivoco comune che <abbr>HTML</abbr> non abbia una gestione degli errori definita. <a href=http://www.whatwg.org/specs/web-apps/current-work/multipage/syntax.html#parsing>La gestione degli errori <abbr>HTML</abbr></a> è in realtà piuttosto ben definita, ma è significativamente più complicata di “fermati e prendi fuoco al primo errore”.)
<p>Alcune persone (me compreso) credono che l’obbligo a una gestione degli errori draconiana sia stato un errore da parte degli inventori di <abbr>XML</abbr>. Non fraintendetemi, posso certamente vedere il fascino di una semplificazione delle regole di gestione degli errori. Ma nella pratica il concetto di “malformazione” è più complicato di quanto sembra, specialmente per documenti <abbr>XML</abbr> (come i feed Atom) che sono pubblicati sul web e serviti attraverso <abbr>HTTP</abbr>. Nonostante la maturità di <abbr>XML</abbr>, che si è assestato sulla gestione draconiana degli errori nel 1997, i rilevamenti mostrano continuamente che una frazione significativa di feed Atom sul web è afflitta da errori di malformazione.
<p>Quindi, ho ragioni sia teoriche che pratiche per riconoscere documenti <abbr>XML</abbr> “a tutti i costi”, cioè per <em>non</em> fermarmi e prendere fuoco al primo errore di malformazione. Se vi trovate a voler fare questa cosa anche voi, <code>lxml</code> può aiutarvi.
<p>Ecco un frammento di un documento <abbr>XML</abbr> difettoso. Ho evidenziato l’errore di malformazione.
<pre class='nd pp'><code><?xml version='1.0' encoding='utf-8'?>
<feed xmlns='http://www.w3.org/2005/Atom' xml:lang='it'>
<title>dive into <mark>&hellip;</mark></title>
...
</feed></code></pre>
<p>Quello è un errore perché l’entità <code>&hellip;</code> non è definita in <abbr>XML</abbr>. (È definita in <abbr>HTML</abbr>.) Se provate a riconoscere questo feed difettoso con le impostazioni di default, <code>lxml</code> si bloccherà sull’entità non definita.
<pre class='nd screen'>
<samp class=p>>>> </samp><kbd class=pp>import lxml.etree</kbd>
<samp class=p>>>> </samp><kbd class=pp>tree = lxml.etree.parse('esempi/feed-broken.xml')</kbd>
<samp class=traceback>Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "lxml.etree.pyx", line 2693, in lxml.etree.parse (src/lxml/lxml.etree.c:52591)
File "parser.pxi", line 1478, in lxml.etree._parseDocument (src/lxml/lxml.etree.c:75665)
File "parser.pxi", line 1507, in lxml.etree._parseDocumentFromURL (src/lxml/lxml.etree.c:75993)
File "parser.pxi", line 1407, in lxml.etree._parseDocFromFile (src/lxml/lxml.etree.c:75002)
File "parser.pxi", line 965, in lxml.etree._BaseParser._parseDocFromFile (src/lxml/lxml.etree.c:72023)
File "parser.pxi", line 539, in lxml.etree._ParserContext._handleParseResultDoc (src/lxml/lxml.etree.c:67830)
File "parser.pxi", line 625, in lxml.etree._handleParseResult (src/lxml/lxml.etree.c:68877)
File "parser.pxi", line 565, in lxml.etree._raiseParseError (src/lxml/lxml.etree.c:68125)
lxml.etree.XMLSyntaxError: Entity 'hellip' not defined, line 3, column 28</samp></pre>
<p>Per riconoscere questo documento <abbr>XML</abbr> difettoso nonostante il suo errore di malformazione, avete bisogno di creare un riconoscitore <abbr>XML</abbr> personalizzato.
<pre class=screen>
<a><samp class=p>>>> </samp><kbd class=pp>parser = lxml.etree.XMLParser(recover=True)</kbd> <span class=u>①</span></a>
<a><samp class=p>>>> </samp><kbd class=pp>tree = lxml.etree.parse('esempi/feed-broken.xml', parser)</kbd> <span class=u>②</span></a>
<a><samp class=p>>>> </samp><kbd class=pp>parser.error_log</kbd> <span class=u>③</span></a>
<samp>esempi/feed-broken.xml:3:28:FATAL:PARSER:ERR_UNDECLARED_ENTITY: Entity 'hellip' not defined</samp>
<samp class=p>>>> </samp><kbd class=pp>tree.findall('{http://www.w3.org/2005/Atom}title')</kbd>
<samp>[<Element {http://www.w3.org/2005/Atom}title at ead510>]</samp>
<samp class=p>>>> </samp><kbd class=pp>title = tree.findall('{http://www.w3.org/2005/Atom}title')[0]</kbd>
<a><samp class=p>>>> </samp><kbd class=pp>title.text</kbd> <span class=u>④</span></a>
<samp class=pp>'dive into '</samp>
<a><samp class=p>>>> </samp><kbd class=pp>print(lxml.etree.tounicode(tree.getroot()))</kbd> <span class=u>⑤</span></a>
<samp class=pp><feed xmlns='http://www.w3.org/2005/Atom' xml:lang='it'>
<title>dive into </title>
.
. [il resto della serializzazione viene omesso per brevità]
.</samp></pre>
<ol>
<li>Per creare un riconoscitore personalizzato, istanziate la classe <code>lxml.etree.XMLParser</code>. Questa classe può prendere <a href=http://codespeak.net/lxml/parsing.html#parser-options>un certo numero di differenti argomenti con nome</a>. Quello a cui siamo interessati qui è l’argomento <var>recover</var>. Quando questo argomento viene impostato a <code>True</code>, il riconoscitore <abbr>XML</abbr> farà del suo meglio per “compensare” gli errori di malformazione.
<li>Per riconoscere un documento <abbr>XML</abbr> utilizzando il vostro riconoscitore personalizzato, passate l’oggetto <var>parser</var> come secondo argomento alla funzione <code>parse()</code>. Notate che ora <code>lxml</code> non solleva alcuna eccezione relativa all’entità <code>&hellip;</code> non definita.
<li>Il riconoscitore mantiene un registro degli errori di malformazione che ha incontrato. (In effetti, questo è vero a prescindere dal fatto di averlo impostato per compensare quegli errori oppure no.)
<li>Dato che non sapeva cosa fare con l’entità <code>&hellip;</code> non definita, il riconoscitore l’ha semplicemente scartata in silenzio. Il contenuto testuale dell’elemento <code>title</code> diventa <code>'dive into '</code>.
<li>Come potete vedere dalla serializzazione, l’entità <code>&hellip;</code> non è stata spostata, ma è stata semplicemente scartata.
</ol>
<p>È importante sottolineare che <strong>non c’è alcuna garanzia di interoperabilità</strong> quando si utilizzano riconoscitori <abbr>XML</abbr> che “compensano” gli errori di malformazione. Un riconoscitore differente potrebbe decidere che ha riconosciuto l’entità <code>&hellip;</code> da <abbr>HTML</abbr>, rimpiazzandola poi con <code>&amp;hellip;</code>. Questa soluzione è “migliore”? Forse. È “più corretta”? No, entrambi i riconoscitori sbagliano allo stesso modo. Il comportamento corretto (in accordo con la specifica <abbr>XML</abbr>) è di fermarsi e prendere fuoco. Se avete deciso di non farlo, siete abbandonati a voi stessi.
<p class=a>⁂
<h2 id=furtherreading>Letture di approfondimento</h2>
<ul>
<li><a href=http://en.wikipedia.org/wiki/XML><abbr>XML</abbr> su Wikipedia.org</a>
<li><a href=http://docs.python.org/3.1/library/xml.etree.elementtree.html>La API <abbr>XML</abbr> di ElementTree</a>
<li><a href=http://effbot.org/zone/element.htm>Elementi e alberi di elementi</a>
<li><a href=http://effbot.org/zone/element-xpath.htm>Il supporto XPath in ElementTree</a>
<li><a href=http://effbot.org/zone/element-iterparse.htm>La funzione iterparse di ElementTree</a>
<li><a href=http://codespeak.net/lxml/><code>lxml</code></a>
<li><a href=http://codespeak.net/lxml/1.3/parsing.html>Riconoscere <abbr>XML</abbr> e <abbr>HTML</abbr> con <code>lxml</code></a>
<li><a href=http://codespeak.net/lxml/1.3/xpathxslt.html>Usare XPath e <abbr>XSLT</abbr> con <code>lxml</code></a>
<li><a href=http://github.com/galvez/xmlwitch/tree/master>xmlwitch</a>
</ul>
<p class=v><a href=file.html rel=prev title='indietro a “File”'><span class=u>☜</span></a> <a href=serializzare-oggetti-python.html rel=next title='avanti a “Serializzare oggetti Python”'><span class=u>☞</span></a>
<p class=c>© 2001–10 <a href=informazioni-sul-libro.html>Mark Pilgrim</a><br>
© 2009–10 <a href=informazioni-sulla-traduzione.html>Giulio Piancastelli</a> per la traduzione italiana
<script src=j/jquery.js></script>
<script src=j/prettify.js></script>
<script src=j/dip3.js></script>