1XML¶
XML (eXtensible Markup Language) es un lenguaje de marcado que define un conjunto de reglas para la codificación de documentos en un formato que es tanto legible por humanos como por máquinas. Fue diseñado para almacenar y transportar datos, y es ampliamente utilizado en aplicaciones web, servicios web y sistemas de intercambio de datos.
XML permite a los usuarios definir sus propias etiquetas y estructuras de datos, lo que lo hace muy flexible y adaptable a diferentes necesidades. A diferencia de HTML, que tiene un conjunto fijo de etiquetas predefinidas, XML permite crear etiquetas personalizadas que describen el contenido de manera más precisa.
XML es un estándar abierto mantenido por el World Wide Web Consortium (W3C) y es ampliamente utilizado en diversas aplicaciones, incluyendo:
Intercambio de datos entre sistemas diferentes.
Configuración de aplicaciones.
Almacenamiento de datos estructurados.
Representación de documentos complejos (como docx, xlsx, etc.)
Difusión de Noticias (RSS, Atom).
1.1Estructura de un documento XML¶
Un documento XML está compuesto por varios elementos clave:
- Prólogo
- Es la parte inicial del documento que puede incluir una declaración XML y otras instrucciones, como la definición de la codificación de caracteres. La declaración XML es opcional pero recomendada, y suele verse así:
<?xml version="1.0" encoding="UTF-8"?>. - Elementos
- Son las unidades básicas de un documento XML y están delimitados por etiquetas de apertura y cierre. Por ejemplo,
<nombre>Juan</nombre>es un elemento que contiene el texto “Juan”. - Atributos
- Son pares clave-valor que proporcionan información adicional sobre un elemento. Se incluyen dentro de la etiqueta de apertura. Por ejemplo,
<persona id="123">tiene un atributoidcon el valor “123”. - Contenido
- Es el texto o los datos que se encuentran entre las etiquetas de apertura y cierre de un elemento.
- Comentarios
- Se pueden incluir comentarios en un documento XML utilizando la sintaxis
<!-- Comentario -->.
El siguiente fragmento muestra un poema estructurado en XML
<?xml version="1.0" encoding="UTF-8"?>
<poema fecha="Abril de 1915" lugar="Granada">
<titulo>Alba</titulo>
<verso>Mi corazón oprimido</verso>
<verso>siente junto a la alborada</verso>
<verso>el dolor de sus amores</verso>
<verso>y el sueño de las distancias. </verso>
</poema>donde se pueden distinguir claramente los diferentes elementos:
poematituloverso
y los atributos de poema
@fecha@lugar
Como se puede observar XML permite estructurar la información en forma jerárquica, como si fuera un árbol, donde los elementos pueden contener otros elementos anidados. Un archivo XML bien formado debe cumplir con ciertas reglas, como tener un único elemento raíz que contenga todos los demás elementos, y todas las etiquetas deben estar correctamente cerradas y anidadas.
Los atributos en XML permiten agregar metadatos o información adicional a los elementos, lo que puede ser útil para describir propiedades o características específicas de los datos. Sin embargo, es importante usarlos con moderación y de manera coherente para evitar complicaciones en la interpretación del documento.
Árbol que representa la estructura del poema en XML.
Árbol que representa la estructura del poema en XML.
Existen varios sitios en internet que permiten visualizar el árbol asociado. Por ejemplo: Code Beautify.
En el ejemplo anterior, el elemento raíz es poema, que contiene como elementos hijos: titulo y varios elementos verso. El elemento poema también tiene dos atributos: fecha y lugar, que proporcionan información adicional sobre el poema.
Que un documento XML se pueda representar como un árbol simplifica la consulta y manipulación de los datos, ya que se pueden utilizar técnicas de recorrido de árboles para acceder a elementos específicos o extraer información relevante.
2XPath: XML Path Language¶
XPath (XML Path Language) es un lenguaje de consulta utilizado para navegar y seleccionar nodos en documentos XML. Proporciona una sintaxis para definir rutas que permiten localizar elementos, atributos y otros nodos dentro de la estructura jerárquica de un documento XML.
Permite describir caminos a través del árbol XML utilizando una notación similar a la de los sistemas de archivos. Por ejemplo, la expresión /poema/titulo selecciona el elemento titulo que es hijo directo del elemento raíz poema. Evaluar una expresión XPath es buscar elementos o atributos en un documento XML que coincidan con los criterios especificados en la expresión. El resultado son todos los nodos que cumplen con esos criterios.
Por ejemplo, la expresión //verso selecciona todos los elementos verso en el documento, independientemente de su posición en la jerarquía. La expresión //verso[text()="Mi corazón oprimido"] selecciona el elemento verso que contiene el texto exacto “Mi corazón oprimido”.
En los ejemplos a continuación usaremos el siguiente documento XML que representa una biblioteca con varios libros y autores.
<?xml version="1.0" encoding="UTF-8"?>
<biblioteca>
<libro>
<titulo>La vida está en otra parte</titulo>
<autor>Milan Kundera</autor>
<fechaPublicacion año="1973" />
<precio>305.50</precio>
</libro>
<revista>
<titulo>Computer Gaming World</titulo>
<editorial>Golden Empire Publications</editorial>
<fechaPublicacion año="1981" />
<precio>669.99</precio>
</revista>
<libro>
<titulo>Pantaleón y las visitadoras</titulo>
<autor fechaNacimiento="28/03/1936">Mario Vargas Llosa</autor>
<fechaPublicacion año="1973" />
<precio>214.48</precio>
</libro>
<libro>
<titulo>Conversación en la catedral</titulo>
<autor fechaNacimiento="28/03/1936">Mario Vargas Llosa</autor>
<fechaPublicacion año="1969" />
<precio>541.78</precio>
</libro>
<revista>
<titulo>PC Users</titulo>
<editorial>RedUsers</editorial>
<fechaPublicacion año="2000" />
<precio>220.50</precio>
</revista>
</biblioteca>
2.1Expresiones XPath para seleccionar nodos¶
| Expresión | Descripción |
|---|---|
/ | Si está al principio de la expresión, indica el nodo raíz, si no, indica “hijo” |
// | Camino. Permite seleccionar nodos en un camino descendiente a partir de la posición actual |
. | Nodo actual |
.. | Padre del nodo actual |
@nombre_atributo | Atributo |
Cada consulta XPath devuelve un conjunto de nodos que cumplen con los criterios especificados en la expresión. Se recomienda realizar pruebas en XPath Tester.
/biblioteca- Selecciona el nodo raíz
biblioteca. /biblioteca/libro- Selecciona todos los nodos
libroque son hijos directos debiblioteca. //autor- Selecciona todos los nodos
autoren el documento, independientemente de su posición en la jerarquía. /biblioteca//titulo- Selecciona todos los nodos
tituloque son descendientes debiblioteca, sin importar cuántos niveles haya entre ellos. En este caso, selecciona los títulos de libros y revistas. //libro/precio- Selecciona todos los nodos
precioque son hijos directos de cualquier nodolibro. //editorial/..- Selecciona el nodo padre de todos los nodos
editorial, que en este caso son nodosrevista. //autor[@fechaNacimiento]- Selecciona todos los nodos
autorque tengan un atributofechaNacimiento.
2.2Predicados¶
Los predicados pueden ser usados para filtrar un conjunto de nodos en base a una condición dada.
Los predicados se escriben entre corchetes ([, ]).
/biblioteca/libro[position() = 1]
: Selecciona el primer nodo libro hijo de biblioteca.
/biblioteca/libro[1]- Equivalente a la expresión anterior, selecciona el primer nodo
librohijo debiblioteca. /biblioteca/libro[last()]- Selecciona el último nodo
librohijo debiblioteca. /biblioteca/libro[last() - 1]- Selecciona el penúltimo nodo
librohijo debiblioteca. /biblioteca/libro[position() < 3]- Selecciona los dos primeros nodos
librohijos debiblioteca. //autor[not(@fechaNacimiento)]- Selecciona todos los nodos
autorque no tengan un atributofechaNacimiento. //autor[@fechaNacimiento="28/03/1936"]- Selecciona todos los nodos
autorcuyo atributofechaNacimientotenga el valor “28/03/1936”.
2.3Selectores y comodines¶
| Expresión | Resultado |
|---|---|
* | Todos los elementos en el nivel actual |
@* | Todos los atributos del nodo actual |
text() | El contenido de texto de un nodo |
/biblioteca/*- Selecciona todos los elementos hijos directos de biblioteca.
/biblioteca//*- Selecciona todos los elementos descendientes de biblioteca.
//autor[@*]- Selecciona todos los elementos autor que tengan algún atributo.
node()- Selecciona todos los nodos del documento.
//titulo/text()- Selecciona el texto (no el nodo completo) de los títulos.
2.4Selección de varios caminos¶
El operador | es el operador de unión permite seleccionar distintos caminos en el documento.
//libro/titulo | //libro/precio- Selecciona todos los nodos titulo y todos los nodos precio hijos de libro |
2.5Comparaciones¶
| Operador | Descripción | Ejemplo |
|---|---|---|
= | Igualdad | precio = 541.78 |
!= | Distinto | precio != 541.78 |
< | Menor estricto | precio < 500 |
<= | Menor o igual | precio <= 541.78 |
> | Mayor estricto | precio > 500 |
>= | Mayor o igual | precio >= 541.78 |
/biblioteca//libro[precio < 350]- Selecciona todos los nodos
libroque tengan como hijo directo un elementopreciocon valor menor a 350. /biblioteca//libro[precio < 350]/titulo- Selecciona todos los nodos
titulohijos de nodoslibroque tengan como hijo directo un elementopreciocon valor menor a 350.
2.6Operadores¶
| Operador | Descripción | Ejemplo |
|---|---|---|
+ | Suma | 6 + 4 |
- | Sustracción | 6 - 4 |
* | Multiplicación | 6 * 4 |
div | División | 8 div 4 |
or | Disyunción | precio = 541.78 or precio = 214.48 |
and | Conjunción | precio > 300 and precio <= 541.78 |
mod | Módulo (resto de la división entera) | 5 mod 2 |
/biblioteca//libro[precio > 300 and precio <= 541.78]- Selecciona todos los nodos
libroque tengan como hijo directo un elementopreciocon valor mayor a 300 y menor o igual a 541.78. /biblioteca//libro[precio = 541.78 or precio = 214.48]- Selecciona todos los nodos
libroque tengan como hijo directo un elementopreciocon valor igual a 541.78 o 214.48. /biblioteca//libro[precio + 100 > 600]- Selecciona los nodos
librodonde el valor del elementopreciosumado a 100 es mayor que 600.
3Recuperación de Información en XML¶
En el contexto de la búsqueda académica y profesional Manning et al., 2008, la recuperación de información sobre documentos XML se divide principalmente en dos tipos de consultas:
- Consultas CO (Content-Only)
- Tratan al XML como una colección de texto plano, ignorando las etiquetas para la búsqueda pero usándolas quizás para mostrar fragmentos de resultados.
- Consultas CAS (Content-and-Structure)
- Permiten al usuario especificar condiciones tanto sobre el contenido como sobre la estructura. Por ejemplo: “buscar libros sobre ‘Python’ donde el autor sea ‘Milan Kundera’”. XPath es la herramienta por excelencia para expresar este tipo de consultas.
3.1El modelo de árbol extendido¶
Para procesar estas consultas, los motores de búsqueda suelen modelar el XML como un árbol extendido donde cada nodo es una unidad indexable. Un desafío clave es decidir la granularidad del índice: ¿deberíamos indexar el libro completo, cada capítulo o cada párrafo? Esta decisión impacta directamente en la precisión y el recall del sistema.
4Características de XPath 2.0¶
XPath 2.0 introduce mejoras significativas sobre la versión 1.0, incluyendo un sistema de tipos más rico, soporte para secuencias, expresiones condicionales y cuantificadores, así como una amplia biblioteca de funciones.
4.1Funciones integradas¶
XPath 2.0 ofrece una gran cantidad de funciones para manipular cadenas, números, fechas y secuencias.
| Función | Descripción | Ejemplo |
|---|---|---|
count(nodos) | Cuenta el número de nodos en una secuencia | count(//libro) |
sum(nodos) | Suma los valores numéricos de los nodos | sum(//precio) |
avg(nodos) | Calcula el promedio de los valores | avg(//precio) |
min(nodos) | Devuelve el valor mínimo | min(//precio) |
max(nodos) | Devuelve el valor máximo | max(//precio) |
contains(s1, s2) | Verdadero si s1 contiene s2 | contains(titulo, 'XML') |
starts-with(s1, s2) | Verdadero si s1 empieza con s2 | starts-with(titulo, 'A') |
ends-with(s1, s2) | Verdadero si s1 termina con s2 | ends-with(titulo, '.') |
upper-case(s) | Convierte la cadena a mayúsculas | upper-case('hola') |
string-length(s) | Devuelve la longitud de la cadena | string-length('abc') |
matches(s, regex) | Verdadero si la cadena cumple con la expresión regular | matches('123', '^\d+$') |
4.2Secuencias¶
En XPath 2.0, todo es una secuencia. Un valor simple es una secuencia de un solo elemento. Las secuencias se pueden construir con paréntesis y comas.
(1, 2, 3): Una secuencia de tres enteros.(1 to 5): Genera la secuencia(1, 2, 3, 4, 5).//libro/titulo: Devuelve una secuencia con todos los nodos título.
4.3Expresiones condicionales (if-then-else)¶
Permiten evaluar condiciones y devolver diferentes resultados.
if (@precio > 500) then 'Caro' else 'Barato'4.4Cuantificadores (some y every)¶
Permiten verificar si alguno o todos los elementos de una secuencia cumplen una condición.
some: Verdadero si al menos un elemento cumple la condición.some $x in //precio satisfies $x > 1000every: Verdadero si todos los elementos cumplen la condición.every $x in //precio satisfies $x > 0
4.5Tablas de referencia rápida¶
Descargar la Hoja de Referencia de XPath 2.0 en PDF.
5XML y Python¶
Python ofrece varias bibliotecas para trabajar con XML, siendo las más comunes xml.etree.ElementTree (nativa), lxml y xml.dom.minidom.
Si bien xml.etree.ElementTree es parte de la biblioteca estándar, su soporte para XPath es limitado (solo soporta XPath 1.0 simplificado). Para utilizar todas las capacidades de XPath 2.0 (incluyendo funciones), utilizaremos la biblioteca elementpath en conjunto con xml.etree.ElementTree.
elementpath es una biblioteca de Python que implementa completamente el estándar XPath 2.0 (y versiones posteriores) para la navegación y consulta de documentos XML. Complementa a xml.etree.ElementTree al permitir el uso de expresiones XPath más complejas y potentes que las que soporta la implementación nativa de Python.
Para instalar elementpath, se puede usar pip:
pip install elementpathfrom xml.etree import ElementTree
import elementpath
# Cargar el documento XML
tree = ElementTree.parse("../_static/code/xml/biblioteca.xml")
root = tree.getroot()
# Realizar una consulta XPath
# Seleccionar todos los títulos de libros
titulos = elementpath.select(root, "/biblioteca/libro/titulo/text()")
print("Títulos de libros:")
for titulo in titulos:
print(titulo)
# Seleccionar todos los autores con fecha de nacimiento
autores_con_fecha = elementpath.select(root, "//autor[@fechaNacimiento]/text()")
print("Autores con fecha de nacimiento:")
for autor in autores_con_fecha:
print(autor)
# Seleccionar libros con precio menor a 300
libros_baratos = elementpath.select( root, "/biblioteca/libro[precio < 300]/titulo/text()")
print("Libros con precio menor a 300:")
for libro in libros_baratos:
print(libro)Output
Títulos de libros:
La vida está en otra parte
Pantaleón y las visitadoras
Conversación en la catedral
Autores con fecha de nacimiento:
Mario Vargas Llosa
Mario Vargas Llosa
Libros con precio menor a 300:
Pantaleón y las visitadoras
Otro ejemplo: Calcular el precio total de los libros
# Calcular el precio total de todos los libros usando la función sum
# de XPath 2.0
total_precio = elementpath.select(root, "sum(/biblioteca/libro/precio)")
print(f"Precio total de todos los libros: {total_precio:.2f}")Output
Precio total de todos los libros: 1061.76
5.1Ejemplos avanzados con XPath 2.0¶
A continuación, veremos cómo utilizar funciones avanzadas, cuantificadores y expresiones condicionales con elementpath.
# Funciones de cadena: starts-with
# Seleccionar libros cuyo título comienza con "La"
libros_la = elementpath.select(
root, "/biblioteca/libro[starts-with(titulo, 'La')]/titulo/text()"
)
print("Libros que empiezan con 'La':")
for libro in libros_la:
print(f"\t- {libro}")Output
Libros que empiezan con 'La':
- La vida está en otra parte
# Cuantificadores: some
# Verificar si hay algún libro con precio mayor a 500
hay_caros = elementpath.select(root, "some $x in //precio satisfies $x > 500")
print(f"¿Hay libros caros (>500)? {hay_caros}")Output
¿Hay libros caros (>500)? True
# Cuantificadores: every
# Verificar si todos los precios son positivos
todos_positivos = elementpath.select(root, "every $x in //precio satisfies $x > 0")
print(f"¿Todos los precios son positivos? {todos_positivos}")Output
¿Todos los precios son positivos? True
# Expresiones condicionales: if-then-else
# Categorizar libros según su precio
categorias = elementpath.select(
root,
"for $libro in /biblioteca/libro return "
"concat($libro/titulo, ': ', "
"if ($libro/precio > 500) then 'Caro' else 'Barato')",
)
print("Categorización de libros:")
for cat in categorias:
print(f"- {cat}")Output
Categorización de libros:
- La vida está en otra parte: Barato
- Pantaleón y las visitadoras: Barato
- Conversación en la catedral: Caro
5.2Sindicación de contenidos: RSS y Atom¶
La sindicación de contenidos es una forma de distribuir información actualizada de sitios web a usuarios que se han suscrito a ellos. Los dos formatos más populares para esto son RSS y Atom, ambos basados en XML.
RSS (Really Simple Syndication)¶
RSS es una familia de formatos de fuentes web estandarizados que se utilizan para publicar trabajos actualizados con frecuencia, como entradas de blogs, titulares de noticias, audio y vídeo. El formato RSS permite a los editores sindicar datos de forma automática. La versión actual es RSS 2.0.
Atom¶
Atom es un estándar más reciente, desarrollado como una alternativa a RSS para solucionar algunas de sus ambigüedades y limitaciones. Fue estandarizado por el IETF (RFC 4287). Atom suele ser más robusto y consistente en su estructura.
Comparación: RSS vs Atom¶
| Característica | RSS 2.0 | Atom 1.0 |
|---|---|---|
| Estandarización | No estandarizado formalmente (mantenido por Harvard) | Estándar IETF (RFC 4287) |
| Fecha de publicación | Elemento <pubDate> (formato RFC 822) | Elemento <updated> (formato ISO 8601) |
| Contenido | Solo soporta texto plano o HTML escapado | Soporta texto, HTML, XHTML y contenido binario (Base64) |
| Identificación | <guid> (opcional) | <id> (obligatorio y único) |
| Autor | <author> (email del autor) | <author> (estructura con nombre, email, uri) |
Lectura de Feeds con Python¶
La biblioteca feedparser es la herramienta estándar en Python para analizar tanto feeds RSS como Atom de manera transparente.
Para instalar feedparser:
pip install feedparserEjemplo 1: Leyendo noticias (RSS)¶
import feedparser
# URL del feed RSS de Clarin
rss_url = "https://www.clarin.com/rss/lo-ultimo/"
# Parsear el feed
feed = feedparser.parse(rss_url)
print(f"Fuente: {feed.feed.title}")
print("Últimas noticias:")
for entry in feed.entries[:3]:
print("-" * 80)
print(f"Título: {entry.title}")
print(f"Link: {entry.link}")Output
Fuente: Clarin.com - Home - Lo último
Últimas noticias:
--------------------------------------------------------------------------------
Título: Cuba comienza a liberar presos después de anunciar un indulto para más de 2.000 personas
Link: https://www.clarin.com/mundo/cuba-comienza-liberar-presos-despues-anunciar-indulto-2000-personas_0_5E13AP7686.html
--------------------------------------------------------------------------------
Título: Es oficial: diez grandes empresas inyectarán 85 millones de dólares al fondo de innovación de Nueva Jersey
Link: https://www.clarin.com/estados-unidos/oficial-grandes-empresas-inyectaran-85-millones-dolares-fondo-innovacion-nueva-jersey_0_J4Jh8TGhQ5.html
--------------------------------------------------------------------------------
Título: Texas, un bastión conservador en EE.UU., es el segundo estado con mayor consumo de OnlyFans: cuántos millones gastaron en la plataforma en 2025
Link: https://www.clarin.com/estados-unidos/texas-bastion-conservador-eeuu-segundo-estado-mayor-consumo-onlyfans-millones-gastaron-plataforma-2025_0_pHMxNXh7Xq.html
Ejemplo 2: Leyendo Gmail (Atom)¶
Gmail proporciona un feed Atom de los correos no leídos. Dado que requiere autenticación, este ejemplo muestra cómo se estructuraría la petición (nota: por seguridad, Gmail requiere contraseñas de aplicación si tienes 2FA activado).
import feedparser
# Reemplazar con tus credenciales (usar contraseña de aplicación)
username = "tu_usuario@gmail.com"
password = "tu_contraseña_de_aplicacion"
# URL del feed Atom de Gmail
gmail_url = f"https://{username}:{password}@mail.google.com/mail/feed/atom"
# Parsear el feed
feed = feedparser.parse(gmail_url)
print(f"Correos no leídos en: {feed.feed.title}")
print(f"Cantidad: {feed.feed.fullcount}")
for entry in feed.entries[:3]:
print("-" * 80)
print(f"De: {entry.author}")
print(f"Asunto: {entry.title}")
print(f"Resumen: {entry.summary}")- Manning, C. D., Raghavan, P., & Schütze, H. (2008). Introduction to Information Retrieval. Cambridge University Press. https://nlp.stanford.edu/IR-book/