ExcelHome技术论坛

 找回密码
 免费注册

QQ登录

只需一步,快速开始

快捷登录

搜索
EH技术汇-专业的职场技能充电站 妙哉!函数段子手趣味讲函数 Excel服务器-会Excel,做管理系统 效率神器,一键搞定繁琐工作
HR薪酬管理数字化实战 Excel 2021函数公式学习大典 Excel数据透视表实战秘技 打造核心竞争力的职场宝典
让更多数据处理,一键完成 数据工作者的案头书 免费直播课集锦 ExcelHome出品 - VBA代码宝免费下载
用ChatGPT与VBA一键搞定Excel WPS表格从入门到精通 Excel VBA经典代码实践指南
查看: 78060|回复: 101

[原创] 【网页采集教程】【高级篇】第一课-使用XMLHTTP异步获取网络文件的方法

  [复制链接]

TA的精华主题

TA的得分主题

发表于 2012-8-5 18:01 | 显示全部楼层 |阅读模式
本帖已被收录到知识树中,索引项:网页交互
本帖最后由 liucqa 于 2014-1-16 10:46 编辑

教程分基础篇和高级篇,陆续更新中...

第一课:http://club.excelhome.net/thread-893760-1-1.html
第二课:http://club.excelhome.net/thread-894527-1-1.html
第三课:http://club.excelhome.net/thread-896608-1-1.html
第四课:http://club.excelhome.net/thread-897117-1-1.html
第五课:http://club.excelhome.net/thread-899268-1-1.html

高级篇:
第一课:http://club.excelhome.net/thread-902266-1-1.html
第二课:http://club.excelhome.net/thread-939881-1-1.html


*****************************俺是上课的分割线*************************************

在之前的教程里,只简单讲解了一些网页采集的初中级方法,对初学者来说,使用同步方式采集数据是最简单也最好操控的。
然而,在网页采集的实际应用中,往往在采集数据的同时还需要与使用者进行交互,以达到所谓的实时处理的目的。同步方式采集数据会造成Excel的假死,不能满足实时操作数据的需求,只有异步方式才能做到采集数据的同时,不影响对Excel数据的操作。

在讲解异步下载文件之前,我们先来看一下XMLHTTP的一些属性

属性                     描述
onreadystatechange     每个状态改变时都会触发这个事件处理器,VBA通常会指到一个类模块
readyState                    请求的状态。有5 个可取值:0 = 未初始化,1 = 正在加载,2 = 已加载,3 = 交互中,4 = 完成
responseText                服务器的响应,表示为一个串
responseXML                服务器的响应,表示为XML。这个对象可以解析为一个DOM 对象
status                           服务器的HTTP 状态码
statusText HTTP          状态码的相应文本

需要我们重点关注的是以下两个属性:
onreadystatechangereadyState

事件句柄
onreadystatechange
每次 readyState 属性改变的时候调用的事件句柄函数。当 readyState 为 3 时,它也可能调用多次。

readyState
HTTP 请求的状态.当一个 XMLHttpRequest 初次创建时,这个属性的值从 0 开始,直到接收到完整的 HTTP 响应,这个值增加到 4。5 个状态中每一个都有一个相关联的非正式的名称,下表列出了状态、名称和含义:
状态         名称                             描述
0         Uninitialized            初始化状态。XMLHttpRequest 对象已创建或已被 abort() 方法重置。
1         Open                     open()方法已调用,但是 send() 方法未调用。请求还没有被发送。
2         Sent                      Send() 方法已调用,HTTP 请求已发送到 Web 服务器。未接收到响应。
3         Receiving               所有响应头部都已经接收到。响应体开始接收但未完成。
4         Loaded HTTP         响应已经完全接收。
readyState 的值不会递减,除非当一个请求在处理过程中的时候调用了 abort() 或 open() 方法。每次这个属性的值增加的时候,都会触发 onreadystatechange 事件句柄。


如果我们想用异步方式来提交XMLHTTP的话,就需要用到这两个属性了。


********************** 以下是C#并行下载的演示动画 ***********************

5.gif

C#的Parallel非常适合处理For循环的并行转化,代码极其简洁:
Task task=Task.Factory.StartNew(() =>
            Parallel.For(start, end+1, (i) => download(i))
            ).ContinueWith((a) => { over(); });

简单解释:
Task.Factory.StartNew  启动新线程,并运行
Parallel.For  并行处理循环
download(i)  循环体里面要执行的下载函数
.ContinueWith  线程结束之后,要进行的操作
over()        输出下载结果


评分

7

查看全部评分

TA的精华主题

TA的得分主题

 楼主| 发表于 2012-8-5 18:02 | 显示全部楼层
本帖最后由 liucqa 于 2012-8-5 19:46 编辑

如果你在百度或者Google搜索的话,XMLHTTP异步下载的代码一大堆,但大多使用Doevents来冒充异步,极少有采用onreadystatechange事件句柄的方法来实现。
用Doevents方法对CPU占用较多,从资源的角度来看,失去了异步的好处。并且后继代码不能运行,所以不是真正的异步操作。

在微软的MSDN上提供了三种方法实现OnReadyStateChange事件:                    http://msdn2.microsoft.com/en-gb/library/ms757030.aspx
1.使用Timer轮询readystate属性
2.使用带WithEvents声明的DomDocument对象load Xml
3.自定义一个类,在类中的方法来处理事件,然后把类赋给XMLHTTP对象的OnReadyStateChange事件

第一个方法不能实现真正的实时异步。
第二个方法,在本论坛有例子  http://club.excelhome.net/thread-760475-1-1.html
此代码我没有测试。
Dim WithEvents objDocument As HTMLDocument
Dim objHtml As New HTMLDocument
Sub ff()
Set objDocument = objHtml.createDocumentFromUrl("http://www.okooo.com/League/LeagueInfoIndex.php?TurnID=" & Cells(2, "i").Text & "&GroupID=2693", vbNullString)
End Sub

Private Sub objDocument_onreadystatechange()
Dim a As Object, i&, j&
Columns("a:h") = ""
If objDocument.readyState = "complete" Then
   Set a = objDocument.getElementsByTagName("table")(2).Rows
    For i = 1 To a.Length - 1
        For j = 0 To a(i).Cells.Length - 1
           Cells(i + 2, j + 1) = a(i).Cells(j).innerText
        Next
    Next
    Range("a1") = objDocument.URL
    Set objDocument = Nothing
    Set objHtml = Nothing
End If
End Sub



本课讲解第三个方法的使用,本方法为真正的异步调用。



评分

1

查看全部评分

TA的精华主题

TA的得分主题

 楼主| 发表于 2012-8-5 18:03 | 显示全部楼层
本帖最后由 liucqa 于 2012-8-5 23:01 编辑

在VBA中指定XMLHTTP.onreadystatechange属性的事件句柄比较麻烦,因为VBA不像在VBS中有getRef函数可以直接指定过程的句柄。
所以我们要建立一个自定义类,将类赋值给XMLHTTP.onreadystatechange属性

方法如下:

首先建立一个名字叫做clsXMLHttpMonitor的类模块
Option Explicit
Dim XMLHttpReq As MSXML2.XMLHttp          '前引用

Public Sub Initialize(ByVal uXMLHttpRequest As MSXML2.XMLHttp)
   Set XMLHttpReq = uXMLHttpRequest
End Sub

Sub ReadyStateChangeHandler()                     '处理
onreadystatechange属性的回调过程。
    'Debug.Print XMLHttpReq.readyState
    If XMLHttpReq.readyState = 4 Then
        If XMLHttpReq.Status = 200 Then
            'Process the response here
        Else
            Debug.Print XMLHttpReq.Status & ", " & XMLHttpReq.responseText
        End If
    End If
End Sub


在标准模块中写入如下代码:
Option Explicit
Dim XMLHttpReq As MSXML2.XMLHttp
Dim XMLHttpMon As clsXMLHttpMonitor

Sub XMLHttpAsync(ByVal URL As String)   
    Set XMLHttpReq = New MSXML2.XMLHttp   
    Dim XMLHttpMonitor As clsXMLHttpMonitor
    Set XMLHttpMonitor = New clsXMLHttpMonitor
    XMLHttpMonitor.Initialize XMLHttpReq   
    XMLHttpReq.OnReadyStateChange = XMLHttpMonitor             '定义
onreadystatechange属性   
    XMLHttpReq.Open "GET", URL, True
    XMLHttpReq.send
End Sub


写完上面的代码,很明显有个问题,VBA如何知道XMLHttpReq.OnReadyStateChange = XMLHttpMonitor定义的onreadystatechange属性要执行的回调过程是哪一个呢?
要解决这个问题,我们要给clsXMLHttpMonitor类指定一个默认的Member,参考文档在下面的链接
http://club.excelhome.net/thread-902038-1-1.html

简单的说,就是将类模块导出到本地硬盘,然后用记事本编辑,在Sub ReadyStateChangeHandler()  下面加入一行
Attribute Value.VB_UserMemId = 0
然后重新导入到VBA中(原来的模块要先删除)
这样Sub ReadyStateChangeHandler() 就变成了类模块的默认过程了。

当你调用XMLHttpAsync过程之后,发生的onreadystatechange事件就会引发Sub ReadyStateChangeHandler()得到执行。



所谓的异步处理XMLHTTP请求,就是使用
onreadystatechange属性定义的类模块来执行发生readyState = 4事件之后的XMLHTTP数据。这样就不会导致Excel被独占而假死。




点评

印象中,VBE引用一下,然后WITHEVENT也可以。不一定类模块。  发表于 2012-8-11 16:29

TA的精华主题

TA的得分主题

 楼主| 发表于 2012-8-5 18:03 | 显示全部楼层
本帖最后由 liucqa 于 2012-8-15 12:13 编辑

下面给大家提供一个演示代码。

XMLHTTP Monitor演示.rar (29.33 KB, 下载次数: 1671)

捕获.JPG

上面的代码是使用onreadystatechange事件,进行XMLHTTP状态监视的一个例子。在这个例子中,你可以随时关闭窗体,不会影响XMLHTTP下载数据。
下载的MP3文件接近8M,请耐心等待。



如果想监控XMLHTTP的下载进度,一个方法是将文件分块下载,根据下载的文件块的"Content-Range"头来显示进度。
下面是演示动画:
2.gif


评分

1

查看全部评分

TA的精华主题

TA的得分主题

 楼主| 发表于 2012-8-5 18:04 | 显示全部楼层
[广告] Excel易用宝 - 提升Excel的操作效率 · Excel / WPS表格插件       ★免费下载 ★       ★ 使用帮助
本帖最后由 liucqa 于 2015-6-5 22:37 编辑

高级篇 第一课小结:

    本课通过实例,讲解了XMLHTTP异步获取网页数据的方法。

    异步获取数据通常应用在文件较大或者要求实时交互的场合,因为编写代码用到类模块,所以结构较为复杂,不适合初学者采用。

    本课四楼给出的实例,只实现了XMLHTTP.readyState状态的监控,这只是异步获取数据的第一个最基本的步骤。通过这个方法,可以扩展写出异步断点续传下载,异步并发下载等更加高级的应用。平常在C语言中经常见到的多线程下载文件代码,在VBA中也有实现的可能了。


下面是异步分块并发下载两个文件的演示
2.gif

********************************************************************************************

http://club.excelhome.net/thread-1209592-1-1.html
winhttp同步和异步并发的测试。机器给力的话,一次发送上千个请求是很轻松的事情,采集速度提高几十倍。






TA的精华主题

TA的得分主题

发表于 2012-8-5 23:21 | 显示全部楼层

TA的精华主题

TA的得分主题

发表于 2012-8-5 23:32 | 显示全部楼层
[广告] VBA代码宝 - VBA编程加强工具 · VBA代码随查随用  · 内置多项VBA编程加强工具       ★ 免费下载 ★      ★使用手册
学习中……

TA的精华主题

TA的得分主题

发表于 2012-8-6 00:26 | 显示全部楼层

TA的精华主题

TA的得分主题

 楼主| 发表于 2012-8-7 01:09 | 显示全部楼层
发现大家光下载不回贴呀,是不是代码太难看不懂?还是懒得回贴呢?

俺正考虑是不是继续往下写

TA的精华主题

TA的得分主题

发表于 2012-8-7 06:55 | 显示全部楼层
[广告] VBA代码宝 - VBA编程加强工具 · VBA代码随查随用  · 内置多项VBA编程加强工具       ★ 免费下载 ★      ★使用手册
本帖最后由 星剑所指 于 2012-8-7 06:56 编辑
liucqa 发表于 2012-8-7 01:09
发现大家光下载不回贴呀,是不是代码太难看不懂?还是懒得回贴呢?

俺正考虑是不是继续往下写


大师啊,
从验证码登陆,翻页获取表格数据,到获取文件.
网页采集,还有什么内容啊

评分

1

查看全部评分

您需要登录后才可以回帖 登录 | 免费注册

本版积分规则

手机版|关于我们|联系我们|ExcelHome

GMT+8, 2024-11-16 12:05 , Processed in 0.037139 second(s), 10 queries , Gzip On, MemCache On.

Powered by Discuz! X3.4

© 1999-2023 Wooffice Inc.

沪公网安备 31011702000001号 沪ICP备11019229号-2

本论坛言论纯属发表者个人意见,任何违反国家相关法律的言论,本站将协助国家相关部门追究发言者责任!     本站特聘法律顾问:李志群律师

快速回复 返回顶部 返回列表