ExcelHome技术论坛

标题: 网页数据采集---网页文档解析篇(json/html/xml) [打印本页]

作者: liu-aguang    时间: 2016-9-27 08:54
标题: 网页数据采集---网页文档解析篇(json/html/xml)
本帖最后由 liu-aguang 于 2016-10-18 21:27 编辑

Json/HTML/XML是目前常见的几种数据网页形式, 解析它们(即分离出文档中需要的文本内容)有很多方法.本文主要介绍浏览器自身的解析方式----JavaScriptDomjQuery. 不要被这些新名词吓倒, 套用一本书名的说法: 其实很简单!

目录
一.  重要的基础知识及技巧
     1. 执行JavaScript语句的环境营造
     2. execScript方法和eval函数
     3. 执行JavaScript的全局函数
     4. 执行自定义函数
     5. JavaScript的数组和对象
     6. 将网页文本写入剪贴板—clipboardData对象

二.  Json与Json内容解析
     1. 什么是Json?
     2. 遍历对象或数组的方法
     3. Jsonp解析方法
     4. 数组和对象自身嵌套或相互嵌套的Json/Jsonp实例

三. 基于HTML DOM解析HTML文档
     1.了解HTML基本概念
     2.了解文档对象模型(HTML DOM)
     3 .HTML文档解析
      <1>应用Document对象的方法与属性解析
      <2>应用节点对象的方法与属性解析.
      <3>应用Table对象解析表数据

四. 基于jQuery对象解析HTML文档
    1. 什么是jQuery?
    2. 基本语法---如何获取元素内容
    3. 选择器---如何选择元素
      <1>元素选择器
      <2>属性选择器
      <3>选择器组合
      <4>选择器内包含过滤
      <5>内容选择器
      <6>type选择器
    4.基本的过滤器---如何聚焦指定元素
    一个应用实例
    5. 遍历---更多选择(过滤)HTML元素方法
       <1>jQuery把HTML结构视为家族树
       <2>遍历祖先
       <3>遍历后代
       <4>遍历同胞
       <5>把当前元素包含在选择集合中
       <6>返回距当前元素最近的祖先元素
       <7>将匹配元素集合缩减为指定范围的子集
       <8>对 jQuery 对象进行迭代,为每个匹配元素执行函数

五. XML文档解析
    1. XML文档结构
    2. XML文档的加载
       <1>通过微软的 XML 解析器加载 XML
       <2>通过Microsoft.XMLHTTP, MSXML2.XMLHTTP等组件加载
    3. 利用XML DOM的属性和方法访问元素的文本内容或属性
       <1>利用XML DOM的方法选择XML元素
       <2>利用XML DOM的属性访问元素的文本内容或属性
       <3>访问元素文本内容和属性的两个实例
    4. 定位XML DOM节点
    5. 访问节点总结
    6. 利用xPath查询XML元素


作者: liu-aguang    时间: 2016-9-27 09:04
本帖最后由 liu-aguang 于 2016-9-29 10:56 编辑

一.  重要的基础知识及技巧

如今浏览器上看到的网页是如此地酷玄与生动, 这都归功于浏览器中内置的JavaScript. 所有动态网页效果都是浏览器将网页文本转换为文档对象模型(DOM), 通过JavaScript编程实现的. 选择()网页的某内容仅是其中很小一部分功能, 这也就是我们说到的解析”. 换一种说法,VBA中仅为提取网页的某些内容, 并不是一定要系统去学习网页编程的一系列知识. 我们只需掌握为数不多的若干句式与语法就可以做到随心所欲地完成解析工作.

1. 执行JavaScript语句的环境营造
你如果要在EXCEL VBA中执行Word VBA 语句,作法是在EXCEL VBA中执行类似语句:
Set oWord=createobject(“Word.Application”)
然后就可以在oWord(word对象)下执行Word VBA语句了. 同样, 我们要在VBA中执行JavaScript语句或函数可以创建浏览器中的Window(窗口)对象, 这个对象下可以执行JavaScript语句或函数:
Set oDom=CreateObject(“HTMLFILE”)   ‘首先创建HTML Dom(html文档对象模型)
Set oWindow=oDom.parentWindow    ‘然后就可创建Window对象
说明: 这里创建的oDomIE7内置的文档对象模型;  oWindow也是IE7的窗口对象.
在没有加载网页文本之前,现在oDom是一个空文档.

2.     execScript方法和eval函数
执行JavaScript语句, 可以使用Window(窗口)对象的execScrip方法或JavaScripteval全局函数. 两者功能类似, 后者还可以直接返回变量值或计算值.

1: execScript用法
Sub execScript()
    Set oDom= CreateObject("HTMLFILE")
    Set oWindow = oDom.parentWindow
    oWindow.execScript"var d=new Date();t=d.getTime()" 'JavaScript代码放入双引号内。
    t =oWindow.t  '请重视这种用法: 如何从javaScript执行语句中获得变量值
    MsgBox" 1970/01/01 至今已过去 " & t & " 毫秒"
End Sub
讨论:execScript可执行JavaScriptVbscript代码,默认执行javaScript. javaScript语句之间用分号分隔。
2a: eval用法
Subevala()
    Set oDom =CreateObject("HTMLFILE")
    Set oWindow =oDom.parentWindow
    oWindow.execScript"str='abcdef'"  
    strLength =oWindow.eval("str.length")
    MsgBox "字符串str的长度为: "& strLength
End Sub


作者: liu-aguang    时间: 2016-9-27 09:06
本帖最后由 liu-aguang 于 2016-10-29 10:02 编辑

2b: eval用法
Sub evalb()
    Set oDom = CreateObject("HTMLFILE")
    Set oWindow = oDom.parentWindow
    oDom.write"<Script></Script>"
    strLength =oWindow.eval("str='abcdef';str.length")
    MsgBox "字符串str的长度为: "& strLength
End Sub
说明: 在使用Eval函数前, 必须先执行一次execScirpt或者在文档对象中写入至少一个<Script>标签. (在实际解析HTML文档时, 由于文档本身包含若干Script标签,就不必单独再写入了)
特别注意:无论是execScript还是eval,JavaScript代码都是以字符串的形式传递给它们。

例3: 用write方式运行javaScript代码
Sub evalc()
  Set oDom = CreateObject("HTMLFILE")
  Set oWindow = oDom.parentWindow
  oDom.write "<Script>str='abcdef';l=str.length</Script>"
  strLength = oWindow.l
  MsgBox "字符串str的长度为: " & strLength
End Sub
说明: 在HTML文档中, <Script>标签专门设计为包围javaScript脚本, 当用write方式加载至HTML文档对象时, 会自动执行该代码.

3. 执行JavaScript的全局函数

对于JavaScript的全局(顶级)函数,不必使用execScripteval. 可以直接在Window下执行. 比如常用的URL/解码函数encodeURIComponent()/decodeURIComponent(), encodeURI()/decodeURI().

3.1: 把字符“中国”转换为它的URL编码
Sub  GFuna()
    Set oDom =CreateObject("HTMLFILE")
    Set oWindow = oDom.parentWindow
    oDom.write  "<Script></Script>" '如果文档中已经含Scirpt标签,就不用写入这句.
    stext = "中国"
    MsgBox oWindow.encodeURIComponent(stext)
End Sub
3.2
Sub  GFunb()
    Set oDom =CreateObject("HTMLFILE")
    Set oWindow = oDom.parentWindow
    oDom.write  "<Script></Script>" '如果文档中已经含Scirpt标签,就不用写入这句.
    stext = "中国"
    MsgBox  oWindow.eval("encodeURIComponent('" & stext & "')")
End Sub

4.执行自定义函数

JavaScript函数与VBA函数意义相近,不过JavaScript无“过程”概念,即使只完成一些动作,不返回值,在VBA中称之为过程,而在javaScript中也叫函数。如:
      不带参数的:
        function a(){var s=5+6;return s}
         功能:计算5+6的值赋给对象s,然后用关键词return返回s到调用处(即函数a返回值为s).
         带参数的:
        functiona(x,y){s=x+y;return s}
说明:上面给出javaScript函数的一般形式,要点:
(1)   关键字function小写;
(2)   参数个数任意;在某些情况下可以没有函数名,叫匿名函数。
(3)   执行的代码用花括号包围;
(4)   如果需要函数返回运算值,用关键字return.


作者: liu-aguang    时间: 2016-9-27 09:07
本帖最后由 liu-aguang 于 2016-10-29 09:04 编辑

4.1
Sub a()
    Set oDom =CreateObject("htmlfile")
    Set oWindow =oDom.parentWindow
    oWindow.execScript "function a(){var s=5+6;return s}"
    MsgBox oWindow.eval("a()")
End Sub

4.2
Sub a1()
    Set oDom =CreateObject("htmlfile")
    Set oWindow = oDom.parentWindow
    oDom.write "<script>function a(){var s=5+6;return s}</script>"
    MsgBox oWindow.eval("a()")
End Sub

4.3
Sub a3()
    b1 = 5
    b2 = 6
    Set oDom =CreateObject("htmlfile")
    Set oWindow = oDom.parentWindow
    oWindow.execScript "function a(x,y){s=x+y;return s}"
    MsgBox oWindow.eval("a(" & b1& "," & b2 & ")")  ‘就是执行函数a(b1,b2)
End Sub

5. JavaScript的数组和对象

(永远记住----JavaScript,变量名/函数名/关键字是大小写敏感的!!!!
在网页解析中,经常用到的基本上就是数组和对象这两个概念.

A.    数组:
定义数组a:
var a=[‘a’,’b’,’c’,1,2,3]
定义空数组a:
var a=[]
说明: (1)方括号; (2)元素间逗号分隔; (3)字符串用单(或双)引号;(4)var是变量类型,可省略的.
读取数组:
(1)   索引法: 如读第一个元素a[0]; 索引号从0开始.
(2)   遍历法: for(k in a){….}
(3)   数组长度: a.length

5.1 索引法
Sub jArr1()
    Set oDom =CreateObject("HTMLFILE")
    Set oWindow = oDom.parentWindow
    oWindow.execScript"a=['a','b','c',1,2,3];"
    MsgBox oWindow.eval("a[0]")
    MsgBox oWindow.eval("a.length")
End Sub

5.2 遍历法
Sub jArr2()
    Set oDom =CreateObject("HTMLFILE")
    Set oWindow = oDom.parentWindow
    oWindow.execScript"a=['a','b','c',1,2,3];"
    MsgBox oWindow.eval("s='';for(k in a){s=s+' ' +a[k];};s")
End Sub
说明:
(1)   JavaScript定义字符时,要用单(或双)引号围起来.s=’中国’;s=’’ (定义空字符)
(2)   遍历数组a,k值是索引号,所以真正的值是a[k];
(3)   “+”号对数字来说是算术加;对字符来说是连接符;
(4)   语句间要用分号隔开;
(5)   当有多个变量时Eval永远返回的是最后语句的值.
(6)   请留意for的句式。


作者: liu-aguang    时间: 2016-9-27 09:08
本帖最后由 liu-aguang 于 2016-10-29 08:59 编辑

B.    对象
定义对象o: var o={name: ’张三’,sex: ’’, age: 20}
说明:(1)花括号; (2)键(名)和值之间冒号分隔;(3)健值对之间逗号分隔
值读取:
(1)   点号法: 如读取性别o.sex
(2)   遍历法: for(k in o){s=s+’ ‘ +o[k]}

5.3 点号法
Sub jObjA()
    Set oDom =CreateObject("HTMLFILE")
    Set oWindow = oDom.parentWindow
    oWindow.execScript "o={'name': '张三','xb': '', 'age':20}"
    MsgBox oWindow.o.xb '或下句方法
    MsgBox oWindow.eval("o.name")
End Sub
说明:由于JavaScript对大小敏感,VBA中会将一些关键字的首字母自动转换为大写,所以一般使用第二句方式读取值.
读者可以测试语句 msgboxoWindow.o.Name 将会出错.此处name会自动转换为Name

5.4 遍历法
Sub jObjB()
    Set oDom =CreateObject("HTMLFILE")
    Set oWindow = oDom.parentWindow
    oWindow.execScript "o={name: '张三',xb: '', age:20}"
    MsgBox oWindow.eval("s=''; for(k in o){s=s+' '+o[k]};s")  '遍历值
    MsgBox oWindow.eval("s='';for(k in o){s=s+' '+k};s")       '遍历键
End Sub

6. 将网页文本写入剪贴板—clipboardData对象

一般情况我们可通过msgbox debug.print来查看和分析网页.也可以用Fiddler来记录和查看分析网页. 在这里我们介绍一种把网页内容写入剪贴板, 然后粘贴到记事本中查看的方法.

:
Sub JsonArr()
    Set oHTML =CreateObject("htmlfile")
    Set oWindow = oHTML.parentWindow
    Set http =CreateObject("Msxml2.XMLHTTP")
    http.Open "GET","http://pv.sohu.com/cityjson", False
    http.send
    strHtml = http.responseText
    oWindow.clipboardData.SetData"text", strHtml   ‘这句代码是把文本strHtml写入剪贴板
End Sub
说明:clipboardDataIE中一个对象,有设置,读取或清除方法:
clipboardData.getData(“text”)
clipboardData.clearData “text”
更多相关知识可百度。


作者: liu-aguang    时间: 2016-9-27 12:07
本帖最后由 liu-aguang 于 2016-9-27 17:40 编辑

同志们要耐心等哈,每修改一次都要审核半天)半天过去了,内容都不能通过审核;都没有心情写了。
作者: liucqa    时间: 2016-9-27 13:05
liu-aguang 发表于 2016-9-27 12:07
半天过去了,内容都不能通过审核;都没有心情写了。

着急就先发到附件里吧
作者: liu-aguang    时间: 2016-9-27 13:18
liucqa 发表于 2016-9-27 13:05
着急就先发到附件里吧

只写了一部分,本想一边写一边修改。
作者: huang1314wei    时间: 2016-9-27 13:19
顶+支持,楼主继续,强势插队围观
作者: liucqa    时间: 2016-9-27 13:23
liu-aguang 发表于 2016-9-27 13:18
只写了一部分,本想一边写一边修改。

现在不能边写边改了,因为审核率貌似接近100%,哈哈
作者: 小花鹿    时间: 2016-9-27 13:29
请教老师,我想从0学习 网页数据采集,需要先看你的哪个帖子?
作者: liu-aguang    时间: 2016-9-27 17:25
小花鹿 发表于 2016-9-27 13:29
请教老师,我想从0学习 网页数据采集,需要先看你的哪个帖子?

网页采集涉及的科目很多,有时不知道“开头”在哪里。你可以先看看论坛上的一些帖子,能懂多少是多少,学会简单网页的采集,逐渐你就会知道该去学习哪些内容了。
网页采集大体上就两个步骤:网页请求和网页解析。网页请求论坛上有大帖子,你可以去学习模仿。我没有写过这方面的帖子。
作者: 屌爆了z    时间: 2016-9-27 17:44
老师终于写html这方面的教程了,由于html解析json没有32位和64位的区别,一直想学但却不得要领,通过翻阅您的帖子对html多少了解一点,但却不能灵活运用。老师的大作必须要顶,支持老师继续写下去
作者: laoau139    时间: 2016-9-27 18:54
好精彩,继续写,
作者: 小花鹿    时间: 2016-9-27 20:28
liu-aguang 发表于 2016-9-27 17:25
网页采集涉及的科目很多,有时不知道“开头”在哪里。你可以先看看论坛上的一些帖子,能懂多少是多少,学 ...

谢谢老师,我的正则就是跟你学的,而且学得很辛苦,不过现在总算熬过来了,对正则有点开窍了,不知道什么时候能入网页数据采集的门
作者: liu-aguang    时间: 2016-9-28 08:56
本帖最后由 liu-aguang 于 2016-10-29 07:56 编辑

二. Json与Json内容解析

1. 什么是Json?
设想一个显示及时股价的网页,股价变化是频繁的。假如股价每次变动服务器都向客户端发送一个完整的网页,显然通信流量会是很大,并且速度也会受影响。解决的办法是: 显示股价的网页中固定不变的内容设计为单独网页;股价数据也单独为一个网页。这样每次股价变化服务器只需要传输变化的股价数据。当股价数据到达客户端后,由事先编制好的javaScript脚本解析股价文本,把数据写入显示股价的网页中。这个单独存放股价的网页文件一般就是Json文件,其扩展名为.json。
Json是一个纯文本文件,唯一功能就是用来存储,传输数据。由于是纯文本且格式简单,所以广泛应用于各种平台。
为了便于JavaScript脚本编程解析, Json数据的存放格式完全按JavaScript数组或对象格式设计.一个完整的Json文本内容, 它有时简单到仅是一个数组或对象形式,再复杂也不过是数组或对象自身嵌套或相互嵌套.比如:

用数组存放一组名单的Josn:
[“张三”,”李四”,”王五”,”孙六”]

用对象存放一个人相关信息的Json:
{“name”:”娇娇”,”sex”:”女”,”age”:18,”address”:”娘家”}
说明: 与JavaScript对象唯一不同处是,键名(名称)都加了引号.

例1:
Sub Json1()
    Set oHTML = CreateObject("htmlfile")
    Set oWindow = oHTML.parentWindow
    Set http = CreateObject("Msxml2.XMLHTTP")
    http.Open "GET", "http://pv.sohu.com/cityjson", False
    http.send
    strHtml = http.responseText
    'oWindow.clipboardData.SetData "text", strHtml
    '得到 var returnCitySN = {"cip": "218.72.14.14", "cid": "330100", "cname": "浙江省杭州市"}; 这就一个json文本。
    oWindow.execScript strHtml   
    MsgBox oWindow.returnCitySN.cip
End Sub
讨论: json文本内容strHtml本身就是一条创建对象语句. 用execScript解析后,即可读取对象returnCitySn中的各项值.

下面的例2-4返回的json数据是:

{"work":4,"fans":281,"art":4,"album":1,"fortune":351702,"article":0,"workVideo":1,"care":195,"workAudio":3,"view":1313023,"playlist":0,"listen":288237,"collect":1}

例2:
Sub Json2()
    Set html = CreateObject("htmlfile")
    Set Window = html.parentWindow
    Set http = CreateObject("Msxml2.XMLHTTP")
    http.Open "GET", "http://kzone.kuwo.cn/mlog/UserVal?uid=1237357&from=profile", False
    http.send
    strHtml = http.responseText ' 得到数据
    ‘Window.clipboardData.SetData "text", strHtml '写入剪贴板
    Window.execScript "var js= " & strHtml  ' 改写成对象创建语句
    Set kuwo = Window.js ' 获取解析后的对象
    MsgBox "访问量:" & kuwo.view
End Sub讨论:
(1)在json中,某键值代表什么意义,这是代码编制者定义的,可以结合网页数据比对确定;
(2)如json不是对象创建格式, 需要改写, 如本例.

2. 遍历对象或数组的方法
例3 遍历提取部分数据的技巧
Sub Json3()
    Set html = CreateObject("htmlfile")
    Set Window = html.parentWindow
    Set http = CreateObject("Msxml2.XMLHTTP")
    http.Open "GET", "http://kzone.kuwo.cn/mlog/UserVal?uid=1237357&from=profile", False
    http.send
    strHtml = http.responseText ' 得到数据
    Window.execScript "var js= " & strHtml  ' 解析 json
    ar = Array("work", "fans", "fortune", "view", "listen") '数组列出需要提取的项目
    For Each k In ar
        MsgBox Window.eval("js." & k)
    Next
End Sub

例4 遍历全部数据的技巧
Sub Json4A()
    Set html = CreateObject("htmlfile")
    Set Window = html.parentWindow
    Set http = CreateObject("Msxml2.XMLHTTP")
    http.Open "GET", "http://kzone.kuwo.cn/mlog/UserVal?uid=1237357&from=profile", False
    http.send
    strHtml = http.responseText ' 得到数据
    Window.execScript "var js= " & strHtml & ";s='';for(k in js){s=s+' '+k}"  'for语句得到由空格分隔的键名字符串对象s
    ar = Split(Trim(Window.s), " ")  '注意字符串s前有一个空格
    For Each k In ar
        MsgBox Window.eval("js." & k)
    Next
End Sub
讨论:
(1)语句s=s+' '+k, 用VBA语句的说法是把键名用空格(' ')连接起来。当然你也可以用其它联结字符。
(2)在VBA中,如果不事先定义s,那么s默认为空字符;但在JavaScript中必然显式定义它为空字符(s=’’)。你试试不定义会发生什么。

Sub Json4B()
    Set html = CreateObject("htmlfile")
    Set Window = html.parentWindow
    Set http = CreateObject("Msxml2.XMLHTTP")
    http.Open "GET", "http://kzone.kuwo.cn/mlog/UserVal?uid=1237357&from=profile", False
    http.send
    strHtml = http.responseText ' 得到数据
    Window.execScript "var js= " & strHtml & ";a=[];for(k in js){a.push(k)}"  'for语句得到键名数组a
    ar = Split(Window.a, ",")  '数组a到VBA环境中自动转换为由逗号分隔的字符串
    For Each k In ar
        MsgBox Window.eval("js." & k)
    Next
End Sub
讨论:
(1) 我们定义了一个空数组a,然后用数组的方法push将循环得到的k值逐一存在入数组a中。
(2) javaScript的数组在VBA环境中返回值时,会自动转换为由逗号分隔的字符串。

Sub Json4C()
    Set html = CreateObject("htmlfile")
    Set Window = html.parentWindow
    Set http = CreateObject("Msxml2.XMLHTTP")
    http.Open "GET", "http://kzone.kuwo.cn/mlog/UserVal?uid=1237357&from=profile", False
    http.send
    strHtml = http.responseText ' 得到数据
    Window.execScript "var js= " & strHtml & ";a=[];for(k in js){a.push(js[k])}"  'for语句得到键值数组a
    ar = Split(Window.a, ",")
    For Each k In ar
        MsgBox k
    Next
End Sub

Sub Json4D()
    Set html = CreateObject("htmlfile")
    Set Window = html.parentWindow
    Set http = CreateObject("Msxml2.XMLHTTP")
    http.Open "GET", "http://kzone.kuwo.cn/mlog/UserVal?uid=1237357&from=profile", False
    http.send
    strHtml = http.responseText ' 得到数据
    Window.execScript "var js= " & strHtml & ";a=[];for(k in js){a.push(js[k])};s=a.join('@')"  'join把数组a转换为“@”分隔的字符串s
    ar = Split(Window.s, "@")
    For Each k In ar
        MsgBox k
    Next
End Sub
讨论:有时可能内容本身就包含逗号,如果用上面数组方法在VBA中返回字符串,我们不方便进一步处理。这时我们可以用javaScript的join方法把数组转换为由指定的分隔符的字符串。

3. Jsonp解析方法
还有一种Json文件称之为Jsonp. 它的数据存放格式与普通Json不同之处是, 它把Json文本内容作为一个函数参数存放. 就象这个样子:  Callback (json)
这里有一个存入电话号码信息的简单Jsonp实例(忘记了来自哪个网站):

cb({mobile:'13012345456',province:'重庆',isp:'中国联通',stock:'1',amount:'10000',maxprice:'0',minprice:'0'});
可以看到在结构体cb(…)的括号内就是一个普通的Json. 下面给出其解析方法:

Sub jsonp1()
    Set oDom = CreateObject("htmlfile")
    Set oWindow = oDom.parentWindow
    strHtml = "cb({mobile:'13012345456',province:'重庆',isp:'中国联通',stock:'1',amount:'10000',maxprice:'0',minprice:'0'});"
    oWindow.execScript "function cb(o){ js=o };"
    oWindow.execScript strHtml
    MsgBox oWindow.js.mobile
End Sub
讨论:
(1) 我们构建了一个名为cb的函数: function cb(o){js=o}. 函数名与jsonp文档内的名字一样;
(2) 这个函数有一个参数o,函数的功能是将传入的参数赋值给js;参数o可以是任意类型的。
(3) 当execScript解析jsonp(即变量strHtml)时,实际上是在执行函数cb(o).这里是把jsonp内的json部分作为参数传递给js, 最后结果是:
  js={mobile:'13012345456',province:'重庆',isp:'中国联通',stock:'1',amount:'10000',maxprice:'0',minprice:'0'}
  这是一个标准的以对象形式存在的Json. 可按常规方法访问各项值.




作者: liu-aguang    时间: 2016-9-28 11:03
本帖最后由 liu-aguang 于 2016-11-11 10:54 编辑

4. 数组和对象自身嵌套或相互嵌套的Json/Jsonp实例

通过上面的讨论,我们明白了json的概念和一般解析方法与技巧。现在我们如果要用json文件来存放2个或多个成员的姓名/性别/年龄信息该怎样做呢?这时可以将通过数组或对象的嵌套来实现:

(1)如果用数组则表示为:
[[‘张三’,’男’,23],[’李四’,’女’,20],[’王五’,’男’,22]]
它的形式是:[[],[],[]],解析方法:

Sub a1()
    Set oDom = CreateObject("htmlfile")
    Set oWindow = oDom.parentWindow
    strHtml = "[['张三','男',23],['李四','女',20],['王五','男',22]]"
    oWindow.execScript "a=" & strHtml
    MsgBox oWindow.eval("a[0][0]")  '第一个成员的姓名
End Sub
讨论:
<1>用execScript获得数组a, “a[0]”是第一个成员,它还是一个数组,所以”a[0][0]”是第一个成员的第一项元素值。读者可以自己去研究遍历方法。
<2>在实际应用中,我们看到的是由很多数值元素构成的数组,那么每一个位置上的数据代表的是什么呢?这是json编制者自定义的,我们要结合网页显示去判断。
<3>这种情况下,也可不用数组嵌套来表示, 而表示为:
       ["张三,男,23","李四,女,20","王五,男,22"]

它用双(或单)引号来限定一个数组元素. 此时,a[0]表示第一个元素, 其值为"张三,男,23", 这是一个字符串.

(2)如果用对象来表示则为:
[{‘name’:’张三’,’sex’:’男’,’age’:23}, {‘name’:’李四’,’sex’:’女’,’age’:20}, {‘name’:’王五’,’sex’:’男’,’age’:22}]
这是数组与对象的嵌套,解法:
Sub ao()
    Set oDom = CreateObject("htmlfile")
    Set oWindow = oDom.parentWindow
    strHtml = "[{'name':'张三','sex':'男','age':23}, {'name':'李四','sex':'女','age':20}, {'name':'王五','sex':'男','age':22}]"
    oWindow.execScript "a=" & strHtml
    MsgBox oWindow.eval("a[0].name")  '第一个成员的姓名
    MsgBox oWindow.eval("a[2].age")  '第三个成员的年龄
End Sub
讨论:每个数组元素是对象,所以先得到数组元素,再根据对象方法读取值。

(3)假如上面只是某公司的一个小组成员,现在希望用json文件同时存放公司名称和每一工作组成员以及公司总人数, 又该如何呢?
可以这样处理:
{‘dw’:’宇宙公司’,’zb1’: [{'name':'张三','sex':'男','age':23}, {'name':'李四','sex':'女','age':20}, {'name':'王五','sex':'男','age':22}],‘zb2’:[{'name':'刘六','sex':'男','age':24}, {'name':'风车车','sex':'女','age':18}, {'name':'汪麻','sex':'男','age':21}],’total’:6}
这个json存放了公司名称,总人数,公司的两个工作组及组内成员信息。可以通过execScript解析后,访问任意内容:

解析方法如下:
Sub ao()
    Set oDom = CreateObject("htmlfile")
    Set oWindow = oDom.parentWindow
    strHtml = "{'dw':'宇宙公司','zb1': [{'name':'张三','sex':'男','age':23}, {'name':'李四','sex':'女','age':20}, {'name':'王五','sex':'男','age':22}],'zb2':[{'name':'刘六','sex':'男','age':24}, {'name':'风车车','sex':'女','age':18}, {'name':'汪麻','sex':'男','age':21}],'total':6}"
    oWindow.execScript "a=" & strHtml
    Debug.Print oWindow.a.dw                           '公司名称
    Debug.Print oWindow.eval("a.total")              '公司总人数. 想想为什么不用上一句方式读取total值呢?
    Debug.Print oWindow.eval("a.zb1[0].name")  '第一工作组的第一位成员姓名
    Debug.Print oWindow.eval("a.zb2[2].age")     '第二工作组的第三位成员年龄
End Sub
讨论:
(1)当execScript解析strHtml后,zb1和zb2是对象a的名称(健名)。但是zb1和zb2的值是数组,所以我们可以用zb1[0]这样的方式表示数组的第一个元素;不过这些数组元素又是对象,所以zb1[0].name就是点号法读取;zb1[0]对象属于对象a下面的一个子对象,于是读取第一工作组的第一位成员姓名的表达式就为:a.zb1[0].name
(2)根据实际提取哪些数据的要求,可以遍历。方法就是用变量来替换方括号内的数值,读者可以自己去尝试。
(3)Json语法小结:

JSON 语法是 JavaScript 对象表示法语法的子集。
  &#8226; 数据在名称/值对中
  &#8226; 数据由逗号分隔
  &#8226; 花括号保存对象
  &#8226; 方括号保存数组

最后用几个在论坛上的回复实例来结束Json解析部分。
例1
http://club.excelhome.net/forum.php?mod=viewthread&tid=1297966
例2
http://club.excelhome.net/forum.php?mod=viewthread&tid=1299461
例3
http://club.excelhome.net/thread-1301123-3-1.html
例4
http://club.excelhome.net/thread-1298157-2-1.html
Sub js()
    Dim arr(1 To 100, 1 To 5)
    sURL = "http://comment.10jqka.com.cn/tzrl/getTzrlData.php?callback=callback_dt&type=data&date=201608"
    Set oDom = CreateObject("HTMLFILE")
    Set oWindow = oDom.parentWindow
    Set oHtml = CreateObject("MSXML2.XMLHTTP")
    oHtml.Open "GET", sURL, False
    oHtml.send
    oWindow.execScript oHtml.responseText & ";function callback_dt(o){oj=o};n=oj.data.length"
    For i = 0 To oWindow.n - 1
        r = r + 1
        arr(r, 1) = oWindow.eval("var oi=oj.data[" & i & "]; oi.date+oi.week;")
        imt = oWindow.eval("s=0;for(x in oi){s+=1;if(s==3){imt=oi[x]}};imt")
        arr(r, 2) = String(imt, "★")
        For j = 0 To oWindow.eval("oi.events.length") - 1
            arr(r, 3) = oWindow.eval("oi.events[" & j & "][0]")
            For Each u In Array("field", "concept")
            arr(r, 4) = arr(r, 4) & "," & oWindow.eval("ar=[];k=oi." & u & "[" & j & "];for(x in k){ar.push(k[x].name);ar}")
            Next
            arr(r, 4) = Trim(Replace(arr(r, 4), ",", " "))
            arr(r, 5) = oWindow.eval("ar=[]'';k=oi.stocks[" & j & "];for(x in k){ar.push(k[x].name);ar}")
            arr(r, 5) = Replace(arr(r, 5), ",", " ")
            r = r + 1
        Next
        r = r - 1
    Next
    Range("a2").Resize(r, 5) = arr
End Sub
说明:代码中有一句:imt = oWindow.eval("s=0;for(x in oi){s+=1;if(s==3){imt=oi[x]}};imt")
目的是为了访问名称:import的值。采取这种特别的方式读取,是因为json编制者”误用”了javaScript关键字import作为名称。用点号法访问这个值会发生错误。

修正: 可以用另一种语法直接读取import的值. 即:
imt=oWindow.eval("oi['import']")




作者: ammyc    时间: 2016-10-3 18:42
好好学习天天向上
作者: liucqa    时间: 2016-10-3 23:17
论坛很少有好文章了,支持一下。

楼主继续!
作者: liu-aguang    时间: 2016-10-4 09:11
liucqa 发表于 2016-10-3 23:17
论坛很少有好文章了,支持一下。

楼主继续!

非常感谢老师的鼓励. 等我把手上的工作完成后, 再继续写下去.
作者: yrf_9    时间: 2016-10-5 01:03
liu-aguang 发表于 2016-9-28 11:03
4. 数组和对象自身嵌套或相互嵌套的Json/Jsonp实例

通过上面的讨论,我们明白了json的概念和一般解析方 ...

请问下大神 为什么有的json对象用单引号 有的用双引号 这是怎么回事 还是您的抓取方法用的是html法吗?
作者: liu-aguang    时间: 2016-10-5 08:19
yrf_9 发表于 2016-10-5 01:03
请问下大神 为什么有的json对象用单引号 有的用双引号 这是怎么回事 还是您的抓取方法用的是html法吗?

在javaScript中, 单引号与双引号是等同的. 不过引号出现嵌套情形时,内部引号就要用单引号; 在VBA中,当出现嵌套情形时, 内部双引号要转义,或者也可使用单引号.
作者: ak47ok    时间: 2016-10-5 10:48
向老师学习,谢谢!
作者: liu-aguang    时间: 2016-10-7 14:15
本帖最后由 liu-aguang 于 2016-10-29 10:00 编辑

三. 基于HTML DOM的HTML文档解析

<一>了解HTML文档的基本概念


1. HTML与标签
HTML文档即是网页, 下面是一个简单的HTML文档:
    <html>
    <head>
    <title>文档标题</title>
    </head>
    <body>
    <h1 align="center">这是标题</h1>
    <p>这是一个段落.</p>
    <p>这是另一个段落.</p>
    </body>
    </html>
HTML文档包含两部分内容:一是纯文本;二是一系列标签.在这个例子中:
文本部分为:

文档标题
这是标题
这是一个段落.

标签有以下几种:
<html>,<head>,<body>,<h1>和<p>

这些标签共同描述了这个网页的显示效果:
<html> 与 </html> 之间的文本描述网页
<head> 与</head>之间的文本描述在浏览器窗口显示的标题
<body> 与 </body> 之间的文本是可见的页面内容
<h1> 与 </h1> 之间的文本被显示为标题,并且左右居中排列
<p> 与 </p> 之间的文本被显示为段落

标签是人们已经预定义的代号,浏览器会根据预定义表现文本内容的显示方式.
标签通常是成对出现的.如:<p>这是一个段落.</p> . 前者为开始标签<p>,后者为结束标签 </p>
标签中可以有若干的属性,如<h1>中包含属性align, 其值为Center,声明该属性,表示让标题居中排列.
HTML预定义了数百种标签及属性来实现各种网页的表现效果. 如果你不是专业网页编程人员, 不必去了解它们的含义. 解析它们是根据其结构来进行的.
不过, 在标签的属性中,有几个属性是用来描述自身的---id, name, class. 在解析中会经常遇到.
(1)id: 例
<h1 id="first">这是一级标题</h1>
在网页中id是唯一的,所以可以用id值为first来唯一确定该标签.

(2)name/Class :
<p><a id="myAnchor" name="myAnchor" href="http://www.w3school.com.cn">Visit W3School.com.cn</a></p>
<div class="cities">..........</div>

name和class在同一网页中在同一网页中描述的标签不是唯一的.

2. HTML元素与HTML

为了表述方便,我们把开始标签与结束标签之间的所有代码称之为元素. 例:
          <h1 align="center">这是标题</h1>
可称之h1元素.
可见元素包含几个部分:
(1)开始标签(<h1>)
(2)结束标签(</h1>)
(3)元素内容(这是标题)
(4)元素属性(align)

定义元素概念后,我们可以认为HTML文档是由HTML元素组成的.在前面给出的HTML文档就包含html,body,h1,p元素.我们注意到元素之间可以嵌套.例子中:
body, Head元素被嵌套在html元素内;
h1,p元素又被嵌套在body元素内.
在实际的网页里, 存在大量嵌套的情形. 例:
<!DOCTYPE html>
<html>
<head>
<style>
.cities {
    background-color:black;
    color:white;
    margin:20px;
    padding:20px;
}
</style>
</head>

<body>

<div class="cities">
<h2>London</h2>
<p>
London is the capital city of England.
It is the most populous city in the United Kingdom,
with a metropolitan area of over 13 million inhabitants.
</p>
</div>

</body>
</html>





作者: liu-aguang    时间: 2016-10-7 14:20
本帖最后由 liu-aguang 于 2016-10-7 16:34 编辑

<二>HTML文档对象模型(HTML DOM)


当HTML文档加载至浏览器时,自身会被转化为HTML文档对象(HTML DOM).这样就可以通过JavaScript编程,实现对任意元素的访问.在HTML DOM中所有事物都是节点:
  整个文档是一个文档节点
  每个 HTML 元素是元素节点
  HTML 元素内的文本是文本节点
  每个 HTML 属性是属性节点  
  注释是注释节点

DOM就是由节点构成的树:HTML DOM树
(, 下载次数: 102)

在HTML DOM树中,各个节点之间存在几种关系: 父,子,同胞.
    在节点树中,顶端节点被称为根(root)
    每个节点都有父节点、除了根(它没有父节点)
    一个节点可拥有任意数量的子
    同胞是拥有相同父节点的节点

下面通过一个实例,阐述各节点关系:
<html>
  <head>
    <title>DOM 教程</title>
  </head>
  <body>
    <h1>DOM 第一课</h1>
    <p>Hello world!</p>
  </body>
</html>
从上面的 HTML 中:
     <html> 节点没有父节点;它是根节点
     <head> 和 <body> 的父节点是 <html> 节点
     文本节点 "Hello world!" 的父节点是 <p> 节点
并且:
     <html> 节点拥有两个子节点:<head> 和 <body>
     <head> 节点拥有一个子节点:<title> 节点
     <title> 节点也拥有一个子节点:文本节点 "DOM 教程"
     <h1> 和 <p> 节点是同胞节点,同时也是 <body> 的子节点
并且:
    <head> 元素是 <html> 元素的首个子节点
    <body> 元素是 <html> 元素的最后一个子节点
     <h1> 元素是 <body> 元素的首个子节点
    <p> 元素是 <body> 元素的最后一个子节点



作者: liu-aguang    时间: 2016-10-7 14:27
本帖最后由 liu-aguang 于 2016-10-20 09:38 编辑

<三>HTML文档解析


HTML文档被转化为HTML DOM后,所有的元素都被定义为了对象.我们可以应用这些对象的属性或方法访问自己需要的内容.


<一>Document对象


Document对象就是指整个HTML文档对象, 在节点树处于顶端.它的一些方法与属性可以帮助我们访问(锁定)一个具体的元素.

1. 在VBE中声明Document有两种方法:


前期引用:(需要在VBE"工具"-"引用"-Microsoft HTML Object Library)
   Dim oDom As New HTMLDocument
   该语句声明一个变量oDom为Document对象.
后期引用:
   Set oDom = CreateObject("HTMLFIlE")
在实践中发现前期引用与后期引用有一定的差别:
如前期引用,则不能用Write方法把网页写入;而后期引用不能识别Class(类)

2.网页文本加载, 加载有两种方式, 例:


Sub a()
    Set oDom = CreateObject("htmlfile")
    oDom.body.innerHTML = "<p>这是一个段落</p>"
End Sub
Sub b()
    Set oDom = CreateObject("HTMLFIlE")
    oDom.write "<p>这是一个段落</p>"
End Sub
讨论:
(1)过程a是把网页代码写入oDom的Body标签内.如果写入的网页有<head>,它会忽略.
(2)过程b会把整个网页写入oDom中(包括<Head>,如果有的话).
(3)两种加载方式最大不同点是:用Write方式加载,它会同时初始化或执行网页中的动态代码.而过程a则不会.
(4)多数情况下,我们采用过程a方式加载网页.

3. Document对象访问元素的方法:


(1)根据元素的ID,Name,TagName,Calss属性访问元素.
语法:
Document.getElementById("id名")                          返回带有指定 ID 的元素。
Document.getElementsByName("元素name值")        VBA中不支持
Document.getElementsByTagName("元素标签名")    返回包含带有指定标签名称的所有元素的节点列表(集合/节点数组)。
Document.getElementsByCalssName("元素Class名")  返回包含带有指定类名的所有元素的节点列表。'必须前期引用


(2)根据Document集合对象访问元素(过时方式,都可以用上面方法替代).
Document.all(i)                       '根据标签在网页中序号访问该元素.
Document.all(name)                '根据标签的name属性访问元素,如果出现多个相同名字,则结果为数组;
Document.all.tags(tagName)    '根据标签名来访问元素,也得到一个数组结果.


例 3.1 根据ID名访问元素内容
Sub getid()
    Set oDom = CreateObject("htmlfile")
    oDom.body.innerHTML = "<h1 id='myHeader'>This is a header</h1>"
    MsgBox oDom.getElementById("myHeader").innerText
End Sub
讨论:
<1>本例由ID锁定元素结点<h1>,利用结点的属性innerText,获得元素<h1>文本的内容.
<2>元素节点对象属性:
innerText   '返回节点元素内容文本
innerHTML   '返回节点元素内容代码
nodeName    '返回节点名,即标签名
nodeValue   '返回节点值
nodeType    '返回节点类型

例3.2 根据标签名访问元素内容
Sub getTagName()
    Set oDom = CreateObject("htmlfile")
    oDom.body.innerHTML = "<h1 class='myHeader'>This is a header</h1><h1>这是另一个标题</h1>"
    MsgBox oDom.getElementsByTagName("h1")(0).innerText
    MsgBox oDom.getElementsByTagName("h1")(1).innerText
End Sub
讨论:
1. 根据标签名访问元素, 得到的是一个数组,可以用索引法(从0开始),或遍历法读取每个值.
2. 在一个网页文档中,可能有若干的相同标签,当我们解析它们时,索引号是从0开始,并且从前彺后排序.

例3.3 根据元素的类名访问元素内容
Sub getTagName()
'    此例必须使用前期引用
    Dim oDom As New HTMLDocument
    oDom.body.innerHTML = "<h1 class='myHeader'>This is a header</h1><h1>这是另一个标题</h1>"
    MsgBox oDom.getElementsByClassName("myHeader")(0).innerText
End Sub


例3.4 获取元素的某个属性值
Sub getAttr()
    Set oDom = CreateObject("htmlfile")
    Dim oDom As New HTMLDocument
    oDom.body.innerHTML = "<a id='myAnchor' href='http://www.microsoft.com'>Microsoft</a>"
    MsgBox oDom.getElementById("myAnchor").href
End Sub
讨论: 访问属性方法  -    元素对象.属性名

4.根据节点的方法与属性访问元素


不是所有标签都有id,name或class属性的.当我们要分离的内容正好在这样的标签中,此时可以用节点的一些方法属性,间接访问到我们需要的内容.


<1>firstChild/lastChild
前者是返回元素的第一个子节点;后者是返回最后一个子节点.
例 4.1
Sub jdTest()
    Set oDom = CreateObject("htmlfile")
    oDom.body.innerHTML = "<ul id='myList'><li>Coffee</li><li>Tea</li></ul>"
    MsgBox oDom.getElementById("myList").FirstChild.innerText
    MsgBox oDom.getElementById("myList").LastChild.innerText
End Sub
讨论: 1>本例是通过父节点来查询子节点.
2> 节点有如下属性:
innerText   '返回节点元素内容文本
innerHTML   '返回节点元素内容代码
nodeName    '返回节点名,即标签名
nodeValue   '返回节点值
nodeType    '返回节点类型

<2>childNodes
返回元素子节点的 NodeList(集合).
例4.2
Sub jdTest()
    Set oDom = CreateObject("htmlfile")
    oDom.body.innerHTML = "<ul id='myList'><li>Coffee</li><li>Tea</li></ul>"
    Set listNode = oDom.getElementById("myList").ChildNodes
    MsgBox listNode.Length '获取子节点数
    MsgBox listNode(0).innerHTML '索引法获取第一个子节点的内容.
End Sub
讨论: 可以用for each 或 for i遍历.

(3)parentNode
返回元素的父节点.
例 4.3
Sub pjdTest()
    Set oDom = CreateObject("htmlfile")
    oDom.body.innerHTML = "<ul><li>Coffee</li><li>Tea</li></ul>"
    Set parNode = oDom.getElementsByTagName("li")(0).ParentNode
    MsgBox parNode.nodeName
    MsgBox parNode.innerText
    MsgBox parNode.innerHTML
End Sub

(4)nextSibling/previousSibling
前者返回下一个同胞,后者返回前一个同胞.
例 4.4
Sub sibTest()
    Dim id
    Set oDom = CreateObject("htmlfile")
    oDom.body.innerHTML = "<ul id='myList'><li id='item1'>Coffee</li><li id='item2'>Tea</li></ul>"
    Set nexNode = oDom.getElementById("item1").NextSibling
    MsgBox nexNode.id
    MsgBox nexNode.innerText
   
    Set preNode = oDom.getElementById("item2").PreviousSibling
    MsgBox preNode.id
    MsgBox preNode.innerText
End Sub


(5)getElementsByTagName()


前面知道Document对象具有该方法, 而其它节点对象也有该方法.

例:测试HTML文档
<div id="nav" class="nav">
            <ul>
                <li><a href='/newindexSpace.aspx?id=7cbf5455-d3cc-42ba-b27a-377ebd3b31fb&type=gw' target='_blank' >OA办公中心</a></li><li><a href='../ShowLv2.aspx?dis=%e6%a0%a1%e5%9b%ad%e5%9b%be%e4%b9%a6%e4%b8%ad%e5%bf%83&type=79e18d1f-ed03-4cf7-9a4f-47a282fb54a6' target='_blank' >校园图书中心</a></li><li><a href='/newindexSpace.aspx?id=96f81bd6-d833-4d74-a07c-8f85496d0c88&type=xy' target='_blank' >校园管理中心</a></li><li><a href='/admini/zp/YiRuiTe.aspx' target='_blank' >实验活动评选</a></li><li><a href='../ShowLv2.aspx?dis=%e8%bf%90%e7%bb%b4%e7%ae%a1%e7%90%86%e4%b8%ad%e5%bf%83&type=9dc96093-7912-4b15-86da-3196b4fa38c3' target='_blank' >运维管理中心</a></li>
            </ul>
</div>

Sub 访问第二个a元素()
    s = "<div id='nav' class='nav'><ul><li><a href='/newindexSpace.aspx?id=7cbf5455-d3cc-42ba-b27a-377ebd3b31fb&type=gw' target='_blank' >OA办公中心</a></li><li><a href='../ShowLv2.aspx?dis=%e6%a0%a1%e5%9b%ad%e5%9b%be%e4%b9%a6%e4%b8%ad%e5%bf%83&type=79e18d1f-ed03-4cf7-9a4f-47a282fb54a6' target='_blank' >校园图书中心</a></li><li><a href='/newindexSpace.aspx?id=96f81bd6-d833-4d74-a07c-8f85496d0c88&type=xy' target='_blank' >校园管理中心</a></li><li><a href='/admini/zp/YiRuiTe.aspx' target='_blank' >实验活动评选</a></li><li><a href='../ShowLv2.aspx?dis=%e8%bf%90%e7%bb%b4%e7%ae%a1%e7%90%86%e4%b8%ad%e5%bf%83&type=9dc96093-7912-4b15-86da-3196b4fa38c3' target='_blank' >运维管理中心</a></li></ul></div>"
    Set oDom = CreateObject("htmlfile")
    oDom.body.innerHTML = s
    MsgBox oDom.getElementById("nav").getElementsByTagName("a")(1).innerText
    MsgBox oDom.getElementById("nav").getElementsByTagName("a")(1).href
End Sub
讨论: 真实的网页中可能有很多的a标签, 采用该方法可以锁定id为nav的Div元素内的a标签.


(6)获取元素(或标签)的属性值
方法1: 通过节点对象与属性名用点联接获取
例4.5
Sub atTest()
    Set oDom = CreateObject("htmlfile")
    oDom.body.innerHTML = "<a href='/jsref/dom_obj_attributes.asp' target='_blank'>Attr 对象</a>"
    Set aNode = oDom.getElementsByTagName("a")(0)
    MsgBox aNode.href
    MsgBox aNode.target
End Sub
方法2:应用节点的getAttribute()属性,例:
Sub atTest1()
    Set oDom = CreateObject("htmlfile")
    oDom.body.innerHTML = "<a href='/jsref/dom_obj_attributes.asp' target='_blank'>Attr 对象</a>"
    Set aNode = oDom.getElementsByTagName("a")(0)
    MsgBox aNode.getAttribute("href")
    MsgBox aNode.getAttribute("target")
End Sub

5. 应用Table对象读取表数据

在HTML DOM中Table代表一个表格,它具有rows和cells两个集合对象,可通过它们读取表格内数据:
rows  返回包含表格中所有行的一个数组
cells 返回包含表格中所有单元格的一个数组。
我们先编制一个两行,每行含有两个单元格的表格:
<table id="myTable" border="1">
<tr>
<td>Row1 cell1</td>
<td>Row1 cell2</td>
</tr>
<tr>
<td>Row2 cell1</td>
<td>Row2 cell2</td>
</tr>
</table>
解析方法:
Sub atTest1()
    Set oDom = CreateObject("htmlfile")
    oDom.body.innerHTML = "<table id='myTable' border='1'><tr><td>Row1 cell1</td><td>Row1 cell2</td></tr><tr><td>Row2 cell1</td><td>Row2 cell2</td></tr></table>"
    Set aNode = oDom.getElementById("myTable")
    rn = aNode.Rows.Length '表总行数
    ReDim arr(1 To rn, 1 To 2)
    For Each r In aNode.Rows
        i = i + 1: j = 0
        For Each c In r.Cells
            j = j + 1
            arr(i, j) = c.innerText
        Next
    Next
    Range("a1").Resize(rn, 2) = arr
End Sub




补充内容 (2017-7-27 20:35):
本部分中谈到根据元素的Name属性访问元素, 在VBA中不支持. 这个说法是错误的, 也就是说可以用getElementsByName()方法.
作者: liucqa    时间: 2016-10-7 17:06
这篇文章不错,弥补了论坛空白,给初学者讲得比较完善了
作者: jsgj2023    时间: 2016-10-7 17:23
一直在找这方面的学习资料,老师这个得顶上去。
作者: liu-aguang    时间: 2016-10-7 17:34
liucqa 发表于 2016-10-7 17:06
这篇文章不错,弥补了论坛空白,给初学者讲得比较完善了

谢谢老师肯定! 欢迎朋友们指正.
作者: liu-aguang    时间: 2016-10-14 12:36
四. 基于 jQuery对象解析HTML

1.  什么是jQuery?

(请在本文第三部分,搞清楚HTML文档的标签,元素,节点这三个概念的区别与联系.)

在相当长的时间里, 浏览器都是基于JavaScript对HTML DOM编程来实现对HTML文档解析的(即节点/元素增加,删除,修改;元素内容与属性的读取和修改),并通过这些操作达成网页的动态效果. 随着HTML版本的升级(目前常见HTML4.01,HTML5),标签及属性种类不断增多,并且不同浏览器生产商的竞争导致不同浏览器对标签及属性的支持产生差异. 这样基于HTML DOM解析HTML文档就显得不完全适应了或者说很笨拙了(这就是为什么有些浏览器可以正常运行某网页的各种效果而有些浏览器不正常的原因). 所以现实状况需要一种不完全依赖于DOM,超越浏览器之间差异,使HTML文档解析更便捷的方式. jQuery技术就是目前最为常用的方式之一.

解决上面说的新问题的方式,是编制一些实现某项解析功能的通用JavaScript函数. 在加载网页时,通过调用这些函数来实现其功能. jQuery,就是由很多能实现某种解析功能的函数构成的一个库.这个库是一个扩展名为js的文件,当我们用浏览器打开某网页时,它会自动下载到客户端指定的临时目录里. 加载网页的过程中,网页内的脚本代码会根据需要调用它们.

自动下载和调用jQuery库,需要在网页的头部(<head></head>)指明jQuery库的URL. 例如:

<head>
<script src="http://www.w3school.com.cn/jquery/jquery.js"></script>
</head>

这个例子加载的是3WSchool网站上jquery库. 你也可以加载任意其它网站上的jquery库,如,加载Microsoft网站上的jQuery库:

<head>
<script  src="http://ajax.microsoft.com/ajax/jquery/jquery-1.4.min.js"></script>
</head>

这个库只有几十个K,你在选择的时候,可以根据需要选择网站速度较快,jQuery版本较高的网站.



作者: liu-aguang    时间: 2016-10-14 14:01
本帖最后由 liu-aguang 于 2016-10-16 08:07 编辑

2. 基本语法
   
   jQuery对象.动作()
   
   <1>" 动作"函数即是对前面指定的对象(元素)进行增加/修改/删除/读取(或修改)元素内容/读取(或修改)元素某属性等. 如:
   
   text()   读取元素文本内容;
   html()   读取元素文本代码;
   attr('xxxx')   读取元素某个属性
   size() 返回选择元素在文档中的出现的次数,也可以用length()
   ......
   
   <2> jQuery对象是通过$函数来描述的: $('选择器') 如:
   
   <p id='myid' name='test'>这是一个段落</p>
   
   描述id为myid的p元素的jQuery对象,可表示为:
   
   $('#myid')
   
   于是想读取元素p的文本内容(这是一个段落)的完整语句:
    $('#myid').text()

  <3>选择器
   jQuery是用"选择器"来指定某个(些)元素的,其书写语法很丰富,灵活,后续专题讲解.

在VBA中用jQuery解析的一个简单的例子(如果第一次执行出错,请再运行一次):
    Sub jTest()
      Set oDom = CreateObject("htmlfile")
      Set oWindow = oDom.parentWindow
      oDom.write "<script  src='http://www.w3school.com.cn/jquery/jquery.js'></script>"
      oDom.write "<p id='myid' name='test'>这是一个段落</p>"
      MsgBox oDom.DocumentElement.innerHTML  '你可以看到你写入的HTML文档
      MsgBox oWindow.eval("$('#myid').text()")
    End Sub
讨论:
<1>必须用write方法将jQuery库引入;
<2>写入主体语句也可不用write,而采用下列代码形式(注意在写入头部时,写入了一个空<body>标签):
  
  Sub jTest()
    Set oDom = CreateObject("htmlfile")
    oDom.write "<script  src='http://www.w3school.com.cn/jquery/jquery.js'></script><body></body>"
    oDom.body.innerHTML = "<p id='myid'>这是一个段落</p>"
    MsgBox oDom.parentWindow.eval("$('#myid').text()")
  End Sub

<3>你在测试上面代码时,可能遇到一个问题: 第一次执行代码总是报错,而以后都能正常运行了.它的原因是第一次执行代码时,jQuery库下载工作还没有完成, 接着就执行jQuery函数了.而第一次下载的jQuery库会缓存在ie临时目录里,所以再次执行代码就不会再从服务器端下载库,而是直接调用临时目录里的jQuery库.
如果想用jQuery来解析文档, 在实际应用中,可以用"msxml2.xmlhttp"组件,首先把jQuery下载到临时目录中. 即增加下列代码块:

  With CreateObject("msxml2.xmlhttp")
    .Open "GET", "http://www.w3school.com.cn/jquery/jquery.js", False
    .send
  End With



作者: liu-aguang    时间: 2016-10-14 16:09
本帖最后由 liu-aguang 于 2016-10-16 20:06 编辑

3.选择器


jQuery可随心所欲地选取元素,就是因为它有一套强大的选择器.

<1>元素选择器: 可以根据元素名(标签名),或者元素名与属性组合来选取元素.例:

A. 根据元素名(标签名)选择元素 如:
$("p") 选取 <p> 元素。

B. 根据元素的ID和calss(类)选择元素. 如:
$(".intro") 选取所有class="intro"的元素.
$("#demo")  选取所有id="demo"的元素.

C. 元素名与id或class组合选择元素.如:
$("p.intro") 选取所有class="intro" 的 <p> 元素。
$("p#demo") 选取所有id="demo" 的 <p> 元素。

例:
HTML文档
<html>
<body>
<h1 class="intro">这是一个标题</h1>
<p>这是第一个段落</p>
<p id="myid" class="intro">这是第二个段落</p>
<p class="intro">这是第三个段落</p>
</body>
</html>

Sub sel()
    Set oDom = CreateObject("htmlfile")
    Set oWindow = oDom.parentWindow
    oDom.write "<script  src='http://www.w3school.com.cn/jquery/jquery.js'></script><body></body>"
    oDom.body.innerHtml = "<html><body><h1 class='intro'>这是一个标题</h1><p>这是第一个段落</p><p id='myid' class='intro'>这是第二个段落</p><p class='intro'>这是第三个段落</p></body></html>"
    MsgBox oDom.DocumentElement.innerHtml  '你可以看到你写入的HTML文档
    MsgBox oWindow.eval("$('#myid').text()")   'id为"myid"元素的文本内容
    MsgBox oWindow.eval("$('p').size()")       '文档中所有p元素的个数
    MsgBox oWindow.eval("$('.intro').size()")  '文档中所有class为intro为HTML元素个数
    MsgBox oWindow.eval("$('p.intro').size()") 'class为intro的所有p元素.
End Sub

说明:除id外,根据其它元素名或属性来选择元素,得到的都是一个数组,可用过滤器或相应jQuery函数来过滤出某一个元素(后面专题讲).

<2>属性选择器: 可以根据元素的属性来选取元素,如:

$("[href]")          选取所有带有 href 属性的元素。
$("[href='#']")      选取所有带有 href 值等于 "#" 的元素。
$("[href!='#']")     选取所有带有 href 值不等于 "#" 的元素。
$("[href$='.jpg']")  选取所有 href 值以 ".jpg" 结尾的元素。

例:HTML文档
<html>
<body>
<a href="http://finance.ifeng.com/" >财经</a>
<a href="http://news.ifeng.com/" >资讯</a>
</body>
</html>

Sub sel2()
    Set oDom = CreateObject("htmlfile")
    Set oWindow = oDom.parentWindow
    oDom.write "<script  src='http://ajax.microsoft.com/ajax/jquery/jquery-1.4.min.js'></script><body></body>"
    oDom.body.innerHtml = "<a href='http://news.ifeng.com/'>资讯</a><a href='http://finance.ifeng.com/' >财经</a>"
    MsgBox oWindow.eval("$('[href]').size()")
    MsgBox oWindow.eval("$(""[href='http://news.ifeng.com/']"").eq(0).text()") 'eq()是一个用索引号过滤的函数
    MsgBox oWindow.eval("$(""[href!='http://news.ifeng.com/']"").eq(0).text()") '这个结果出乎意料,貌似不支持该形式.
End Sub

<3>选择器组合
$("div p") 表示div元素内包含的所有p元素. 选择器之间用空格表示包含前辈与后辈关系.
$("th,td,.intro") 表示选择元素名为th,td和类名为intro的所有元素. 选择器之间用逗号分隔,表示所有匹配选择的元素
例: 读取下列HTML代码段内的价格:
<div class="p-price">
<strong class="J_627913" data-price="86.90"><em>&yen;</em><i>86.90</i></strong></div>
  1. Sub 价格()
  2.   Set oDom = CreateObject("htmlfile")
  3.   Set oWindow = oDom.parentWindow
  4.   oDom.write "<script src='http://ajax.microsoft.com/ajax/jquery/jquery-1.4.min.js'></script><body></body>"
  5.   oDom.body.innerHTML = "<div class='p-price'><strong class='J_627913' data-price='86.90'><em>&yen;</em><i>86.90</i></strong></div>"
  6.   MsgBox oWindow.eval("$('.p-price i').eq(0).text()")
  7. End Sub
复制代码


<4>过滤型选择器


例:HTML文档
<html>
<body>
<h1 class="intro">这是一个标题</h1>
<p>这是第一个段落</p>
<p id="myid" class="intro">这是第二个段落</p>
<p class="intro">这是第三个段落</p>
</body>
</html>

Sub sel3()
    Set oDom = CreateObject("htmlfile")
    Set oWindow = oDom.parentWindow
    oDom.write "<script  src='http://www.w3school.com.cn/jquery/jquery.js'></script><body></body>"
    oDom.body.innerHtml = "<html><body><h1 class='intro'>这是一个标题</h1><p>这是第一个段落</p><p id='myid' class='intro'>这是第二个段落</p><p class='intro'>这是第三个段落</p></body></html>"
    MsgBox oWindow.eval("$('p:eq(0)').text()")     '第一个p元素. 语法: 冒号eq(索引)    '
    MsgBox oWindow.eval("$('.intro:eq(1)').text()")
    '与上面等价方式
    MsgBox oWindow.eval("$('p').eq(0).text()")
End Sub


<5>内容选择器

jQuery提供了可以根据元素文本内容来选择元素的方法.
语法:
$(":contains('text')")
text 元素文本内容中包含此内容
例:
测试HTML
<html>
<body>
<h1>Welcome to My Homepage</h1>
<p class="intro">My name is Donald</p>
<p>I live in Duckburg</p>
<p>My best friend is Mickey</p>
<div id="choose">
Who is your favourite:
<ul>
<li>Goofy</li>
<li>Mickey</li>
<li>Pluto</li>
</ul>
</div>
</body>
</html>

Sub containsTest()
  Set oDom = CreateObject("htmlfile")
  Set oWindow = oDom.parentWindow
  oDom.write "<script src='http://ajax.microsoft.com/ajax/jquery/jquery-1.4.min.js'></script><body></body>"
  oDom.body.innerHTML = "<html><body><h1>Welcome to My Homepage</h1><p class='intro'>My name is Donald</p><p>I live in Duckburg</p><p>My best friend is Mickey</p><div id='choose'>Who is your favourite:<ul><li>Goofy</li><li>Mickey</li><li>Pluto</li></ul></div></body></html>"
  MsgBox oWindow.eval("$('p:contains(is)').size()")     '文本内容包含"is"的P元素.
  MsgBox oWindow.eval("$('p:contains(is)').eq(0).text()")
End Sub

<6>Type选择器

可根据元素的type值选择元素.
例:HTML文档
<html>
<body>
<form action="">
Name: <input type="text" name="user" />
<br />
Password: <input type="password" name="password" />
<br />
<button type="button">Useless Button</button>
<input type="button" value="Another useless button" />
<br />
<input type="reset" value="Reset" />
<input type="submit" value="Submit" />
<br />
</form>
</body>
</html>

Sub typeTest()
  Set oDom = CreateObject("htmlfile")
  Set oWindow = oDom.parentWindow
  oDom.write "<script src='http://ajax.microsoft.com/ajax/jquery/jquery-1.4.min.js'></script><body></body>"
  oDom.body.innerHTML = "<html><body><form action=''>Name: <input type='text' name='user' /><br />Password: <input type='password' name='password' /><br /><button type='button'>Useless Button</button><input type='button' value='Another useless button' /><br /><input type='reset' value='Reset' /><input type='submit' value='Submit' /><br /></form></body></html>"
  MsgBox oWindow.eval("$(':text').attr('name')")
  MsgBox oWindow.eval("$(':password').attr('name')")
  MsgBox oWindow.eval("$(':reset').val()")
End Sub
更多的选择器可参考:http://www.w3school.com.cn/jquery/jquery_ref_selectors.asp




作者: liu-aguang    时间: 2016-10-14 16:16
4.过滤器


除了通过id选取的元素外,其它通过选择器得到的都是一个集合数组.为了缩小搜索范围,jQuery提供了一组基本的过滤函数:


first()  第一个子节点
last()   最后一个子节点.
eq(i)    用索引i来过滤; 索引从0开始的.
filter() 允许您规定一个标准。不匹配这个标准的元素会被从集合中删除,匹配的元素会被返回
not()    返回不匹配标准的所有元素。

例4.1 HTML文档
<html>
<body>
<h1>欢迎来到我的主页</h1>
<div>
<p>这是 div 中的一个段落。</p>
</div>
<div>
<p>这是 div 中的另一个段落。</p>
</div>
<p>这也是段落。</p>
</html>
</body>

Sub jTest()
    Set oDom = CreateObject("htmlfile")
    oDom.write "<script  src='http://www.w3school.com.cn/jquery/jquery.js'></script><body></body>"
    oDom.body.innerHTML = "<h1>欢迎来到我的主页</h1><div><p>这是 div 中的一个段落。</p></div><div><p>这是 div 中的另一个段落。</p></div><p>这也是段落。</p>"
    MsgBox oDom.parentWindow.eval("$('div p').first().text()")
    MsgBox oDom.parentWindow.eval("$('div p').last().text()")
    MsgBox oDom.parentWindow.eval("$('div p').eq(1).text()")
    MsgBox oDom.parentWindow.eval("$('p').eq(2).text()")
End Sub

例4.2 HTML文档
<h1>欢迎来到我的主页</h1>
<p>我是唐老鸭。</p>
<p class="intro">我住在 Duckburg。</p>
<p class="intro">我爱 Duckburg。</p>
<p>我最好的朋友是 Mickey。</p>

Sub filterT()
    Set oDom = CreateObject("htmlfile")
    oDom.write "<script  src='http://www.w3school.com.cn/jquery/jquery.js'></script><body></body>"
    oDom.body.innerHTML = "<h1>欢迎来到我的主页</h1><p>我是唐老鸭。</p><p class='intro'>我住在 Duckburg。</p><p class='intro'>我爱 Duckburg。</p><p>我最好的朋友是 Mickey。</p>"
    MsgBox oDom.parentWindow.eval("$('p').filter('.intro').eq(0).text()")
    MsgBox oDom.parentWindow.eval("$('p').not('.intro').eq(0).text()")
End Sub



作者: liu-aguang    时间: 2016-10-14 21:35

一个应用实例: 根据ISBN(9787121098284)从缺书网读取该书号对应图书信息

Sub 获取图书信息()
    sURL1 = "http://www.queshu.com/search/?c=9787121098284"  '
    Set xmlhttp = CreateObject("WinHttp.WinHttpRequest.5.1")
    Set oDom = CreateObject("htmlfile")
    Set oWindow = oDom.parentWindow
    oDom.write "<script src='http://ajax.microsoft.com/ajax/jquery/jquery-1.4.min.js'></script><body></body>"
   
    With xmlhttp
        .Open "GET", sURL1, False
        .send
        stext = .responsetext
    End With
   
    'oWindow.clipBoardData.setData "text", stext '写入剪贴板,粘贴在记事本中查看
    oDom.body.innerhtml = stext
   
    '网页中显示该书号共包含多少种书
    n = Mid(oWindow.eval("$('.main_b_crumbs strong').eq(0).text()"), 2, 1)
    For j = 1 To Val(n) '遍历每图书的各项信息
        
        '读取定价
        jg = oWindow.eval("$('.dingjia').eq(" & j - 1 & ").text()")
        
        '读取作者及出版日期等信息
        pcy = oWindow.eval("$('.left').parent().prev().eq(" & j - 1 & ").text()")
        
        '读取出版社
        cps = oDom.getElementById("class_left").innerText
        
        '读取书名
        sm = oWindow.eval("$('.img120').eq(" & j - 1 & ").attr('alt')")
    Next
   
End Sub



作者: liu-aguang    时间: 2016-10-16 09:08
5. 遍历---更多选择(或聚焦)HTML元素方法

前面介绍了用各种选择器选择HTML元素, 并且用过滤器缩小选择范围的方法. jQuery还另外提供了大量函数用于选择或过滤HTML元素, 它称之为遍历.所谓"遍历",指以选择的某元素为基准,在该元素的上辈,下辈和同辈之间寻找/查询另外一种元素. 这适合当期望查询的元素不好直接用选择器确定时,通过另外一种容易确定的元素来间接选定它.

<1> jQuery把HTML结构视为家族树

在讲述HTML DOM的时候,我们知道节点之间是由父/子/同胞关系构成的一颗树. 在jQuery看来,这颗树不仅仅是这父/子/同胞关系,而是一颗家族树. 下面通过一个实例来理解:


一段HTML文档:
<div>
<ul>
<li><b>品名</b></li>
<li><span>香蕉</span></li>
</ul>
</div>
它的家族树:
(, 下载次数: 243)

<2>遍历祖先
parent()      返回被选元素的直接父元素。该方法只会向上一级对 DOM 树进行遍历。
parents()     返回被选元素的所有祖先元素,它一路向上直到文档的根元素 (<html>)。
parentsUntil()返回介于两个给定元素之间的所有祖先元素。

例 parent()
测试HTML文档:
<html>
<head>
</head>
<body>
<div class="ancestors">
  <div style="width:500px;">div (曾祖父)
    <ul>ul (祖父)  
      <li>li (直接父)
        <span>span</span>
      </li>
    </ul>   
  </div>
  <div style="width:500px;">div (祖父)   
    <p>p (直接父)
        <span>span</span>
      </p>
  </div>
</div>
</body>
</html>
  1. Sub parentTest()
  2.   Set oDom = CreateObject("htmlfile")
  3.   Set oWindow = oDom.parentWindow
  4.   oDom.write "<script src='http://ajax.microsoft.com/ajax/jquery/jquery-1.4.min.js'></script><body></body>"
  5.   oDom.body.innerHTML = "<div class='ancestors'>  <div style='width:500px;'>div (曾祖父)    <ul>ul (祖父)        <li>li (直接父)        <span>span</span>      </li>    </ul>     </div>  <div style='width:500px;'>div (祖父)       <p>p (直接父)        <span>span</span>      </p>   </div></div>"
  6.   MsgBox oWindow.eval("$('span').parent().size()")
  7.   MsgBox oWindow.eval("$('span').parent().eq(0).html()")
  8. End Sub
复制代码


例2 parents()
测试HTML:
<html>
<body class="ancestors">body (曾曾祖父)
  <div style="width:500px;">div (曾祖父)
    <ul>ul (祖父)  
      <li>li (直接父)
        <span>span</span>
      </li>
    </ul>   
  </div>
</body>
</html>
Sub parentsTest()
  Set oDom = CreateObject("htmlfile")
  Set oWindow = oDom.parentWindow
  oDom.write "<script src='http://ajax.microsoft.com/ajax/jquery/jquery-1.4.min.js'></script><body></body>"
  oDom.body.innerHTML = "<html><body class='ancestors'>body (曾曾祖父)  <div style='width:500px;'>div (曾祖父)    <ul>ul (祖父)        <li>li (直接父)        <span>span</span>      </li>    </ul>     </div></body></html>"
  MsgBox oWindow.eval("$('span').parents().size()")
  MsgBox oWindow.eval("$('span').parents('ul').eq(0).html()") '您也可以使用可选参数来过滤对祖先元素的搜索。
End Sub

例3 parentsUntil()
测试文档同例2
Sub parentsUTest()
  Set oDom = CreateObject("htmlfile")
  Set oWindow = oDom.parentWindow
  oDom.write "<script src='http://ajax.microsoft.com/ajax/jquery/jquery-1.4.min.js'></script><body></body>"
  oDom.body.innerHTML = "<html><body class='ancestors'>body (曾曾祖父)  <div style='width:500px;'>div (曾祖父)    <ul>ul (祖父)        <li>li (直接父)        <span>span</span>      </li>    </ul>     </div></body></html>"
  MsgBox oWindow.eval("$('span').parentsUntil('div').size()")  '元素span与div之间的元素
  MsgBox oWindow.eval("$('span').parentsUntil('div').eq(0).html()")
End Sub



作者: liu-aguang    时间: 2016-10-16 09:40
<3>遍历后代

children()    返回被选元素的所有直接子元素。该方法只会向下一级对 DOM 树进行遍历.
find()        返回被选元素的后代元素,一路向下直到最后一个后代。
可以用可选参数过滤返回结果.

例 3.1 children()
测试HTML:
<html>
<body>
<div class="descendants" style="width:500px;">div (当前元素)
  <p class="1">p (子)
    <span>span (孙)</span>     
  </p>
  <p class="2">p (子)
    <span>span (孙)</span>
  </p>
</div>
</body>
</html>


Sub childrenTest()
  Set oDom = CreateObject("htmlfile")
  Set oWindow = oDom.parentWindow
  oDom.write "<script src='http://ajax.microsoft.com/ajax/jquery/jquery-1.4.min.js'></script><body></body>"
  oDom.body.innerHTML = "<html><body><div class='descendants' style='width:500px;'>div (当前元素)<p class='1'>p (子)<span>span (孙)</span></p><p class='2'>p (子)<span>span (孙)</span></p> </div></body></html>"
  MsgBox oWindow.eval("$('div').children().size()")
  MsgBox oWindow.eval("$('div').children('p.1').html()") '可以使用可选参数来过滤对子元素的搜索。
End Sub

例3.2 find()
测试HTML同上


Sub findTest()
  Set oDom = CreateObject("htmlfile")
  Set oWindow = oDom.parentWindow
  oDom.write "<script src='http://ajax.microsoft.com/ajax/jquery/jquery-1.4.min.js'></script><body></body>"
  oDom.body.innerHTML = "<html><body><div class='descendants' style='width:500px;'>div (当前元素)<p class='1'>p (子)<span>span (孙)</span></p><p class='2'>p (子)<span>span (孙)</span></p> </div></body></html>"
  MsgBox oWindow.eval("$('div').find('span').size()")
  MsgBox oWindow.eval("$('div').find('span:eq(0)').text()")
  MsgBox oWindow.eval("$('div').find('*').size()") '返回 <div> 的所有后代
End Sub


作者: liu-aguang    时间: 2016-10-16 10:09
<4> 遍历同胞

有许多有用的方法让我们在 DOM 树进行水平遍历:
siblings()  返回被选元素的所有同胞元素。您也可以使用可选参数来过滤对同胞元素的搜索。
next()      返回被选元素的下一个同胞元素。该方法只返回一个元素。
nextAll()   返回被选元素的所有跟随的同胞元素。
nextUntil() 返回介于两个给定参数之间的所有跟随的同胞元素。
下面三个方法的工作方式与上面的方法类似,只不过方向相反而已:它们返回的是前面的同胞元素(在 DOM 树中沿着同胞元素向后遍历,而不是向前)。
prev()
prevAll()
prevUntil()

例4.1 siblings()
测试HTML:
<html>
<body class="siblings">
<div>div (父)
  <p>p</p>
  <span>span</span>
  <h2>h2</h2>
  <h3>h3</h3>
  <p>p</p>
</div>
</body>
</html>


Sub siblingsTest()
  Set oDom = CreateObject("htmlfile")
  Set oWindow = oDom.parentWindow
  oDom.write "<script src='http://ajax.microsoft.com/ajax/jquery/jquery-1.4.min.js'></script><body></body>"
  oDom.body.innerHTML = "<html><body class='siblings'><div>div (父)<p>p</p><span>span</span><h2>h2</h2><h3>h3</h3><p>p</p></div></body></html>"
  MsgBox oWindow.eval("$('h2').siblings().size()")   '返回 <h2> 的所有同胞元素
  MsgBox oWindow.eval("$('h2').siblings('p').size()") '返回属于 <h2> 的同胞元素的所有 <p> 元素
  MsgBox oWindow.eval("$('h2').siblings('p:eq(0)').text()")
End Sub

例4.2 next()
测试HTML同上

Sub siblingsTest()
  Set oDom = CreateObject("htmlfile")
  Set oWindow = oDom.parentWindow
  oDom.write "<script src='http://ajax.microsoft.com/ajax/jquery/jquery-1.4.min.js'></script><body></body>"
  oDom.body.innerHTML = "<html><body class='siblings'><div>div (父)<p>p</p><span>span</span><h2>h2</h2><h3>h3</h3><p>p</p></div></body></html>"
  MsgBox oWindow.eval("$('h2').next().text()") '只返回一个元素
End Sub

例4.3 nextAll()
测试HTML同上

Sub nextaTest()
  Set oDom = CreateObject("htmlfile")
  Set oWindow = oDom.parentWindow
  oDom.write "<script src='http://ajax.microsoft.com/ajax/jquery/jquery-1.4.min.js'></script><body></body>"
  oDom.body.innerHTML = "<html><body class='siblings'><div>div (父)<p>p</p><span>span</span><h2>h2</h2><h3>h3</h3><p>p</p></div></body></html>"
  MsgBox oWindow.eval("$('h2').nextAll().size()")
  MsgBox oWindow.eval("$('h2').nextAll().eq(0).text()")
End Sub

例4.4 nextUntil()
测试HTML:
<html>
<body class="siblings">
<div>div (父)
  <p>p</p>
  <span>span</span>
  <h2>h2</h2>
  <h3>h3</h3>
  <h4>h4</h4>
  <h5>h5</h5>
  <h6>h6</h6>
  <p>p</p>
</div>
</body>
</html>

Sub nextUTest()
  Set oDom = CreateObject("htmlfile")
  Set oWindow = oDom.parentWindow
  oDom.write "<script src='http://ajax.microsoft.com/ajax/jquery/jquery-1.4.min.js'></script><body></body>"
  oDom.body.innerHTML = "<html><body class='siblings'><div>div (父)  <p>p</p>  <span>span</span>  <h2>h2</h2>  <h3>h3</h3>  <h4>h4</h4>  <h5>h5</h5>  <h6>h6</h6>  <p>p</p></div></body></html>"
  MsgBox oWindow.eval("$('h2').nextUntil('h6').size()") '返回介于 <h2> 与 <h6> 元素之间的所有同胞元素
End Sub



作者: liu-aguang    时间: 2016-10-16 20:40
<5>把当前元素包含在选择集合中


.andSelf()

例5
测试HTML
<html>
<body>
<ul>
   <li>list item 1</li>
   <li>list item 2</li>
   <li class="third-item">list item 3</li>
   <li>list item 4</li>
   <li>list item 5</li>
</ul>
</body>
</html>


Sub andSelfTest()
  Set oDom = CreateObject("htmlfile")
  Set oWindow = oDom.parentWindow
  oDom.write "<script src='http://ajax.microsoft.com/ajax/jquery/jquery-1.4.min.js'></script><body></body>"
  oDom.body.innerHTML = "<html><body><ul>   <li>list item 1</li>   <li>list item 2</li>   <li class='third-item'>list item 3</li>   <li>list item 4</li>   <li>list item 5</li></ul></body></html>"
  MsgBox oWindow.eval("$('li.third-item').nextAll().size()")
  MsgBox oWindow.eval("$('li.third-item').nextAll().andSelf().size()")
  MsgBox oWindow.eval("$('li.third-item').nextAll().andSelf().eq(0).text()")
End Sub


<6> 返回距当前元素最近的祖先元素


closest() 方法获得匹配选择器的第一个祖先元素,从当前元素开始沿 DOM 树向上。


例6
测试HTML
<html>
<body>
  <ul>
    <li><b>Click me!</b></li>
    <li>You can also <b>Click me!</b></li>
  </ul>
</body>
</html>


Sub closestTest()
  Set oDom = CreateObject("htmlfile")
  Set oWindow = oDom.parentWindow
  oDom.write "<script src='http://ajax.microsoft.com/ajax/jquery/jquery-1.4.min.js'></script><body></body>"
  oDom.body.innerHTML = "<html><body>  <ul> <li><b>Click me!</b></li><li>You can also <b>Click me!</b></li>  </ul></body></html>"
  MsgBox oWindow.eval("$('b').closest('li').html()")
End Sub



作者: liu-aguang    时间: 2016-10-16 20:43
<7>将匹配元素集合缩减为指定范围的子集。


定义和用法
slice() 把匹配元素集合缩减为指定的指数范围的子集。
语法
.slice(selector,end)
参数
描述
selector
基于 0 的整数值,指示开始选取元素的位置。
如果是负数,则指示从集合末端开始的偏移量。
end
基于 0 的整数值,指示结束选取元素的位置。
如果是负数,则指示从集合末端开始的偏移量。
如果省略,则选取范围会在集合末端结束。

例7.1
HTML文档:
<html>
<body>
<p>This is a paragraph1.</p>
<p>This is a paragraph2.</p>
<p>This is a paragraph3.</p>
<p>This is a paragraph4.</p>
<p>This is a paragraph5.</p>
<p>This is a paragraph6.</p>
</body>
</html>

Sub sliceTest1()
  Set oDom = CreateObject("htmlfile")
  Set oWindow = oDom.parentWindow
  oDom.write "<script src='http://ajax.microsoft.com/ajax/jquery/jquery-1.4.min.js'></script><body></body>"
  oDom.body.innerHTML = "<html><body><p>This is a paragraph1.</p><p>This is a paragraph2.</p><p>This is a paragraph3.</p><p>This is a paragraph4.</p><p>This is a paragraph5.</p><p>This is a paragraph6.</p></body></html>"
  MsgBox oWindow.eval("$('p').slice(0,2).size()")
  MsgBox oWindow.eval("$('p').slice(0,2).eq(0).text()")
End Sub

例7.2 HTML文档同上

Sub sliceTest2()
  Set oDom = CreateObject("htmlfile")
  Set oWindow = oDom.parentWindow
  oDom.write "<script src='http://ajax.microsoft.com/ajax/jquery/jquery-1.4.min.js'></script><body></body>"
  oDom.body.innerHTML = "<html><body><p>This is a paragraph1.</p><p>This is a paragraph2.</p><p>This is a paragraph3.</p><p>This is a paragraph4.</p><p>This is a paragraph5.</p><p>This is a paragraph6.</p></body></html>"
  MsgBox oWindow.eval("$('p').slice(2).size()")  '如果省略end,则选择直到最后一个p元素
  MsgBox oWindow.eval("$('p').slice(2).eq(0).text()")
End Sub

例7.3HTML文档同上

Sub sliceTest3()
  Set oDom = CreateObject("htmlfile")
  Set oWindow = oDom.parentWindow
  oDom.write "<script src='http://ajax.microsoft.com/ajax/jquery/jquery-1.4.min.js'></script><body></body>"
  oDom.body.innerHTML = "<html><body><p>This is a paragraph1.</p><p>This is a paragraph2.</p><p>This is a paragraph3.</p><p>This is a paragraph4.</p><p>This is a paragraph5.</p><p>This is a paragraph6.</p></body></html>"
  MsgBox oWindow.eval("$('p').slice(2,4).size()")  
  MsgBox oWindow.eval("$('p').slice(2,4).eq(0).text()")
End Sub

例7.4 HTML文档同上

Sub sliceTest4()
  Set oDom = CreateObject("htmlfile")
  Set oWindow = oDom.parentWindow
  oDom.write "<script src='http://ajax.microsoft.com/ajax/jquery/jquery-1.4.min.js'></script><body></body>"
  oDom.body.innerHTML = "<html><body><p>This is a paragraph1.</p><p>This is a paragraph2.</p><p>This is a paragraph3.</p><p>This is a paragraph4.</p><p>This is a paragraph5.</p><p>This is a paragraph6.</p></body></html>"
  MsgBox oWindow.eval("$('p').slice(-2,-1).size()")  
  MsgBox oWindow.eval("$('p').slice(-2,-1).eq(0).text()")
End Sub


作者: liu-aguang    时间: 2016-10-16 21:05
本帖最后由 liu-aguang 于 2016-10-17 08:36 编辑

<8>对 jQuery 对象进行迭代,为每个匹配元素执行函数

定义和用法
each() 方法规定为每个匹配元素规定运行的函数。

语法
$(selector).each(function(index,element))
参数
描述
function(index,element)
必需。为每个匹配元素规定运行的函数。
index - 选择器的 index 位置
element - 当前的元素(也可使用 "this" 选择器)

例:测试HTML文档
<html>
<head>
<body>
<ul>
<li>Coffee</li>
<li>Milk</li>
<li>Soda</li>
</ul>
</body>
</html>

Sub eachTest()
  Set oDom = CreateObject("htmlfile")
  Set oWindow = oDom.parentWindow
  oDom.write "<script src='http://ajax.microsoft.com/ajax/jquery/jquery-1.4.min.js'></script><body></body>"
  oDom.body.innerHTML = "<html><head><body><ul><li>Coffee</li><li>Milk</li><li>Soda</li></ul></body></html>"
  MsgBox oWindow.eval("a=[];$('li').each(function(){a.push($(this).text())});a")
End Sub


Sub eachTest1()
Set oDom = CreateObject("htmlfile")
Set oWindow = oDom.parentWindow
oDom.write "<script src='http://ajax.microsoft.com/ajax/jquery/jquery-1.4.min.js'></script><body></body>"
oDom.body.innerHTML = "<html><head><body><ul><li>Coffee</li><li>Milk</li><li>Soda</li></ul></body></html>"
MsgBox oWindow.eval("a=[];$('li').each(function(i,o){if(i!=1){a.push($(o).text())}});a")
End Sub




作者: liu-aguang    时间: 2016-10-17 10:40
本帖最后由 liu-aguang 于 2016-10-18 11:30 编辑

五. XML文档解析

1. XML文档结构


<1>什么是XML?
前面已经知道, json是用来存储和传输数据的. 还有一种与json相同功能的"重量级"文件--XML, 它也是Web中较为常见的数据网页.不过它不用数组或对象来组织数据, 而是用标记来组织. 如:下面是一个用XML格式存储的两本书的相关信息(书名,作者,出版年,价格)的例子.


<?xml version="1.0" encoding="ISO-8859-1"?>  '第一行通常是XML声明xml版本,所用编码.
<bookstore>
<book category="COOKING">
  <title lang="en">水浒传</title>
  <author>(明)施耐庵</author>
  <year>2005</year>
  <price>20.00</price>
</book>
<book category="CHILDREN">
  <title lang="en">红楼梦</title>
  <author>(清)曹雪芹</author>
  <year>2005</year>
  <price>20.00</price>
</book>
</bookstore>

XML 指可扩展标记语言(EXtensible Markup Language),是纯文本.  可以看出,结构上XML与HTML很相似,但它们俩为不同目的而设计:
...XML 被设计为传输和存储数据,其焦点是数据的内容。
...HTML 被设计用来显示数据,其焦点是数据的外观。
...HTML 旨在显示信息,而 XML 旨在传输信息。
...XML标签没有被预定义,您需要自行定义标签; HTML标签是预定义的.
...XML被设计为具有自我描述性。

<2>XML语法特征
...所有 XML 元素都须有关闭标签
如:<book>....</book>
...XML 标签对大小写敏感
如: <Message>这是错误的。</message>
...XML 必须正确地嵌套
如:<b><i>This text is bold and italic</b></i>
在HTML中不会出问题,但在XML中是错误的.
...XML 文档必须有根元素
XML 文档必须有一个元素是所有其他元素的父元素。该元素称为根元素。
<root>
  <child>
    <subchild>.....</subchild>
  </child>
</root>


<3>XML文档的树形结构
在描述HTML树时, 我们用了标签,元素,属性,节点以及表达节点之间关系时的先辈/后代,父/子/同胞等概念.在XML中同样适用. 在解析XML文档时, 首先要转化为XML DOM(XML文档模型(对象)).

根据 DOM,XML 文档中的每个成分都是一个节点
DOM 是这样规定的:
在上面的 XML 中,根节点是 <bookstore>。文档中的所有其他节点都被包含在 <bookstore> 中。
根节点 <bookstore> 有四个 <book> 节点。
第一个 <book> 节点有四个节点:<title>, <author>, <year> 以及 <price>,其中每个节点都包含一个文本节点,"水浒传", "(明)施耐庵", "2005" 以及 "20"。

文本总是存储在文本节点中在 DOM 处理中一个普遍的错误是,认为元素节点包含文本。
不过,元素节点的文本是存储在文本节点中的。
在这个例子中:<year>2005</year>,元素节点 <year>,拥有一个值为 "2005" 的文本节点。
"2005" 不是 <year> 元素的值!

[attach]1983737[/attach]


各节点彼此间有等级关系:
父、子和同级节点用于描述这种关系。父节点拥有子节点,位于相同层级上的子节点称为同级节点(兄弟或姐妹)。
下面的图片展示出节点树的一个部分,以及节点间的关系:
(, 下载次数: 121)
<4>几个XML实例
(提示: 你可以用Fiddler记录打开以下链接后的会话, 在Fiddler中观察XML的树形结构)
一个XML的目录
http://www.w3school.com.cn/example/xmle/cd_catalog.xml
一个XML的植物目录
http://www.w3school.com.cn/example/xmle/plant_catalog.xml
一个简单的菜单
http://www.w3school.com.cn/example/xmle/simple.xml





作者: 草本植物1983    时间: 2016-10-17 23:17
高人,麻烦看一下,我这个什么原因,XMLHTTP抓取的网页与实际输入网址不一样
Sub test()
    Dim strRespText$, tt$, i&, DW$
    Dim URL
    Dim URL2
    URL = "http://www.tianyancha.com"         
    URL2 = URL & "/search?key=" & UTF8_URLEncoding("浙江奥特服饰有限公司") & "&checkFrom=searchBox"
    Cells(9, 9) = URL2
    With CreateObject("Microsoft.XMLHTTP")
        .Open "GET", URL2, False
        .send
        tt = .responsetext
        With CreateObject("new:{1C3B4210-F441-11CE-B9EA-00AA006B1A69}")   
            .SetText tt                                                    '因为XMLHTTP默认是UTF-8,不能识别gb2312,会发现数据乱码
            .PutInClipboard                                                '所以不能采用.responsetext对象来得到字符串
        End With
        MsgBox DW
    End With
End Sub
作者: liu-aguang    时间: 2016-10-18 09:25
草本植物1983 发表于 2016-10-17 23:17
高人,麻烦看一下,我这个什么原因,XMLHTTP抓取的网页与实际输入网址不一样
Sub test()
    Dim strResp ...

Sub testw()  
    URL2 = "http://www.tianyancha.com/search/%E6%B5%99%E6%B1%9F%E5%A5%A5%E7%89%B9%E6%9C%8D%E9%A5%B0%E6%9C%89%E9%99%90%E5%85%AC%E5%8F%B8.json?"
    With CreateObject("Microsoft.XMLHTTP")
        .Open "GET", URL2, False
        .setRequestHeader "loop", "null"
        .send
        tt = .responseText
    End With
End Sub

作者: fxl447098457    时间: 2016-10-18 09:36
好文章,现mark收藏下,慢慢品读。
作者: liu-aguang    时间: 2016-10-18 15:26
2. XML文档的加载


XML DOM 含有遍历 XML 树以及访问、插入、删除节点的方法(函数)。所以,在访问并处理 XML 文档之前,必须把它载入 XML DOM 对象。加载有以下三种方式:

<1>通过微软的 XML 解析器加载 XML.

例2.1:加载一个XML文件(books.xml)

Sub loadXMLb()
    Set xmlDoc = CreateObject("Microsoft.XMLDOM")
    xmlDoc.async = "false"
    xmlDoc.Load ("http://www.w3school.com.cn/example/xdom/books.xml")
End Sub

代码解释:
第一行创建空的微软 XML 文档对象
第二行关闭异步加载,这样可确保在文档完整加载之前,解析器不会继续执行脚本
第三行告知解析器加载名为 "books.xml" 的文档

例2.2 :加载一个XML字符串

Sub loadxmla()
    Text = "<bookstore>"
    Text = Text + "<book>"
    Text = Text + "<title>Harry Potter</title>"
    Text = Text + "<author>J K. Rowling</author>"
    Text = Text + "<year>2005</year>"
    Text = Text + "</book>"
    Text = Text + "</bookstore>"
    Set xmlDoc = CreateObject("Microsoft.XMLDOM")
    xmlDoc.async = "false"
    xmlDoc.loadxml (Text)
End Sub

说明: loadXML() 方法用于加载字符串(文本),而 load() 用于加载文件。

<2>通过Microsoft.XMLHTTP, MSXML2.XMLHTTP等组件加载

例:2.3

Sub loadXMLc()
    Set ohttp = CreateObject("msxml2.xmlhttp")
    ohttp.Open "GET", "http://www.w3school.com.cn/example/xdom/books.xml", False
    ohttp.send
    Set xmlDoc = ohttp.responseXML   'responseXML属性返回XML DOM
End Sub


作者: 天之雨露    时间: 2016-10-18 16:18
本帖最后由 天之雨露 于 2016-10-18 16:26 编辑
liu-aguang 发表于 2016-10-18 09:25
Sub testw()  
    URL2 = "http://www.tianyancha.com/search/%E6%B5%99%E6%B1%9F%E5%A5%A5%E7%89%B9%E ...

我也看了42楼求助
你的URl2会失效,现在无效了.貌似不是普通网页

在浏览器上tianyancha主页搜索 浙江奥特服饰有限公司
马上使用你给的网址会返回正确数据.
但1,2次后很快会失效.显示
{"state":"ok","message":"","data":[{"id":null,"name":"浙江奥特服饰有限公司","type":1}]}

我用火狐 web开发者跟踪一下.
原始搜索页和你给的   url2=%E6%B5%9.json?这个网址 消息头中cookie 有8个参数,改变过,有不同的.
我想问
(1)是哪里,什么,怎样让 url2  %e6..B8.json?失效.
(2)如何模拟让url2一直有效,可以得到正确的返回值.
以上2个问题能详细讲一下吗,谢谢

作者: liu-aguang    时间: 2016-10-18 16:18
本帖最后由 liu-aguang 于 2016-10-20 13:12 编辑

3.利用XML DOM的属性和方法访问元素的文本内容或属性

在介绍HTML文档解析方法的时候, 我们是利用HTML DOM的方法或属性来访问HTML元素的; 这里利用XML DOM来访问XML文档元素与前面谈到的方式法几乎完全一样.


<1>利用XML DOM的方法选择XML元素


(下面假设x是一个节点对象)
x.getElementsByTagName(name) - 获取带有指定标签名称的所有元素
x.getElementByID(id) - 获取带有指定id的节点
x.getElementsByName(name) - 获取带有指定名称的所有节点

<2>利用XML DOM的属性访问元素的文本内容或属性

一些典型的 DOM 属性:
x.nodeName - x 的名称
x.nodeValue - x 的值
x.parentNode - x 的父节点
x.childNodes - x 的子节点
x.attributes - x 的属性节点
x.text - x的文本
x.length - x包含的元素个数


nodeValue 属性
nodeValue 属性规定节点的值。
----元素节点的 nodeValue 是 undefined 或 null
----文本节点的 nodeValue 是文本本身
----属性节点的 nodeValue 是属性值

<3>访问元素文本内容和属性的两个实例

Sub loadxmla1()
    Text = "<bookstore>"
    Text = Text + "<book>"
    Text = Text + "<title>Harry Potter</title>"
    Text = Text + "<author>J K. Rowling</author>"
    Text = Text + "<year>2005</year>"
    Text = Text + "</book>"
    Text = Text + "</bookstore>"
    Set xmlDoc = CreateObject("Microsoft.XMLDOM")
    xmlDoc.async = "false"
    xmlDoc.loadxml (Text)
    MsgBox xmlDoc.getElementsByTagName("title")(0).ChildNodes(0).nodevalue
    MsgBox xmlDoc.getElementsByTagName("title")(0).ChildNodes(0).Text
    MsgBox xmlDoc.getElementsByTagName("title")(0).Text
End Sub

解释:
xmlDoc - 由解析器创建的 XML DOM
getElementsByTagName("title")[0] - 第一个 <title> 元素
childNodes[0] - <title> 元素的第一个子节点 (文本节点)
nodeValue - 节点的值 (文本自身)
在上面的例子中,getElementsByTagName 是方法,而 childNodes , nodeValue和text 是属性。

Sub loadXMLb()
    Set xmlDoc = CreateObject("Microsoft.XMLDOM")
    xmlDoc.async = "false"
    xmlDoc.Load ("http://www.w3school.com.cn/example/xdom/books.xml")
    Set x = xmlDoc.getElementsByTagName("book")
    '下面三个等价方式
    MsgBox x(0).attributes(0).Text
    MsgBox x(0).attributes(0).nodevalue
    MsgBox x(0).attributes.getNamedItem("category").nodevalue
End Sub

该实例的部分XML文档:
<?xml version="1.0" encoding="ISO-8859-1"?>
<!--  Copyright w3school.com.cn -->
<!-- W3School.com.cn bookstore example -->
<bookstore>
<book category="children">
<title lang="en">Harry Potter</title>
<author>J K. Rowling</author>
<year>2005</year>
<price>29.99</price>
</book>
<book category="cooking">
<title lang="en">Everyday Italian</title>
<author>Giada De Laurentiis</author>
<year>2005</year>
<price>30.00</price>
</book>
........................................





作者: 天之雨露    时间: 2016-10-18 16:42
本帖最后由 天之雨露 于 2016-10-18 16:44 编辑

我也看了42楼求助
你的URl2会失效,现在无效了.貌似不是一般普通网页

在浏览器上tianyancha主页搜索 浙江奥特服饰有限公司
马上使用你给的网址会返回正确数据.
但1,2次后很快会失效.显示
{"state":"ok","message":"","data":[{"id":null,"name":"浙江奥特服饰有限公司","type":1}]}

发现
原始搜索页和你给的   url2=%E6%B5%9.json?这个网址 cookie 有8个参数,会发生变化.
我想问
(1)是哪里,什么,怎样让 url2  %e6..B8.json?失效.是不是就是cookie引起的
(2)如何模拟让url2一直有效,可以得到正确的返回值.
以上2个问题能详细讲一下吗,谢谢

我刚刚在这帖子里发了一贴,莫名奇妙消失不见了,也没有系统提醒删除,不知道为什么
作者: liu-aguang    时间: 2016-10-18 17:10
4. 定位XML DOM节点

与定位HTML DOM节点相同:
在 XML DOM 中,节点的关系被定义为节点的属性:
  x.parentNode - x的父节点
  x.childNodes - x的所有子节点
  x.firstChild - x的第一个子节点
  x.lastChild  - x的最后一个子节点
  x.nextSibling - x的下一个同胞节点
  x.previousSibling - x的前一个同胞节点

5. 访问节点总结

您可以通过三种方法来访问节点:
<1>通过使用 getElementsByTagName() 方法
<2>通过循环(遍历)节点树
<3>通过利用节点的关系在节点树中导航

例5.1 使用节点列表中的下标号来访问节点


Sub loadXML()
    Set xmlDoc = CreateObject("Microsoft.XMLDOM")
    xmlDoc.async = "false"
    xmlDoc.Load ("http://www.w3school.com.cn/example/xdom/books.xml")
    Set x = xmlDoc.getElementsByTagName("title")
    MsgBox x(2).ChildNodes(0).nodevalue
End Sub
说明:本例使用 getElementsByTagname() 方法来获得 "books.xml" 中的第三个 <title> 元素。


例5.2 使用length属性来循环节点


Sub 访问2()
    Set xmlDoc = CreateObject("Microsoft.XMLDOM")
    xmlDoc.async = "false"
    xmlDoc.Load ("http://www.w3school.com.cn/example/xdom/books.xml")
    Set x = xmlDoc.getElementsByTagName("title")
    For i = 0 To x.Length - 1
        MsgBox x(i).ChildNodes(0).nodevalue
    Next
End Sub
说明: 本例使用 length 属性来循环 "books.xml" 中的所有 <title> 元素。


例5.3 循环元素节点


Sub 访问2()
    Set xmlDoc = CreateObject("Microsoft.XMLDOM")
    xmlDoc.async = "false"
    xmlDoc.Load ("http://www.w3school.com.cn/example/xdom/books.xml")
    Set x = xmlDoc.DocumentElement.ChildNodes
    For Each k In x
        MsgBox k.nodeName
    Next
End Sub
说明: documentElement 属性可返回文档的根节点。

例5.4 使用节点的关系来循环元素节点


Sub 访问4()
    Set xmlDoc = CreateObject("Microsoft.XMLDOM")
    xmlDoc.async = "false"
    xmlDoc.Load ("http://www.w3school.com.cn/example/xdom/books.xml")
    Set x = xmlDoc.getElementsByTagName("book")(0).ChildNodes
    Set y = xmlDoc.getElementsByTagName("book")(0).FirstChild
    For i = 0 To x.Length - 1
        If y.NodeType = 1 Then
            MsgBox y.nodeName
        End If
        Set y = y.NextSibling
    Next
End Sub


说明: 本例使用 nodeType 属性和 nextSibling 属性来处理 "books.xml" 中的元素节点。
nodeType 属性
nodeType 属性规定节点的类型。
nodeType 是只读的。
最重要的节点类型是:
元素类型 节点类型
元素        1
属性        2
文本        3
注释        8
文档        9



作者: liu-aguang    时间: 2016-10-18 17:35
天之雨露 发表于 2016-10-18 16:42
我也看了42楼求助
你的URl2会失效,现在无效了.貌似不是一般普通网页

没有发现你说的情况. 该网页请求不需要Cookie也可正确返回json; 你可以用WinHttp.WinHttpRequest.5.1测试.
作者: 天之雨露    时间: 2016-10-18 18:12
liu-aguang 发表于 2016-10-18 17:35
没有发现你说的情况. 该网页请求不需要Cookie也可正确返回json; 你可以用WinHttp.WinHttpRequest.5.1测试 ...

用你43楼中vba代码可以正确返回,成功

我直接用2种浏览器出现问题,也许是我电脑哪里设置原因吧。
(, 下载次数: 105)



作者: liu-aguang    时间: 2016-10-18 21:24
6. 利用xPath查询XML元素


<1>xPath简介


XPath 是一门在 XML 文档中查找信息的语言。XPath 可用来在 XML 文档中对元素和属性进行遍历。XPath 使用路径表达式来选取 XML 文档中的节点或者节点集。这些路径表达式和我们在常规的电脑文件系统中看到的表达式非常相似。

例:XML文档
<?xml version="1.0" encoding="ISO-8859-1"?>
<bookstore>
<book>
  <title lang="eng">Harry Potter</title>
  <price>29.99</price>
</book>
<book>
  <title lang="eng">Learning XML</title>
  <price>39.95</price>
</book>
</bookstore>

xPath路径表示式示例:
/bookstore/book/title   book元素的所有title子元素
/bookstore/book[1]/title  第二个book元素的所有title元素
xPath语法极其丰富, 可以灵活自由选择XML元素. 有兴趣的朋友可以参考:http://www.w3school.com.cn/xpath/index.asp

<2>XML DOM中使用xPath示例


XML DOM节点有两个方法可以使用xPath:

A. selectSingleNode() 方法查找和 XPath 查询匹配的一个节点
语法:
nodeObject.selectSingleNode(query)
query XPath 查询串。
返回值
匹配查询的一个单独的 Node。如果没有,则为 null。
说明
这个特定于 IE 的方法计算一个 XPath 表达式,使用这个节点作为 context 节点。它返回找到的第一个匹配节点,如果没有匹配的节点就返回 null。这个 selectSingleNode() 方法只在 XML 文档节点上存在,在 HTML 文档中不存在。

例:测试XML文档如上
Sub xPathXMLb()
    Text = "<?xml version='1.0' encoding='ISO-8859-1'?><bookstore><book>  <title lang='eng'>Harry Potter</title>  <price>29.99</price></book><book>  <title lang='eng'>Learning XML</title>  <price>39.95</price></book></bookstore>"
    Set xmlDoc = CreateObject("Microsoft.XMLDOM")
    xmlDoc.async = "false"
    xmlDoc.loadXML (Text)
    Set x = xmlDoc.SelectSingleNode("/bookstore/book/title")    '第一个book节点的首个title节点
    Set y = xmlDoc.SelectSingleNode("/bookstore/book[1]/title") '第二个Book节点的首个title节点
    MsgBox x.ChildNodes(0).nodevalue    'x.FirstChild.nodevalue
    MsgBox x.attributes(0).nodevalue
    MsgBox y.FirstChild.nodevalue
End Sub


B. selectNodes() 方法用一个 XPath 查询选择节点
语法:
nodeObject.selectNodes(query)
query XPath 查询串。
返回值
包含了匹配查询的节点的一个 NodeList。
说明
这个特定于 IE 的方法计算一个 XPath 表达式,使用该节点作为查询的根节点,并且将结果作为一个 NodeList 返回。这个 selectNodes() 方法只用于 XML 文档节点,不用于 HTML 文档节点。

例:测试XML文档同上
Sub xPathXMLa()
    Text = "<?xml version='1.0' encoding='ISO-8859-1'?><bookstore><book>  <title lang='eng'>Harry Potter</title>  <price>29.99</price></book><book>  <title lang='eng'>Learning XML</title>  <price>39.95</price></book></bookstore>"
    Set xmlDoc = CreateObject("Microsoft.XMLDOM")
    xmlDoc.async = "false"
    xmlDoc.loadXML (Text)
    Set x = xmlDoc.SelectNodes("/bookstore/book/title")
    MsgBox x.Length
    MsgBox x(0).FirstChild.nodevalue
End Sub



作者: 叻咕仔    时间: 2016-10-19 16:57
找遍了工具发现没有收藏帖子的功能,只能做个标记
作者: xel_fan    时间: 2016-10-23 17:34
留脚印,必须加精啊!!!
作者: wormtwo    时间: 2016-10-27 14:05
此乃神贴!鉴定完毕!楼主辛苦了!
作者: wormtwo    时间: 2016-10-28 10:02
本帖最后由 wormtwo 于 2016-10-28 10:03 编辑

如何提取这段的属性值呢?
  1. <caseItemLog><WL B="9073185407748296660" I="9073185407748296660" N="14700356538634" P="4053418715206535326" PN="张三" ICD="1470819508317" IUD="1470819508317" AS="0"/><WL B="7981047663275560771" I="7981047663275560771" N="14700356538637" P="5471147329064354838" PN="于李四" ICD="1470819508317" IUD="1474212600407" AS="0,26,5"/></caseItemLog>
复制代码

比如提取B,PN的值?
作者: liu-aguang    时间: 2016-10-28 12:46
wormtwo 发表于 2016-10-28 10:02
如何提取这段的属性值呢?

比如提取B,PN的值?
  1. Sub test()
  2.     s = "<caseItemLog><WL B='9073185407748296660' I='9073185407748296660' N='14700356538634' P='4053418715206535326' PN='寮犱笁' ICD='1470819508317' IUD='1470819508317' AS='0'/><WL B='7981047663275560771' I='7981047663275560771' N='14700356538637' P='5471147329064354838' PN='浜庢潕鍥' ICD='1470819508317' IUD='1474212600407' AS='0,26,5'/></caseItemLog>"

  3.     Set xmlDoc = CreateObject("Microsoft.XMLDOM")
  4.     xmlDoc.async = "false"
  5.     xmlDoc.LoadXML (s)
  6.     For Each k In xmlDoc.getElementsByTagName("WL")(0).Attributes '第一个WL元素的属性
  7.        MsgBox k.Text
  8.     Next
  9. End Sub
复制代码

作者: venomlk    时间: 2016-10-29 13:03
多谢大神们的指点,发帖辛苦了
作者: herro_gs    时间: 2016-11-7 21:08
selectSingleNode和selectNodes不能用于html,那我用xmlhttp加载的http代码有什么办法用xpath啊?试过loadXML直接把ResponseText,发现无法加载,返回false。大神怎么解决?
作者: liu-aguang    时间: 2016-11-8 08:12
herro_gs 发表于 2016-11-7 21:08
selectSingleNode和selectNodes不能用于html,那我用xmlhttp加载的http代码有什么办法用xpath啊?试过loadX ...

xPath不能用于HTML节点查询.
作者: herro_gs    时间: 2016-11-14 23:33
liu-aguang 发表于 2016-11-8 08:12
xPath不能用于HTML节点查询.

C#有个库可以,不知道vba有没有类似的库?
作者: billzhaopa    时间: 2016-11-17 15:04
好贴,收藏,感谢。
作者: xdwy81129    时间: 2016-11-25 01:18
楼主很费心,写了n多东西,真正的良师呀,支持楼主
作者: 岁月无敌    时间: 2016-12-4 16:33
mark,后续学习下。
作者: blackttea1    时间: 2016-12-4 19:14

好好学习天天向上
作者: lcjy11185    时间: 2016-12-4 23:05
严重收藏,好东东
作者: 鱼求雨    时间: 2016-12-14 00:01
感谢楼主分享,太受益了。十分感谢。
作者: q550723216    时间: 2016-12-14 14:11
太优秀了,先送上膝盖,在细细拜读!ORZ
作者: tsh134    时间: 2016-12-14 21:05

感谢楼主分享,太受益了。十分感谢。
作者: 52873731    时间: 2017-1-15 13:48
受教了,谢谢楼主分享
作者: excelzhusj    时间: 2017-1-17 20:56
liu-aguang 发表于 2016-9-28 08:56
二. Json与Json内容解析

1. 什么是Json?

Sub Json2()
    Set html = CreateObject("htmlfile")
    Set Window = html.parentWindow
    Set http = CreateObject("Msxml2.XMLHTTP")
    http.Open "GET", "http://kzone.kuwo.cn/mlog/UserVal?uid=1237357&from=profile", False
    http.send
    strHtml = http.responseText ' 得到数据
    ‘Window.clipboardData.SetData "text", strHtml '写入剪贴板
    Window.execScript "var js= " & strHtml  ' 改写成对象创建语句
    Set kuwo = Window.js ' 获取解析后的对象
    MsgBox "访问量:" & kuwo.view
End Sub

老师,运行这段代码报错 变量未定义请问是什么地方存在问题吗? 还有var是json对象创建格式所必须的吗?
作者: xdwy81129    时间: 2017-2-1 03:57
看完了,很精彩,内容很多,确实很辛苦,得慢慢学习呀
作者: 中原一布衣    时间: 2017-2-1 09:24
嗯,很有价值,容我慢慢消化。
作者: 紫川文秀    时间: 2017-3-11 23:13
把楼主大大的内容贴在一个文档里,列了目录方便大家下载备查.
作者: 紫川文秀    时间: 2017-3-12 18:07
楼主,请教一个问题,获取oDom的父窗口,也就是.parentWindow,如果用createobject,参数应该写什么?

比如建个HTMLdocument对象是createobject("HTMLfile")

若是建个htmlwindow2对象呢?是createobject("???")

作者: 紫川文秀    时间: 2017-3-13 22:35
liu-aguang 发表于 2016-9-27 09:04
一.  重要的基础知识及技巧
如今浏览器上看到的网页是如此地酷玄与生动, 这都归功于浏览器中内置的JavaScr ...

前面我问的关于是否有直接createobject  htmlwindow对象的方法,以便于在C#中类似于VBA里执行JavaScript,我找到方法了
  1. System.Type jstype = System.Type.GetTypeFromProgID("MSScriptControl.ScriptControl");
  2. dynamic js = System.Activator.CreateInstance(jstype);
  3. js.Language = "JScript";
  4. js.ExecuteStatement("str='abcde'");
  5. Console.WriteLine(js.eval("str"));
  6. Console.WriteLine(js.eval("str.length"));
  7. Console.ReadLine();
复制代码

作者: liu-aguang    时间: 2017-3-14 11:37
紫川文秀 发表于 2017-3-13 22:35
前面我问的关于是否有直接createobject  htmlwindow对象的方法,以便于在C#中类似于VBA里执行JavaScript, ...

那就好,反正我不懂C.
作者: rgcsm    时间: 2017-4-2 10:18
liu-aguang 发表于 2017-3-14 11:37
那就好,反正我不懂C.

老师,您好!请帮忙看一个帖子,这个怎么解决。谢谢!
http://club.excelhome.net/thread-1332669-1-1.html
作者: xxzcc    时间: 2017-4-9 00:41
深入浅出,从未如此简单,能找到这贴,真是获益良多。基本需要的问题都能从中得到解答
作者: yeminqiang    时间: 2017-4-9 10:44
如此神贴,顶
作者: yxp    时间: 2017-4-14 18:51
Mark 以后学习,今天脑袋灌满了。
作者: qbliu    时间: 2017-4-16 22:36
liu-aguang 发表于 2016-9-27 09:07
例4.1Sub a()    Set oDom =CreateObject("htmlfile")    Set oWindow =oDom.parentWindow    oWindow.exec ...

楼主功力深厚,内力精纯啊!拜读过很多帖子了
作者: ilikevba2011    时间: 2017-4-17 17:50
先顶一下,后面仔细学习。
作者: 八条眉毛    时间: 2017-4-18 04:33
支持大神力作!!大神威武
作者: shedog    时间: 2017-5-23 12:22
提示: 作者被禁止或删除 内容自动屏蔽
作者: hsw212    时间: 2017-6-5 10:59
liu-aguang 发表于 2016-9-28 11:03
4. 数组和对象自身嵌套或相互嵌套的Json/Jsonp实例

通过上面的讨论,我们明白了json的概念和一般解析方 ...

  Debug.Print oWindow.eval("a.total")              '公司总人数. 想想为什么不用上一句方式读取total值呢?
---
老师,请问这个如果 oWindow.a.Total 这么写为什么编辑器会自动把total给大写呢?
作者: liu-aguang    时间: 2017-6-5 11:09
hsw212 发表于 2017-6-5 10:59
Debug.Print oWindow.eval("a.total")              '公司总人数. 想想为什么不用上一句方式读取total ...

没有为什么. VBE编辑器中内置有"自动语法检测"功能, 对关键字会按事先设定的大小写修正.
作者: hsw212    时间: 2017-6-5 18:32
liu-aguang 发表于 2017-6-5 11:09
没有为什么. VBE编辑器中内置有"自动语法检测"功能, 对关键字会按事先设定的大小写修正.

total 也是vBE的内置关键字?
作者: liu-aguang    时间: 2017-6-5 19:37
hsw212 发表于 2017-6-5 18:32
total 也是vBE的内置关键字?

(, 下载次数: 424)

作者: hsw212    时间: 2017-6-7 11:03
liu-aguang 发表于 2016-10-14 21:35
一个应用实例: 根据ISBN(9787121098284)从缺书网读取该书号对应图书信息

Sub 获取图书信息()

老师再麻烦您帮忙看看附件中的这个程序,这个是原封不动拷贝您的程序,上午还能运行,突然就提示错误。
n = Mid(oWindow.Eval("$('.main_b_crumbs strong').eq(0).text()"), 2, 1)
就是这里出错,为什么呢?谢谢

作者: liu-aguang    时间: 2017-6-7 11:39
hsw212 发表于 2017-6-7 11:03
老师再麻烦您帮忙看看附件中的这个程序,这个是原封不动拷贝您的程序,上午还能运行,突然就提示错误。
...

Eval写法错误. 应为: eval
作者: hsw212    时间: 2017-6-7 12:04
liu-aguang 发表于 2017-6-7 11:39
Eval写法错误. 应为: eval

谢谢老师,被vbe搞疯,总自动帮我转换大小写,呵呵
作者: VBA万岁    时间: 2017-6-8 16:15
好帖!
整理成Excel文档如附件,分4个大块。每块的每段代码后都设置了一个测试按钮。
(, 下载次数: 696)
作者: liucqa    时间: 2017-6-8 18:39
liu-aguang 发表于 2016-9-27 12:07
(同志们要耐心等哈,每修改一次都要审核半天)半天过去了,内容都不能通过审核;都没有心情写了。

有QQ没,留一个
作者: liu-aguang    时间: 2017-6-8 19:17
liucqa 发表于 2017-6-8 18:39
有QQ没,留一个

已经在你的插件群了哈.
作者: liucqa    时间: 2017-6-8 20:04
liu-aguang 发表于 2017-6-8 19:17
已经在你的插件群了哈.

哦,为啥看不到你的名,你改改群名字b
作者: VBA万岁    时间: 2017-6-9 10:12
liu-aguang 发表于 2017-6-7 11:39
Eval写法错误. 应为: eval

这个教程写得很不错,如liucqa学导所说,填补了网抓方面的很多空白...
请教一下兄弟,通过比较Sub 获取图书信息1、Sub 获取图书信息2、Sub 获取图书信息3三段代码发现,jQuery在定位HTML文档的标签方面确实有很大的优势,能够使代码变得简捷。但发现其实用HTML的Document对象来访问元素也很方便的,尤其是根据元素的ID,Name,TagName,Calss属性访问元素,问题是我的VBA代码无法用Calss属性来访问元素(例如:sm = HTML.getElementsByClassName("img120")(j - 1).getAttribute("alt") '本例前期引用仍不能识别Class(类),这句代码会运行出错),即便是进行了前期绑定——在VBE编辑器中引用了“Microsoft HTML Object Library”。是不是需要在我的电脑中增加,Calss类或库什么的?如何操作?
作者: liu-aguang    时间: 2017-6-9 10:40
VBA万岁 发表于 2017-6-9 10:12
这个教程写得很不错,如liucqa学导所说,填补了网抓方面的很多空白...
请教一下兄弟,通过比较Sub 获取 ...

getElementsByClassName()方法, 从IE9开始才增加的. 所以本文中介绍了jQuery, 作为解析那些以class为主的网页方法的补充.
至于你遇到的问题, 没有具体的环境, 不好说原因.
作者: codexq    时间: 2017-6-9 11:12
AFK了一两年了,回来看看论坛,看到还是这么多大神,我就放心了
作者: liu-aguang    时间: 2017-6-9 11:44
codexq 发表于 2017-6-9 11:12
AFK了一两年了,回来看看论坛,看到还是这么多大神,我就放心了

AFK是啥?大神基本离开, 俺是小鬼类.




欢迎光临 ExcelHome技术论坛 (https://club.excelhome.net/) Powered by Discuz! X3.4