首先感叹下:冬至了,别人都去聚餐言欢,我去孤独写这些文字
之所以写这个东西,是一个网管朋友出50元大洋让我写的,有了这个东东,在网吧可以很轻松的做网吧增值广告了.当时是星期天,想着又没有事做,就答应了,虽知星期天的事情还真多,知道昨天晚上才搞定.
我们来看下这位朋友的要求:
1、网站址:http://www.v232.com/?username,后面的username最好能在配置文件中配置。
2、打开软件后搜索一次(搜索关键词最好从我那网站调用,因为我那网站的关键字是随机网络热门关键字地址是http://61.187.248.172:8099/listc.do这个地址最好也能配置,因为有时会变)
3、搜索一次后还必须要点击一次搜索到的结果,不然会视为无效搜索。
4、等上几秒(最好可以通过配置文件配置)再运行一次搜索即可,当然最好能让他运行三次,运行三次后还是等上几十分钟(最好能配置)再运行。
5、全后台运行,不能移动改变鼠标位置。
一条条分析下:
第一个要求,too easy ,编程花费时间可以忽略不计,用ini文件,用GetPrivateProfile**的api搞定
第二个要求:分两步,
(1)获取关键字,CInternetSession + CHttpFile后再截取字符串搞定(当然换了地址,代码换了就不行了)
(2)搜索一次,这个一次搜索,过程也是比较难搞的,大家可以打开那个http://www.v232.com/?username这个网站其实用搜狗的广告联盟,过程挺简单的,给搜索框填写关键字,然后单击搜索按钮,这里起初我想的也是很简单的,可是问题确实不断的出来,不过最终都解决了,要不我也不敢在这些这些字了,呵呵
这个过程要用本文的主角IHMLDocument2了,而且还用到了跨域访问,可以看那个网站,是把搜狗网站用iframe引进来了,两个document属于不同的域,访问就不便了,这个接口竟然也有这种限制.不过不用怕,网页都到自己的电脑上了,自己的电脑,难道还不听自己的话么.如果真是这样我宁愿把电脑砸了.
第三个要求:没什么了,第二个要求中的差不多,其实应该还简单的,因为搜索结果是一个框架的,而且又没有跨域的问题.不过唯一不好的就是搜索结果的链接默认是在新窗口打开的,这样的话,这个软件就不能够足够的隐藏了,因为会打开一个新的浏览器窗口了,解决办法,就是在当前打开了,所以更改链接的target属性.
第四个要求:用SetTimer搞定即可.
第五个要求:没什么了,隐藏对话框窗口.
先搞个图片:
下面就开始主演登场了
首先盗用别人的一张图来展示下网页的结构
[page]
我用的是VS2008中的activeX Microsoft web browser控件所以可以很容易获取到IHTMLDocument2对象
m_ie是与activeX Microsoft web browser控件关联的变量,CComPtr是Com了,可以实现智能指针
CComPtr<IHTMLDocument2> spHtmlDoc = NULL;
hr = spDispDoc->QueryInterface(IID_IHTMLDocument2,(void **)&spHtmlDoc);
if (FAILED(hr) || NULL == spHtmlDoc)
{
//return FALSE;
}
上面在分析中说了要第二个要求中,有框架的,还要跨域的,可以看上图,我们知道对已访问框架网页的IHtmlDocument2,我们需要要获取Frame对象,然后是Window对象,然后再次才是框架网页的IHTMLDocument2对象.
所以就按此顺序来了
pSate = _com_util::ConvertBSTRToString(state);
if(strcmp(pSate,"complete")==0) //检测下状态
{
}
else
{
return;
}
//开始解析网页
CComPtr<IHTMLWindow2> pHTMLWnd = NULL;
CComPtr<IHTMLDocument2> pChilDoc = NULL;
LPDISPATCH lpDispatch;
CComPtr<IHTMLFramesCollection2> pFrames=NULL;
spHtmlDoc->get_frames(&pFrames);
long frameCount = 0;
pFrames->get_length(&frameCount);
//获取第一个frame
VARIANT varindex,varresult;
varresult.vt=VT_DISPATCH;
varindex.vt = VT_I4;
varindex.lVal = 0;
hr = pFrames->item(&varindex, &varresult);
if (S_OK != hr)
{
return;
}
lpDispatch = (LPDISPATCH)varresult.ppdispVal;//为了跨域访问
if(SUCCEEDED(lpDispatch->QueryInterface(IID_IHTMLWindow2, (LPVOID *)&pHTMLWnd)))
{
pChilDoc = HtmlWindowToHtmlDocument(pHTMLWnd); //为了跨域访问,此函数突破跨域限制
if (pChilDoc==NULL)
{
return;
}
}
else
{
return;
}
做这些访问操作,需要多加判断,或者用try catch了.
主要是这个IDispatch,刚才我也弄得头晕了,后来就明白了,可以把这个看成对封装一大堆元素东西的结构,就像我们访问Document中的元素,一次得到都是封装的,这个IDsipach可以返回我们想要的,记得用get_all,get_frames,get_forms等返回的都是一块东西,我们没有办法直接访问,然后有一个专门拆包的人,我们说要个input元素,然后这个人就到包里找,找到了就给我们,找不到了,就不进行操作了.就像我高中学校里的传达室一样,每次都有很多邮件在一个箱子里,我没有权利去检查里面有没有我或者我同学的信,我每次都得问那老爷爷有没有"谁谁谁"的信,老爷爷在里面找半天,有了就给我,没了,也不理我,继续给其他同学找信.
传达室就像一个Document对象,而箱子就是一些元素的集合,老爷爷就是IDispatch了,信和邮件就是我们要的网页元素了,像a input table等.
上面的代码中有突破跨域的,其实也是很实用的,这个传达室没有我的邮件,我得到另一个找找看了,不能吊死到一棵树上呀
上面用到得HtmlWindowToHtmlDocument函数是从MSDN的forum上找到得,经过我稍微的改版,改成适合我当前工程的函数,下面贴出来了
{
ATLASSERT(spWindow != NULL);
CComQIPtr<IServiceProvider> spServiceProvider = spWindow;
if (spServiceProvider == NULL)
{
return CComPtr<IWebBrowser2>();
}
CComPtr<IWebBrowser2> spWebBrws;
HRESULT hRes = spServiceProvider->QueryService(IID_IWebBrowserApp, IID_IWebBrowser2, (void**)&spWebBrws);
if (hRes != S_OK)
{
return CComPtr<IWebBrowser2>();
}
return spWebBrws;
}
// Converts a IHTMLWindow2 object to a IHTMLDocument2. Returns NULL in case of failure.
// It takes into account accessing the DOM across frames loaded from different domains.
CComPtr<IHTMLDocument2> CTimerSerachDlg::HtmlWindowToHtmlDocument(CComPtr<IHTMLWindow2> spWindow)
{
ATLASSERT(spWindow != NULL);
CComPtr<IHTMLDocument2> spDocument;
HRESULT hRes = spWindow->get_document(&spDocument);
if ((S_OK == hRes) && (spDocument != NULL))
{
// The html document was properly retrieved.
return spDocument;
}
// hRes could be E_ACCESSDENIED that means a security restriction that
// prevents scripting across frames that loads documents from different internet domains.
CComPtr<IWebBrowser2> spBrws = HtmlWindowToHtmlWebBrowser(spWindow);
if (spBrws == NULL)
{
return CComPtr<IHTMLDocument2>();
}
// Get the document object from the IWebBrowser2 object.
CComPtr<IDispatch> spDisp; hRes = spBrws->get_Document(&spDisp);
spDocument = spDisp;
return spDocument;
}
英文注释都保留了,原理就是同window来获取Document对象了,我早说了,自己的电脑能不听自己的话么.即使不听,也得教训下,让它听话.
其实上面基本就是核心代码了,这里需要注意的就是VARIANT 和BSTR类型变量的使用,这个网上的资料还是很多的,我看网上的资料没看明白,看msdn明白了.
VARIANT 就是一个为了实现语言无关性,Com接口中的.用的时候,就是指定下当前的数据类型,然后往指定的数据类型的属性中赋值就可以了,是一个封装了大多数基本数据类型的类.
还有一个难点就是,到了搜索结果页,直接用IHTMLLinkElement2 和IHTMLLinkElement都得不到A的链接,无奈了,用IHTMElement2 IHTMElement 都可以了的,起初直接用IHTMLLinkElement2强制转换IHTMElement2 ,然后修改链接的target(IHTMLLinkElement2才有的)属性,不行,肯定不行了,向下类的继承也知道不行的.这折腾了我好长时间.就又到msdn看呀看,终于看了到了IHTMLElement 的SetAttribute、RemoveAttribute,顿感救星来了,果然如此,用SetAttribute("target","_self");当然里面的字符串要用BSTR了,到此就完成了,中间隔时间,我全部用SetTimer来完成,下面贴出OnTimer的代码(我调试的代码都在,有的代码是没有用的了)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
BSTR state = {0};
char *pSate=NULL;
if (nIDEvent==1)
{
GetKeyWord();
HRESULT hr = E_FAIL;
CComPtr<IDispatch> spDispDoc = m_ie.get_Document();
CComPtr<IHTMLDocument2> spHtmlDoc = NULL;
hr = spDispDoc->QueryInterface(IID_IHTMLDocument2,(void **)&spHtmlDoc);
if (FAILED(hr) || NULL == spHtmlDoc)
{
//return FALSE;
}
spHtmlDoc->get_readyState(&state);
pSate = _com_util::ConvertBSTRToString(state);
if(strcmp(pSate,"complete")==0) //检测下状态
{
}
else
{
return;
}
//开始解析网页
CComPtr<IHTMLWindow2> pHTMLWnd = NULL;
CComPtr<IHTMLDocument2> pChilDoc = NULL;
LPDISPATCH lpDispatch;
CComPtr<IHTMLFramesCollection2> pFrames=NULL;
spHtmlDoc->get_frames(&pFrames);
long frameCount = 0;
pFrames->get_length(&frameCount);
//获取第一个frame
VARIANT varindex,varresult;
varresult.vt=VT_DISPATCH;
varindex.vt = VT_I4;
varindex.lVal = 0;
hr = pFrames->item(&varindex, &varresult);
if (S_OK != hr)
{
return;
}
lpDispatch = (LPDISPATCH)varresult.ppdispVal;
if(SUCCEEDED(lpDispatch->QueryInterface(IID_IHTMLWindow2, (LPVOID *)&pHTMLWnd)))
{
pChilDoc = HtmlWindowToHtmlDocument(pHTMLWnd);
if (pChilDoc==NULL)
{
return;
}
}
else
{
return;
}
CComPtr<IHTMLElementCollection> pForms = NULL;
pChilDoc->get_forms(&pForms);
long formCount = 0;
pForms->get_length(&formCount);
CComPtr<IDispatch> formDis = NULL;
_variant_t index = 0;
hr = pForms->item(index,index,&formDis);
if (hr!=S_OK)
{
return;
}
//get the form interface
CComPtr<IHTMLFormElement> pFirstForm = NULL;
hr = formDis->QueryInterface(IID_IHTMLFormElement,(void **)&pFirstForm);
if (hr!=S_OK)
{
return;
}
formDis.Release();
BSTR formName = {0};
pFirstForm->get_name(&formName);
CComPtr<IDispatch> inputDis = NULL;
index = 4;
pFirstForm->item(index,index,&inputDis);
CComPtr<IHTMLInputTextElement> qElem = NULL;
inputDis->QueryInterface(IID_IHTMLInputTextElement,(void**)&qElem);
inputDis.Release();
BSTR tagName={0};
BSTR id={0};
qElem->get_name(&tagName);
BSTR value = {0};
qElem->put_value(value);
value=m_keyWord.AllocSysString();
qElem->put_value(value);
SysFreeString(value);
//成功后,就不用了
KillTimer(1);
SetTimer(2,5000,NULL);//开始单击按钮
}
if (nIDEvent==2)
{
HRESULT hr = E_FAIL;
CComPtr<IDispatch> spDispDoc = m_ie.get_Document();
CComPtr<IHTMLDocument2> spHtmlDoc = NULL;
hr = spDispDoc->QueryInterface(IID_IHTMLDocument2,(void **)&spHtmlDoc);
if (FAILED(hr) || NULL == spHtmlDoc)
{
//return FALSE;
}
spHtmlDoc->get_readyState(&state);
pSate = _com_util::ConvertBSTRToString(state);
if(strcmp(pSate,"complete")==0)
{
}
else
{
return;
}
//开始解析网页
CComPtr<IHTMLWindow2> pHTMLWnd = NULL;
CComPtr<IHTMLDocument2> pChilDoc = NULL;
LPDISPATCH lpDispatch;
CComPtr<IHTMLFramesCollection2> pFrames=NULL;
spHtmlDoc->get_frames(&pFrames);
long frameCount = 0;
pFrames->get_length(&frameCount);
//获取第一个frame
VARIANT varindex,varresult;
varresult.vt=VT_DISPATCH;
varindex.vt = VT_I4;
varindex.lVal = 0;
hr = pFrames->item(&varindex, &varresult);
if (S_OK != hr)
{
return;
}
lpDispatch = (LPDISPATCH)varresult.ppdispVal;
if(SUCCEEDED(lpDispatch->QueryInterface(IID_IHTMLWindow2, (LPVOID *)&pHTMLWnd)))
{
pChilDoc = HtmlWindowToHtmlDocument(pHTMLWnd);
if (pChilDoc==NULL)
{
return;
}
}
else
{
return;
}
CComPtr<IHTMLElementCollection> pForms = NULL;
pChilDoc->get_forms(&pForms);
long formCount = 0;
pForms->get_length(&formCount);
CComPtr<IDispatch> formDis = NULL;
_variant_t index = 0;
hr = pForms->item(index,index,&formDis);
if (hr!=S_OK)
{
return;
}
//get the form interface
CComPtr<IHTMLFormElement> pFirstForm = NULL;
hr = formDis->QueryInterface(IID_IHTMLFormElement,(void **)&pFirstForm);
if (hr!=S_OK)
{
return;
}
formDis.Release();
BSTR formName = {0};
pFirstForm->get_name(&formName);
CComPtr<IDispatch> inputDis = NULL;
index = 5;
pFirstForm->item(index,index,&inputDis);
CComPtr<IHTMLElement> qElem = NULL;
inputDis->QueryInterface(IID_IHTMLElement,(void**)&qElem);
inputDis.Release();
BSTR tagName={0};
BSTR id={0};
qElem->get_tagName(&tagName);
qElem->get_title(&tagName);
/*BSTR value = {0};
qElem->put_value(value);
value=m_keyWord.AllocSysString();
qElem->put_value(value);
SysFreeString(value); */
qElem->click();
//成功后,就不用了
KillTimer(2);
SetTimer(3,5000,NULL);//开始单击搜索结果
}
安徽新华电脑学校专业职业规划师为你提供更多帮助【在线咨询】