Leyendo archivos XML#

En este script hacemos una pequeña introducción al uso de BeautifulSoup para extraer datos de una página XML.

Acerca de este ejemplo#

Una forma común de transmitir datos por Internet es el uso del formato XML.

En este ejemplo les muestro datos de tipos de cambio del peso chileno, el euro, y la libra esterlina, del 5 de enero de 2015 al 30 de enero de 2021. Los datos fueron descargados del sitio web del FMI.

En el archivo XML se presenta una observación a la vez. Por ejemplo, la primera observación está representada así:

<EFFECTIVE_DATE VALUE="05-Jan-2015">
   <RATE_VALUE CURRENCY_CODE="Chilean peso" ISO_CHAR_CODE="CLP">
      612.47
   </RATE_VALUE>
   <RATE_VALUE CURRENCY_CODE="Euro" ISO_CHAR_CODE="EUR">
      1.1915
   </RATE_VALUE>
   <RATE_VALUE CURRENCY_CODE="U.K. pound" ISO_CHAR_CODE="GBP">
      1.5254
   </RATE_VALUE>
 </EFFECTIVE_DATE>

Nuestra tarea consiste en extraer los datos de cada observación, y concatenarlos en una tabla de pandas.

import numpy as np
import pandas as pd
from bs4 import BeautifulSoup

Archivo de datos

GITHUB_REPO = "https://raw.githubusercontent.com/randall-romero/econometria/master/data/"
DATAPATH = GITHUB_REPO if 'google.colab' in str(get_ipython()) else '../../data/'

filename = 'Exchange_Rate_Report.xml'

Esta función nos ayudará a convertir los datos a pandas

def soup_to_pandas(dato_como_soup):
    """
    Convierte una observación del archivo XML a una tabla de una observación en pandas.

    Por ejemplo, la observación descrita arriba se convierte a:

                       CLP     EUR     GBP
        05-Jan-2015  612.47  1.1915  1.5254

    """

    datodict = {dato['ISO_CHAR_CODE'] : dato.getText() for dato in dato_como_soup.findAll('RATE_VALUE')}

    return pd.DataFrame(datodict, index=[dato_como_soup['VALUE']])

Listos para procesar el archivo

with open(DATAPATH + filename, mode='r') as page:
    soup = BeautifulSoup(page, 'xml', from_encoding='utf-8')
    obs_strings = soup.findAll('EFFECTIVE_DATE')
    datos = pd.concat([soup_to_pandas(s) for s in obs_strings])

Convertimos los datos a float (numéricos)

datos = datos.replace('N/A',np.nan).astype(float)

Convertimos el índice a fechas (períodos)

datos.index = pd.to_datetime(datos.index)
datos.index = datos.index.to_period('D')
datos = datos.reindex(pd.period_range(datos.index[0], datos.index[-1],freq='d'))

Rellenamos datos del fin de semana

datos.fillna(method='ffill', inplace=True)
datos.plot(subplots=True, figsize=[12,8])
array([<AxesSubplot:>, <AxesSubplot:>, <AxesSubplot:>], dtype=object)
../../_images/Python-11--Leyendo-archivos-XML_14_1.png