martes, 26 de febrero de 2019

Excel y MySQL (I)

Como hemos ya mencionado, hoy en día el uso de internet es tan masivo que no es extraño que se compartan no solo archivos sino también datos a través de su uso. No extraña, tampoco, el uso de sistemas que usan motores de base de datos ubicados en “hosting” que permiten el acceso remoto desde, literal, cualquier lugar del planeta. Por supuesto muchos priorizan el php o java (o similares) para crear sistemas y usarlos en páginas web que trabajen con los motores de base de datos de dichos “hosting”.

Probablemente MySQL debe ser uno de los mencionados motores que más se usa hoy en día y al ser Microsoft Excel una de las aplicaciones de escritorio más usada, y no solo como hoja de calculo que es su función principal, no es raro encontrar muchas personas que preguntan cómo conectar ambos programas. Aunque yo considero que no es lo idóneo usar Excel como “Front End”, entiendo que a muchos les resulta más fácil por uso y por eso la constante pregunta; por ello vamos a explicar cómo lograrlo.

Si bien se puede obtener datos de un servidor MYSQL con el complemento “MySQL for Excel” o a través de Power Query, el uso de VBA permite, desde mi punto de vista, una mejor manipulación de los datos.

Lo primero es comentar que usaremos ADO para ello. ActiveX Data Objects (ADO) es un mecanismo para comunicarse con bases de datos y así poder trabajar con sus datos, es compatible con VBA y, además, permite conexiones con MySQL. Otra cosa importante, hay que descargar un Driver ODBC para lograr la conexión. Si tu sistema operativo es de 32 bits y por ende tu Office igual, no tendrás mayor problema en usar el Driver que hayas instalado porque será para sistemas operativos de 32 bits, pero si tu sistema operativo es de 64 bits hay algo a tomar en cuenta y es que el Driver a usar deberá ser de los mismos bits que tu Office. Para que se entienda:

Sistema Operativo

Office

Driver MySQL

32 bits

32 bits

32 bits

64 bits

32 bits

32 bits

64 bits

64 bits

64 bits

Para el ejemplo yo usé “MySQL ODBC 8.0 Unicode Driver”.

Ahora sí comencemos con el ejemplo propiamente dicho. Lo primero es activar la referencia a “Microsoft ActiveX Data Object 6.1 Library” en donde el 6.1 puede variar dependiendo de tu versión de Office. Luego declararemos las variables, una para la conexión y otra para el recordset:

Dim cnn As ADODB.Connection
Dim rst As ADODB.Recordset 

Luego vamos a declarar variables en base a los parámetros que usaremos como el nombre del servidor, la base de datos, el usuario, entre otros. Para ver los parámetros ver este enlace.

Dim BD$, Servidor$, User$, Clave$

Ahora procederemos a dar valores a las variables, a crear la cadena de conexión y a establecer dicha conexión:

Let Servidor = "NombreoIPdelServidor": Let BD = "NombreBasedeDatos"
Let User = "UsuariodelServidor": Let Clave = "TuClave" 
cnn.ConnectionString = "DRIVER={MySQL ODBC 8.0 Unicode Driver};" _
     & "SERVER=" & Servidor & ";DATABASE=" & BD & ";" _
         & "UID=" & User & ";PWD=" & Clave & ";PORT=3306;OPTION=131072" 
cnn.Open MsgBox "Conectado a mi BD MySQL" 

Si todo salió bien el MsgBox se habrá activado lo que quiere decir que logramos conectarnos al servidor. Ah, por cierto, el puerto 3306 es el que se prioriza para usar servidores que no sean el “localhost” y en el “Option” el 131072 es para “habilitar opciones seguras” (revisar el enlace de párrafos arriba para más detalles). Si no se logró la conexión sería bueno revisar en el “phpmyadmin” que el acceso parra conexiones remotas del servidor esté activado (ojo que no todos los servidores lo tienen/permiten). Todo junto debe quedar así:

Sub TrabajarconMySQL()
Dim cnn As ADODB.Connection 
Dim rst As ADODB.Recordset 
Dim BD$, Servidor$, User$, Clave$ 

Set cnn = New ADODB.Connection 
Let Servidor = "NombreoIPdelServidor": Let BD = "NombreBasedeDatos" 
Let User = "UsuariodelServidor": Let Clave = "TuClave" 

cnn.ConnectionString = "DRIVER={MySQL ODBC 8.0 Unicode Driver};" _
     & "SERVER=" & Servidor & ";DATABASE=" & BD & ";" _
         & "UID=" & User & ";PWD=" & Clave & ";PORT=3306;OPTION=131072" 

cnn.Open 
MsgBox "Conectado a mi BD MySQL" 
cnn.Close 
Set cnn = Nothing 

End Sub 

Si queremos conectarnos a nuestro servidor de MySQL instalado en nuestra PC (como el que crea WampServer), basta cambiar así:

Let Servidor = "localhost": Let BD = "NombreBasedeDatos"
Let User = "root": Let Clave = "" 

cnn.ConnectionString = "DRIVER={MySQL ODBC 8.0 Unicode Driver};" _
     & "SERVER=" & Servidor & ";DATABASE=" & BD & ";" _
         & "UID=" & User & ";PWD=" & Clave & ";PORT=3306;OPTION=131072" 

En donde lo único que deberán cambiar es el nombre de la base de datos.

Ahora vamos a obtener los datos de una de las tablas. Para este usaremos SQL, obtendremos todos los datos de “Tabla1”, incluido los nombres de los campos, y colocaremos todos los datos en una hoja de nuestro Excel usando bucles con variables para de ese modo no tener que ajustar al número de campos o registros:

Sub Conectar() 

Dim cnn As ADODB.Connection 
Dim rst As ADODB.Recordset 
Dim nCampos&, x As Integer 
Dim nRegistros&, y As Long 
Dim sql$, BD$, Servidor$, User$, Clave$ 

Set cnn = New ADODB.Connection 
Set rst = New ADODB.Recordset 
Let sql = "Select * From Tabla1" 
Let Servidor = "NombreoIPdelServidor": Let BD = "NombreBasedeDatos" 
Let User = "UsuariodelServidor": Let Clave = "TuClave" 

cnn.ConnectionString = "DRIVER={MySQL ODBC 8.0 Unicode Driver};" _
     & "SERVER=" & Servidor & ";DATABASE=" & BD & ";" _
         & "UID=" & User & ";PWD=" & Clave & ";PORT=3306;OPTION=131072" 

cnn.Open 

With rst
     .CursorLocation = adUseClient
     .CursorType = adOpenKeyset
     .LockType = adLockOptimistic
     .Open sql, cnn, , , adCmdText
End With 

Let nCampos = rst.Fields.Count 
Let nRegistros = rst.RecordCount 

For x = 1 To nCampos
     Cells(1, x) = rst.Fields(x - 1).Name 
Next x 

For y = 1 To nRegistros
     For x = 1 To nCampos
         Cells(y + 1, x).Value = rst.Fields(x - 1).Value
     Next x
     rst.MoveNext
 Next y 

cnn.Close 
Set rst = Nothing: Set cnn = Nothing 

End Sub 

Por supuesto también usando SQL podríamos enviar más datos y/o modificar los ya existentes, pero eso será para otra ocasión. Hasta la próxima.

Abraham Valencia

lunes, 18 de febrero de 2019

Interactuando con internet (II): Extraer datos de tablas web

Ya hemos visto en un artículo anterior (Enlace) como identificar objetos/elementos de una página web y sobre todo como interactuar con ellos, pero algo de lo que no hablamos fue de cómo extraer datos de por ejemplo las tablas que no es difícil encontrar en ellas.

Por supuesto que cuando hablamos de tablas de una página web lo primero que probablemente se nos viene a la cabeza es usar la herramienta de Excel “Obtener datos externos”, que nos permite elegir las tablas que identifica en dicha web e importarlas a nuestra hoja de Excel. En muchos casos la página quizá tenga usuario y contraseña, pero la herramienta no permite visualizar la web y sus campos, así como ingresar esos datos y seguir navegando hasta que cargue la página con las tablas; una vez que ocurre eso elegimos la tabla que deseemos y listo, tendremos los datos en nuestra hoja de Excel.

:

Es muy probable que queramos que esto sea automático y para ello podemos por supuesto usar VBA. Esto puede lograrse usando el objeto “QueryTable” y obteniendo así los datos de la web deseada a través de macros y de tener usuario y clave, podremos usar su propiedad “PostText” para “enviarlos” y poder seguir navegando. La desventaja de este método (con VBA o no), es que nos importa los datos completos de la tabla, no hay forma de indicar que sean solo algunas filas y/o columnas. Entonces ¿cuál es la solución?.

La forma que voy a sugerir en esta ocasión es trabajar directamente sobre los objetos/elementos que en las webs está definidos por la etiqueta “Table”. Para eso usaremos el método “getElementsByTagName” y debemos recordar/saber también que los índices de las tablas de una web comienzan en cero (0). Aprovecharemos que la Universidad de Valencia (España) tiene una web con varias tablas de ejemplo: Enlace

En esta ocasión no solo usaremos el objeto “Internet Explorer” sino que también haremos lo propio con objetos HTMLTable y HTMLDocument. Para ello lo primero será activar las referencias a “Microsoft HTML Object Library”, en seguida vamos a declarar tres variables

Dim ie As Object
Dim doc As HTMLDocument
Dim htmTable As HTMLTable  

La primera es para nuestro objeto “Internet Explorer”, la segunda es para hacer referencia a la propiedad “Document” (Obtiene, a su vez, el objeto de la web activa) de dicho objeto y la tercera hará referencia a la etiqueta “Table”, es decir a la tabla o tablas de las que extraeremos los datos. Dicho eso, y suponiendo que queremos los datos de la primera tabla de la web de ejemplo, agregaremos estas líneas:

Set ie = CreateObject("InternetExplorer.Application")
ie.Navigate "https://www.uv.es/jac/guia/tablaeje.htm"
Do: DoEvents: Loop Until ie.ReadyState = 4 
Set doc = ie.Document 
Set htmTable = doc.getElementsByTagName("Table")(0) 

Hasta ese momento ya estamos conectados a la tabla, pero para extraer los datos necesitamos saber algunas cosas como por ejemplo que dichas tablas tienen una colección “Rows” (filas) y estás a su vez tienen la propiedad “Lenght” que retorna el número de elementos de la colección correspondiente. Entonces, para obtener el número de filas de la tabla usaremos lo siguiente:

htmTable.Rows.Length

Del mismo modo, para saber cuántas celdas /columnas tiene una fila, usaremos la colección “Cells” que tienen dichas filas y podemos nuevamente usar la propiedad “Lenght”. Ah, no olvidar que, según su índice, la primera fila es la cero (0), entonces para saber/obtener el número de celdas/columnas, usaremos lo siguiente:

htmTable.Rows(0).Cells.Length

Para obtener el valor de las celdas usaremos la propiedad “InnerText”. Por ejemplo, en la misma tabla para obtener el valor de la primera celda de la fila superior (en este caso la letra “A”):

htmTable.Rows(0).Cells(0).innerText

Agreguemos todo en la macro de este modo:

Sub tablaenWeb()

Dim ie As Object 
Dim htmTable As HTMLTable 
Dim doc As HTMLDocument 

Set ie = CreateObject("InternetExplorer.Application") 
ie.Navigate "https://www.uv.es/jac/guia/tablaeje.htm" 
Do: DoEvents: Loop Until ie.ReadyState = 4 

Set doc = ie.Document 
Set htmTable = doc.getElementsByTagName("Table")(0) 

MsgBox "La primera tabla tiene " & htmTable.Rows.Length & " filas" 
MsgBox "Además, su primera fila tiene " & htmTable.Rows(0).Cells.Length & " columnas" 
MsgBox "Y la primera celda de la fila superior tiene el siguiente valor en ella " & htmTable.Rows(0).Cells(0).innerText 

ie.Visible = True 

Set ie = Nothing: Set doc = Nothing: Set htmTable = Nothing 

End Sub 

Ahora vamos a usar la decima tabla de la web (índice 9) para extraer sus datos y llevarlos a nuestra hoja. Ya con las colecciones mencionadas bastará colocar todo en bucles:

Sub tablaenWeb() 
Dim ie As Object 
Dim htmTable As HTMLTable 
Dim doc As HTMLDocument 
Dim nFilas As Integer, nColumnas As Integer, x As Integer, y As Integer 

Set ie = CreateObject("InternetExplorer.Application") 

ie.Navigate "https://www.uv.es/jac/guia/tablaeje.htm" 
Do: DoEvents: Loop Until ie.ReadyState = 4 

Set doc = ie.Document 
Set htmTable = doc.getElementsByTagName("Table")(9) 
Let nFilas = htmTable.Rows.Length 
Let nColumnas = htmTable.Rows(0).Cells.Length 

For x = 1 To nFilas
     For y = 1 To nColumnas
         Cells(x, y).Value = htmTable.Rows(x - 1).Cells(y - 1).innerText
     Next y 
Next x 

ie.Visible = True 

Set ie = Nothing: Set doc = Nothing: Set htmTable = Nothing 

End Sub 
Esto último, sobre todo, creo yo, será útil cuando no se sabe la cantidad de filas y columnas de una tabla, sobre todo en las webs de consulta de datos. Y eso es todo por hoy.

Abraham Valencia

miércoles, 30 de enero de 2019

Interactuando con internet (I)

Sin duda el uso de internet para diversas, tareas, consultas, trabajos, etc., es cada vez mayor y del mismo modo el número de usuarios/as de Microsoft Excel sigue en ascenso lo que ocasiona que muchas de las preguntas en diversos foros sea sobre cómo usar/navegar por internet desde Excel.

Por supuesto que la respuesta es el uso de VBA pero lo más importante es reconocer y/o identificar los objetos de las páginas web con las que queremos interactuar. Algo que es importante aclarar es que si no deseamos usar aplicaciones (o librerías u otros) de terceros necesariamente interactuaremos con Internet Explorer, no con Edge o Chrome..

Lo primero será crear un objeto “Internet Explorer” del siguiente modo:

Dim IE As Object
Set IE = CreateObject("InternetExplorer.Application") 

Después usaremos su método “Navigate” para abrir una página web y la propiedad “Visible” para que se vea la aplicación recién abierta.

IE.navigate "https://www.google.com.pe" 
IE.Visible = True Set 
IE = Nothing 

De ese modo tendremos el IE abierto con google cargado. Por cierto, para ver/leer sobre otras propiedades, eventos y métodos del objeto Internet Explorer, miren por aquí: Enlace

Ahora vamos a interactuar con los objetos (elementos) dentro de Google, pero para eso debemos saber que será necesario usar la propiedad “Document” así como cualquiera de los cuatro métodos siguientes:

getElementById
getElementsByTagName  
getElementsByClassName  
getElementsByName 

Con cualquiera de ellos podremos identificar el objeto/elemento con el cual queremos interactuar, solo basta encontrar el Id, Tag, Clase (Class) o Nombre (name) y usar el método correspondiente. No todos los objetos/elementos tienen/usan las cuatro, pero nos bastará hallar una para poder lograr nuestro cometido.

Siguiendo con Google, vamos a suponer que lo tenemos abierto en nuestro Chrome, entonces situaremos el cursor dentro del cuadro de texto de la web y le daremos click derecho con lo que se nos mostrará el siguiente menú contextual:

Automáticamente le daremos Click a “Inspeccionar” y nos dará el siguiente resultado:

Se puede apreciar que el cuadro de texto tiene dos formas que nos ayudan a identificarlo, su clase (Class = gLFyf gsfi) y su nombre (Name = q). Usaremos el nombre y a su vez la propiedad “Value” del objeto/elemento, por lo que tendríamos que colocar así:

IE.document.getElementsByName("q").Value = "Hola a todos"
Previo a eso agregaremos un bucle que permita que, antes de intentar enviar valores al cuadro de texto, la página web cargue completamente. Para eso usaremos la propiedad “ReadyState” con el valor cuatro (4) equivalente a que el objeto ha recibido todos los datos. Entonces, hasta el momento tendremos esto:Entonces, dicho todo eso, nuestra macro debería quedar así:
Sub Navegar() 
Dim IE As Object 
Set IE = CreateObject("InternetExplorer.Application") 
IE.Navigate "https://www.google.com.pe" 
Do Until IE.ReadyState = 4     
 DoEvents 
Loop 
IE.document.getElementsByName("q").Value = "Hola a todos" 
IE.Visible = True 
Set IE = Nothing 
End Sub 

Al correr la macro nos dará un error en la línea en donde estamos intentando enviar un valor (una frase) al cuadro de texto de la web. Dado que en el caso de las web muchas veces los métodos arrojan resultados de lista de nodos, algunas veces se hace necesario identificar dicho índice. Entonces, para evitar dicho error agregaremos el índice del objeto/elemento del siguiente modo:

IE.document.getElementsByName("q")(0).Value = " Hola a todos"

Ahora solo nos falta hacer “click” al botón correspondiente para terminar. Si usamos otra vez “Inspeccionar” pero esta vez previamente colocando el cursor sobre el botón “Buscar con Google”, veremos que su nombre es “btnK”. Con ese nombre (al que agregaremos también el índice) y su evento “Click” habremos terminado. Coloca esta línea posterior a la que vimos antes de este párrafo:

IE.Document.getElementsByName("btnK")(0).Click

Listo, ahora al correr la macro abrirá Internet Explorer, cargará Google, colocará “Hola a todos” y buscará esa frase dándonos como resultado miles de enlaces.

Hasta la próxima.

Abraham Valencia

martes, 15 de enero de 2019

Excel Online

En una época en que el trabajo “en línea” es cada vez mayor, no es de extrañar que muchos usuarios y usuarias pregunten sobre la posibilidad de trabajar con programas como Excel justamente a través de internet.

Para todos/as ellos/as Microsoft ofrece desde hace algún tiempo “Excel Online”, que no es nada más y nada menos que una versión de Microsoft Excel que ha sido adaptada para trabajar desde cualquier navegador de Internet.

Para muchas personas la gran ventaja es que dicha versión web es gratuita y tienen casi todas las características de las versiones de escritorio. Ah, por supuesto que algo que es obligatorio es tener una cuenta que sea reconocida por Microsoft (Outlook, Hotmail, etc.). De ser así basta entrar aquí: Enlace

Al estar, además, “Excel Online” asociado a una cuenta, te ofrece la ventaja de que todo se puede guardar en tu “OneDrive”. Otro de las ventajas que ofrece eso, es que no es necesario guardar los cambios permanentemente. Siguiendo con las ventajas es que, al tratarse de una aplicación que se ofrece a través de la web, se pueden usar desde cualquier sistema operativo: Windows, Mac, Linux, etc. e incluso desde tabletas y teléfonos móviles.

Ah, claro, si tenemos que hablar de las desventajas, por lo menos para mí es que no puede trabajar con macros en “Excel Online”; claro, se puede abrir un libro que contiene dichas macros, pero no podrá acceder a ellas ni usarlas. Eso sí, puede editarse las hojas y guardar el libro que contiene las macros y dichas macros permanecerán en el libro sin problema.

Entonces, ya saben, si necesitan trabajar con Excel en cualquier momento y no están en su computadora, anímense y usen "Excel Online". Hasta la próxima.

Abraham Valencia