Formatos y fuentes de datos 9 min de lectura

Web scraping con Excel y VBA

Web scraping con Excel y VBA: peticiones HTTP a páginas web, parseo de HTML y JSON y actualización automática de las tablas sin programas externos.

EW
Equipo Web-Scraping.es
Recopilación de datos para las necesidades del negocio
Publicado: 4 marzo 2025

La extracción de datos de internet suele asociarse con Python o con servicios especializados. Pero si los datos deben acabar directamente en una tabla, calcularse con fórmulas y mostrarse a los compañeros, Excel junto con VBA sigue siendo una de las vías más rápidas para obtener el resultado. No hay que instalar un intérprete, configurar un entorno ni explicar en contabilidad qué es pip install. Abra el libro, pulse un botón — y los datos están en la hoja.

En este artículo desmontamos cómo funciona el scraping con VBA: qué objetos usar para las peticiones HTTP, cómo parsear HTML y JSON, cómo volcar el resultado en las celdas y cómo no toparse con un bloqueo. Los ejemplos son funcionales: puede copiarlos en el editor de VBA y ejecutarlos.

Cuándo Excel y VBA son una buena elección

Merece la pena recurrir a VBA cuando:

  • el resultado va a vivir de todos modos en Excel (un informe, un panel, un registro de cotizaciones);
  • el volumen de datos es pequeño o medio — decenas o miles de filas, no millones;
  • se necesita una automatización «de un botón» para personas sin conocimientos de programación;
  • la fuente entrega los datos mediante una petición HTTP simple o una API abierta.

Si lo que hace falta es una escala seria, sortear protecciones complejas de JavaScript o lanzar peticiones en paralelo, es mejor mirar hacia Python (requests, BeautifulSoup, Playwright). En ese tipo de tareas, VBA toca techo enseguida.

Las herramientas dentro de VBA

Para hacer scraping en VBA existen varios «motores» principales:

Objeto Función Cuándo aplicarlo
MSXML2.XMLHTTP / ServerXMLHTTP Peticiones HTTP La vía principal para obtener la respuesta del servidor
WinHttp.WinHttpRequest.5.1 Peticiones HTTP Alternativa con timeouts configurables
HTMLDocument (MSHTML) Parseo de HTML Cuando hay que extraer elementos por etiquetas/clases
RegExp (VBScript) Expresiones regulares Extracción puntual dentro del texto
Split / InStr / Mid Funciones de cadena Parseo simple de JSON y de texto sin bibliotecas
QueryTables / Power Query Tablas ya montadas Cuando la página entrega una tabla HTML limpia

La mayoría de estos objetos se instancian «al vuelo» con CreateObject, es decir, no exigen añadir referencias al proyecto a mano. Es una comodidad: el libro funciona en cualquier máquina con Excel.

La petición HTTP básica

El scraper más simple se limita a obtener el texto de una página. Esta función hace una petición GET y devuelve el HTML o el JSON como cadena:

vba
Function GetResponse(ByVal url As String) As String
    Dim http As Object
    Set http = CreateObject("MSXML2.XMLHTTP")

    http.Open "GET", url, False
    ' Nos hacemos pasar por un navegador normal: muchos sitios cortan las peticiones sin User-Agent
    http.setRequestHeader "User-Agent", _
        "Mozilla/5.0 (Windows NT 10.0; Win64; x64)"
    http.send

    If http.Status = 200 Then
        GetResponse = http.responseText
    Else
        GetResponse = "ERROR: " & http.Status & " " & http.statusText
    End If

    Set http = Nothing
End Function

El tercer argumento de OpenFalse — indica una petición síncrona: el código espera la respuesta. Para la mayoría de las tareas es suficiente. La cabecera User-Agent es crítica: sin ella, una parte de los servidores devuelve un 403 o un captcha.

Parseo de JSON sin bibliotecas

VBA no sabe parsear JSON «de fábrica», pero para respuestas simples bastan las funciones de cadena. Supongamos que la API devolvió:

json
{"price": 152.34, "currency": "USD", "symbol": "AAPL"}

El valor de un campo puede extraerse con una función pequeña:

vba
Function ExtractJsonValue(ByVal json As String, ByVal key As String) As String
    Dim pattern As String
    Dim startPos As Long, endPos As Long

    pattern = """" & key & """:"
    startPos = InStr(json, pattern)
    If startPos = 0 Then Exit Function

    startPos = startPos + Len(pattern)
    ' Saltamos la comilla si el valor es una cadena
    If Mid(json, startPos, 1) = """" Then startPos = startPos + 1

    ' El final del valor es una coma, una llave de cierre o una comilla
    endPos = startPos
    Do While endPos <= Len(json)
        Dim ch As String
        ch = Mid(json, endPos, 1)
        If ch = "," Or ch = "}" Or ch = """" Then Exit Do
        endPos = endPos + 1
    Loop

    ExtractJsonValue = Trim(Mid(json, startPos, endPos - startPos))
End Function

Este enfoque funciona con objetos planos. Si la estructura es anidada y compleja, es mejor incorporar un parser de JSON ya hecho para VBA (por ejemplo, el módulo abierto VBA-JSON de Tim Hall) — convierte la respuesta en Dictionary y Collection, con los que se trabaja con mucha más comodidad.

La cadena «petición HTTP → parseo del JSON → escritura en la celda» es el patrón básico sobre el que se construye el scraping de tipos de cambio. La mayoría de los servicios de los bancos centrales — incluido el feed de tipos de referencia del BCE — y de las APIs de divisas devuelven precisamente JSON o XML, y la función de extracción de valores descrita arriba cubre el 80% de los casos. El análisis detallado de una solución completa con actualización automática por temporizador está en el artículo «Scraping de tipos de cambio».

Parseo de HTML con MSHTML

Cuando los datos no están en una API sino directamente en el marcado de la página, resulta cómodo el objeto HTMLDocument. Permite buscar elementos igual que en el navegador — por id, etiquetas y clases.

vba
Function ParseHtmlElement(ByVal url As String, ByVal elementId As String) As String
    Dim http As Object, htmlDoc As Object
    Set http = CreateObject("MSXML2.XMLHTTP")

    http.Open "GET", url, False
    http.setRequestHeader "User-Agent", "Mozilla/5.0"
    http.send

    Set htmlDoc = CreateObject("htmlfile")
    htmlDoc.body.innerHTML = http.responseText

    Dim el As Object
    Set el = htmlDoc.getElementById(elementId)
    If Not el Is Nothing Then
        ParseHtmlElement = Trim(el.innerText)
    End If

    Set http = Nothing
    Set htmlDoc = Nothing
End Function

Si hay que extraer varios elementos por clase o etiqueta, conviene recorrer la colección:

vba
Sub ParseAllRows(ByVal url As String)
    Dim http As Object, htmlDoc As Object
    Set http = CreateObject("MSXML2.XMLHTTP")
    http.Open "GET", url, False
    http.setRequestHeader "User-Agent", "Mozilla/5.0"
    http.send

    Set htmlDoc = CreateObject("htmlfile")
    htmlDoc.body.innerHTML = http.responseText

    Dim rows As Object, i As Long
    Set rows = htmlDoc.getElementsByTagName("tr")

    For i = 0 To rows.Length - 1
        ' Escribimos el texto de cada fila de la tabla en la hoja, a partir de la fila 2
        Cells(i + 2, 1).Value = Trim(rows.Item(i).innerText)
    Next i

    Set http = Nothing
    Set htmlDoc = Nothing
End Sub

Expresiones regulares

A veces el valor buscado está incrustado en el texto sin un envoltorio cómodo. En ese caso, RegExp saca del apuro:

vba
Function ExtractByRegex(ByVal text As String, ByVal pattern As String) As String
    Dim re As Object
    Set re = CreateObject("VBScript.RegExp")
    re.Global = False
    re.IgnoreCase = True
    re.pattern = pattern

    Dim matches As Object
    Set matches = re.Execute(text)
    If matches.Count > 0 Then
        ' Devolvemos el primer grupo de captura
        ExtractByRegex = matches(0).SubMatches(0)
    End If

    Set re = Nothing
End Function

' Ejemplo: extraer el número de una cadena tipo "Precio: 152.34 EUR"
' value = ExtractByRegex(s, "Precio:\s*([\d\.]+)")

Escritura del resultado en la hoja

Escribir los datos celda a celda es lento. Si las filas son muchas, acumúlelas en un array y vuélquelas con una sola asignación:

vba
Sub WriteArrayFast(data() As Variant)
    Dim n As Long
    n = UBound(data) - LBound(data) + 1
    ' Volcamos la columna entera en una sola operación
    Range("A1").Resize(n, 1).Value = Application.Transpose(data)
End Sub

Este volcado es decenas de veces más rápido que un bucle que escribe en cada celda, sobre todo con la actualización de pantalla desactivada:

vba
Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual
' ... scraping y escritura ...
Application.Calculation = xlCalculationAutomatic
Application.ScreenUpdating = True

Ejemplo práctico: una tabla de cotizaciones

Montemos un pequeño scraper que recorre una lista de tickers, solicita el precio a una API ficticia y vuelca el resultado en la hoja.

vba
Sub ParseQuotes()
    Dim tickers As Variant
    tickers = Array("AAPL", "MSFT", "GOOGL", "TSLA")

    Dim i As Long, url As String, response As String, price As String

    ' Encabezados de la tabla
    Cells(1, 1).Value = "Ticker"
    Cells(1, 2).Value = "Precio"
    Cells(1, 3).Value = "Hora"

    Application.ScreenUpdating = False

    For i = LBound(tickers) To UBound(tickers)
        url = "https://example-api.com/quote?symbol=" & tickers(i)
        response = GetResponse(url)              ' la función de la sección anterior
        price = ExtractJsonValue(response, "price")

        Cells(i + 2, 1).Value = tickers(i)
        Cells(i + 2, 2).Value = Val(price)
        Cells(i + 2, 3).Value = Now

        ' Pausa entre peticiones para no cargar el servidor y no ganarse un bloqueo
        Application.Wait Now + TimeValue("0:00:01")
    Next i

    Application.ScreenUpdating = True
    MsgBox "Listo: se cargaron " & (UBound(tickers) + 1) & " cotizaciones", vbInformation
End Sub

Es un esqueleto simplificado. En la práctica, para la extracción de cotizaciones bursátiles se añaden el parseo del volumen negociado, de la variación porcentual y de los históricos, además del tratamiento de los fines de semana y de las horas de cierre de la bolsa. La implementación completa, con actualización automática y formato condicional, está en el artículo «Extracción de cotizaciones bursátiles».

Manejo de errores y robustez

Las peticiones de red fallan: el servidor no responde, salta el timeout, llega un formato inesperado. El scraper tiene que sobrevivir a todo eso, no morir en el primer error.

vba
Function SafeGet(ByVal url As String, Optional retries As Long = 3) As String
    Dim attempt As Long
    For attempt = 1 To retries
        On Error Resume Next
        Dim http As Object
        Set http = CreateObject("WinHttp.WinHttpRequest.5.1")
        http.SetTimeouts 5000, 5000, 10000, 10000   ' resolve, connect, send, receive
        http.Open "GET", url, False
        http.setRequestHeader "User-Agent", "Mozilla/5.0"
        http.send

        If Err.Number = 0 And http.Status = 200 Then
            SafeGet = http.responseText
            On Error GoTo 0
            Exit Function
        End If
        On Error GoTo 0

        ' La pausa antes del siguiente intento crece cada vez
        Application.Wait Now + TimeValue("0:00:0" & attempt)
    Next attempt

    SafeGet = ""   ' se agotaron todos los intentos
End Function

El objeto WinHttpRequest resulta aquí más cómodo que XMLHTTP precisamente por su método SetTimeouts: permite fijar de forma explícita los límites de espera y no quedarse colgado para siempre.

Ética y limitaciones

Unas cuantas reglas que ahorran nervios y reputación:

  • Lea robots.txt y las condiciones de uso. No todos los sitios permiten la recogida automatizada de datos.
  • Haga pausas entre peticiones. Decenas de peticiones por segundo parecen un ataque y llevan al baneo de la IP.
  • Prefiera las APIs oficiales. Si la fuente tiene API, úsela: es más estable y legal.
  • No extraiga datos personales sin base legal ni consentimiento.
  • Cachee el resultado. Si el tipo de cambio se actualiza una vez al día, no hace falta castigar al servidor cada minuto.

La alternativa sin código: Power Query

Conviene recordar que para muchas tareas VBA ni siquiera es necesario. El Power Query integrado en Excel (Datos → Obtener datos → Desde la web) sabe cargar tablas HTML y respuestas JSON desde la interfaz, con actualización automática programada. Si la fuente entrega una tabla limpia o una API REST sin autenticación enrevesada, Power Query resolverá la tarea más rápido y sin una sola línea de código. VBA queda para los casos en que hacen falta lógica, ramificaciones, bucles sobre una lista y un parseo fuera de lo estándar.

Conclusión

El scraping con VBA se construye con unos pocos ladrillos: la petición HTTP (XMLHTTP o WinHttp), el parseo de la respuesta (funciones de cadena, RegExp o HTMLDocument), la escritura en las celdas y el manejo de errores. Una vez dominado ese conjunto, se puede automatizar la recogida de casi cualquier dato tabular sin salir del libro de Excel de siempre.

Dos escenarios clásicos con los que resulta cómodo practicar:

  • Scraping de tipos de cambio — una fuente JSON/XML sencilla, ideal para el primer scraper.
  • Extracción de cotizaciones bursátiles — algo más compleja: lista de tickers, actualizaciones frecuentes, formato.

Ambos están desarrollados en artículos aparte — empiece por el que más se parezca a su tarea; las funciones descritas aquí (GetResponse, ExtractJsonValue, SafeGet) serán el fundamento común de los dos.