Во вложении класс и пример работы с ним! Очень много времени трачу именно на загрузку страниц и т.д. сейчас активно читаю MSDN и забугорные форму и как это реализуется на других языках, cURL и т.д. ищу альтернативу особенно ИНДУССКОМУ IE (ой как они его любят же). В общем случае мы используем один из этих объектов и получаем ответы от сервера так:
Код
'Dim XMLHttp As Object: Set XMLHttp = CreateObject("Microsoft.XMLHTTP")
'Dim XMLHttp As Object: Set XMLHttp = CreateObject("WinHttp.WinHttpRequest.5.1")
'Dim XMLHttp As Object: Set XMLHttp = CreateObject("MSXML2.ServerXMLHTTP")
'Dim XMLHttp As Object: Set XMLHttp = CreateObject("MSXML2.XMLHttp") '
'Dim XMLHttp As Object: Set XMLHttp = New WinHttpRequest
XMLHttp.Open "GET", "site.com", False 'OR "POST" type and True for redirect
'XMLHttp.SetRequestHeader "User-Agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0)"
'XMLHttp.SetRequestHeader "Content-Type", "application/x-www-form-urlencoded"
'XMLHttp.SetRequestHeader "Cache-Control", "no-store, no-cache"
'XMLHttp.SetRequestHeader "Pragma", "no-cache"
'XMLHttp.Send StrConv("title=Title&cat=Cat&post=PostText&pas=tipapass", vbFromUnicode)
XMLHttp.Send '"title=Title&cat=Cat&post=PostText&pas=tipapass" << тут дольше всего висит
Debug.Print XMLHttp.ResponseText
Set XMLHttp = Nothing
Если кто сталкивался или знает XMLHttp.Send действительно отправляет и ждет ответа или может только отправить и не ждать или он и так только отправляет какие еще есть варианты?
По сути в общем смысле идея следующая: 1)сделать или массив или несколько объектов через рекурсию, любого из перечисленных выше или может даже IE 2)создали, отправили сайт на загрузку, проверили загрузился или нет 1 раз, если нет: 2.1)создали, отправили сайт на загрузку, проверили загрузился или нет ПЕРВЫЙ САЙТ, потом наш, если нет: 2.n)создали, отправили сайт на загрузку, проверили загрузился или нет ПЕРВЫЙ САЙТ, если да - забрали данные, убили обьект, проверили следующий, до n
Реально ли так сделать? - пока еще думаю на счет реализации и преимущества в скорости. Пока по сравнению всех выше перечисленных объектов WinHttp.WinHttpRequest.5.1 - работает дольше всех в 2.5 раза, но грузит все сайты (и китайские) с любого URL, у него есть TimeAut, если сильно долго грузится и т.д., в теории есть и
Const TIMEOUT& = 6 ' в секундах
Function GetResponse(ByVal URL$) As String
On Error Resume Next: Err.Clear
Static xmlhttp As WinHttpRequest
If xmlhttp Is Nothing Then Set xmlhttp = New WinHttpRequest
xmlhttp.Open "GET", URL$, True: DoEvents
xmlhttp.Send: DoEvents
If Not xmlhttp.WaitForResponse(TIMEOUT&) Then
Debug.Print "timeout", URL: Exit Function
End If
GetResponse = xmlhttp.responsetext
End FunctionSub test() ' пример использования
On Error Resume Next
txt = GetResponse("http://ExcelVBA.ru/")
Debug.Print Len(txt) ' возвращает длину текста: 62737 символов
End Sub
Цитата
Реально ли так сделать?
думаю, да но сложно сам подумывал сделать такое для своей программы Парсер сайтов, но пока обхожусь однопоточной загрузкой
ваши варианты вряд ли подойдут при асихнронном запросе макрос висит на строке WaitForResponse при синхронном - на строке Send (т.е. пока ответ сервера не получен, - другие команды макросов выполняться не будут, - т.к. VBA однопоточный)
как вариант, - написать отдельный компонент (не на VBA)? подключить к проекту, - и через него работать с многопоточностью
О Значит кто-то все-таки сталкивался и проверял, да ваш сайт вообще нечто - он меня и вдохновил и обучил за прошлый год и сейчас переодически смотрю обновления.
Цитата
Игорь пишет: (т.е. пока ответ сервера не получен, - другие команды макросов выполняться не будут, - т.к. VBA однопоточный) как вариант, - написать отдельный компонент (не на VBA)? подключить к проекту, - и через него работать с многопоточностью
Да об этом то и идет речь минимальная мысль это IE их можно много открывать и проверять загруженность страницы (но он долго грузит), далее средства cURL или чего-то подобного (сейчас уже пытаюсь реализовать проблема в передаче данных или через буфер или файлы) + ко всему у нас есть CMD, в котором что-то можно выделить, но опять-же не хотелось бы батник таскать с программой (нужно программно создавать) и т.д. Сейчас еще смотрю что можно сделать написав ADD IN для VBA по типу MZTOOLZ, какие там есть возможности. Как вариант думаю написать что-то на Delphi или C++.
Пока только реальный способ только через ИЕ, даже вот
Скрытый текст
Цитата
InternetExplorer in Action In the above section, the code that instantiated IE waited for it to fully load a page (with the Do…Loop statement). This is because IE runs asynchronously with our VB code. There can be instances when we don’t want to wait for the page to complete loading. In fact, we can leverage both its asynchronous nature as well as the fact that IE is a standalone application designed to display webpages to a human. Consider a scenario where we want to respond to user action by displaying information from different web pages. For example, in an Excel worksheet with a column of stock symbols, as the user clicks in a different cell we want to show information about that stock. The code to show the information in an instance of InternetExplorer is below. It checks if it still has access to the asynchronously running IE. If not, it instantiates a new copy. In either case it uses InternetExplorer’s Navigate method to direct the browser to a specific web page.
Код
Option Explicit
Dim IEApp As InternetExplorer 'Need a reference to Microsoft Internet Controls
Sub AsyncIE(ByVal URL As String)
'URL contains the complete string to pass to IE, _
e.g., http://www.google.com/search?q=tushar+mehta
On Error Resume Next
IEApp.Navigate URL
If Err.Number <> 0 Then
Set IEApp = Nothing
Set IEApp = New InternetExplorer
IEApp.Visible = True 'Some things don't work unless it's visible
IEApp.Navigate URL
End If
If IEApp Is Nothing Then Exit Sub
End Sub
только что вспомнил, когда делал и искал таймер нашел асинхронный таймер на вба, который работал прекрасно! к Стати вот нашел ту статью на хабре http://habrahabr.ru/sandbox/67296/ (про таймер)
Ну вот ночь не прошла даром и после долгого покраснения моих глаз, а потом ОТЛИЧНОГО СНА (очень рекомендую тем у кого-то что-то не получается) мы получили пока следующее: Module - Test Async
Скрытый текст
Код
Option Explicit
Public xmlHttpRequest As MSXML2.XMLHttp 'объявляем наш объект
Sub Test()
On Error GoTo FailedState
'If Not xmlHttpRequest Is Nothing Then Set xmlHttpRequest = Nothing
Dim MyXmlHttpHandler As CXMLHTTPHandler 'объявляем обработчик
Dim url As String
url = "http://www.planetaexcel.ru/forum/index.php?PAGE_NAME=message&FID=1&TID=60760&TITLE_SEO=60760-mnogopotochnost-ili-ee-imitatsiya&MID=508807&result=edit#message508807"
Set xmlHttpRequest = New MSXML2.XMLHttp 'ниже описал, что НЕЛЬЗЯ 2 сразу загружать ТОЛЬКО 1 сайт
' Create an instance of the wrapper class. \ Инициализируем обработчик
Set MyXmlHttpHandler = New CXMLHTTPHandler
MyXmlHttpHandler.Initialize xmlHttpRequest
' Assign the wrapper class object to onreadystatechange. \ объясняем как действовать во время загрузки - с помощью обработчика
xmlHttpRequest.OnReadyStateChange = MyXmlHttpHandler
' Get the page stuff asynchronously. \ загружаем страницу
xmlHttpRequest.Open "GET", url, True
xmlHttpRequest.Send "" 'никаких подвисаний
Exit Sub
FailedState:
MsgBox Err.Number & ": " & Err.Description
End Sub
Sub MyXmlHttpHandler()
'выполняем ЛЮБЫЕ действия после загрузки сайта, единственное не знаю как вернуться в основную процедуру делать метки? resume next?
MsgBox xmlHttpRequest.responseText
End Sub
class CXMLHTTPHandler В этом классе есть 1 секрет который мы не видим и из-за чего все это дело работает, а именно: 1)нужно удалить и експортировать код 2)через блокнот или что-то подобное открыть и дописать после: "Sub OnReadyStateChange()" строчку "Attribute Value.VB_UserMemId = 0" 3)снова импортировать и смотреть на результат, взято из статьи http://www.cpearson.com/excel/DefaultMember.aspx
Скрытый текст
Код
Option Explicit
Dim m_xmlHttp As MSXML2.XMLHttp
Public Sub Initialize(ByRef xmlHttpRequest As MSXML2.XMLHttp)
Set m_xmlHttp = xmlHttpRequest
End Sub
Sub OnReadyStateChange()
Debug.Print m_xmlHttp.readyState
If m_xmlHttp.readyState = 4 Then
If m_xmlHttp.Status = 200 Then
MsgBox m_xmlHttp.responseText ' вот эта строка не срабатывает, но срабатывает в обработчике
Else
'Error happened
End If
End If
End Sub
И что вы думаете - работает , да еще и как, как минимум, нет подвисания, а значит можно спокойно делать все что угодно пока грузится(как минимум Ваш PI прогресс не висит, а это уже что-то), НО после прочтения первой статьи я понял что нельзя загружать несколько сайтов одновременно, только делать что-то другое т.к. обьект 1 и класс 1, дальше напишу как сделать так чтоюы можно было несколько сайтов загружать одновременно. И еще не знаю что делать если выключена библиотека с MSXML2.XMLHttp? П.С. Начал переводить главную статью готово на 10%
Интересно конечно, я еще посмотрю, но для этого нам нужно что? - таскать с собой этот файл или в создавать считывать из него и т.д. в любом случае использовать не ОЗУ, а тормознутый ВИНТ, что ой как не хорошо и тормозит, конечно по сравнению с тем что сейчас есть, лучше, но все-равно немного не-то.
Еще перевожу статью, как переведу - выложу, статья офигенная и результат - тот что нужно! Смысл тот же что и в предыдущем сообщении, но теперь можно делать сколько угодно асинхронных запросов!
цепочка: 1)запрос пользователя (список сайтов, ссылок, запросов ...) 2) отправка запросов (ждем по вайлу пока одна из глобальных переменных не поменялась) , как только меняется 3.1)пошел запрос на следующий сайт в эту глобальную переменную 3.2)выполняем действие с переменной, после обработки ее идем к пункту 2) 4)обработано все, запись данных
Выложу код главный сюда:
Скрытый текст
Код
Option Explicit
Dim XMLHttpReq As MSXML2.XMLHttp, _
bIAmAvailable As Boolean, _
ResponseProcessor As String, _
sURL As String, sMsg As String
Property Get IAmAvailable() As Boolean
IAmAvailable = bIAmAvailable
End Property
Sub ReadyStateChangeHandler()
'Debug.Print XMLHttpReq.readyState
If XMLHttpReq.readyState = 4 Then
If XMLHttpReq.Status = 200 Then
'Process the response here
'hrefToHyperLink XMLHttpReq.responseText, ActiveCell
Application.Run ResponseProcessor, XMLHttpReq, sURL, sMsg
bIAmAvailable = True
Else
Debug.Print XMLHttpReq.Status & ", " & XMLHttpReq.responseText
End If
End If
End Sub
Public Sub XMLHttpCall(ByVal ReqMethod As String, _
ByVal URL As String, ByVal uResponseProcessor As String, _
Optional ByVal AsyncCall As Boolean = True, _
Optional ByVal uMsg As String = "")
Set XMLHttpReq = New MSXML2.XMLHTTP
If AsyncCall Then
sURL = URL: sMsg = uMsg
ResponseProcessor = uResponseProcessor
XMLHttpReq.OnReadyStateChange = Me
End If
With XMLHttpReq
.Open ReqMethod, URL, AsyncCall
.send uMsg
If Not AsyncCall Then Application.Run uResponseProcessor, XMLHttpReq, URL, uMsg
End With
End Sub
и класс:
Скрытый текст
Код
Option Explicit
Option Base 0
Dim XMLHttpMon() As clsXMLHttpMonitor
Private Function findAvailMon() As clsXMLHttpMonitor
Dim I As Integer, Done As Boolean
I = LBound(XMLHttpMon)
Do
If XMLHttpMon(I) Is Nothing Then
Done = True
ElseIf XMLHttpMon(I).IAmAvailable Then
Done = True
Else
I = I + 1
Done = I > UBound(XMLHttpMon)
End If
Loop Until Done
If I > UBound(XMLHttpMon) Then _
ReDim Preserve XMLHttpMon(UBound(XMLHttpMon) + 1)
Set XMLHttpMon(I) = New clsXMLHttpMonitor
Set findAvailMon = XMLHttpMon(I)
End Function
Public Sub XMLHttpCall(ByVal ReqMethod As String, _
ByVal URL As String, ByVal uResponseProcessor As String, _
Optional ByVal AsyncCall As Boolean = True, _
Optional ByVal uMsg As String)
Dim XMLHttpMon As clsXMLHttpMonitor
Set XMLHttpMon = findAvailMon()
XMLHttpMon.XMLHttpCall ReqMethod, URL, uResponseProcessor, AsyncCall, uMsg
End Sub
Private Sub Class_Initialize()
ReDim XMLHttpMon(0)
End Sub
Статью почти перевел, есть еще пару вопросов и мне нужна Ваша помощь. 1)Пока не пойму как реализовать CreateObject, с поздним связыванием... 2)Почему-то некоторые сайты не грузились, когда заменил на MSXML2.ServerXMLHTTP - пошло, но намного медленней и хочу реализовать следующее: Использовать сначала MSXML2.XMLHTTP, а если будет ошибка - то MSXML2.ServerXMLHTTP (Хотя нет знаю, создам 2 копии классов и функций, просто буду при ошибке вызывать другой) Сейчас протестировал и MSXML2.XMLHTTP работает но через раз.
Заменил все объявления MSXML на Object и связал в одном месте, только что протестировал - все прекрасно работает. Но проблема с загрузкой через раз - остается. 1)Решено 2)Тоже вроде решено
уже встраиваю в один из проектов - прирост скорости ОГРОМНЫЙ + никаких подвисаний, сейчас делаю краштесты, возможно получится что-то уложить буду по десятку страниц сверху загружать.
Как бы это смешно не звучало, но у меня проблема с синхронизацией асинхронного модуля))) Сам модуль пока не готов, но вот то что сейчас использую:
Скрытый текст
Код
Dim XMLHttpManager As New clsXMLHttpManager
Dim XMLHttpServerManager As New clsXMLHttpServerManager
Public AsyncXMLHttpResponse() As String, AsyncXMLHttpURLS() As String, PublicI As Long
Sub InicializeAsyncXMLHttp(Optional ByVal StartNumber As Long = 0)
PublicI = StartNumber
ReDim AsyncXMLHttpResponse(0 To PublicI)
ReDim AsyncXMLHttpURLS(0 To PublicI)
End Sub
Sub AsyncXMLHttpGet(ByVal URL As String)
If RECheck(URL) Then XMLHttpManager.XMLHttpCall "GET", URL, "AsyncXMLHttpProcessor"
End Sub
Sub AsyncXMLHttpServerGet(ByVal URL As String)
If RECheck(URL) Then XMLHttpServerManager.XMLHttpCall "GET", URL, "AsyncXMLHttpProcessor"
End Sub
Sub AsyncXMLHttpProcessor(XMLHttpReq As Object, _
ByVal URL As String, ByVal sMsg As String)
ReDim Preserve AsyncXMLHttpResponse(0 To PublicI + 1)
ReDim Preserve AsyncXMLHttpURLS(0 To PublicI + 1)
If XMLHttpReq.ResponseText <> "" Then
AsyncXMLHttpResponse(PublicI) = XMLHttpReq.ResponseText
Else
AsyncXMLHttpResponse(PublicI) = "Error"
End If
AsyncXMLHttpURLS(PublicI) = URL
PublicI = PublicI + 1
' ReDim Preserve AsyncXMLHttpResponse(0 To PublicI + 1)
' ReDim Preserve AsyncXMLHttpURLS(0 To PublicI + 1)
End Sub
и сама процедура, парсим ссылки на картинки
Скрытый текст
Код
If LastRow(Sheets(1), 4) = 1 Then
InicializeAsyncXMLHttp
For I = 2 To LastRow(Sheets(1), 3)
pi.SubAction , "Обрабатывается ссылка $index из $count", "$time"
ReadSettings
If I = NextI Then
Erase AsyncXMLHttpResponse
If Not LastRow(Sheets(1), 3) < I Then
For K = I To I + 7
AsyncXMLHttpGet (Cells(K, 3).Value)
Sleep 100: DoEvents
Next
NextI = I + 7
End If
End If
While PublicI = (I - 2): DoEvents: Wend
pi.SubAction , "Обрабатывается ссылка $index из $count", "$time"
html$ = AsyncXMLHttpResponse(I - 2)
Set FindsImg = GetTxt(html$, "src=.(.*?\.(\w){3})(.\d{1,}){1,}\.\w{3}""")
Id = AsyncXMLHttpURLS(I - 2)
rowNum = Application.Match(Id, Rng, 0)
If FindsImg.Count >= 1 Then
If CInt(CountImages) > CInt(FindsImg.Count - 1) Then CountImages = FindsImg.Count - 1
Cells(rowNum, 4) = ""
For J = 1 To CountImages
Cells(rowNum, 4) = Cells(rowNum, 4) & FindsImg(J).SubMatches(0) & ",": DoEvents
Next
End If
Next
Исправил предварительно записав переменную т.е. вместо
Код
Set FindsImg = GetTxt(AsyncXMLHttpResponse(I - 2), "src=.(.*?\.(\w){3})(.\d{1,}){1,}\.\w{3}""")
Код
html$ = AsyncXMLHttpResponse(I - 2)
Set FindsImg = GetTxt(html$, "src=.(.*?\.(\w){3})(.\d{1,}){1,}\.\w{3}""")
Добрый день! При запуске Skype появляется окно с ошибкой: Во время выполнения произошла ошибка. Запустить отладку? Строка: 1 Ошибка: XMLHttpReguest - определение отсутствует. ДА НЕТ После нажатия на НЕТ Во время выполнения произошла ошибка. Запустить отладку? Строка: 8 Ошибка: window. msrCrypto/suble - есть null или не является объектом. ДА НЕТ ЧТО ЭТО вообще такое?? Будет ли это мешать работе Skype? Я в этом ничего не понимаю. Нади ли что-то с этим делать? Объясните, пожалуйста, кто знает. А то просто раздражает, когда чего-то вдруг вылазит, а ты не понимаешь что нужно делать или может , наоборот ничего не делать? Спасибо.