Analizando XML con BeautifulSoup en Python

En esta guía, veremos cómo extraer y analizar datos de archivos XML con BeautifulSoup y lxml. Practiquemos analizando una fuente RSS y guardándola en un archivo CSV.

Introducción

Lenguaje de marcado extensible (XML) es un lenguaje de marcado que es popular debido a la forma en que estructura los datos. Encontró uso en la transmisión de datos (que representan objetos serializados) y archivos de configuración.

A pesar de la creciente popularidad de JSON, aún puede encontrar XML en el archivo de manifiesto de desarrollo de Android, las herramientas de compilación de Java/Maven y las API de SOAP en la web. Por lo tanto, analizar XML sigue siendo una tarea común que un desarrollador tendría que hacer.

En Python, podemos leer y analizar XML aprovechando dos bibliotecas: BeautifulSoup y LXML.

En esta guía, veremos cómo extraer y analizar datos de archivos XML con BeautifulSoup y LXML, y almacenaremos los resultados con Pandas.

Configuración de LXML y BeautifulSoup

Primero necesitamos instalar ambas bibliotecas. Crearemos una nueva carpeta en su espacio de trabajo, configuraremos un entorno virtual e instalaremos las bibliotecas:

1
2
3
4
5
$ mkdir xml_parsing_tutorial
$ cd xml_parsing_tutorial
$ python3 -m venv env # Create a virtual environment for this project
$ . env/bin/activate # Activate the virtual environment
$ pip install lxml beautifulsoup4 # Install both Python packages

Ahora que tenemos todo configurado, ¡hagamos un poco de análisis!

Análisis de XML con lxml y BeautifulSoup

El análisis siempre depende del archivo subyacente y de la estructura que utiliza, por lo que no hay una sola bala de plata para todos los archivos. BeautifulSoup los analiza automáticamente, pero los elementos subyacentes dependen de la tarea.

Por lo tanto, es mejor aprender a analizar con un enfoque práctico. Guarde el siguiente XML en un archivo en su directorio de trabajo - teachers.xml:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
<?xml version="1.0" encoding="UTF-8"?>
<teachers>
    <teacher>
        <name>Sam Davies</name>
        <age>35</age>
        <subject>Maths</subject>
    </teacher>
    <teacher>
        <name>Cassie Stone</name>
        <age>24</age>
        <subject>Science</subject>
    </teacher>
    <teacher>
        <name>Derek Brandon</name>
        <age>32</age>
        <subject>History</subject>
    </teacher>
</teachers>

La etiqueta <teachers> indica la raíz del documento XML, la etiqueta <teacher> es un elemento secundario o subelemento de <teachers></teachers>, con información sobre una persona singular. <name>, <age>, <subject> son elementos secundarios de la etiqueta <teacher> y nietos de la etiqueta <teachers>.

La primera línea, <?xml version="1.0" encoding="UTF-8"?>, en el documento de ejemplo anterior se denomina prólogo XML. Siempre viene al principio de un archivo XML, aunque es completamente opcional incluir un prólogo XML en un documento XML.

El prólogo XML que se muestra arriba indica la versión de XML utilizada y el tipo de codificación de caracteres. En este caso, los caracteres del documento XML se codifican en UTF-8.

Ahora que entendemos la estructura del archivo XML, podemos analizarlo. Cree un nuevo archivo llamado teachers.py en su directorio de trabajo e importe la biblioteca BeautifulSoup:

1
from bs4 import BeautifulSoup

{.icon aria-hidden=“true”}

Nota: Como habrás notado, ¡no importamos lxml! Con la importación de BeautifulSoup, LXML se integra automáticamente, por lo que no es necesario importarlo por separado, pero no se instala como parte de BeautifulSoup.

Ahora leamos el contenido del archivo XML que creamos y almacenémoslo en una variable llamada sopa para que podamos comenzar a analizar:

1
2
3
4
5
with open('teachers.xml', 'r') as f:
    file = f.read() 

# 'xml' is the parser used. For html files, which BeautifulSoup is typically used for, it would be 'html.parser'.
soup = BeautifulSoup(file, 'xml')

La variable sopa ahora tiene el contenido analizado de nuestro archivo XML. Podemos usar esta variable y los métodos adjuntos para recuperar la información XML con código Python.

Digamos que queremos ver solo los nombres de los profesores del documento XML. Podemos obtener esa información con unas pocas líneas de código:

1
2
3
names = soup.find_all('name')
for name in names:
    print(name.text)

Ejecutar python teacher.py nos daría:

1
2
3
Sam Davis 
Cassie Stone 
Derek Brandon

El método find_all() devuelve una lista de todas las etiquetas coincidentes que se le pasan como argumento. Como se muestra en el código anterior, soup.find_all('name') devuelve todas las etiquetas <name> en el archivo XML. Luego iteramos sobre estas etiquetas e imprimimos su propiedad text, que contiene los valores de las etiquetas.

Mostrar datos analizados en una tabla

Vayamos un paso más allá, analizaremos todo el contenido del archivo XML y lo mostraremos en un formato tabular.

Reescribamos el archivo teachers.py con:

 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
from bs4 import BeautifulSoup

# Opens and reads the xml file we saved earlier
with open('teachers.xml', 'r') as f:
    file = f.read()

# Initializing soup variable
soup = BeautifulSoup(file, 'xml')

# Storing <name> tags and elements in names variable
names = soup.find_all('name')

# Storing <age> tags and elements in 'ages' variable
ages = soup.find_all('age')

# Storing <subject> tags and elements in 'subjects' variable
subjects = soup.find_all('subject')

# Displaying data in tabular format
print('-'.center(35, '-'))
print('|' + 'Name'.center(15) + '|' + ' Age ' + '|' + 'Subject'.center(11) + '|')
for i in range(0, len(names)):
    print('-'.center(35, '-'))
    print(
        f'|{names[i].text.center(15)}|{ages[i].text.center(5)}|{subjects[i].text.center(11)}|')
print('-'.center(35, '-'))

La salida del código anterior se vería así:

1
2
3
4
5
6
7
8
9
-----------------------------------
|      Name     | Age |  Subject  |
-----------------------------------
|   Sam Davies  |  35 |   Maths   |
-----------------------------------
|  Cassie Stone |  24 |  Science  |
-----------------------------------
| Derek Brandon |  32 |  History  |
-----------------------------------

¡Felicitaciones! ¡Acabas de analizar tu primer archivo XML con BeautifulSoup y LXML! Ahora que se siente más cómodo con la teoría y el proceso, probemos un ejemplo más real.

Hemos formateado los datos como una tabla como precursor para almacenarlos en una estructura de datos versátil. Es decir, en el próximo mini proyecto, almacenaremos los datos en un DataFrame de Pandas.

If you aren't already familiar with DataFrames - read our Python con Pandas: Guía de marcos de datos!

Analizar una fuente RSS y almacenar los datos en un CSV

En esta sección, analizaremos una fuente RSS de The New York Times News y almacenaremos esos datos en un archivo CSV.

{.icon aria-hidden=“true”}

RSS es la abreviatura de Really Simple Syndication. Una fuente RSS es un archivo que contiene un resumen de las actualizaciones de un sitio web y está escrito en XML. En este caso, la fuente RSS de The New York Times contiene un resumen de las actualizaciones diarias de noticias en su sitio web. Este resumen contiene enlaces a comunicados de prensa, enlaces a imágenes de artículos, descripciones de noticias y más. Las fuentes RSS también se utilizan para permitir que las personas obtengan datos sin raspar sitios web como una buena muestra de los propietarios de sitios web.

Aquí hay una instantánea de un feed RSS de The New York Times:

Imagen que muestra un documento XML que contiene actualizaciones de noticias relacionadas con los EE. UU. en el sitio web de The New York Times News.

Puede obtener acceso a diferentes fuentes RSS del New York Times de diferentes continentes, países, regiones, temas y otros criterios a través de este Enlace.

Es importante ver y comprender la estructura de los datos antes de que pueda comenzar a analizarlos. Los datos que nos gustaría extraer del feed RSS sobre cada noticia son:

  • Identificador único global (GUID)
  • Título
  • Fecha de publicación
  • Descripción

Ahora que estamos familiarizados con la estructura y tenemos objetivos claros, ¡comencemos nuestro programa! Necesitaremos la biblioteca requests y la biblioteca pandas para recuperar los datos y convertirlos fácilmente a un archivo CSV.

If you haven't worked with requests before, read out Módulo Guía de solicitudes de Python!

Con solicitudes, podemos realizar solicitudes HTTP a sitios web y analizar las respuestas. En este caso, podemos usarlo para recuperar sus fuentes RSS (en XML) para que BeautifulSoup pueda analizarlo. Con pandas, podremos formatear los datos analizados en una tabla y, finalmente, almacenar el contenido de la tabla en un archivo CSV.

En el mismo directorio de trabajo, instale requests y pandas (su entorno virtual aún debería estar activo):

1
$ pip install requests pandas

En un nuevo archivo, nyt_rss_feed.py, importemos nuestras bibliotecas:

1
2
3
import requests
from bs4 import BeautifulSoup
import pandas as pd

Luego, hagamos una solicitud HTTP al servidor de The New York Times para obtener su fuente RSS y recuperar su contenido:

1
2
url = 'https://rss.nytimes.com/services/xml/rss/nyt/US.xml'
xml_data = requests.get(url).content 

Con el código anterior, hemos podido obtener una respuesta de la solicitud HTTP y almacenar su contenido en la variable xml_data. La biblioteca requests devuelve datos como bytes.

Ahora, cree la siguiente función para analizar los datos XML en una tabla en Pandas, con la ayuda de BeautifulSoup:

 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
def parse_xml(xml_data):
  # Initializing soup variable
    soup = BeautifulSoup(xml_data, 'xml')

  # Creating column for table
    df = pd.DataFrame(columns=['guid', 'title', 'pubDate', 'description'])

  # Iterating through item tag and extracting elements
    all_items = soup.find_all('item')
    items_length = len(all_items)
    
    for index, item in enumerate(all_items):
        guid = item.find('guid').text
        title = item.find('title').text
        pub_date = item.find('pubDate').text
        description = item.find('description').text

       # Adding extracted elements to rows in table
        row = {
            'guid': guid,
            'title': title,
            'pubDate': pub_date,
            'description': description
        }

        df = df.append(row, ignore_index=True)
        print(f'Appending row %s of %s' % (index+1, items_length))

    return df

La función anterior analiza los datos XML de una solicitud HTTP con BeautifulSoup, almacenando su contenido en una variable sopa. Se hace referencia al marco de datos de Pandas con filas y columnas para los datos que nos gustaría analizar a través de la variable df.

Luego iteramos a través del archivo XML para encontrar todas las etiquetas con <elemento>. Al iterar a través de la etiqueta <item>, podemos extraer sus etiquetas secundarias: <guid>, <title>, <pubDate> y <description>. Note cómo usamos el método find() para obtener solo un objeto. Agregamos los valores de cada etiqueta secundaria a la tabla Pandas.

Ahora, al final del archivo después de la función, agregue estas dos líneas de código para llamar a la función y crear un archivo CSV:

1
2
df = parse_xml(xml_data)
df.to_csv('news.csv')

Ejecute python nyt_rss_feed.py para crear un nuevo archivo CSV en su directorio de trabajo actual:

1
2
3
4
Appending row 1 of 24
Appending row 2 of 24
...
Appending row 24 of 24

El contenido del archivo CSV se vería así:

Imagen que muestra la salida CSV de un feed RSS analizado para The New York Times

{.icon aria-hidden=“true”}

Nota: La descarga de datos puede demorar un poco según su conexión a Internet y la fuente RSS. El análisis de datos puede demorar un poco dependiendo de su CPU y recursos de memoria también. El feed que hemos usado es bastante pequeño, por lo que debería procesarse rápidamente. Tenga paciencia si no ve resultados inmediatamente.

¡Felicitaciones, analizó con éxito una fuente RSS de The New York Times News y la convirtió en un archivo CSV!

Conclusión

En esta guía, aprendimos cómo podemos configurar BeautifulSoup y LXML para analizar archivos XML. Primero obtuvimos práctica al analizar un archivo XML simple con datos de maestros, y luego analizamos la fuente RSS de The New York Times, convirtiendo sus datos en un archivo CSV.

Puede usar estas técnicas para analizar otros XML que pueda encontrar y convertirlos a los diferentes formatos que necesite.