DOM应用---遍历网页中的元素

在我们编写的程序中,如果想要实现对浏览器打开的网页进行监视、模拟操纵、动态提取用户输入、动态修改......等功能,那么请你抽出宝贵的时间,继续往下阅读。本文介绍的知识和示例程序都是围绕如何遍历 HTML 中的表单(form)并枚举出表单域的属性为目标的,对于网页中的其它元素,比如图象、连接、脚本等等,应用同样的方法都可以轻松实现。

二、网页的文档层次结构

IE 浏览器,采用 DOM(文档对象模型)来管理网页的数据。它通过一个容器(IWebBrowser2/IHTMLWindow2)来装载网页文档(IHTMLDocument2),而一个文档,又可以由 0 或多个贞(frame)组成,管理这些贞的接口叫“框架集合(IHTMLFramesCollection2)”,而每个贞的容器又是IHTMLWindow2,和IWebBrowser2一样,它也装载着各自的文档(IHTMLDocument2)。因此,我们的第一个任务,就是想方设法能够得到IHTMLDocument2的接口。因为文档可能包含贞,而贞又包含着子文档,子文档可能再包含贞......,如此要得到所有的文档,这里有一个递归遍历的处理过程。

得到文档(IHTMLDocument2)后,下一步任务就是要设法取得表单了(IHTMLFormElement)。因为在一个文档中可以包含 0 或多个表单(form),而管理这些表单的又是一个表单集合(IHTMLElementCollection),所以必须先得到集合,然后再枚举出所有的表单条目了。

得到表单(IHTMLFormElement)后,接下来的事情就简单了,逐个提取表单中的元素(也叫表单域 IHTMLInputElement)就可以读写这些域的属性了。

说了半天,我估计初次接触的朋友一定没有听懂:( 呵呵,还是用图的方式表示一下吧,这样比较清晰一些。

三、程序实现

<1> 取得 IHTMLDocument2 的接口指针。根据IE浏览器的运行方式,有多种不同的方式可以获取文档指针。

<1.1> 如果你在程序中使用MFC的 CHtmlView 视来浏览网页。

取得文档的方法最简单,调用 CHtmlView::GetHtmlDocument() 函数。

<1.2> 如果你的程序中使用了“Web 浏览器” 的ActiveX 控件。

取得文档的方法也比较简单,调用 CWebBrowser2::GetDocument() 函数。

<1.3> 如果你的程序是用 ATL 写的 ActiveX 控件。

那么需要调用 IOleClientSite::GetContainer 得到 IOleContainer 接口,然后就可以通过 QueryInterface() 查询得到 IHTMLDocument2 的接口。主要代码如下:

1.CComPtr
< IOleContainer > spContainer;

2.m_spClientSite->GetContainer(
&spContainer );

3.CComQIPtr
< IHTMLDocument2 > spDoc = spContainer;

4.if (
spDoc )

5.{

6.//
已经得到了 IHTMLDocument2 的接口指针

7.}

<1.4> 如果你的程序是用 MFC 写的 ActiveX 控件。

那么需要调用 COleControl::GetClientSite() 得到 IOleContainer 接口,然后的操作和<1.3>是一致的了。

<1.5> IE 浏览器作为独立的进程正在运行。

每个运行的浏览器(IE 和 资源浏览器)都会在 ShellWindows 中进行登记,因此我们要通过 IShellWindows 取得实例(示例程序中使用的就是这个方法)。主要代码如下:

01.#include
< atlbase.h >

02.#include
< mshtml.h >

03.

04.void FindFromShell()

05.{

06.CComPtr<
IShellWindows > spShellWin;

07.HRESULT hr
= spShellWin.CoCreateInstance( CLSID_ShellWindows );

08.if (
FAILED( hr ) )    
return;

09.

10.long nCount=0;

11.spShellWin->get_Count(&nCount);   //
取得浏览器实例个数

12.

13.for(long i=0;
i spDisp;

14.hr=spShellWin->Item(CComVariant(
i ), &spDisp );

15.if (
FAILED( hr ) )   
continue;

16.

17.CComQIPtr<
IWebBrowser2 > spBrowser = spDisp;

18.if (
!spBrowser )     
continue;

19.

20.spDisp.Release();

21.hr
= spBrowser->get_Document( &spDisp );

22.if (
FAILED ( hr ) )  
continue;

23.

24.CComQIPtr<
IHTMLDocument2 > spDoc = spDisp;

25.if (
!spDoc )         
continue;

26.

27.//
程序运行到此,已经找到了 IHTMLDocument2 的接口指针

28.}

29.}

<1.6> IE 浏览器控件被一个进程包装在一个子窗口中。那么你首先要得到那个进程的顶层窗口句柄(使用 FindWindow() 函数,或其它任何可行的方法),然后枚举所有子窗口,通过判断窗口类名是否是“Internet Explorer_Server”,从而得到浏览器的窗口句柄,再向窗口发消息取得文档的接口指针。主要代码如下:

01.#include
< atlbase.h >

02.#include
< mshtml.h >

03.#include
< oleacc.h >

04.#pragma
comment ( lib, "oleacc" )

05.

06.BOOL CALLBACK
EnumChildProc(
HWND hwnd,LPARAM lParam)

07.{

08.TCHAR szClassName[100];

09.

10.::GetClassName(
hwnd,  &szClassName,  
sizeof(szClassName)
);

11.if (
_tcscmp( szClassName,  _T(
"Internet
Explorer_Server"
)
) == 0 )

12.{

13.*(HWND*)lParam
= hwnd;

14.return FALSE;       //
找到第一个 IE 控件的子窗口就停止

15.}

16.else    return TRUE;        //
继续枚举子窗口

17.};

18.

19.void FindFromHwnd(HWND hWnd)

20.{

21.HWND hWndChild=NULL;

22.::EnumChildWindows(
hWnd, EnumChildProc, (
LPARAM)&hWndChild
);

23.if(NULL
== hWndChild)   
return;

24.

25.UINT nMsg
= ::RegisterWindowMessage( _T(
"WM_HTML_GETOBJECT")
);

26.LRESULT lRes;

27.::SendMessageTimeout(
hWndChild, nMsg, 0L, 0L, SMTO_ABORTIFHUNG, 1000, (
DWORD*)
&lRes );

28.

29.CComPtr
< IHTMLDocument2 > spDoc;

30.HRESULT hr
= ::ObjectFromLresult ( lRes, IID_IHTMLDocument2, 0 , (
LPVOID *)
&spDoc );

31.if (
FAILED ( hr ) )    
return;

32.

33.//
程序运行到此,已经找到了 IHTMLDocument2 的接口指针

34.}

<2> 得到了 IHTMLDocument2 接口指针后,如果网页是单贞的,那么转第<4>步骤。如果是多贞(有子框架)则还需要遍历所有的子框架。这些子框架(IHTMLWindow2),被保存在集合中(IHTMLFramesCollection2),取得集合指针的方法比较简单,取属性 IHTMLDocument2::get_frames()。

<3> 首先取得子框架的总数目 IHTMLFramesCollection::get_length(),接着就可以循环调用 IHTMLFramesCollection::item()函数一个一个地取得子框架 IHTMLWindow2 指针,然后转第<1>步。

<4> 一个文档中可能拥有多个表单,因此还是同样的道理,先要取得表单的集合(IHTMLElementCollection,其实这个不光是表单的集合,其他元素的集合,比如图片集合也是用它)。这个操作也很简单,取得属性 IHTMLDocument2::get_forms()。

<5> 属性 IHTMLElementCollection::get_length() 得到表单总数目,就可以循环取得每一个表单指针了 IHTMLElementCollection::item()。

<6> 在第<5>步中的item()函数,得到的是一个IDispatch的指针,你通过QueryInterface()查询,就可以得到 某类型输入的指针,代码如下:

01.//
假设 spDisp 是由IHTMLElementCollection::item() 得到的 IDispatch 指针

02.CComQIPtr
< IHTMLInputTextElement >     spInputText(spDisp);

03.CComQIPtr
< IHTMLInputButtonElement >   spInputButton(spDisp);

04.CComQIPtr
< IHTMLInputHiddenElement >   spInputHidden(spDisp);

05.......

06.if (
spInputText )

07.{

08.//如果是文本输入表单域

09.}

10.else if (
spInputButton )

11.{

12.//如果是按纽输入表单域

13.}

14.else if (
spInputHiddent )

15.{

16.//如果是隐藏输入表单域

17.}

18.else if ........    //其它输入类型

上面的方法,由于使用具体类型的接口指针,因此程序的效率比较高。但是通过 QueryInterface 接口查询,然后再进行条件判断显然是比较烦琐的,所以这个方法适合于特定的已知网页设计内容的程序。在示例程序中,我则是直接使用 IDispatch 接口进行操作的,这个方式执行起来稍微慢一些,但程序比较简单。主要代码和说明如下:

01.#include
< atlbase.h >

02.CComModule 
_Module;    
//
由于需要使用 CComDispatchDriver 的 IDispatch 包装类ATL智能指针,所以这个是必须的

03.#include
< atlcom.h >

04.......

05.long nElemCount=0;      //表单域的总数目

06.spFormElement->get_length(
&nElemCount );

07.

08.for(long j=0;
j< nElemCount; j++)

09.{

10.CComDispatchDriver
spInputElement;  
//
IDispatch 的智能指针

11.spFormElement->item(
CComVariant( j ), CComVariant(), &spInputElement );

12.

13.CComVariant
vName,vVal,vType;   
//
域名称,域值,域类型

14.spInputElement.GetPropertyByName(
L
"name",
&vName );

15.spInputElement.GetPropertyByName(
L
"value",&vVal 
);

16.spInputElement.GetPropertyByName(
L
"type",
&vType );

17.//
使用 IDispatch 的智能指针的好处就是:象上面这样读取、设置属性很简单

18.//
另外调用 Invoke 函数也异常方便,Invoke0(),Invoke1(),Invoke2()....

19.......

20.}

四、结束语

示例程序在 VC6 下编译执行通过。运行方法:随便启动几个 IE 浏览网页,最好是有表单输入的网页。然后执行示例的 EXE 程序即可。到这里,就到这里了......祝大家学习快乐 ^-^

时间: 2024-10-11 00:38:26

DOM应用---遍历网页中的元素的相关文章

给网页中的元素添加onclick事件(引自锋利的jQuery)

<!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta name="generator" content="editplus" /> <title>给网页中的元素添加onclick事件</p></title> <script src="jquery-1.7.1

JS获取网页中HTML元素的几种方法分析

getElementById getElementsByName getElementsByTagName 大概介绍 getElementById ,getElementsByName ,getElementsByTagName ###adv### 后两个是得到集合,byid只是得到单个对象 getElementById 的用法 举个例子: <a id="link1" name="link1" href=http://homepage.yesky.com>

C#获取网页中某个元素的位置,并模拟点击

我们在开发中,往往要得到网页中某个元素的位置,并且点击它.要模拟一次鼠标点击并不难,只要调用一个API就行了,关键就是怎么样得到这个元素的位置,还有判断是否要滚动滚动条,要滚动多少行能让元素显示出来.当然我们可以动态改变它的CSS,让它在特定的位置显示出来,但这个方法只对比较简单的网页有效. 那我们怎么才能得到网页的位置呢,首先我们来看一张图片 从这里我们可以看到五个offset的属性,这里我们主要利用offsetparent, offsetleft 和offsettop,我们用offsetpa

python中的enumerate函数用于遍历序列中的元素以及它们的下标

enumerate 函数用于遍历序列中的元素以及它们的下标: >>> for i,j in enumerate(('a','b','c')): print i,j 0 a1 b2 c>>> for i,j in enumerate([1,2,3]): print i,j 0 11 22 3>>> for i,j in enumerate({'a':1,'b':2}):    #注意字典,只返回KEY值!! print i,j 0 a1 b >&g

第9课、解析网页中的元素-四周学会爬虫系统

目标:爬取本地网页中,评分大于3的文章,并打印出来 准备: 安装Python3.0. 安装PyCharm,用于开发Python的集成环境. 安装BeautifulSoup库,学习爬虫需要的库. BeautifulSoup:是一个可以从HTML和XML中读取数据的库.库也叫模块,在笨办法学Python中叫做特征,是其他程序员开发好的一些代码,可以拿来导入(import),然后调用.开发之前最好先学习一下Python的基本知识,<笨办法学python>或者是<编程小白的第一本python&g

Android中如何解析网页,获取网页中的元素内容

问题: 由于android的WebView等相关类没有提供解析html网页内容的接口,我们想要获取网页的内容并解析出我们想要的元素内容,用android的固有API是没办法了. 这里我提供两种解析html思路:第一种,使用第三方解析html库:第二种,使用JAVA与JS回调,通过JS解析html. 之前研究了一下htmlparser这个开源库,但是发现和android提供的库有冲突,但其在纯java应用程序下是可行的. htmlparser下载地址:http://htmlparser.sourc

Js---获取网页中HTML元素的几种分析方法

getElementById getElementsByName getElementsByTagName 大概介绍 getElementById ,getElementsByName ,getElementsByTagName 后两个是得到集合,byid只是得到单个对象 getElementById 的用法 举个例子: <a id="link1" name="link1" href=http://homepage.yesky.com>网页陶吧</

selenium IDE--存储网页中的元素

业务场景:当某一个业务的下一步操作需要用到上一步操作页面显示的元素或文字,改怎么弄呢?恩,跟着我走吧. value值:javascript{storedVars['此处输入自定义的名称'];} 录制以下脚本信息: 其中第二行storeText是这样出来的 第三行需要手动输入. 运行的结果是这样: 原理是,打开一个页面,右键定位页面你需要的文字元素,然后找到storetext命令,存储这些文本,存储的时候你可以自己任意取自己想取的名字 上述例子中取的名字为onTheTextPage,其实可以换一个

遍历数组中的元素(含es6方法)

假如有这样一个数组.arr = [12,34,45,46,36,58,36,59],现在要遍历该数组. 方法1:以前我们可能会这样做: for(var i=0;i<arr.length;i++){ console.log(arr[i]+"\n") } 方法2:自ES5发布后,我们可以使用内置的forEach方法来遍历数组. arr.forEach(function(val){ console.log(val+"\n") }) 这段代码看起来简单,然而有些缺陷,