Rys*_*zyk 12 excel vba xmlhttprequest excel-vba web-scraping
下面是我在宏中实现的3个请求的声明变量.我列出了他们使用的库以及他们在评论中的后期绑定:
Dim XMLHTTP As New MSXML2.XMLHTTP 'Microsoft XML, v6.0 'Set XMLHTTP = CreateObject("MSXML2.XMLHTTP.6.0")
Dim ServerXMLHTTP As New MSXML2.ServerXMLHTTP 'Microsoft XML, v6.0 'Set ServerXMLHTTP = CreateObject("MSXML2.ServerXMLHTTP.6.0")
Dim http As New WinHttpRequest 'Microsoft WinHttp Services, version 5.1 'Set http = CreateObject("WinHttp.WinHttpRequest.5.1")
Run Code Online (Sandbox Code Playgroud)
我有一些旧的Web抓取宏,它使用Internet Explorer自动化.我想清理编码并加快这些请求的速度.
不幸的是,我注意到,MSXML2.ServerXMLHTTP并且WinHttpRequest在线商店的20个产品测试(34和35秒)比IE自动化更慢,图片和活动脚本关闭(24秒)!MSXML2.XMLHTTP在18秒内执行.我常常看到这三个请求中的一些请求比其他请求快2-3倍的情况,所以我总是测试哪一个表现最好,但从来没有任何请求丢失到IE自动化.
带有结果的主页面如下,它是一页上的所有结果,1500多个,所以请求需要一些时间(如果粘贴到MS Word,则为6500页):
www.justbats.com/products/bat type~baseball /?sortBy = TotalSales Descending&page = 1&size = 2400
然后我从主结果页面打开单个链接:
http://www.justbats.com/product/2017-marucci-cat-7-bbcor-baseball-bat--mcbc7/24317/
我想知道这3个请求是否都是我必须从没有浏览器自动化的网站获取数据的选项.此外 - 浏览器自动化有多可能超过其中一些要求?
UPDATE
我已经使用Robin Mackenzie提供的程序测试了主要结果页面,在运行之前清除了IE缓存.至少在这个特定页面上,缓存似乎没有明显的好处,因为后续请求产生了类似的结果.IE禁用了活动脚本,没有加载图像.
IE自动化方法,文档长度:7593346个字符,处理时间:8秒
WinHTTP方法,文档长度:7824059个字符,处理时间:29秒
XML HTTP方法,文档长度:7830217字符,处理时间:4秒
Server XML HTTP方法,文档长度:7823958个字符,处理时间:26秒
URL下载文件方法,文件长度:7830346字符,处理时间:7秒
对我来说非常令人惊讶的是这些方法返回的字符数量的差异.
除了你提到的方法:
您可以考虑另外两种方法:
CreateDocumentFromUrl方法MSHTML.HTMLDocumentURLDownloadToFileA还有一些其他的Windows API我忽略了,如InternetOpen,InternetOpenUrl等潜在的性能将通过猜测响应长度,缓冲响应,等等的复杂性所抵消。
使用该CreateDocumentFromUrl方法,您的示例网站存在问题,因为它尝试HTMLDocument在一个框架中创建一个不允许的错误,例如:
禁止装帧
和
为了帮助保护您输入到本网站的信息的安全,此内容的发布者不允许将其显示在框架中。
所以我们不应该使用这种方法。
我认为您需要等效的phpfile_get_contents并找到了此方法。它很容易使用(检查此链接),并且在处理大型请求时性能优于其他方法(例如,当您购买超过 2000 个棒球棒时尝试使用它)。该XMLHTTP还的方法使用URLMon图书馆,所以我想这种方式只是切出位中间人逻辑,因为你必须做一些文件系统处理显然有不利的一面。
Private Declare Function URLDownloadToFileA Lib "urlmon" (ByVal pCaller As Long, _
ByVal szURL As String, ByVal szFileName As String, ByVal dwReserved As Long, _
ByVal lpfnCB As Long) As Long
Sub TestUrlDownloadFile(strUrl As String)
Dim dteStart As Date
Dim dteFinish As Date
Dim strTempFileName As String
Dim strResponse As String
Dim objFso As FileSystemObject
On Error GoTo ExitFunction
dteStart = Now
strTempFileName = "D:\foo.txt"
DownloadFile strUrl, strTempFileName
Set objFso = New FileSystemObject
With objFso.OpenTextFile(strTempFileName, ForReading)
strResponse = .ReadAll
.Close
End With
objFso.DeleteFile strTempFileName
dteFinish = Now
Debug.Print "URL download file method"
Debug.Print "Document length: " & Len(strResponse) & " chars"
Debug.Print "Processed in: " & Format(dteFinish - dteStart, "s") & " seconds"
Debug.Print VBA.vbNewLine
ExitFunction:
If Err.Number <> 0 Then
Debug.Print Err.Description
End If
End Sub
'http://www.vbaexpress.com/forum/archive/index.php/t-27050.html
Private Function DownloadFile(URL As String, LocalFilename As String) As Boolean
Dim lngRetVal As Long
lngRetVal = URLDownloadToFileA(0, URL, LocalFilename, 0, 0)
If lngRetVal = 0 Then DownloadFile = True
End Function
Run Code Online (Sandbox Code Playgroud)
使用该方法URLDownloadToFileA下载示例 URL 需要大约 1-2 秒,而使用该XMLHTTP方法需要4-5 秒(完整代码如下)。
网址:
www.justbats.com/products/bat type~baseball/?sortBy=TotalSales Descending&page=1&size=2400
这是输出:
Testing...
XML HTTP method
Document length: 7869753 chars
Processed in: 4 seconds
URL download file method
Document length: 7869753 chars
Processed in: 1 seconds
Run Code Online (Sandbox Code Playgroud)
这包括讨论的所有方法,例如 IE 自动化、WinHTTPRequest、XMLHTTP、ServerXMLHTTP、CreateDocumentFromURL 和 URLDownloadFile。
您需要项目中的所有这些参考:
这里是:
Option Explicit
Private Declare Function URLDownloadToFileA Lib "urlmon" (ByVal pCaller As Long, _
ByVal szURL As String, ByVal szFileName As String, ByVal dwReserved As Long, _
ByVal lpfnCB As Long) As Long
Sub Test()
Dim strUrl As String
strUrl = "http://www.justbats.com/products/bat type~baseball/?sortBy=TotalSales Descending&page=1&size=2400"
Debug.Print "Testing..."
Debug.Print VBA.vbNewLine
'TestIE strUrl
'TestWinHHTP strUrl
TestXMLHTTP strUrl
'TestServerXMLHTTP strUrl
'TestCreateDocumentFromUrl strUrl
TestUrlDownloadFile strUrl
End Sub
Sub TestIE(strUrl As String)
Dim dteStart As Date
Dim dteFinish As Date
Dim objIe As InternetExplorer
Dim objHtml As MSHTML.HTMLDocument
Dim strResponse As String
On Error GoTo ExitFunction
dteStart = Now
Set objIe = New SHDocVw.InternetExplorer
With objIe
.navigate strUrl
.Visible = False
While .Busy Or .readyState <> READYSTATE_COMPLETE
DoEvents
Wend
Set objHtml = .document
strResponse = objHtml.DocumentElement.outerHTML
.Quit
End With
dteFinish = Now
Debug.Print "IE automation method"
Debug.Print "Document length: " & Len(strResponse) & " chars"
Debug.Print "Processed in: " & Format(dteFinish - dteStart, "s") & " seconds"
Debug.Print VBA.vbNewLine
ExitFunction:
If Err.Number <> 0 Then
Debug.Print Err.Description
End If
If Not objIe Is Nothing Then
objIe.Quit
End If
Set objIe = Nothing
End Sub
Sub TestWinHHTP(strUrl As String)
Dim dteStart As Date
Dim dteFinish As Date
Dim objHttp As WinHttp.WinHttpRequest
Dim objDoc As HTMLDocument
Dim strResponse As String
On Error GoTo ExitFunction
dteStart = Now
Set objHttp = New WinHttp.WinHttpRequest
With objHttp
.Open "get", strUrl, False
.setRequestHeader "Cache-Control", "no-cache"
.setRequestHeader "Pragma", "no-cache"
.setRequestHeader "If-Modified-Since", "Sat, 1 Jan 2000 00:00:00 GMT"
.send
.WaitForResponse
strResponse = .responseText
End With
dteFinish = Now
Debug.Print "WinHTTP method"
Debug.Print "Document length: " & Len(strResponse) & " chars"
Debug.Print "Processed in: " & Format(dteFinish - dteStart, "s") & " seconds"
Debug.Print VBA.vbNewLine
ExitFunction:
If Err.Number <> 0 Then
Debug.Print Err.Description
End If
Set objDoc = Nothing
Set objHttp = Nothing
End Sub
Sub TestXMLHTTP(strUrl As String)
Dim dteStart As Date
Dim dteFinish As Date
Dim objXhr As MSXML2.XMLHTTP60
Dim objDoc As MSHTML.HTMLDocument
Dim strResponse As String
On Error GoTo ExitFunction
dteStart = Now
Set objXhr = New MSXML2.XMLHTTP60
With objXhr
.Open "get", strUrl, False
.setRequestHeader "Cache-Control", "no-cache"
.setRequestHeader "Pragma", "no-cache"
.setRequestHeader "If-Modified-Since", "Sat, 1 Jan 2000 00:00:00 GMT"
.send
While .readyState <> 4
DoEvents
Wend
strResponse = .responseText
End With
dteFinish = Now
Debug.Print "XML HTTP method"
Debug.Print "Document length: " & Len(strResponse) & " chars"
Debug.Print "Processed in: " & Format(dteFinish - dteStart, "s") & " seconds"
Debug.Print VBA.vbNewLine
ExitFunction:
If Err.Number <> 0 Then
Debug.Print Err.Description
End If
Set objDoc = Nothing
Set objXhr = Nothing
End Sub
Sub TestServerXMLHTTP(strUrl As String)
Dim dteStart As Date
Dim dteFinish As Date
Dim objXhr As MSXML2.ServerXMLHTTP60
Dim objDoc As MSHTML.HTMLDocument
Dim strResponse As String
On Error GoTo ExitFunction
dteStart = Now
Set objXhr = New MSXML2.ServerXMLHTTP60
With objXhr
.Open "get", strUrl, False
.setRequestHeader "Cache-Control", "no-cache"
.setRequestHeader "Pragma", "no-cache"
.setRequestHeader "If-Modified-Since", "Sat, 1 Jan 2000 00:00:00 GMT"
.send
While .readyState <> 4
DoEvents
Wend
strResponse = .responseText
End With
dteFinish = Now
Debug.Print "Server XML HTTP method"
Debug.Print "Document length: " & Len(strResponse) & " chars"
Debug.Print "Processed in: " & Format(dteFinish - dteStart, "s") & " seconds"
Debug.Print VBA.vbNewLine
ExitFunction:
If Err.Number <> 0 Then
Debug.Print Err.Description
End If
Set objDoc = Nothing
Set objXhr = Nothing
End Sub
Sub TestUrlDownloadFile(strUrl As String)
Dim dteStart As Date
Dim dteFinish As Date
Dim strTempFileName As String
Dim strResponse As String
Dim objFso As FileSystemObject
On Error GoTo ExitFunction
dteStart = Now
strTempFileName = "D:\foo.txt"
If DownloadFile(strUrl, strTempFileName) Then
Set objFso = New FileSystemObject
With objFso.OpenTextFile(strTempFileName, ForReading)
strResponse = .ReadAll
.Close
End With
objFso.DeleteFile strTempFileName
Else
Debug.Print "Error downloading file from URL: " & strUrl
GoTo ExitFunction
End If
dteFinish = Now
Debug.Print "URL download file method"
Debug.Print "Document length: " & Len(strResponse) & " chars"
Debug.Print "Processed in: " & Format(dteFinish - dteStart, "s") & " seconds"
Debug.Print VBA.vbNewLine
ExitFunction:
If Err.Number <> 0 Then
Debug.Print Err.Description
End If
End Sub
'http://www.vbaexpress.com/forum/archive/index.php/t-27050.html
Private Function DownloadFile(URL As String, LocalFilename As String) As Boolean
Dim lngRetVal As Long
lngRetVal = URLDownloadToFileA(0, URL, LocalFilename, 0, 0)
If lngRetVal = 0 Then
DownloadFile = True
Else
DownloadFile = False
End If
End Function
Sub TestCreateDocumentFromUrl(strUrl As String)
Dim dteStart As Date
Dim dteFinish As Date
Dim strResponse As String
Dim objDoc1 As HTMLDocument
Dim objDoc2 As HTMLDocument
On Error GoTo ExitFunction
dteStart = Now
Set objDoc1 = New HTMLDocument
Set objDoc2 = objDoc1.createDocumentFromUrl(strUrl, "null")
While objDoc2.readyState <> "complete"
DoEvents
Wend
strResponse = objDoc2.DocumentElement.outerHTML
Debug.Print strResponse
dteFinish = Now
Debug.Print "HTML Document Create from URL method"
Debug.Print "Document length: " & Len(strResponse) & " chars"
Debug.Print "Processed in: " & Format(dteFinish - dteStart, "s") & " seconds"
Debug.Print VBA.vbNewLine
ExitFunction:
If Err.Number <> 0 Then
Debug.Print Err.Description
End If
Set objDoc2 = Nothing
Set objDoc1 = Nothing
End Sub
Run Code Online (Sandbox Code Playgroud)
大部分时间都花在等待服务器的响应上。因此,如果您想缩短执行时间,请并行发送请求。
我也会使用“Msxml2.ServerXMLHTTP.6.0”对象/接口,因为它没有实现任何缓存。
这是一个工作示例:
Sub TestRequests()
GetUrls _
"http://stackoverflow.com/questions/34880012", _
"http://stackoverflow.com/questions/34880013", _
"http://stackoverflow.com/questions/34880014", _
"http://stackoverflow.com/questions/34880015", _
"http://stackoverflow.com/questions/34880016", _
"http://stackoverflow.com/questions/34880017"
End Sub
Private Sub OnRequest(url, xhr)
xhr.Open "GET", url, True
xhr.setRequestHeader "Content-Type", "text/html; charset=UTF-8"
xhr.setRequestHeader "User-Agent", "Mozilla/5.0 (Windows NT 6.1; rv:25.0) Gecko/20100101 Firefox/25.0"
xhr.Send
End Sub
Private Sub OnResponse(url, xhr)
Debug.Print url, Len(xhr.ResponseText)
End Sub
Public Function GetUrls(ParamArray urls())
Const WORKERS = 10
' create http workers
Dim wkrs(0 To WORKERS * 2 - 1), i As Integer
For i = 0 To UBound(wkrs) Step 2
Set wkrs(i) = CreateObject("Msxml2.ServerXMLHTTP.6.0")
Next
' send the requests in parallele
Dim index As Integer, count As Integer, xhr As Object
While count <= UBound(urls)
For i = 0 To UBound(wkrs) Step 2
Set xhr = wkrs(i)
If xhr.readyState And 3 Then ' if busy
xhr.waitForResponse 0.01 ' wait 10ms
ElseIf Not VBA.IsEmpty(wkrs(i + 1)) And xhr.readyState = 4 Then
OnResponse urls(wkrs(i + 1)), xhr
count = count + 1
wkrs(i + 1) = Empty
End If
If VBA.IsEmpty(wkrs(i + 1)) And index <= UBound(urls) Then
wkrs(i + 1) = index
OnRequest urls(index), xhr
index = index + 1
End If
Next
Wend
End Function
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
4987 次 |
| 最近记录: |