# Marathon Stats — VBA парсер live-статистики VBA-модуль для Excel, который загружает live-статистику спортивных событий с сайта [Marathon Bet (BY)](https://www.marathonbet.by/su/live/45356) и выводит результаты в отформатированную таблицу. ## Возможности - Загрузка HTML-страницы через встроенные COM-объекты (без внешних зависимостей) - Корректная обработка кириллицы (UTF-8) через `ADODB.Stream` - Парсинг HTML по DOM-маркерам Marathon: лиги, команды, счёт, время, коэффициенты - Профессиональное оформление Excel: цветовые схемы, группировка по лигам, замороженные панели ## Быстрый старт ### Из Excel 1. Открыть Excel, нажать `Alt+F11` (редактор VBA) 2. **File → Import File** → выбрать `MarathonStats.bas` 3. Закрыть редактор, нажать `Alt+F8` → выбрать `FetchMarathonStats` → **Run** ### Из PowerShell (автоматизация) ```powershell powershell.exe -ExecutionPolicy Bypass -File RunMarathon.ps1 ``` > **Требование:** В Excel должен быть включён доступ к объектной модели VBA: > `File → Options → Trust Center → Trust Center Settings → Macro Settings → > Trust access to the VBA project object model` ## Структура файлов | Файл | Описание | |---|---| | `MarathonStats.bas` | Основной VBA-модуль (импортируется в Excel) | | `RunMarathon.ps1` | PowerShell-скрипт для автоматического запуска | | `MarathonStats.xlsm` | Результат — Excel-книга с данными | --- ## Подробное описание кода ### Точка входа ```vba Public Sub FetchMarathonStats() ``` Главная процедура. Последовательно выполняет: 1. Создаёт/очищает лист `LiveStats` 2. Загружает HTML-страницу (`FetchPage`) 3. Парсит события (`ParseHTML`) 4. Записывает данные и форматирует таблицу --- ### Загрузка страницы (Fetch) Ключевая проблема: сервер Marathon отправляет ответы в **gzip-сжатии** и разрывает соединение с клиентами, которые не умеют его обрабатывать. Решение — двухуровневая стратегия с двумя COM HTTP-объектами. #### `FetchPage(url As String) As String` Диспетчер — пробует два метода по очереди: ``` FetchPage ├─ FetchWithXMLHTTP() ← основной └─ FetchWithWinHTTP() ← резервный ``` #### Основной метод: `FetchWithXMLHTTP(url)` Использует **`MSXML2.XMLHTTP.6.0`** — клиентский COM-объект (на базе WinInet, как IE). Автоматически распаковывает gzip, что критично для Marathon. ```vba Set http = CreateObject("MSXML2.XMLHTTP.6.0") http.Open "GET", url, False http.setRequestHeader "User-Agent", "Mozilla/5.0 ..." http.setRequestHeader "Accept-Language", "ru-RU,ru;q=0.9" http.Send ``` Заголовки имитируют браузер Chrome: - **User-Agent** — без него сервер может отклонить запрос - **Accept-Language: ru-RU** — для получения русскоязычной версии страницы **Декодирование UTF-8** (критически важно для кириллицы): ```vba Dim bodyBytes() As Byte bodyBytes = http.responseBody ' сырые байты ответа Dim stream As Object Set stream = CreateObject("ADODB.Stream") stream.Type = 1 ' adTypeBinary stream.Open stream.Write bodyBytes ' записать байты stream.Position = 0 ' перемотать в начало stream.Type = 2 ' adTypeText stream.Charset = "UTF-8" ' указать кодировку FetchWithXMLHTTP = stream.ReadText ' прочитать как текст stream.Close ``` Почему нельзя просто использовать `http.ResponseText`: - VBA работает в кодировке Windows-1251 (ANSI) - `ResponseText` может неверно интерпретировать UTF-8 байты - `ADODB.Stream` гарантирует корректное преобразование UTF-8 → Unicode #### Резервный метод: `FetchWithWinHTTP(url)` Использует **`WinHttp.WinHttpRequest.5.1`** с заголовком `Accept-Encoding: identity`, который просит сервер отправить **несжатый** ответ. Обходит проблему gzip на уровне протокола. ```vba http.setRequestHeader "Accept-Encoding", "identity" ``` Декодирование UTF-8 — аналогичное через `ADODB.Stream`. #### Почему не работают другие подходы | Метод | Результат | Причина | |---|---|---| | `MSXML2.ServerXMLHTTP.6.0` | Соединение разорвано | Не обрабатывает gzip автоматически | | `.NET HttpWebRequest` | Соединение разорвано | Аналогичная проблема | | `WinHttp` без `identity` | Мусор вместо текста | Получает gzip-байты как строку | | `InternetExplorer.Application` | Пустая страница | Контент загружается через JavaScript | --- ### Парсинг HTML #### `ParseHTML(html As String)` Последовательный однопроходный сканер. Ищет ближайший из трёх маркеров и обрабатывает его в зависимости от типа: ``` Маркеры в HTML Marathon: ───────────────────────────────────────────────────────── "category-label simple-live" → название лиги (в