| You are here: Inicio > Inmersión en Python > Scripts y flujos | << >> | ||||
Inmersión en PythonPython de novato a experto |
|||||
Uno de los puntos más fuertes de Python es su enlace dinámico, y un uso muy potente del enlace dinámico es el objeto tipo fichero.
Muchas funciones que precisan una entrada de datos podrían simplemente tomar un nombre fichero, abrirlo para lectura, leerlo y cerrarlo cuando hubieran acabado. Pero no lo hacen. En su lugar toman un objeto de tipo fichero.
En su caso más sencillo un objeto tipo fichero es cualquier objeto con un método read con un parámetro size opcional, que devuelve una cadena. Cuando se le invoca sin el parámetro size, lee cualquier cosa que quede dentro de la fuente de datos y devuelve todos esos datos en una sola cadena. Cuando se la invoca con el parámetro size, lee y de vuelve sólo esa cantidad de datos desde la fuente; cuando la invocan de nuevo continúa donde lo dejó y devuelve el siguiente paquete de datos.
Así es como funciona la lectura desde ficheros reales; la diferencia es que no nos estamos limitando a ficheros. La fuente de entrada puede ser cualquier cosa: un fichero en el disco, una página web e incluso una cadena de contenido fijo. Mientras pase a la función un objeto que parezca un fichero, y la función sólo llame al método read del objeto, la función puede manejar cualquier tipo de fuente de datos sin necesidad de código específico para cada tipo.
En caso de que se esté preguntando qué tiene esto que ver con el procesamiento de XML, minidom.parse es una de esas funciones que puede tomar objetos de fichero.
>>> from xml.dom import minidom >>> fsock = open('binary.xml')>>> xmldoc = minidom.parse(fsock)
>>> fsock.close()
>>> print xmldoc.toxml()
<?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>
| Primero abrimos el fichero del disco. Esto nos da un objeto de fichero. | |
| Pasamos el objeto de fichero a minidom.parse, que llama al método read de fsock y lee el documento XML del fichero. | |
| Asegúrese de llamar al método close del objeto de fichero tras haber terminado con él. minidom.parse no lo hará automáticamente. | |
| Llamar al método toxml() del documento XML devuelto lo imprime entero. |
Bien, todo eso parece una colosal pérdida de tiempo. Después de todo, ya hemos visto que minidom.parse puede tomar el nombre del fichero y hacer automáticamente esas tonterías de abrirlo y cerrarlo. Y es cierto que si sabes que sólo vas a analizar un fichero local, se le puede pasar el nombre y minidom.parse es lo suficientemente inteligente para Hacer Lo Correcto™. Pero observe lo parecido (y sencillo) que es analizar un documento XML sacado directamente de 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> [...corte...]
| Como vio en un capítulo anterior, urlopen toma la URL de una página web y devuelve un objeto de tipo fichero. Aún más importante, este objeto tiene un método read que devuelve el HTML fuente de la página web. | |
| Ahora pasamos el objeto de fichero a minidom.parse que invoca obedientemente el método read del objeto y analiza los datos XML que devuelve read. El hecho de que los datos XML estén viniendo directamente de la página web es completamente irrelevante. minidom.parse no sabe qué son las páginas web, y no le importan las páginas web; sólo sabe tratar con ficheros de tipo objeto. | |
| Tan pronto como haya acabado, asegúrese de cerrar el objeto de fichero que le da urlopen. | |
| Por cierto, esta URL es real y verdaderamente es XML. Es una representación XML de los últimos titulares de Slashdot, un sitio de noticias y chismorreo tecnológico. |
>>> 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>
Bien, así que podemos usar la función minidom.parse para analizar tanto ficheros locales como URLs remotas, pero para analizar cadenas usamos... una función diferente. Eso significa que si quiere poder tomar la entrada desde un fichero, una URL o una cadena, necesita una lógica especial para comprobar si es una cadena, y llamar a parseString en su lugar. Qué insatisfactorio.
Si hubiera una manera de convertir una cadena en un objeto de fichero, entonces podríamos pasársela simplemente a minidom.parse. De hecho, hay un módulo diseñado específicamente para hacer eso: 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)>>> ssock.close() >>> print xmldoc.toxml() <?xml version="1.0" ?> <grammar><ref id="bit"><p>0</p><p>1</p></ref></grammar>
Así que ya sabemos cómo usar una única función, minidom.parse, para analizar un documento XML almacenado en una página web, en un fichero local o en una cadena fija. Para una página web usaremos urlopen para obtener un objeto de tipo fichero; para un fichero local usaremos open; y para una cadena, usaremos StringIO. Ahora llevémoslo un paso más adelante y generalicemos también éstas diferencias.
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 función openAnything toma un solo parámetro, source, y devuelve un fichero de tipo objeto. source es una cadena de algún tipo; que puede ser una URL (como 'http://slashdot.org/slashdot.rdf'), una ruta absoluta o parcial a un fichero local (como 'binary.xml') o una cadena que contenga los datos del XML a ser analizado, propiamente dicho. | |
| Primero vemos si source es una URL. Lo hacemos mediante fuerza bruta: intentamos abrirlo como una URL e ignoramos los errores causados por abrir algo que no es una URL. En realidad esto es elegante en el sentido de que si urllib admite alguna vez nuevos tipos de URL, usted les estará dando soporte sin tener que reprogramarlo. Si urllib es capaz de abrir source, entonces return le saca de la función inmediatamente y no se llegan a ejecutar las siguientes sentencias try. | |
| Por otro lado, si urllib le grita diciéndole que source no es una URL válida, asumirá que es la ruta a un fichero en el disco e intentará abrirlo. De nuevo, no usaremos ninguna sofisticación para comprobar si source es un nombre de fichero válido o no (de todas maneras, las reglas para la validez de los nombres de fichero varían mucho entre plataformas, así que probablemente lo haríamos mal). En su lugar, intentará abrir el fichero a ciegas y capturaremos cualquier error en silencio. | |
| A estas alturas debemos asumir que source es una cadena que contiene los datos (ya que lo demás no ha funcionado), así que usaremos StringIO para crear un objeto de tipo fichero partiendo de ella y devolveremos eso. (En realidad, dado que usamos la función str, source ni siquiera tiene por qué ser una cadena; puede ser cualquier objeto y usaremos su representación como cadena, tal como define su método especial __str__). |
Ahora podemos usar la función openAnything junto con minidom.parse para construir una función que tome una source que se refiera de alguna manera a un documento XML (una URL, un fichero local o un documento XML fijado en una cadena) y lo analice.
<< Transición |
| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | |
Entrada, salida y error estándar >> |