Descargar el tipo de cambio de Banxico con Python

Me di a la tarea de programar un sencillo script en Python para descargar el tipo de cambio oficial de Banxico.

El programa que descarga el tipo de cambio de Banxico.

Es algo sencillo, más bien didáctico. Todo comenzó con una publicación de Fernando Romo en la que compartía un script para descargar el tipo de cambio usando Perl.

Y me dije a mi mismo … Mi mismo ¿Y si lo haces en Python?

— ¡Orale! y puse manos en el teclado.

En este artículo

Descargar, Buscar, Almacenar.

Creo que el proceso es muy sencillo.

  1. Descargar la información de internet.
  2. Buscar la fecha y el tipo de cambio, en mi caso no necesito nada más.
  3. Y almacenar esa información en algún lado.

Lo primero es darle un vistazo a la fuente de información, que es un RSS de Banxico.

<?xml version="1.0" encoding="ISO-8859-1"?>
<rdf:RDF
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns="http://purl.org/rss/1.0/"
xmlns:cb="http://staging.bis.org/rss-cb/1.0/"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:dcterms="http://purl.org/dc/terms/"
xmlns:xsi="http://www.w3c.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.w3c.org/1999/02/22-rdf-syntax-ns#rdf.xsd">
   <channel rdf:about="http://www.banxico.org.mx/rsscb/rss?canal=tipCam&amp;idioma=es">
      <title>Banco de México, Tipo de cambio en dólares de los EE.UU.A. (FIX)</title>
      <link>https://www.banxico.org.mx/tipcamb/main.do?page=tip&amp;idioma=sp</link>
      <description>Tipo de cambio para solventar obligaciones denominadas en dólares de los EE.UU.A., pagaderas en la República Mexicana.</description>
      <dc:language>es</dc:language>
      <items>
         <rdf:Seq>
            <rdf:li rdf:resource="http://www.banxico.org.mx/portal-mercado-cambiario/index.html/20220901"/>
         </rdf:Seq>
      </items>
   </channel>
   <item rdf:about="http://www.banxico.org.mx/portal-mercado-cambiario/index.html/20220901">
      <title><![CDATA[MX: 20.2473 MXN = 1 USD 2022-09-01 BM FIX]]></title>
      <link>https://www.banxico.org.mx/tipcamb/main.do?page=tip&amp;idioma=sp</link>
      <description><![CDATA[Este tipo de cambio es determinado por el Banco de México los días hábiles bancarios con base en un promedio de las cotizaciones del mercado de cambios al mayoreo para operaciones liquidables el segundo día hábil bancario siguiente.]]></description>
      <dc:date>2022-09-01T12:01:01-05:00</dc:date>
      <dc:language>es</dc:language>
      <dc:format>text/html</dc:format>
      <dc:creator>Banco de México</dc:creator>
      <cb:simpletitle>FIX</cb:simpletitle>
      <cb:statistics>
         <cb:country>MX</cb:country>
         <cb:institutionAbbrev>BM</cb:institutionAbbrev>
         <cb:exchangeRate>
            <cb:value frequency="daily business" decimals="4">20.2473</cb:value>
            <cb:baseCurrency>USD</cb:baseCurrency>
            <cb:targetCurrency>MXN</cb:targetCurrency>
            <cb:rateName>FIX</cb:rateName>
         </cb:exchangeRate>
      </cb:statistics>
   </item>
</rdf:RDF>

ElementTree vs. minidom

Ok, estoy trabajando un RSS que es básicamente un archivo XML. Pues a donde quiera que volteaba el enfoque tradicional es usar ElementTree para trabajar con ellos, seguí varios tutoriales, hice algunos ejemplos, pero nada parecía funcionar. Simplemente no logré obtener la información de la fecha y el tipo de cambio.

Ahora bien, no es la única forma de atacar el problema. En el camino se me ocurrió hacer una búsqueda sencilla, o quizás algo de Expresiones Regulares.

Me topé entonces con minidom que para efectos prácticos me resultaba exactamente igual que ElementTree con la pequeña diferencia de que si funcionaba.

Obtuve lo que quería con un reducido número de líneas de código.

Guardar la información en una base de datos SQlite

En un principio pensé en almacenar la información descargada en una base de datos MySQL, peeeeroooo … es algo que de momento no voy a utilizar. Así que decidí intentar algo nuevo con SQlite y Wooow… es simple, sencillo, ligero, integrado a Python.

El destino final de la información lo elige usted, puede ser una base de datos, una hoja de cálculo, enviarlo por correo, hasta un texto delimitado por comas podría funcionar.

En el código podrán ver algunas líneas extras con comentarios, algunas son para crear la base de datos, borrarla, agregar información.

El código

# Descargar el Tipo De Cambio ... dtdc.py
# ----------------------------------------

"""
Este es un código de ejemplo. Descarga el tipod de cambio de un RSS de Banxico.

La información la puede almacenar en una base de datos, en una hoja de cálculo
o donde usted guste.

Fuente de información:
https://www.banxico.org.mx/rsscb/rss?BMXC_canal=fix&BMXC_idioma=es

Referencias:
- https://docs.python.org/es/3/library/xml.dom.minidom.html?highlight=minidom#module-xml.dom.minidom
- https://docs.python.org/es/3/library/sqlite3.html?highlight=sqlite#sqlite3-tutorial

"""
import requests
from xml.dom import minidom
import sqlite3

URL = "https://www.banxico.org.mx/rsscb/rss?BMXC_canal=fix&BMXC_idioma=es"

archivo_xml = "descarga.xml"

respuesta = requests.get(URL)

# Guardamos el archivo para consulta y referencia.
# pero es opcional
open(archivo_xml, "wb").write(respuesta.content)

xml = minidom.parseString(respuesta.content)

# Obtenemos los valores que deseamos.
fecha = xml.getElementsByTagName("dc:date")[0].firstChild.nodeValue[0:10]
exchange_rate = xml.getElementsByTagName("cb:value")[0].firstChild.nodeValue

# Y los guardamos en una base de datos.
# En este caso SQLite

conexion = sqlite3.connect("tipos_de_cambio.db")
cur = conexion.cursor()

# Si no existe la base de datos, la creamos.
try:
    conexion.execute("""create table tipos_de_cambio (
                              id integer primary key autoincrement,
                              fecha text,
                              tipo_de_cambio real
                    )""")
    print("Se creo la base de datos.")
except sqlite3.OperationalError:
    print("La base de datos ya existe")

# En caso de ser necesario borramos la base de datos
# conexion.execute("drop table tipos_de_cambio")

# Insertamos los valores en la base de datos.
conexion.execute(
    "insert into tipos_de_cambio(fecha,tipo_de_cambio) values (?,?)", (fecha, exchange_rate))
conexion.commit()

# Mostramos el contenido de la base de datos.
cursor = conexion.execute("select * from tipos_de_cambio")
for fila in cursor:
    print(fila)

conexion.close()

Conclusiones

Les comentaba al principio este código es más bien didáctico. Un ejercicio útil. Se puede ejecutar mediante una tarea cron cada día si lo quieren poner a trabajar.

Como siempre estoy abierto a sugerencias. Se que hay más de una forma de atacar un problema. Python es tan vasto que estoy seguro que hay una forma mejor y más sencilla de descargar esta información.

Saludos y hasta la próxima.

¡Me encantaría saber que opinas!

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.