You are here: Partenza > Dive Into Python > Elaborare XML > Astrarre le sorgenti di ingresso | << >> | ||||
Dive Into PythonPython per programmatori esperti |
Uno dei punti di forza di Python è il suo binding dinamico, ed uno degli usi più potenti del binding dinamico sono gli oggetti file (ovvero, che si comportano come dei file).
Molte funzioni che richiedono una sorgente di ingresso potrebbero semplicemente prendere un nome di un file, aprirle il file in lettura, leggerlo e chiuderlo quando hanno finito. Ma non lo fanno. Prendono, invece, degli oggetti file.
Nel caso più semplice, un oggetto file è un oggetto dotato di un metodo read con un parametro opzionale size, che ritorna una stringa. Quando viene chiamato senza il parametro size, legge tutto ciò che c'è da leggere dalla sorgente di ingresso e ritorna tutti i dati come una sola stringa. Quando viene chiamato con il parametro size, legge quella quantità dalla sorgente di ingresso e ritorna quella quantità di dati; quando viene chiamato nuovamente, riprende da dove aveva lasciato e ritorna il prossimo spezzone di dati.
È così che funziona la lettura dai veri file; la differenza è che non ci stiamo limitando ai veri file. La sorgente di ingresso potrebbe essere qualunque cosa: un file su disco, una pagina web, anche una stringa hard-coded. Fino a quando passiamo un oggetto file-like alla funzione e la funzione chiama semplicemente il metodo read dell'oggetto, la funzione può gestire ogni genere di sorgente di ingresso senza la necessità di codice specifico per ogni tipo.
Nel caso vi stiate chiedendo cosa ha a che vedere con l'analisi dell'XML, minidom.parse è una di quelle funzioni che può prendere un oggetto file.
>>> from xml.dom import minidom >>> fsock = open('binary.xml') >>> xmldoc = minidom.parse(fsock) >>> fsock.close() >>> print xmldoc <?xml version="1.0" ?> <grammar> <ref id="bit"> <p>0</p> <p>1</p> </ref> <ref id="byte"> <p><xref id="bit"/><xref id="bit"/><xref id="bit"/><xref id="bit"/>\ <xref id="bit"/><xref id="bit"/><xref id="bit"/><xref id="bit"/></p> </ref> </grammar>
Per prima cosa, apriamo il file su disco. Questo ci restituisce un oggetto di tipo file. | |
Passiamo l'oggetto file a minidom.parse, che chiama il metodo read di fsock e legge il documento XML dal file su disco. | |
Siate sicuri di chiamare il metodo close sull'oggetto file quando avete finito. minidom.parse non lo farà per voi. |
Bene, tutto questo sembra una colossale perdita di tempo. Dopo tutto, abbiamo già visto che minidom.parse può semplicemente prendere il nome del file ed effettuare tutte le operazioni di apertura e chiusura autonomamente. Ed è vero che se sapete di dover analizzare un file locale, potete passargli il nome del file e minidom.parse è sufficientemente intelligente da Fare La Cosa Giusta ™. Ma notate quanto sia simile, e facile, analizzare un documento XML direttamente da Internet.
>>> import urllib >>> usock = urllib.urlopen('http://slashdot.org/slashdot.rdf') >>> xmldoc = minidom.parse(usock) >>> usock.close() >>> print xmldoc.toxml() <?xml version="1.0" ?> <rdf:RDF xmlns="http://my.netscape.com/rdf/simple/0.9/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <channel> <title>Slashdot</title> <link>http://slashdot.org/</link> <description>News for nerds, stuff that matters</description> </channel> <image> <title>Slashdot</title> <url>http://images.slashdot.org/topics/topicslashdot.gif</url> <link>http://slashdot.org/</link> </image> <item> <title>To HDTV or Not to HDTV?</title> <link>http://slashdot.org/article.pl?sid=01/12/28/0421241</link> </item> [...snip...]
Come abbiamo visto nel capitolo precedente, urlopen prende una pagina web URL e restituisce un oggetto file. Più importante, questo oggetto ha un metodo read che restituisce il sorgente HTML della pagina web. | |
Ora passiamo l'oggetto file a minidom.parse, che obbedientemente chiama il metodo read dell'oggetto ed analizza i dati XML che il metodo read restituisce. Il fatto che questi dati XML stiano arrivando da una pagina web è completamente irrilevante. minidom.parse non sa niente di pagine web e non gli importa nulla delle pagine web; sa solo come usare gli oggetti file. | |
Non appena avete finito, siate certi di chiudere l'oggetto file-like che urlopen restituisce. | |
A proposito, questa URL è reale e contiene davvero XML. È una rappresentazione XML delle attuali intestazioni su Slashdot, un sito di tecnica e pettegolezzi. |
>>> contents = "<grammar><ref id='bit'><p>0</p><p>1</p></ref></grammar>" >>> xmldoc = minidom.parseString(contents) >>> print xmldoc.toxml() <?xml version="1.0" ?> <grammar><ref id="bit"><p>0</p><p>1</p></ref></grammar>
Ok, allora possiamo usare la funzione minidom.parse per analizzare sia file locali che URL remote, ma per analizzare stringhe, usiamo ... un'altra funzione. Significa che se vogliamo essere in grado di leggere un file, una URL o una stringa, abbiamo bisogno di una logica speciale che controlli se è una stringa, e chiamare la funzione parseString al suo posto. Davvero insoddisfacente.
Se ci fosse un modo per trasformare una stringa in un oggetto file-like, allora potremmo semplicemente passare tale oggetto a minidom.parse. Ed infatti, c'è un modulo specificamente disegnato per svolgere questo compito: StringIO.
>>> contents = "<grammar><ref id='bit'><p>0</p><p>1</p></ref></grammar>" >>> import StringIO >>> ssock = StringIO.StringIO(contents) >>> ssock.read() "<grammar><ref id='bit'><p>0</p><p>1</p></ref></grammar>" >>> ssock.read() '' >>> ssock.seek(0) >>> ssock.read(15) '<grammar><ref i' >>> ssock.read(15) "d='bit'><p>0</p" >>> ssock.read() '><p>1</p></ref></grammar>' >>> ssock.close()
>>> contents = "<grammar><ref id='bit'><p>0</p><p>1</p></ref></grammar>" >>> ssock = StringIO.StringIO(contents) >>> xmldoc = minidom.parse(ssock) >>> print xmldoc.toxml() <?xml version="1.0" ?> <grammar><ref id="bit"><p>0</p><p>1</p></ref></grammar>
Così ora sappiamo come usare una singola funzione, minidom.parse, per analizzare un documento XML memorizzato in una pagina web, in un file locale o in una stringa hard-coded. Per una pagina web, usiamo urlopen per ottenere un oggetto file; per un file locale, usiamo open; per una stringa, usiamo StringIO. Proseguiamo ora di un ulteriore passo per generalizzare meglio questa differenza.
def openAnything(source): # try to open with urllib (if source is http, ftp, or file URL) import urllib try: return urllib.urlopen(source) except (IOError, OSError): pass # try to open with native open function (if source is pathname) try: return open(source) except (IOError, OSError): pass # treat source as string import StringIO return StringIO.StringIO(str(source))
La funzione openAnything prende un solo parametro, source, e ritorna un oggetto file. source è una stringa di qualche tipo; può essere una URL (come 'http://slashdot.org/slashdot.rdf'), un percorso completo o parziale verso un file locale (come 'binary.xml'), o una stringa che contiene i dati XML da analizzare. | |
Per prima cosa, vediamo se source è una URL. Lo facciamo usando la forza bruta: proviamo ad aprirla come una URL e silenziosamente ignoriamo gli errori causati cercando di aprire qualcosa che non è una URL. È elegante nel senso che, se urllib mai supporterà nuovi tipi di URL in futuro, li supporteremo anche noi senza problemi. | |
Se urllib ci sgrida sostenendo che source non è una URL valida, assumiamo che sia il percorso di un file su disco e proviamo ad aprirlo. Ancora, non facciamo nulla per controllare se source è un nome di file valido o meno (le regole sulla validità del nome di un file variano molto tra diverse piattaforme, dunque le sbaglieremmo comunque). Invece, proviamo ciecamente ad aprire il file e silenziosamente catturiamo gli errori. | |
A questo punto, dobbiamo assumere che source sia una stringa con dei dati precodificati all'interno (visto che nient'altro ha funzionato), allora usiamo StringIO per creare un oggetto file da essa e lo ritorniamo. (Infatti, siccome stiamo usando la funzione str, source non deve necessariamente essere una stringa; potrebbe essere qualunque oggetto e noi useremo la sua rappresentazione sotto forma di stringa, come definita dal metodo speciale __str__.) |
Ora possiamo usare la funzione openAnything in congiunzione con minidom.parse per creare una funzione, che prende un parametro source facente riferimento in qualche modo ad un documento XML (sia una URL, od il nome di un file locale o una stringa contenente un documento XML) e lo analizza.
<< Accedere agli attributi di un elemento |
| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | |
Standard input, output, ed error >> |