Парсинг через Excel / VBA

Парсинг средствами Excel и VBA: запросы к страницам, разбор HTML и автоматическое обновление таблиц без внешних программ.

КP
Команда Parsing.agency
Сбор данных под задачи бизнеса
Опубликовано: 4 марта 2025

Парсинг данных из интернета обычно ассоциируется с Python или специализированными сервисами. Но если данные нужно сразу положить в таблицу, посчитать формулами и показать коллегам — Excel вместе с VBA остаётся одним из самых быстрых способов получить результат. Не нужно ставить интерпретатор, настраивать окружение и объяснять бухгалтеру, что такое pip install. Открыл книгу, нажал кнопку — данные на листе.

В этой статье разберём, как устроен парсинг средствами VBA: какие объекты использовать для HTTP-запросов, как разбирать HTML и JSON, как складывать результат в ячейки и не нарваться на блокировку. Примеры — рабочие, их можно скопировать в редактор VBA и запустить.

Когда Excel / VBA — хороший выбор

VBA имеет смысл брать, когда:

  • результат всё равно живёт в Excel (отчёт, дашборд, журнал котировок);
  • объём данных небольшой или средний — десятки–тысячи строк, а не миллионы;
  • нужна автоматизация «по кнопке» для людей без навыков программирования;
  • источник отдаёт данные через простой HTTP-запрос или открытый API.

Если же нужен серьёзный масштаб, обход сложной JavaScript-защиты или параллельные запросы — лучше посмотреть в сторону Python (requests, BeautifulSoup, Playwright). VBA в таких задачах быстро упирается в потолок.

Инструменты внутри VBA

Для парсинга в VBA есть несколько основных «движков»:

Объект Назначение Когда применять
MSXML2.XMLHTTP / ServerXMLHTTP HTTP-запросы Основной способ получить ответ сервера
WinHttp.WinHttpRequest.5.1 HTTP-запросы Альтернатива с гибкими таймаутами
HTMLDocument (MSHTML) Разбор HTML Когда нужно вытащить элементы по тегам/классам
RegExp (VBScript) Регулярные выражения Точечное извлечение из текста
Split / InStr / Mid Строковые функции Простой разбор JSON и текста без библиотек
QueryTables / Power Query Готовые таблицы Когда страница отдаёт чистую HTML-таблицу

Большинство этих объектов подключаются «на лету» через CreateObject, то есть не требуют ручного добавления ссылок в проект. Это удобно: книга работает на любой машине с Excel.

Базовый HTTP-запрос

Самый простой парсер — это получить текст страницы. Вот функция, которая делает GET-запрос и возвращает HTML или JSON как строку:

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

    http.Open "GET", url, False
    ' Притворяемся обычным браузером — многие сайты режут запросы без 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

Третий аргумент OpenFalse означает синхронный запрос: код ждёт ответа. Для большинства задач этого достаточно. Заголовок User-Agent критичен: без него часть серверов возвращает 403 или капчу.

Разбор JSON без библиотек

VBA не умеет парсить JSON «из коробки», но для простых ответов хватает строковых функций. Допустим, API вернул:

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

Извлечь значение поля можно небольшой функцией:

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)
    ' Пропускаем кавычку, если значение строковое
    If Mid(json, startPos, 1) = """" Then startPos = startPos + 1

    ' Конец значения — запятая, закрывающая скобка или кавычка
    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

Этот подход работает для плоских объектов. Если структура вложенная и сложная, лучше подключить готовый JSON-парсер для VBA (например, открытый модуль VBA-JSON от Tim Hall) — он превращает ответ в Dictionary и Collection, с которыми удобно работать.

Связка «HTTP-запрос → разбор JSON → запись в ячейку» — это основной шаблон, по которому строится парсинг курсов валют. Большинство сервисов ЦБ и валютных API отдают именно JSON или XML, и описанная выше функция извлечения значений покрывает 80% случаев. Подробный разбор готового решения с автообновлением по таймеру — в материале «Парсинг курсов валют».

Разбор HTML через MSHTML

Когда данные лежат не в API, а прямо в разметке страницы, удобно использовать объект HTMLDocument. Он позволяет искать элементы так же, как в браузере — по id, тегам и классам.

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

Если нужно вытащить несколько элементов по классу или тегу, пригодится перебор коллекции:

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
        ' Пишем текст каждой строки таблицы на лист, начиная со 2-й строки
        Cells(i + 2, 1).Value = Trim(rows.Item(i).innerText)
    Next i

    Set http = Nothing
    Set htmlDoc = Nothing
End Sub

Регулярные выражения

Иногда нужное значение спрятано в тексте без удобной обёртки. Тогда выручает RegExp:

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
        ' Возвращаем первую группу захвата
        ExtractByRegex = matches(0).SubMatches(0)
    End If

    Set re = Nothing
End Function

' Пример: достать число из строки вида "Цена: 152.34 руб"
' value = ExtractByRegex(s, "Цена:\s*([\d\.]+)")

Запись результата на лист

Записывать данные в ячейки по одной — медленно. Если строк много, собирайте их в массив и выгружайте одним присваиванием:

vba
Sub WriteArrayFast(data() As Variant)
    Dim n As Long
    n = UBound(data) - LBound(data) + 1
    ' Выгружаем весь столбец за одну операцию
    Range("A1").Resize(n, 1).Value = Application.Transpose(data)
End Sub

Такая выгрузка в десятки раз быстрее, чем цикл с записью в каждую ячейку, особенно при отключённом обновлении экрана:

vba
Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual
' ... парсинг и запись ...
Application.Calculation = xlCalculationAutomatic
Application.ScreenUpdating = True

Практический пример: парсинг таблицы котировок

Соберём небольшой парсер, который проходит по списку тикеров, запрашивает цену через условный API и складывает результат на лист.

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

    ' Заголовки таблицы
    Cells(1, 1).Value = "Тикер"
    Cells(1, 2).Value = "Цена"
    Cells(1, 3).Value = "Время"

    Application.ScreenUpdating = False

    For i = LBound(tickers) To UBound(tickers)
        url = "https://example-api.com/quote?symbol=" & tickers(i)
        response = GetResponse(url)              ' функция из раздела выше
        price = ExtractJsonValue(response, "price")

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

        ' Пауза между запросами, чтобы не нагружать сервер и не словить блок
        Application.Wait Now + TimeValue("0:00:01")
    Next i

    Application.ScreenUpdating = True
    MsgBox "Готово: загружено " & (UBound(tickers) + 1) & " котировок", vbInformation
End Sub

Это упрощённый каркас. На практике для парсинга котировок акций добавляют разбор объёма торгов, изменения в процентах, исторических данных, а также обработку выходных дней и нерабочих часов биржи. Полную реализацию с автообновлением и условным форматированием смотрите в материале «Парсинг котировок акций».

Обработка ошибок и устойчивость

Сетевые запросы падают: сервер не ответил, таймаут, неверный формат. Парсер должен это переживать, а не вылетать на первой же ошибке.

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

        ' Пауза перед повторной попыткой растёт с каждым разом
        Application.Wait Now + TimeValue("0:00:0" & attempt)
    Next attempt

    SafeGet = ""   ' все попытки исчерпаны
End Function

Объект WinHttpRequest здесь удобнее XMLHTTP именно из-за метода SetTimeouts — можно явно задать пределы ожидания и не зависнуть навсегда.

Этика и ограничения

Несколько правил, которые экономят нервы и репутацию:

  • Читайте robots.txt и условия использования. Не все сайты разрешают автоматический сбор данных.
  • Делайте паузы между запросами. Десятки запросов в секунду выглядят как атака и приводят к бану IP.
  • Предпочитайте официальные API. Если у источника есть API — используйте его: это стабильнее и легально.
  • Не парсите персональные данные без оснований и согласия.
  • Кэшируйте результат. Если курс валюты обновляется раз в день, не надо дёргать сервер каждую минуту.

Альтернатива без кода: Power Query

Стоит упомянуть, что для многих задач VBA вообще не нужен. Встроенный в Excel Power Query (Данные → Получить данные → Из интернета) умеет загружать HTML-таблицы и JSON-ответы через интерфейс, с автообновлением по расписанию. Если источник отдаёт чистую таблицу или REST API без хитрой авторизации — Power Query решит задачу быстрее и без единой строки кода. VBA остаётся для случаев, когда нужна логика, ветвление, циклы по списку и нестандартный разбор.

Итог

VBA-парсинг строится из нескольких кирпичиков: HTTP-запрос (XMLHTTP или WinHttp), разбор ответа (строковые функции, RegExp или HTMLDocument), запись в ячейки и обработка ошибок. Освоив этот набор, можно автоматизировать сбор практически любых табличных данных прямо внутри привычной книги Excel.

Два классических сценария, на которых удобно тренироваться:

  • Парсинг курсов валют — простой JSON/XML-источник, идеальный для первого парсера.
  • Парсинг котировок акций — чуть сложнее: список тикеров, частые обновления, форматирование.

Оба разобраны в отдельных материалах — начните с того, что ближе к вашей задаче, а описанные здесь функции (GetResponse, ExtractJsonValue, SafeGet) будут общим фундаментом для обоих.