ExcelHome技术论坛

 找回密码
 免费注册

QQ登录

只需一步,快速开始

快捷登录

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

[原创] 网页数据采集---网页文档解析篇(json/html/xml)

  [复制链接]

TA的精华主题

TA的得分主题

发表于 2016-10-5 01:03 | 显示全部楼层
本帖已被收录到知识树中,索引项:网页交互
liu-aguang 发表于 2016-9-28 11:03
4. 数组和对象自身嵌套或相互嵌套的Json/Jsonp实例

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

请问下大神 为什么有的json对象用单引号 有的用双引号 这是怎么回事 还是您的抓取方法用的是html法吗?

TA的精华主题

TA的得分主题

 楼主| 发表于 2016-10-5 08:19 | 显示全部楼层
[广告] Excel易用宝 - 提升Excel的操作效率 · Excel / WPS表格插件       ★免费下载 ★       ★ 使用帮助
yrf_9 发表于 2016-10-5 01:03
请问下大神 为什么有的json对象用单引号 有的用双引号 这是怎么回事 还是您的抓取方法用的是html法吗?

在javaScript中, 单引号与双引号是等同的. 不过引号出现嵌套情形时,内部引号就要用单引号; 在VBA中,当出现嵌套情形时, 内部双引号要转义,或者也可使用单引号.

评分

1

查看全部评分

TA的精华主题

TA的得分主题

发表于 2016-10-5 10:48 | 显示全部楼层
[广告] Excel易用宝 - 提升Excel的操作效率 · Excel / WPS表格插件       ★免费下载 ★       ★ 使用帮助
向老师学习,谢谢!

TA的精华主题

TA的得分主题

 楼主| 发表于 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>




评分

1

查看全部评分

TA的精华主题

TA的得分主题

 楼主| 发表于 2016-10-7 14:20 | 显示全部楼层
[广告] Excel易用宝 - 提升Excel的操作效率 · Excel / WPS表格插件       ★免费下载 ★       ★ 使用帮助
本帖最后由 liu-aguang 于 2016-10-7 16:34 编辑

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


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

DOM就是由节点构成的树:HTML DOM树
捕获.PNG

在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> 元素的最后一个子节点


评分

1

查看全部评分

TA的精华主题

TA的得分主题

 楼主| 发表于 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()方法.

评分

2

查看全部评分

TA的精华主题

TA的得分主题

发表于 2016-10-7 17:06 | 显示全部楼层
[广告] Excel易用宝 - 提升Excel的操作效率 · Excel / WPS表格插件       ★免费下载 ★       ★ 使用帮助
这篇文章不错,弥补了论坛空白,给初学者讲得比较完善了

TA的精华主题

TA的得分主题

发表于 2016-10-7 17:23 | 显示全部楼层
一直在找这方面的学习资料,老师这个得顶上去。

TA的精华主题

TA的得分主题

 楼主| 发表于 2016-10-7 17:34 | 显示全部楼层
liucqa 发表于 2016-10-7 17:06
这篇文章不错,弥补了论坛空白,给初学者讲得比较完善了

谢谢老师肯定! 欢迎朋友们指正.

TA的精华主题

TA的得分主题

 楼主| 发表于 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版本较高的网站.


评分

1

查看全部评分

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

本版积分规则

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

GMT+8, 2024-12-22 19:33 , Processed in 0.052693 second(s), 8 queries , Gzip On, MemCache On.

Powered by Discuz! X3.4

© 1999-2023 Wooffice Inc.

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

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

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