请教关于对于网页从登陆到表单录入连续操作的问题 selenium框架, Automation方式访问网页 解决方案 » 免费领取超大流量手机卡,每月29元包185G流量+100分钟通话, 中国电信官方发货 在CHtmlView::OnDocumentComplete事件处理函数中判断页面是否加载完全。virtual void OnDocumentComplete(LPCTSTR lpszURL);当Navigate打开一个URL后,比如www.baidu.com会触发很多次OnDocumentComplete,但最后一次触发(即百度首页全部加载完成)LPCTSTR lpszURL传进来的这个参数是"http:://www.baidu.com"由此可以来判断页面是否完全加载完毕如果加载完毕,再进行操作 我对网页进行了测试发现情况如下:http://10.22.8.11:7777/hdyh/登陆界面http://10.22.8.11:7777/hdyh/main2.jsp操作界面而在操作界面我无论点击按钮还是提交表单貌似url都没变啊。。那怎么判断? 我还发现一个问题,我为了测试程序代码我添加了MessageBox,代码如下/*第一步登陆网站*/ ::CoInitialize(NULL); //初始化COM CComPtr<IHTMLDocument2> pIHTMLDocument2 =(IHTMLDocument2*)CHtmlView::GetHtmlDocument(); HRESULT hr; bool id_name=false,id_pass=false;//验证用户信息填入 //bool is_in=false;//验证是否登录成功 CComQIPtr<IHTMLElementCollection> spElementCollection; hr=pIHTMLDocument2->get_all(&spElementCollection); if(SUCCEEDED(hr)) { long nEleCount=0;//取得元素数目 VARIANT index; hr=spElementCollection->get_length(&nEleCount); if(SUCCEEDED(hr)) { for(long i=0; i<nEleCount; i++) { CComPtr<IDispatch> pDisp=NULL;//取得第i项表单 V_VT(&index) = VT_I4; V_I4(&index) = i; hr = spElementCollection->item( index,index, &pDisp );//获取一个元素 if (SUCCEEDED(hr)) { CComQIPtr<IHTMLElement> pElem; //网页元素 hr = pDisp->QueryInterface(IID_IHTMLElement,(void **)&pElem ); if (SUCCEEDED(hr)) { BSTR className, tagName; pElem->get_tagName(&tagName);//标签名称匹配 pElem->get_className(&className);//类名称匹配 CString strTagName(tagName);//BSTR转换CString CString strClassName(className); if ("INPUT" == strTagName)//标签名称匹配貌似都是大写INPUT!!!! { CString strAttribute = "id";//属性id VARIANT ret; BSTR attribute = strAttribute.AllocSysString();//CString转换BSTR hr = pElem->getAttribute(attribute, 0 , &ret);//获取该类型的属性值 if (ret.vt == VT_BSTR) { strAttribute = ((BSTR)ret.pcVal); } SysFreeString(attribute); if (SUCCEEDED(hr) && "TxtUserName" == strAttribute)//输入用户匹配 { if (SUCCEEDED(hr)) { CString str="chengxiaowei"; BSTR bsStr = str.AllocSysString(); pElem->put_innerText(bsStr); id_name=true; } } if (SUCCEEDED(hr) && "TxtPassword" == strAttribute)//输入密码匹配 { if (SUCCEEDED(hr)) { CString str2="111111"; BSTR bsStr2 = str2.AllocSysString(); pElem->put_innerText(bsStr2); id_pass=true; } } if (SUCCEEDED(hr) && "IbtnEnter" == strAttribute)//按钮匹配 { if(id_name && id_pass) { pElem->click(); is_in=true; } } } } } } } } ::CoUninitialize(); //释放COM //Sleep(2000); ::CoInitialize(NULL); //初始化COM /*第二步菜单选择*/ //操作脚本 //bool is_second=false; if(is_in && m_nCount==1) { CComQIPtr<IHTMLDocument2> spDoc =(IHTMLDocument2*)CHtmlView::GetHtmlDocument();; CComPtr<IDispatch> spScript; hr=spDoc->get_Script(&spScript); if(SUCCEEDED(hr)) { MessageBox("1","test",MB_OK); //测试发现增加对话框可以实现,是否时间不够 CComBSTR bstrMember("loadFrame"); DISPID dispid = NULL; hr = spScript->GetIDsOfNames(IID_NULL,&bstrMember,1,LOCALE_SYSTEM_DEFAULT,&dispid); Sleep(200); if(SUCCEEDED(hr)) { //MessageBox("1","show",MB_OK); DISPPARAMS dispparams; memset(&dispparams, 0, sizeof dispparams); dispparams.cArgs = 1;//表示参数的计数 dispparams.rgvarg = new VARIANT[dispparams.cArgs];//表示对参数数组的引用 for( int i = 0; i < 1; i++) { CString param1="jsp/ss/rz/rz_all.jsp"; CComBSTR bstr_param1=param1.AllocSysString(); bstr_param1.CopyTo(&dispparams.rgvarg[i].bstrVal); dispparams.rgvarg[i].vt = VT_BSTR; } dispparams.cNamedArgs = 0;//表示命名参数的计数 EXCEPINFO excepInfo; memset(&excepInfo, 0, sizeof excepInfo); CComVariant vaResult; UINT nArgErr = (UINT)-1; // initialize to invalid arg hr = spScript->Invoke(dispid,IID_NULL,0,DISPATCH_METHOD,&dispparams,&vaResult,&excepInfo,&nArgErr); if(SUCCEEDED(hr)) { is_second=true; } delete [] dispparams.rgvarg; } } } ::CoUninitialize(); //释放COM我发现在红字代码加了之后弹出MessageBox之后可以实现第二步骤,否则代码不实行第二步骤。这个没搞懂。 我对网页进行了测试发现情况如下:http://10.22.8.11:7777/hdyh/登陆界面http://10.22.8.11:7777/hdyh/main2.jsp操作界面而在操作界面我无论点击按钮还是提交表单貌似url都没变啊。。那怎么判断?URL没变说明页面是AJAX动态加载的,点击按钮时JS会向服务器请求内容,这种情况下CHtmlView::OnDocumentComplete事件也是能触发的,URL是什么不知道的话,你可以调试一下看看,点一个按钮试试,记录下页面加载过程中最后一个OnDocumentComplete事件传进来的LPCTSTR lpszURL参数 比如在CHtmlView::OnDocumentComplete里打下断点。点调试运行程序,鼠标点一下网页上的某个按钮,页面加载完后CHtmlView::OnDocumentComplete中断点会断下,然后看下它的LPCTSTR lpszURL参数 我研究了下发现确实有不同,现在遇到一个新的问题就是网页中会Alert出消息对话框,如何自动点击确认或者关闭掉。 我今天用SendMessage函数,先找到Alert提示对话框句柄,然后再找到按钮句柄,模拟按钮点击为什么没有反应呢。代码如下//查找Alert窗口并关闭 HWND g_hWndWindow = ::FindWindow(NULL,"来自网页的消息");//查找对话框句柄 if(g_hWndWindow!=0) { HWND g_hWndText = ::FindWindowEx(g_hWndWindow,NULL,NULL,"保存成功");//查找按钮 if(g_hWndText!=0) { ::SendMessage(g_hWndText,WM_KEYDOWN,VK_RETURN,NULL); ::SendMessage(g_hWndText,WM_KEYUP,VK_RETURN,NULL); KillTimer(1);//关闭定时器 } } 我测试了发现代码::SendMessage(g_hWndWindow, WM_CLOSE, 0, 0);这句可以关闭窗口。::PostMessage(g_hWndText,BM_CLICK,0,0);//点击按钮没反应这是怎么回事。 你可以用SPY++看一看,点确定按钮时,按钮上并没有收到BM_CLICK消息,而是对话框窗口收到了WM_COMMAND消息,其中NotifyCode为BN_CLICKED。为了关闭这个对话框,方法很多,直接对对话框发送WM_CLOSE消息或WM_COMMAND消息,或对上面的按钮发送WM_LBUTTONDOWN WM_LBUTTONUP消息等等。但是这些办法都是等对话框出现了才去关闭,多少都会闪一下。最好的办法是让它彻底不弹出。方式是动态修改HMTL的内容,把Alert去掉。更简单一点就是在HTML中动态加入Alert函数的新定义,把原本的Alert覆盖掉。比如在HTML中加入:<script type="text/javascript"> function alert(str) { ; }</script>这样,整个页面上的Alert就不会弹出任何东西了 我现在遇到一个问题,表单连续录入发现无法实现连贯性。也就是网页中有三个按钮分别为新建,保存,返回。网页内容输入后调用按钮保存,弹出Alert对话框,查找窗口关闭后调用新建按钮,继续网页内容输入调用按钮保存,这样重复循环操作。我代码测试后无法实现。不知道是代码问题还是什么,请问你有什么思路吗。我的思路在OnTimer里添加循环但是测试了无法实现连续性一直断在关闭Alert代码处。 我测试发现我点击了保存按钮弹出了保存成功对话框,但是网页上通过查询却看不到我录入的信息,这是怎么回事。代码如下/*第五步台账登陆*/ ::CoInitialize(NULL); //初始化COM if(is_fourth && cs_url=="http://10.22.8.11:7777/hdyh/jsp/ss/rz/jdby_new.jsp")//m_nCount==7 { CComQIPtr<IHTMLDocument2> spHMDoc =(IHTMLDocument2*)CHtmlView::GetHtmlDocument();; CComPtr<IHTMLFramesCollection2> spHMFramesCollection2; spHMDoc->get_frames(&spHMFramesCollection2); //取得框架frame的集合 long nHMFrameCount; //取得子框架个数 nHMFrameCount=0; hr=spHMFramesCollection2->get_length(&nHMFrameCount); if (FAILED(hr)|| 0==nHMFrameCount) return; for(long i=0; i<nHMFrameCount; i++) { CComVariant vHMDispWin2; //取得子框架的自动化接口 hr = spHMFramesCollection2->item(&CComVariant(i), &vHMDispWin2); if (FAILED(hr)) continue; CComQIPtr<IHTMLWindow2> spHMWin2 = vHMDispWin2.pdispVal; if (!spHMWin2) continue; //取得子框架的 IHTMLWindow2 接口 CComPtr<IHTMLDocument2> spHMDoc2; spHMWin2->get_document(&spHMDoc2); //取得子框架的 IHTMLDocument2 接口 //测试是否在下一层框架 CComPtr<IHTMLFramesCollection2> sHMFramesCollection2; spHMDoc2->get_frames(&sHMFramesCollection2); //取得框架frame的集合 long nHMFrameCount2=0; //取得子框架个数 hr=sHMFramesCollection2->get_length(&nHMFrameCount2); if(SUCCEEDED(hr)) { for(long i2=0; i2<nHMFrameCount2; i2++) { CComVariant vpHMDispWin2; //取得子框架的自动化接口 hr = sHMFramesCollection2->item(&CComVariant(i2), &vpHMDispWin2); if(SUCCEEDED(hr)) { CComQIPtr<IHTMLWindow2> sHMWin2 = vpHMDispWin2.pdispVal; CComPtr<IHTMLDocument2> sHMDoc2; sHMWin2->get_document(&sHMDoc2); //取得子框架的 IHTMLDocument2 接口 CComQIPtr<IHTMLElementCollection> spHMElementCollection; hr=sHMDoc2->get_all(&spHMElementCollection); if (SUCCEEDED(hr)) { long nHMEleCount=0; //取得元素数目 VARIANT HMindex; hr=spHMElementCollection->get_length(&nHMEleCount); if (SUCCEEDED(hr)) { //测试是否此处可进行循环登记////// int in_i;//switch里面不可变量定义和初始化一步 for(in_i=0;in_i<stringArray.size()/2;in_i++) { for(long i3=0; i3<nHMEleCount; i3++) { IDispatch *pHMDisp = NULL; //取得第i项表单 V_VT(&HMindex) = VT_I4; V_I4(&HMindex) = i3; hr = spHMElementCollection->item(HMindex,HMindex, &pHMDisp );//Get an element IHTMLElement* pHMElem; //网页元素 hr = pHMDisp->QueryInterface(IID_IHTMLElement,(void **)&pHMElem ); BSTR className, tagName; pHMElem->get_tagName(&tagName);//标签名称匹配 pHMElem->get_className(&className);//类名称匹配 CString strTagName(tagName);//BSTR转换CString CString strClassName(className); if(!is_new) { if ("INPUT" == strTagName)//标签名称匹配貌似都是大写INPUT { CString strAttribute = "id";//属性 VARIANT ret; BSTR attribute = strAttribute.AllocSysString();//CString转换BSTR hr = pHMElem->getAttribute(attribute, 0 , &ret);//获取该类型的属性值 if (ret.vt == VT_BSTR) { strAttribute = ((BSTR)ret.pcVal); } SysFreeString(attribute); if (SUCCEEDED(hr) && "qsrq" == strAttribute)//匹配 { CString str=stringArray.at(in_i*2);//"2014-02-19 09时" BSTR bsStr = str.AllocSysString(); pHMElem->put_innerText(bsStr); num_infoin++; }......... if ("TEXTAREA" == strTagName) { CString strAttribute3 = "id";//属性 CString str="设备运行正常";//值班内容 BSTR bsStr = str.AllocSysString(); CString str2="无"; BSTR bsStr2 = str2.AllocSysString(); VARIANT ret3; BSTR attribute3 = strAttribute3.AllocSysString();//CString转换BSTR hr = pHMElem->getAttribute(attribute3, 0 , &ret3);//获取该类型的属性值 if (ret3.vt == VT_BSTR) { strAttribute3 = ((BSTR)ret3.pcVal); } SysFreeString(attribute3); if (SUCCEEDED(hr) && "fdjzqk" == strAttribute3)//匹配 { pHMElem->put_innerText(bsStr); num_infoin++; } } if(num_infoin%14==0)//判断信息录入完全后进行点击保存 { if ("IMG" == strTagName) { CString strAttribute4 = "id";//属性 VARIANT ret4; BSTR attribute4 = strAttribute4.AllocSysString();//CString转换BSTR hr = pHMElem->getAttribute(attribute4, 0 , &ret4);//获取该类型的属性值 if (ret4.vt == VT_BSTR) { strAttribute4 = ((BSTR)ret4.pcVal); } SysFreeString(attribute4); if (SUCCEEDED(hr) && "save" == strAttribute4)//id匹配 { pHMElem->click(); is_new=true; num_win++; } } } } if(is_new) { //查找Alert窗口并关闭/////////////////////////////////////////////////// HWND g_hWndWindow = ::FindWindow(NULL,"来自网页的消息");//查找对话框句柄 if(g_hWndWindow!=0) { HWND g_hWndText = ::FindWindowEx(g_hWndWindow,NULL,NULL,"保存成功");//查找按钮 if(g_hWndText!=0) { //MessageBox("1","1",MB_OK); ::SendMessage(g_hWndWindow, WM_CLOSE, 0, 0);//关闭窗口 num_win++; } } } ////////////////////////////////// if(is_new && num_win%2==0)//判断保存按钮后并且关闭提示框后点击新建按钮 { if ("IMG" == strTagName) { CString strAttribute5 = "id";//属性 VARIANT ret5; BSTR attribute5 = strAttribute5.AllocSysString();//CString转换BSTR hr = pHMElem->getAttribute(attribute5, 0 , &ret5);//获取该类型的属性值 if (ret5.vt == VT_BSTR) { strAttribute5 = ((BSTR)ret5.pcVal); } SysFreeString(attribute5); if (SUCCEEDED(hr) && "new" == strAttribute5)//id匹配 { pHMElem->click(); is_new=false; num_infoin=0; if(in_i==stringArray.size()/2-1) { KillTimer(1); } break; } } } } } } } } } } } } ::CoUninitialize(); //释放COM 这个只能是你自己慢慢研究,调试了。调试一下看看,不能持续循环录入时,代码是停在了哪里。错误处理完善一点,认真处理每一处可能出现错误的地方,判断每一个返回的指针是否为空,每一处出错后执行断言或抛出异常,这样有助于找到错误所在。另外,页面或页面上的框架的内容重新加载、刷新后,之前获取的有关该文档上的各种元素指针可能失效。进行一次录入-提交后,一定要等页面加载完成,触发CHtmlView::OnDocumentComplete事件才可进行下一轮录入提交操作,或是留出足够长的时间间隔。还有就是你的代码嵌套层次太深了,不利于调试、排错,重构一下,拆分为多个小函数 恩,我今天早上调试了。找到问题了。但是不知道原因。你看看为什么。if(is_new) { HWND g_hWndWindow = ::FindWindow(NULL,"来自网页的消息");//查找对话框句柄 if(g_hWndWindow!=0) { HWND g_hWndText = ::FindWindowEx(g_hWndWindow,NULL,NULL,"保存成功");//查找按钮 if(g_hWndText!=0) { ::SendMessage(g_hWndWindow, WM_CLOSE, 0, 0);//关闭窗口 num_win++; //MessageBox("5","show",MB_OK); } } }这段代码主要用于关闭Alert窗口,我设定当关闭一次时num_win计数加一,用于后续操作判断。但是我调试了发现当第一次弹出Alert窗口后,代码获取到然后发送关闭窗口后,计数加了1,后面定时器继续调试时,莫名这段代码又被调用一次,照理说,窗口句柄应该找不到了,因为窗口被关闭了,但是我猜是不是SendMessage发送的销毁指令和定时器是异步啊。所以在销毁指令前又调用了一次。另外因为网站是内网,我下班之后也调试不了。上班之后需要另外接线,调试时相当麻烦。郁闷。还有就是关于嵌套我也感觉是有点多,但是我查了下我处理得网站是框架中套框架,你的意思是让我自定义函数然后重复调用? 我现在又发现一个问题,就是处理网页时,网页中表格Table点击其中某一个单元格,发现我获取元素接口之后Click发现没反应。该单元格源文件中代码如下:<td align="center"> <A href="yhd.jsp?fm=1&dwdm2=BZ01&qssj=2014-01-01&zzsj=2014-03-17">0</A> </td> <td align="center"> <A href="fmjc.jsp?fm=1&dwdm2=BZ01&qssj=2014-01-01&zzsj=2014-03-17">0</A> </td>它就是一个链接形式,但是每一天链接形式貌似不一样有日期后缀,所以不能判断固定网址。我不知道如何点击该单元格了。了 获取到单元格tb标签的元素后,还的获取到其中包含的<A>标签元素指针,再Click 如何获取A标签的指针?没有id之类的东西啊。。 IHTMLElement::get_children即可获取td标签元素内的a标签元素td标签元素不是也没有ID吗,你是怎么获取的呢? 我还是有点不太理解,我知道遍历,问题是我如何知道搜索到的是我要的指针接口。譬如搜索到了20个节点,我怎么知道循环中第几个是我要的接口。我的代码如下//搜索表格内容 IDispatch *pDisp; VARIANT index; _variant_t varID = "mytab"; // 表格元素ID V_VT(&index) = VT_I4; V_I4(&index) = 0; hr = spElementCollection->item(varID, index, &pDisp); // 获得表格位置 if (SUCCEEDED(hr) && (pDisp != NULL)) { IHTMLTable * pTable; // 获得表格元素接口 hr = pDisp->QueryInterface(IID_IHTMLTable,(void **)&pTable ); if (SUCCEEDED(hr) && (pTable != NULL)) { IHTMLElementCollection* pColl2; pTable->get_rows(&pColl2); // 获取表格行 IDispatch* pDisp2; VARIANT index2; V_VT(&index2)=VT_I4; V_I4(&index2) = 1; hr = pColl2->item( index2,index2, &pDisp2 ); // 获取第2行位置 if ( (SUCCEEDED(hr)) && (pDisp2 != NULL) ) {//必须保证hr成功和pDisp不为NULL IHTMLTableRow* pRow; // 获取行元素接口 hr = pDisp2->QueryInterface(IID_IHTMLTableRow,(void **)&pRow); if( (SUCCEEDED(hr)) && (pRow != NULL) ) { IHTMLElementCollection* pColl3; pRow->get_cells(&pColl3); // 获取格子元素 IDispatch* pDisp3; VARIANT index3; V_VT(&index3)=VT_I4; V_I4(&index3) = 8; hr = pColl3->item( index3,index3, &pDisp3 ); // 获取第2行第(7,8)格元素 if ( (SUCCEEDED(hr)) && (pDisp2 != NULL) ) { IHTMLElement* pElem; // 获取元素接口 hr = pDisp3->QueryInterface(IID_IHTMLElement,(void **)&pElem); if(SUCCEEDED(hr)) { pElem->click(); is_third=true; } } } } } } 我看了下网上代码最后通过innerText来判断是否为获取到的A标签,我这个源文件上文本为0.这个怎么搞?我现在自己测试的在找到表格中第几行第几个单元格之后用你说的get_chirldren获取不到啊 首先,你要获取到td标签元素的IHTMLElement指针,然后调用get_chirldren获取到一个IDispatch指针,QueryInterface得到IHTMLElementCollection元素容器指针,然后用IHTMLElementCollection::item获取容器内a标签元素指针。至于在一个循环遍历中,如何知道哪个是你要的元素,可以根据这个元素的特征来判断,innerText也是一种方法,如果innerText获取不到字符串的话,尝试get_innerHTML、get_outerHTML、get_outerText。一般它的html写的比较标准的话,是能获取到的。但是有的html写的不太标准的,确实会出现获取不到任何内容的情况。 是啊我这发现这个网页写的不规范啥都没有。不知道如何判断。我现在又发现网页中一个图片没有信息可以进行判断它的html代码为<td align=center width=80><img src="../../../css/images/tjby.jpg" onclick="tjby();" style="cursor:hand;" align="absbottom"></td>至于之前的A标签我实现点击了但是我发现一个问题,为何用IHTMLElementCollection::item获取容器内a标签元素指针时候,我判断了下元素集合的大小,发现为0但是取第一个值能click所以我很晕,忙了半天以为没取到谁知道已经取到指针了 这个img标签元素,你可以根据它的"属性"来判断,src、style、align这些都是它的属性。其中src属性可以明确区分出来。可以用IHTMLElement::getAttribute取属性进行判断。或是QueryInterface得到这个img标签元素的IHTMLImgElement指针后,用IHTMLImgElement::get_src获取进行判断。有时候使用mshtml出现空指针、找不到元素、返回内容为空的情况时,可以手写一个简单的html文件进行测试、分析问题出在哪里。实在不行的话用javascript试试看看能不能实现,总之mshtml能实现的功能,javascript几乎都能实现,并且javascript就是专门搞这个的。 如何在MFC工程中打开两个不同数据库odbc数据源 手写0-9识别 VC怎么实现呀?还要做界面 没有这方面的经验 有问题请教?急 请教一个关于vc编译器的问题。 急啊,dll中使用STL的问题 求助:一个界面控件中SendMessage向父控件发消息,但不知道怎么在引用实例类中接收消息 有关字符串处理的问题 急急急...单文档里的对话框对何操作视图 关于controlbar的问题 CEDITBOX 关于MessageBox()函数的用法? CFile的SeekToEnd无法获得文件句柄?
virtual void OnDocumentComplete(LPCTSTR lpszURL);当Navigate打开一个URL后,比如www.baidu.com
会触发很多次OnDocumentComplete,但最后一次触发(即百度首页全部加载完成)
LPCTSTR lpszURL传进来的这个参数是"http:://www.baidu.com"
由此可以来判断页面是否完全加载完毕
如果加载完毕,再进行操作
http://10.22.8.11:7777/hdyh/
登陆界面http://10.22.8.11:7777/hdyh/main2.jsp
操作界面
而在操作界面我无论点击按钮还是提交表单貌似url都没变啊。。那怎么判断?
/*第一步登陆网站*/
::CoInitialize(NULL); //初始化COM
CComPtr<IHTMLDocument2> pIHTMLDocument2 =(IHTMLDocument2*)CHtmlView::GetHtmlDocument();
HRESULT hr;
bool id_name=false,id_pass=false;//验证用户信息填入
//bool is_in=false;//验证是否登录成功 CComQIPtr<IHTMLElementCollection> spElementCollection;
hr=pIHTMLDocument2->get_all(&spElementCollection);
if(SUCCEEDED(hr))
{
long nEleCount=0;//取得元素数目
VARIANT index;
hr=spElementCollection->get_length(&nEleCount); if(SUCCEEDED(hr))
{
for(long i=0; i<nEleCount; i++)
{
CComPtr<IDispatch> pDisp=NULL;//取得第i项表单
V_VT(&index) = VT_I4;
V_I4(&index) = i;
hr = spElementCollection->item( index,index, &pDisp );//获取一个元素
if (SUCCEEDED(hr))
{
CComQIPtr<IHTMLElement> pElem; //网页元素
hr = pDisp->QueryInterface(IID_IHTMLElement,(void **)&pElem );
if (SUCCEEDED(hr))
{
BSTR className, tagName;
pElem->get_tagName(&tagName);//标签名称匹配
pElem->get_className(&className);//类名称匹配
CString strTagName(tagName);//BSTR转换CString
CString strClassName(className);
if ("INPUT" == strTagName)//标签名称匹配貌似都是大写INPUT!!!!
{
CString strAttribute = "id";//属性id
VARIANT ret;
BSTR attribute = strAttribute.AllocSysString();//CString转换BSTR
hr = pElem->getAttribute(attribute, 0 , &ret);//获取该类型的属性值
if (ret.vt == VT_BSTR)
{
strAttribute = ((BSTR)ret.pcVal);
}
SysFreeString(attribute);
if (SUCCEEDED(hr) && "TxtUserName" == strAttribute)//输入用户匹配
{
if (SUCCEEDED(hr))
{
CString str="chengxiaowei";
BSTR bsStr = str.AllocSysString();
pElem->put_innerText(bsStr);
id_name=true;
}
}
if (SUCCEEDED(hr) && "TxtPassword" == strAttribute)//输入密码匹配
{
if (SUCCEEDED(hr))
{
CString str2="111111";
BSTR bsStr2 = str2.AllocSysString();
pElem->put_innerText(bsStr2);
id_pass=true;
}
}
if (SUCCEEDED(hr) && "IbtnEnter" == strAttribute)//按钮匹配
{
if(id_name && id_pass)
{
pElem->click();
is_in=true;
}
}
}
}
}
}
}
}
::CoUninitialize(); //释放COM //Sleep(2000);
::CoInitialize(NULL); //初始化COM
/*第二步菜单选择*/
//操作脚本
//bool is_second=false;
if(is_in && m_nCount==1)
{
CComQIPtr<IHTMLDocument2> spDoc =(IHTMLDocument2*)CHtmlView::GetHtmlDocument();;
CComPtr<IDispatch> spScript;
hr=spDoc->get_Script(&spScript);
if(SUCCEEDED(hr))
{
MessageBox("1","test",MB_OK);
//测试发现增加对话框可以实现,是否时间不够
CComBSTR bstrMember("loadFrame");
DISPID dispid = NULL;
hr = spScript->GetIDsOfNames(IID_NULL,&bstrMember,1,LOCALE_SYSTEM_DEFAULT,&dispid);
Sleep(200);
if(SUCCEEDED(hr))
{
//MessageBox("1","show",MB_OK);
DISPPARAMS dispparams;
memset(&dispparams, 0, sizeof dispparams);
dispparams.cArgs = 1;//表示参数的计数
dispparams.rgvarg = new VARIANT[dispparams.cArgs];//表示对参数数组的引用
for( int i = 0; i < 1; i++)
{
CString param1="jsp/ss/rz/rz_all.jsp";
CComBSTR bstr_param1=param1.AllocSysString();
bstr_param1.CopyTo(&dispparams.rgvarg[i].bstrVal);
dispparams.rgvarg[i].vt = VT_BSTR;
}
dispparams.cNamedArgs = 0;//表示命名参数的计数
EXCEPINFO excepInfo;
memset(&excepInfo, 0, sizeof excepInfo);
CComVariant vaResult;
UINT nArgErr = (UINT)-1; // initialize to invalid arg
hr = spScript->Invoke(dispid,IID_NULL,0,DISPATCH_METHOD,&dispparams,&vaResult,&excepInfo,&nArgErr);
if(SUCCEEDED(hr))
{
is_second=true;
}
delete [] dispparams.rgvarg;
}
}
}
::CoUninitialize(); //释放COM
我发现在红字代码加了之后弹出MessageBox之后可以实现第二步骤,否则代码不实行第二步骤。这个没搞懂。
http://10.22.8.11:7777/hdyh/
登陆界面http://10.22.8.11:7777/hdyh/main2.jsp
操作界面
而在操作界面我无论点击按钮还是提交表单貌似url都没变啊。。那怎么判断?
URL没变说明页面是AJAX动态加载的,点击按钮时JS会向服务器请求内容,这种情况下CHtmlView::OnDocumentComplete事件也是能触发的,URL是什么不知道的话,你可以调试一下看看,点一个按钮试试,记录下页面加载过程中最后一个OnDocumentComplete事件传进来的LPCTSTR lpszURL参数
点调试运行程序,鼠标点一下网页上的某个按钮,页面加载完后CHtmlView::OnDocumentComplete中断点会断下,然后看下它的LPCTSTR lpszURL参数
//查找Alert窗口并关闭
HWND g_hWndWindow = ::FindWindow(NULL,"来自网页的消息");//查找对话框句柄
if(g_hWndWindow!=0)
{
HWND g_hWndText = ::FindWindowEx(g_hWndWindow,NULL,NULL,"保存成功");//查找按钮
if(g_hWndText!=0)
{
::SendMessage(g_hWndText,WM_KEYDOWN,VK_RETURN,NULL);
::SendMessage(g_hWndText,WM_KEYUP,VK_RETURN,NULL);
KillTimer(1);//关闭定时器
}
}
::SendMessage(g_hWndWindow, WM_CLOSE, 0, 0);
这句可以关闭窗口。
::PostMessage(g_hWndText,BM_CLICK,0,0);//点击按钮
没反应这是怎么回事。
更简单一点就是在HTML中动态加入Alert函数的新定义,把原本的Alert覆盖掉。
比如在HTML中加入:<script type="text/javascript">
function alert(str) { ; }
</script>这样,整个页面上的Alert就不会弹出任何东西了
代码如下/*第五步台账登陆*/
::CoInitialize(NULL); //初始化COM
if(is_fourth && cs_url=="http://10.22.8.11:7777/hdyh/jsp/ss/rz/jdby_new.jsp")//m_nCount==7
{
CComQIPtr<IHTMLDocument2> spHMDoc =(IHTMLDocument2*)CHtmlView::GetHtmlDocument();;
CComPtr<IHTMLFramesCollection2> spHMFramesCollection2;
spHMDoc->get_frames(&spHMFramesCollection2); //取得框架frame的集合
long nHMFrameCount; //取得子框架个数
nHMFrameCount=0;
hr=spHMFramesCollection2->get_length(&nHMFrameCount);
if (FAILED(hr)|| 0==nHMFrameCount) return;
for(long i=0; i<nHMFrameCount; i++)
{
CComVariant vHMDispWin2; //取得子框架的自动化接口
hr = spHMFramesCollection2->item(&CComVariant(i), &vHMDispWin2);
if (FAILED(hr)) continue;
CComQIPtr<IHTMLWindow2> spHMWin2 = vHMDispWin2.pdispVal;
if (!spHMWin2) continue; //取得子框架的 IHTMLWindow2 接口
CComPtr<IHTMLDocument2> spHMDoc2;
spHMWin2->get_document(&spHMDoc2); //取得子框架的 IHTMLDocument2 接口
//测试是否在下一层框架
CComPtr<IHTMLFramesCollection2> sHMFramesCollection2;
spHMDoc2->get_frames(&sHMFramesCollection2); //取得框架frame的集合
long nHMFrameCount2=0; //取得子框架个数
hr=sHMFramesCollection2->get_length(&nHMFrameCount2);
if(SUCCEEDED(hr))
{
for(long i2=0; i2<nHMFrameCount2; i2++)
{
CComVariant vpHMDispWin2; //取得子框架的自动化接口
hr = sHMFramesCollection2->item(&CComVariant(i2), &vpHMDispWin2);
if(SUCCEEDED(hr))
{
CComQIPtr<IHTMLWindow2> sHMWin2 = vpHMDispWin2.pdispVal;
CComPtr<IHTMLDocument2> sHMDoc2;
sHMWin2->get_document(&sHMDoc2); //取得子框架的 IHTMLDocument2 接口
CComQIPtr<IHTMLElementCollection> spHMElementCollection;
hr=sHMDoc2->get_all(&spHMElementCollection);
if (SUCCEEDED(hr))
{
long nHMEleCount=0; //取得元素数目
VARIANT HMindex;
hr=spHMElementCollection->get_length(&nHMEleCount);
if (SUCCEEDED(hr))
{
//测试是否此处可进行循环登记//////
int in_i;//switch里面不可变量定义和初始化一步
for(in_i=0;in_i<stringArray.size()/2;in_i++)
{
for(long i3=0; i3<nHMEleCount; i3++)
{
IDispatch *pHMDisp = NULL; //取得第i项表单
V_VT(&HMindex) = VT_I4;
V_I4(&HMindex) = i3;
hr = spHMElementCollection->item(HMindex,HMindex, &pHMDisp );//Get an element
IHTMLElement* pHMElem; //网页元素
hr = pHMDisp->QueryInterface(IID_IHTMLElement,(void **)&pHMElem );
BSTR className, tagName;
pHMElem->get_tagName(&tagName);//标签名称匹配
pHMElem->get_className(&className);//类名称匹配
CString strTagName(tagName);//BSTR转换CString
CString strClassName(className); if(!is_new)
{
if ("INPUT" == strTagName)//标签名称匹配貌似都是大写INPUT
{
CString strAttribute = "id";//属性
VARIANT ret;
BSTR attribute = strAttribute.AllocSysString();//CString转换BSTR
hr = pHMElem->getAttribute(attribute, 0 , &ret);//获取该类型的属性值
if (ret.vt == VT_BSTR)
{
strAttribute = ((BSTR)ret.pcVal);
}
SysFreeString(attribute);
if (SUCCEEDED(hr) && "qsrq" == strAttribute)//匹配
{
CString str=stringArray.at(in_i*2);//"2014-02-19 09时"
BSTR bsStr = str.AllocSysString();
pHMElem->put_innerText(bsStr);
num_infoin++;
}
.........
if ("TEXTAREA" == strTagName)
{
CString strAttribute3 = "id";//属性
CString str="设备运行正常";//值班内容
BSTR bsStr = str.AllocSysString();
CString str2="无";
BSTR bsStr2 = str2.AllocSysString();
VARIANT ret3;
BSTR attribute3 = strAttribute3.AllocSysString();//CString转换BSTR
hr = pHMElem->getAttribute(attribute3, 0 , &ret3);//获取该类型的属性值
if (ret3.vt == VT_BSTR)
{
strAttribute3 = ((BSTR)ret3.pcVal);
}
SysFreeString(attribute3);
if (SUCCEEDED(hr) && "fdjzqk" == strAttribute3)//匹配
{
pHMElem->put_innerText(bsStr);
num_infoin++;
}
}
if(num_infoin%14==0)//判断信息录入完全后进行点击保存
{
if ("IMG" == strTagName)
{
CString strAttribute4 = "id";//属性
VARIANT ret4;
BSTR attribute4 = strAttribute4.AllocSysString();//CString转换BSTR
hr = pHMElem->getAttribute(attribute4, 0 , &ret4);//获取该类型的属性值
if (ret4.vt == VT_BSTR)
{
strAttribute4 = ((BSTR)ret4.pcVal);
}
SysFreeString(attribute4);
if (SUCCEEDED(hr) && "save" == strAttribute4)//id匹配
{
pHMElem->click();
is_new=true;
num_win++;
}
}
}
} if(is_new)
{
//查找Alert窗口并关闭///////////////////////////////////////////////////
HWND g_hWndWindow = ::FindWindow(NULL,"来自网页的消息");//查找对话框句柄
if(g_hWndWindow!=0)
{
HWND g_hWndText = ::FindWindowEx(g_hWndWindow,NULL,NULL,"保存成功");//查找按钮
if(g_hWndText!=0)
{
//MessageBox("1","1",MB_OK);
::SendMessage(g_hWndWindow, WM_CLOSE, 0, 0);//关闭窗口
num_win++;
}
}
}
//////////////////////////////////
if(is_new && num_win%2==0)//判断保存按钮后并且关闭提示框后点击新建按钮
{
if ("IMG" == strTagName)
{
CString strAttribute5 = "id";//属性
VARIANT ret5;
BSTR attribute5 = strAttribute5.AllocSysString();//CString转换BSTR
hr = pHMElem->getAttribute(attribute5, 0 , &ret5);//获取该类型的属性值
if (ret5.vt == VT_BSTR)
{
strAttribute5 = ((BSTR)ret5.pcVal);
}
SysFreeString(attribute5);
if (SUCCEEDED(hr) && "new" == strAttribute5)//id匹配
{
pHMElem->click();
is_new=false;
num_infoin=0;
if(in_i==stringArray.size()/2-1)
{
KillTimer(1);
}
break;
}
}
}
}
}
}
}
}
}
}
}
}
::CoUninitialize(); //释放COM
调试一下看看,不能持续循环录入时,代码是停在了哪里。
错误处理完善一点,认真处理每一处可能出现错误的地方,判断每一个返回的指针是否为空,每一处出错后执行断言或抛出异常,这样有助于找到错误所在。
另外,页面或页面上的框架的内容重新加载、刷新后,之前获取的有关该文档上的各种元素指针可能失效。
进行一次录入-提交后,一定要等页面加载完成,触发CHtmlView::OnDocumentComplete事件才可进行下一轮录入提交操作,或是留出足够长的时间间隔。
还有就是你的代码嵌套层次太深了,不利于调试、排错,重构一下,拆分为多个小函数
if(is_new)
{
HWND g_hWndWindow = ::FindWindow(NULL,"来自网页的消息");//查找对话框句柄
if(g_hWndWindow!=0)
{
HWND g_hWndText = ::FindWindowEx(g_hWndWindow,NULL,NULL,"保存成功");//查找按钮
if(g_hWndText!=0)
{
::SendMessage(g_hWndWindow, WM_CLOSE, 0, 0);//关闭窗口
num_win++;
//MessageBox("5","show",MB_OK);
}
}
}
这段代码主要用于关闭Alert窗口,我设定当关闭一次时num_win计数加一,用于后续操作判断。但是我调试了发现当第一次弹出Alert窗口后,代码获取到然后发送关闭窗口后,计数加了1,后面定时器继续调试时,莫名这段代码又被调用一次,照理说,窗口句柄应该找不到了,因为窗口被关闭了,但是我猜是不是SendMessage发送的销毁指令和定时器是异步啊。所以在销毁指令前又调用了一次。
另外因为网站是内网,我下班之后也调试不了。上班之后需要另外接线,调试时相当麻烦。郁闷。
还有就是关于嵌套我也感觉是有点多,但是我查了下我处理得网站是框架中套框架,你的意思是让我自定义函数然后重复调用?
该单元格源文件中代码如下:
<td align="center">
<A href="yhd.jsp?fm=1&dwdm2=BZ01&qssj=2014-01-01&zzsj=2014-03-17">0</A>
</td>
<td align="center">
<A href="fmjc.jsp?fm=1&dwdm2=BZ01&qssj=2014-01-01&zzsj=2014-03-17">0</A>
</td>
它就是一个链接形式,但是每一天链接形式貌似不一样有日期后缀,所以不能判断固定网址。我不知道如何点击该单元格了。了
我的代码如下
//搜索表格内容
IDispatch *pDisp;
VARIANT index;
_variant_t varID = "mytab"; // 表格元素ID
V_VT(&index) = VT_I4;
V_I4(&index) = 0;
hr = spElementCollection->item(varID, index, &pDisp); // 获得表格位置
if (SUCCEEDED(hr) && (pDisp != NULL))
{
IHTMLTable * pTable; // 获得表格元素接口
hr = pDisp->QueryInterface(IID_IHTMLTable,(void **)&pTable );
if (SUCCEEDED(hr) && (pTable != NULL))
{
IHTMLElementCollection* pColl2;
pTable->get_rows(&pColl2); // 获取表格行
IDispatch* pDisp2;
VARIANT index2;
V_VT(&index2)=VT_I4;
V_I4(&index2) = 1;
hr = pColl2->item( index2,index2, &pDisp2 ); // 获取第2行位置
if ( (SUCCEEDED(hr)) && (pDisp2 != NULL) )
{//必须保证hr成功和pDisp不为NULL
IHTMLTableRow* pRow; // 获取行元素接口
hr = pDisp2->QueryInterface(IID_IHTMLTableRow,(void **)&pRow);
if( (SUCCEEDED(hr)) && (pRow != NULL) )
{
IHTMLElementCollection* pColl3;
pRow->get_cells(&pColl3); // 获取格子元素
IDispatch* pDisp3;
VARIANT index3;
V_VT(&index3)=VT_I4;
V_I4(&index3) = 8;
hr = pColl3->item( index3,index3, &pDisp3 ); // 获取第2行第(7,8)格元素
if ( (SUCCEEDED(hr)) && (pDisp2 != NULL) )
{
IHTMLElement* pElem; // 获取元素接口
hr = pDisp3->QueryInterface(IID_IHTMLElement,(void **)&pElem);
if(SUCCEEDED(hr))
{
pElem->click();
is_third=true;
}
}
}
}
}
}
但是有的html写的不太标准的,确实会出现获取不到任何内容的情况。
<td align=center width=80>
<img src="../../../css/images/tjby.jpg" onclick="tjby();" style="cursor:hand;" align="absbottom">
</td>
至于之前的A标签我实现点击了但是我发现一个问题,为何用IHTMLElementCollection::item获取容器内a标签元素指针时候,我判断了下元素集合的大小,发现为0但是取第一个值能click所以我很晕,忙了半天以为没取到谁知道已经取到指针了
其中src属性可以明确区分出来。可以用IHTMLElement::getAttribute取属性进行判断。
或是QueryInterface得到这个img标签元素的IHTMLImgElement指针后,用IHTMLImgElement::get_src获取进行判断。有时候使用mshtml出现空指针、找不到元素、返回内容为空的情况时,可以手写一个简单的html文件进行测试、分析问题出在哪里。实在不行的话用javascript试试看看能不能实现,总之mshtml能实现的功能,javascript几乎都能实现,并且javascript就是专门搞这个的。