Пользовательские функции VBA Excel для парсинга сайтов, html-страниц и файлов, возвращающие их текстовое содержимое. Примеры записи текста в переменную.
Парсинг html-страниц (msxml2.xmlhttp)
Пользовательская функция GetHTML1 (VBA Excel) для извлечения (парсинга) текстового содержимого из html-страницы сайта по ее URL-адресу с помощью объекта «msxml2.xmlhttp»:
Function GetHTML1(ByVal myURL As String) As String On Error Resume Next With CreateObject(«msxml2.xmlhttp») .Open «GET», myURL, False .send Do: DoEvents: Loop Until .readyState = 4 GetHTML1 = .responseText End With End Function |
Парсинг сайтов (WinHttp.WinHttpRequest.5.1)
Пользовательская функция GetHTML2 (VBA Excel) для извлечения (парсинга) текстового содержимого из html-страницы сайта по ее URL-адресу с помощью объекта «WinHttp.WinHttpRequest.5.1»:
Function GetHTML2(ByVal myURL As String) As String On Error Resume Next With CreateObject(«WinHttp.WinHttpRequest.5.1») .Open «GET», myURL, False .send Do: DoEvents: Loop Until .readyState = 4 GetHTML2 = .responseText End With End Function |
Парсинг файлов (ADODB.Stream)
Пользовательская функция GetText (VBA Excel) для извлечения (парсинга) текстового содержимого из файла (.txt, .csv, .mhtml), сохраненного на диск компьютера, по его полному имени (адресу) с помощью объекта «ADODB.Stream»:
Function GetText(ByVal myFile As String) As String On Error Resume Next With CreateObject(«ADODB.Stream») .Charset = «utf-8» .Open .LoadFromFile myFile GetText = .ReadText .Close End With End Function |
Примеры записи текста в переменную
Общая формула записи текста, извлеченного с помощью пользовательских функций VBA Excel, в переменную:
Dim htmlText As String htmlText = GetHTML1(«Адрес сайта (html-страницы)») htmlText = GetHTML2(«Адрес сайта (html-страницы)») htmlText = GetText(«Полное имя файла») |
Конкретные примеры:
htmlText = GetHTML1(«https://internettovary.ru/nabor-dlya-vyrashchivaniya-veshenki/») htmlText = GetHTML2(«https://internettovary.ru/nabor-dlya-vyrashchivaniya-veshenki/») htmlText = GetText(«C:UsersEvgeniyDownloadsНовый текстовый документ.txt») htmlText = GetText(«C:UsersEvgeniyDownloadsИспользование msxml2.xmlhttp в Excel VBA.mhtml») |
В понятие «парсинг», кроме извлечения текстового содержимого сайтов, html-страниц или файлов, входит поиск и извлечение конкретных данных из всего полученного текстового содержимого.
Пример извлечения email-адресов из текста, присвоенного переменной, смотрите в последнем параграфе статьи: Регулярные выражения (объекты, свойства, методы).
Парсинг содержимого тегов
Извлечение содержимого тегов с помощью метода getElementsByTagName объекта HTMLFile:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
Sub Primer1() Dim myHtml As String, myFile As Object, myTag As Object, myTxt As String ‘Извлекаем содержимое html-страницы в переменную myHtml с помощью функции GetHTML1 myHtml = GetHTML1(«https://internettovary.ru/sadovaya-nozhovka-sinitsa/») ‘Создаем объект HTMLFile Set myFile = CreateObject(«HTMLFile») ‘Записываем в myFile текст из myHtml myFile.body.innerHTML = myHtml ‘Присваиваем переменной myTag коллекцию одноименных тегов, имя которого ‘указанно в качестве аргумента метода getElementsByTagName Set myTag = myFile.getElementsByTagName(«p») ‘Выбираем, содержимое какого тега по порядку, начинающегося с 0, нужно извлечь myTxt = myTag(5).innerText MsgBox myTxt ‘Большой текст может не уместиться в MsgBox, тогда для просмотра используйте окно Immediate ‘Debug.Print myTxt End Sub |
С помощью этого кода извлекается текст, расположенный между открывающим и закрывающим тегами. В примере — это текст 6-го абзаца (p) между 5-й (нумерация с 0) парой отрывающего <p> и закрывающего </p> тегов.
Примеры тегов, используемых в html: "p"
, "title"
, "h1"
, "h2"
, "table"
, "div"
, "script"
.
Пример извлечения содержимого тега "title"
:
Sub Primer2() Dim myHtml As String, myFile As Object, myTag As Object, myTxt As String myHtml = GetHTML1(«https://internettovary.ru/sadovaya-nozhovka-sinitsa/») Set myFile = CreateObject(«HTMLFile») myFile.body.innerHTML = myHtml Set myTag = myFile.getElementsByTagName(«title») myTxt = myTag(0).innerText MsgBox myTxt End Sub |
Парсинг содержимого Id
Извлечение текстового содержимого html-элементов, имеющих уникальный идентификатор — Id, с помощью метода getElementById объекта HTMLFile:
Sub Primer3() Dim myHtml As String, myFile As Object, myTag As Object, myTxt As String myHtml = GetHTML1(«https://internettovary.ru/sadovaya-nozhovka-sinitsa/») Set myFile = CreateObject(«HTMLFile») myFile.body.innerHTML = myHtml ‘Присваиваем переменной myTag html-элемент по указанному в скобках Id Set myTag = myFile.getElementById(«attachment_465») ‘Присваиваем переменной myTxt текстовое содержимое html-элемента с Id myTxt = myTag.innerText MsgBox myTxt ‘Большой текст может не уместиться в MsgBox, тогда для просмотра используйте окно Immediate ‘Debug.Print myTxt End Sub |
Для реализации представленных здесь примеров могут понадобиться дополнительные библиотеки. В настоящее время у меня подключены следующие (к данной теме могут относиться последние шесть):
- Visual Basic For Applications
- Microsoft Excel 16.0 Object Library
- OLE Automation
- Microsoft Office 16.0 Object Library
- Microsoft Forms 2.0 Object Library
- Ref Edit Control
- Microsoft Scripting Runtime
- Microsoft Word 16.0 Object Library
- Microsoft Windows Common Controls 6.0 (SP6)
- Microsoft ActiveX Data Objects 6.1 Library
- Microsoft ActiveX Data Objects Recordset 6.0 Library
- Microsoft HTML Object Library
- Microsoft Internet Controls
- Microsoft Shell Controls And Automation
- Microsoft XML, v6.0
С этим набором библиотек все примеры работают. Тестирование проводилось в VBA Excel 2016.
Если использовать тип Long, то файл ограничится размером в 2Гб, думаю тогда можно сделать тип Single. Встречал в интернете, что некоторым нужно было обрабатывать большие файлы, размером в 10Гб.
Размер буфера я пробовал менять от 100Кб до 10Мб, особой разницы я не заметил, зато при 10Мб требуется мегабайт 20 оперативки дополнительно. Величина в 512Кб вполне подходит и по скорости и по объему оперативки.
scripting.fso не использовал, т.к. посчитал что данный способ простой и вполне понятный, мне кажется это на скорость не повлияет, т.к. операций чтения файла происходит немного, и при этом эта библиотека все равно не имеет операторов, чтобы читать файл задом наперед построчно.
Обработку ошибок при открытии файла не писал, т.к. сроки поджимали, важно было сделать сам алгоритм
Модуль и вправду большой получился. Оформлять в виде класса идея хорошая, может и сделаю, пока не до этого.
Всем спасибо. Может кому пригодится.
Для начала, создайте в вашем файле Excel (куда вы будете добавлять макрос запуска парсера) отдельный VBA-модуль, и поместите туда следующий код:
#If VBA7 Then ' Office 2010-2013 Private Declare PtrSafe Function URLDownloadToFile Lib "urlmon" Alias "URLDownloadToFileA" _ (ByVal pCaller As LongPtr, ByVal szURL As String, ByVal szFileName As String, _ ByVal dwReserved As LongPtr, ByVal lpfnCB As LongPtr) As LongPtr #Else ' Office 2003-2007 Private Declare Function URLDownloadToFile Lib "urlmon" Alias "URLDownloadToFileA" _ (ByVal pCaller As Long, ByVal szURL As String, ByVal szFileName As String, _ ByVal dwReserved As Long, ByVal lpfnCB As Long) As Long #End If Sub RunSpecificParser(ByVal ParserName$) On Error Resume Next If Not AddinStarted Then Exit Sub ' если программа «Парсер» не запущена, то выход из макроса res$ = Application.Run("StartParser", ParserName$) If Len(res$) Then ' если парсер не запустился - в переменной res$ будет текст ошибки MsgBox res$, vbCritical, "Ошибка запуска парсера" Exit Sub Else ' MsgBox "Парсер был запущен, и завершил свою работу", vbInformation End If End Sub Function AddinStarted() As Boolean On Error Resume Next ' проверяем, запущена ли надстройка Parser Test$ = Application.Run("ParserAddinTest") If Err.Number = 0 Then AddinStarted = True: Exit Function If Err.Number = 1004 Then ' макрос не выполнен - надстройка не запущена ' читаем в реестре путь к файлу надстройки, пытаемся найти и запустить надстройку AddinPath$ = GetSetting("Parser", "Setup", "AddinPath", "") If FileExists(AddinPath$) Then Set WB = Workbooks.Open(AddinPath$) ' пробуем открыть (запустить) надстройку t = Timer: Err.Raise 777 While (Err > 0) And (Abs(Timer - t) < 6) Err.Clear: DoEvents: Test$ = Application.Run("ParserAddinTest") ' снова проверяем Wend If Err.Number = 0 Then AddinStarted = True: Exit Function End If End If ' надстройка не запустилась, не найдена, или какая-то другая проблема ttl$ = "Для работы этого файла необходима надстройка «Парсер сайтов»" msg$ = "Необходимая для работы этого файла надстройка «Parser» не найдена на вашем компьютере." & vbNewLine & vbNewLine & _ "Скачать и запустить надстройку?" If MsgBox(msg, vbQuestion + vbOKCancel, ttl$) = vbCancel Then Exit Function URL$ = "http://excelvba.ru/updates/download.php?addin=Parser" AddinPath$ = CreateObject("WScript.Shell").SpecialFolders("Desktop") & "Parser.xla" Kill AddinPath$ If URLDownloadToFile(0, URL$, AddinPath$, 0, 0) = 0 Then ' надстройка успешно загружена If FileExists(AddinPath$) Then Workbooks.Open AddinPath$ ' пробуем открыть (запустить) надстройку Err.Clear: Test$ = Application.Run("ParserAddinTest") ' снова проверяем If Err.Number = 0 Then AddinStarted = True: Exit Function End If End If msg$ = "Не удалось скачать и запустить надстройку с сайта ExcelVBA.ru" & vbNewLine & _ "(возможно, приложению Excel закрыт доступ в интернет)" & vbNewLine & vbNewLine & _ "После нажатия кнопки ОК в этом сообщении, будет открыта страница программы," & vbNewLine & _ "где вы сможете скачать надстройку «Parser» (после чего запустить её, и продолжить работу с этим файлом)" MsgBox msg$, vbExclamation, "При загрузке или запуске надстройки возникли проблемы" CreateObject("wscript.Shell").Run "http://excelvba.ru/programmes/Parser" End Function Private Function FileExists(ByVal filename$) As Boolean On Error Resume Next: FileExists = CreateObject("Scripting.FileSystemObject").FileExists(filename$) End Function
После этого, в модуль ЭтаКнига (если вы хотите, чтобы парсер запускался вместе с открытием вашего файла Excel) добавьте следующий код:
Private Sub Workbook_Open() ' автоматически срабатывает при открытии книги RunSpecificParser "НазваниеЗапускаемогоПарсера" End Sub
Во вложении — файл с этим кодом, а также с возможностью для пользователя отменить автоматический запуск парсера в течение 5 секунд после открытия файла. Такой файл можно закинуть в планировщик задач Windows
Есть excel-файлы, несколько сотен.
Из них нужно как-то извлечь некоторые строки, по заданному принципу (текст в них отличается).
И залить эти строки в один новый экселевский файл.
Можно сделать руками, но лень. И еще есть вариант, что данная задача будет периодически повторяться.
Подскажите, пожалуйста, какими инструментами лучше это программно сделать? Автоматически по очереди пооткрывать все файлы в директории, пропарсить, взять нужные строки, залить в новый файл.
Может, был у кого похожий опыт.
Я начал смотреть в сторону python и www.python-excel.org
Может лучше сделать это visual basic’ом? Или вообще в самом экселе есть такая возможность?
Спасибо!
P.S. Все сделал так: сперва нашел плагин для эксель, бесплатно и быстро сливающий много эксель файлов в один. Вот он.
Затем искал конкретные макросы, удаляющие строки по различным критериям, типа — пустая ячейка, текст в ячейке, цифры в ячейке.
В общем, даже не пришлось сильно разбираться с VBA, все сделал готовыми средствами.
Acid Burn Пользователь Сообщений: 585 |
Привет, Планета! Опять нужна Ваша помощь. Т.е. получить нужный результат можно, задав на поиск в каждой книге символ «=». Скрин . SOS!!! |
galina mur Пользователь Сообщений: 245 |
#2 28.06.2013 00:37:07 макет формирования
|
||
ikki Пользователь Сообщений: 9709 |
#3 28.06.2013 07:16:59
Изменено: ikki — 29.06.2013 05:02:47 фрилансер Excel, VBA — контакты в профиле |
||
Acid Burn Пользователь Сообщений: 585 |
#4 28.06.2013 21:22:21 galina mur, ikki, LightZ, огромное Вам спасибо!
Прикрепленные файлы
Изменено: Acid Burn — 06.07.2013 12:59:40 |
|
LightZ Пользователь Сообщений: 1748 |
Еще желательно добавить цикл по всем листам Киса, я хочу Вас спросить, как художник — художника: Вы рисовать умеете? |
LightZ Пользователь Сообщений: 1748 |
1. Почитайте справку про методы FileDialog’a Киса, я хочу Вас спросить, как художник — художника: Вы рисовать умеете? |
Acid Burn Пользователь Сообщений: 585 |
#7 06.07.2013 20:19:47 LightZ, через FileDialog у меня не получилось, про булевую переменную вообще не понял.
Просто не люблю засорять темы — потом самому же сложно перечитывать. |
||
vikttur Пользователь Сообщений: 47199 |
Если изменяете так, что в последующих сообщениях искажается смысл или теряется привязка, то мусора становится больше. Обращайте на это вимание. |
LightZ Пользователь Сообщений: 1748 |
Acid Burn, Киса, я хочу Вас спросить, как художник — художника: Вы рисовать умеете? |
Acid Burn Пользователь Сообщений: 585 |
#10 06.07.2013 23:24:33 Так-то «результат» был выложен в посте #4.
Изменено: Acid Burn — 06.07.2013 23:26:51 |
||
LightZ Пользователь Сообщений: 1748 |
#11 07.07.2013 10:47:39 А нужный результат Вы так и не приложили…
Киса, я хочу Вас спросить, как художник — художника: Вы рисовать умеете? |
||
Acid Burn Пользователь Сообщений: 585 |
LightZ, Вы меня абсолютно правильно поняли. |
LightZ Пользователь Сообщений: 1748 |
1. Подумайте логически — как может работать неправильно?)) Киса, я хочу Вас спросить, как художник — художника: Вы рисовать умеете? |
Acid Burn Пользователь Сообщений: 585 |
1. Забыл изменить имя переменной. |
LightZ Пользователь Сообщений: 1748 |
#15 09.07.2013 23:08:26 Такое чувство, что Вы читаете мои сообщения через строку или же вообще не читаете.
это а) и б) Киса, я хочу Вас спросить, как художник — художника: Вы рисовать умеете? |
||
Acid Burn Пользователь Сообщений: 585 |
LightZ, вот моя тестовая папка : |
LightZ Пользователь Сообщений: 1748 |
#17 10.07.2013 21:17:16 Посмотрел. А зачем вообще использовать Dir?
Киса, я хочу Вас спросить, как художник — художника: Вы рисовать умеете? |
||
Acid Burn Пользователь Сообщений: 585 |
Кстати, да. Спасибо Вам, LightZ! Я что-то и не подумал. LightZ, в посте #5 Вы предлагали предварительно занести данные в массив. Изменено: Acid Burn — 11.07.2013 09:44:58 |
LightZ Пользователь Сообщений: 1748 |
#19 11.07.2013 10:52:09
Что? По поводу массивов, вот пример:
Киса, я хочу Вас спросить, как художник — художника: Вы рисовать умеете? |
||||
Acid Burn Пользователь Сообщений: 585 |
>> Что? |
LightZ Пользователь Сообщений: 1748 |
А что именно не понятно? Киса, я хочу Вас спросить, как художник — художника: Вы рисовать умеете? |
Acid Burn Пользователь Сообщений: 585 |
Не могу понять, как использовать — ни зачитать, ни выгрузить в Excel. |
LightZ Пользователь Сообщений: 1748 |
#23 11.07.2013 23:20:43 Всё просто, вот пример:
В данном примере адрес можно получить только если в A1 есть значение. Советую почитать Изменено: LightZ — 11.07.2013 23:24:33 Киса, я хочу Вас спросить, как художник — художника: Вы рисовать умеете? |
||
Acid Burn Пользователь Сообщений: 585 |
LightZ, почитать и понять — разные вещи. |
Acid Burn Пользователь Сообщений: 585 |
#25 31.08.2013 16:48:46 Один замечательный человек помог кардинально улучшить мой макрос. Надеюсь, кому-то пригодится. Прикрепленные файлы
|