采集网页选定部分全攻略
2006-07-22 22:01:52 来源:WEB开发网 闂傚倸鍊搁崐鎼佸磹閹间礁纾归柟闂寸绾惧綊鏌熼梻瀵割槮缁炬儳缍婇弻鐔兼⒒鐎靛壊妲紒鐐劤缂嶅﹪寮婚悢鍏尖拻閻庨潧澹婂Σ顔剧磼閻愵剙鍔ょ紓宥咃躬瀵鎮㈤崗灏栨嫽闁诲酣娼ф竟濠偽i鍓х<闁绘劦鍓欓崝銈囩磽瀹ュ拑韬€殿喖顭烽幃銏ゅ礂鐏忔牗瀚介梺璇查叄濞佳勭珶婵犲伣锝夘敊閸撗咃紲闂佺粯鍔﹂崜娆撳礉閵堝洨纾界€广儱鎷戦煬顒傗偓娈垮枛椤兘骞冮姀銈呯閻忓繑鐗楃€氫粙姊虹拠鏌ュ弰婵炰匠鍕彾濠电姴浼i敐澶樻晩闁告挆鍜冪床闂備浇顕栭崹搴ㄥ礃閿濆棗鐦遍梻鍌欒兌椤㈠﹤鈻嶉弴銏犵闁搞儺鍓欓悘鎶芥煛閸愩劎澧曠紒鈧崘鈹夸簻闊洤娴烽ˇ锕€霉濠婂牏鐣洪柡灞诲妼閳规垿宕卞▎蹇撴瘓缂傚倷闄嶉崝搴e垝椤栫偛桅闁告洦鍨扮粻鎶芥倵閿濆簼绨藉ù鐘荤畺濮婃椽妫冨☉娆愭倷闁诲孩鐭崡鎶芥偘椤曗偓瀹曞爼顢楁径瀣珫婵犳鍣徊鍓р偓绗涘洤绠查柛銉墮閽冪喖鏌i弬鎸庢喐闁荤喎缍婇弻娑⑩€﹂幋婵囩亪濡炪値鍓欓悧鍡涒€旈崘顔嘉ч幖绮光偓鑼嚬缂傚倷绶¢崰妤呭箰閹间焦鍋╅柣鎴f绾偓闂佺粯鍔曠粔闈浳涢崘顔兼槬闁逞屽墯閵囧嫰骞掗幋婵愪紑閻庤鎸风粈渚€鍩為幋锔藉亹闁圭粯甯╂导鈧紓浣瑰劤瑜扮偟鍒掑▎鎾宠摕婵炴垶鐭▽顏堟煙鐟欏嫬濮囨い銉︾箞濮婃椽鏌呴悙鑼跺濠⒀傚嵆閺岀喖鎼归锝呯3闂佹寧绻勯崑娑㈠煘閹寸姭鍋撻敐搴樺亾椤撴稒娅婇柡灞界У濞碱亪骞忕仦钘夊腐闂備焦鐪归崐鏇㈠箠閹邦喗顫曢柟鎯х摠婵挳鏌涢幘鏉戠祷闁告挸宕—鍐Χ閸℃浠搁梺鑽ゅ暱閺呮盯鎮鹃悜钘壩ㄧ憸澶愬磻閹剧粯鏅查幖绮瑰墲閻忓秹姊虹紒妯诲鞍婵炲弶锕㈡俊鐢稿礋椤栨氨鐤€闂傚倸鐗婄粙鎰姳閼测晝纾藉ù锝堟閻撴劖鎱ㄥΟ绋垮婵″弶鍔欓獮妯兼嫚閼碱剦妲伴梻浣稿暱閹碱偊宕愭繝姣稿洭寮舵惔鎾存杸濡炪倖姊婚妴瀣啅閵夛负浜滄い鎾跺仜濡插鏌i敐鍥у幋妤犵偞甯¢獮瀣籍閳ь剟鎮楁繝姘拺閻熸瑥瀚崕妤呮煕濡 鍋撻悢鎻掑緧婵犵數濮烽弫鍛婃叏閻戣棄鏋侀柛娑橈攻閸欏繑銇勯幘鍗炵仼缁炬儳顭烽弻鐔煎礈瑜忕敮娑㈡煃闁垮鐏﹂柕鍥у楠炴帡宕卞鎯ь棜缂傚倸鍊风粈渚€藝闁秴鏋佸┑鐘虫皑瀹撲線鏌涢埄鍐姇闁稿﹦鍏橀弻娑樷攽閸℃浼€濡炪倖姊归崝鏇㈠煘閹达附鍊婚柛銉㈡櫇鏍¢梻浣告啞閹稿鎮烽敂鐣屸攳濠电姴娲﹂崵鍐煃閸濆嫬鏆熼柨娑欑矒濮婇缚銇愰幒鎴滃枈闂佸憡鐟ユ鎼佸煝閹炬枼鍫柛顐ゅ枔閸樻悂鏌h箛鏇炰户缁绢厼鐖煎畷鎴﹀箻鐠囪尙鐤€婵炶揪绲介幉锟犲磹椤栫偞鈷戠痪顓炴噹娴滃綊鎮跺☉鏍у姦闁糕斁鍋撳銈嗗笒閸燁偊鎯冨ú顏呯厸濞达絽婀辨晶顏堟煃鐟欏嫬鐏撮柟顔界懇瀵爼骞嬮悩杈敇闂傚倷绀佸﹢杈ㄧ仚闂佺濮ょ划搴ㄥ礆閹烘绫嶉柛顐ゅ枎娴犺櫣绱撴担鍓插創妞ゆ洘濞婇弫鍐磼濞戞艾骞堥梻浣告惈濞层垽宕濆畝鍕€堕柣妯肩帛閻撴洟鏌熼懜顒€濡煎ù婊勫劤閳规垿鏁嶉崟顐℃澀闂佺ǹ锕ラ悧鐘茬暦濠靛鏅濋柍褜鍓熼垾锕傚锤濡も偓閻掑灚銇勯幒宥堝厡缂佺姴澧介埀顒€鍘滈崑鎾斥攽閻樿京绐旈柛瀣殔閳规垿顢欑涵鐑界反濠电偛鎷戠徊鍨i幇鏉跨闁瑰啿纾崰鎾诲箯閻樼粯鍤戦柤绋跨仛濮f劙姊婚崒姘偓鐑芥嚄閼哥數浠氭繝鐢靛仜椤曨參宕楀Ο渚殨妞ゆ劑鍊栫€氭氨鈧懓澹婇崰鏍р枔閵婏妇绡€闁汇垽娼ф牎缂佺偓婢樼粔鐟邦嚕閺屻儱绠甸柟鐑樼箘閸炵敻鏌i悩鐑橆仩閻忓繈鍔岄蹇涘Ψ瑜夐崑鎾舵喆閸曨剙纰嶅┑鈽嗗亝缁诲倿锝炶箛娑欐優闁革富鍘鹃敍婊冣攽閳藉棗鐏犻柟纰卞亰閿濈偛顓奸崶鈺冿紳婵炶揪缍侀ˉ鎾诲礉瀹ュ鐓欑紒瀣仢閺嗛亶鏌i敐鍥у幋妤犵偛顑夐弫鍐焵椤掑倻涓嶅┑鐘崇閸嬶綁鏌涢妷鎴濆暟妤犲洭鎮楃憴鍕碍缂佸鎸抽垾鏃堝礃椤斿槈褔鏌涢埄鍏狀亪妫勫鍥╃=濞达絽澹婇崕鎰版煕閵娿儱顣崇紒顔碱儏椤撳吋寰勭€n亖鍋撻柨瀣ㄤ簻闁瑰搫绉堕ˇ锔锯偓娈垮枛閻忔繈鍩為幋锕€鐓¢柛鈩冾殘娴狀垶姊洪崨濠庣劶闁告洦鍙庡ú鍛婁繆閵堝繒鍒伴柛鐕佸灦瀹曟劙宕归锝呭伎濠碘槅鍨抽崢褎绂嶆ィ鍐╁€垫慨妯煎亾鐎氾拷

本文示例源代码或素材下载
在 VCKBASE 混了这么久竟然没有写出一篇文章,想想很是惭愧,每当在这里看到一篇好文,这种感觉尤甚,总结我在程序员加油站中的一些技术点写了这个文章(虽然程序员加油站还要继续开发,但是由于时间关系不知道什么时候能完成),如果有时间我还会写一些文章的,我的写作水平可能很差,希望读者能够包涵。
程序原理:
一、在IE菜单中加入菜单项
在注册表HKEY_CURRENT_USER\Software\Microsoft\Internet Explorer\MenuExt项下建立一个新项,项的名称即为出现在菜单中的标题
将新建项的默认值设定为一个URL地址,当用户点击菜单项后,IE就会调用URL指向的页面中的脚本。
二、如何控制菜单项在合适的时候显示
下面再介绍一下上面注册项中Contexts项的作用,通过该项可以制定菜单项在右键点击IE中的什么对象时出现,它可以为以下值的“或”组合
对象 | 值 |
缺省 | 0x1 |
图片 | 0x2 |
控件 | 0x4 |
表单域 | 0x8 |
选择文本 | 0x10 |
锚点 | 0x20 |
超链接 | 0x22 |
例如上面我们希望菜单项在用户点击图片或者超链接时出现,那么我们就将值设置为
dword:00000022
既在点击图片 或者 锚点时出现菜单。一个锚点是页面中描述一个超链接的对象。如果不设置Contexts 项,则菜单项会在点击任何对象时出现在右键菜单中。
注:
一二部分我引用了《如何在IE右键菜单中添加菜单项以及如何添加IE任务栏按钮》这篇文章的部分内容,详细内容请看:
http://www.csdn.net/develop/read_article.asp?id=3621
三、编辑点击菜单项执行的script脚本
这个脚本的文件名和1中的链接文件一致这个是我用的脚本:
<script language="VBScript">
Sub OnContextMenu()
set nc=CreateObject("Test2.testa.1")
nc.GetHtmlText()
end sub
Call OnContextMenu()
</script>
//看到网海拾贝中这样用
<script language="VBScript">
Sub OnContextMenu()
NCWEBPAGE=1
NCSELWEBPAGE=2
NCSELTEXT=3
NCALLTEXT=4
NCIMAGE=5
NCALLIMAGE=6
NCALLLINK=7
NCALLLINKTITLE=8
NCSELSOURCECODE=9
NCSOURCECODE=10
On Error Resume Next
set nc=CreateObject("NcActive.NcCollect")
if err<>0 then
MsgBox("网海拾贝没有正确安装")
else
//但是这个参数至今不知道如何在控件中得到
call Nc.Gethtmldoc(NcSelText,external.menuArguments.document)
end if
end sub
Call OnContextMenu()
</script>
四、在注册表中加入
HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall
\\程序员加油站加入ArticlePath - 文档存盘路径(string),ArticleNumber -文档序号(dword)。
五、写一个ATL的DLL,就是上面脚本中调用的那个对象,提供一个接口GetHtmlText(),看到有些其他程序使用external.menuArguments.document做为参数,可是我没试验成功,无法直接获得其中的document所以只能用笨方法了,取得当前窗口,然后取得IE子窗口的句柄,然后取得document指针,取得选取的内容,然后保存网页,并下载图片。
下面就介绍一下ATL组件的制作,主要技术包括ATL编程,IE编程,注册表操作。
实现如下:
STDMETHODIMP Ctesta::GetHtmlText()
{
//保存网页内容的目录
char chFilePath[MAX_PATH];
DWORD Number = 0;
CRegistry reg;
reg.Open(HKEY_LOCAL_MACHINE,
"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\程序员加油站\\");
BOOL ret = reg.ReadDWORD("ArticleNumber",&Number);
if(!ret)
return S_FALSE;
//读取保存网页文件的目录
ret = reg.ReadString("ArticlePath",chFilePath);
if(!ret)
return S_FALSE;
//取得当前活动窗口的窗口句柄
HWND hWnd = GetActiveWindow();
CoInitialize( NULL );
//显式装载 MSAA 判断是否被安装
HINSTANCE hInst = ::LoadLibrary( _T("OLEACC.DLL") );
if ( hInst != NULL )
{
if ( hWnd != NULL )
{
HWND hWndChild=NULL;
// 取得当前窗口的IE子窗口指针
::EnumChildWindows( hWnd, EnumChildProc, (LPARAM)&hWndChild );
if ( hWndChild )
{
//定义IE文档
CComPtr pHTMLDoc;
LRESULT lRes;
//由于WM_HTML_GETOBJECT非Windows标准消息,所以需要RegisterWindowMessage
UINT nMsg = ::RegisterWindowMessage( _T("WM_HTML_GETOBJECT") );
::SendMessageTimeout( hWndChild, nMsg, 0L, 0L, SMTO_ABORTIFHUNG, 1000, (DWORD*)&lRes );
LPFNOBJECTFROMLRESULT pfObjectFromLresult = (LPFNOBJECTFROMLRESULT)::GetProcAddress( hInst, _T("ObjectFromLresult") );
if ( pfObjectFromLresult != NULL )
{
HRESULT hr;
//获取网页的IHTMLDocument2接口
hr = (*pfObjectFromLresult)( lRes, IID_IHTMLDocument, 0, (void**)&pHTMLDoc );
if ( SUCCEEDED(hr) )
{
CComPtr pSelObj;
CComPtr pTxtRange;
//根据IHTMLDocument2指针取得IHTMLSelectionObject接口指针
pHTMLDoc->get_selection(&pSelObj);
//再获得IHTMLTxtRange指针
hr = pSelObj->createRange((IDispatch**)&pTxtRange);
if(!CheckResult(hr,"pTxtRange"))
return hr;
//选择所有被选择的内容
pTxtRange->select();
BSTR bstrTxt,bstrTxt1;
char strPath[MAX_PATH];
char *strTxt = NULL;
char*strTxt1 = NULL;
//取得主站域名
CComPtr pLocation;
pHTMLDoc->get_location(&pLocation);
pLocation->get_hostname(&bstrTxt1);
strTxt1 = _com_util::ConvertBSTRToString(bstrTxt1);
sprintf(strPath,"http://%s",strTxt1);
SaveCharToFile(strTxt1,debugpath);
//取得选中的内容
pTxtRange->get_htmlText(&bstrTxt);
strTxt = _com_util::ConvertBSTRToString(bstrTxt);
//下载内容中的图片资源,并修改相应链接
std::string webpage;
char chSavePath[MAX_PATH];
sprintf(chSavePath,"%s\\T%ld.htm",chFilePath,Number);
CreateAllDirectory(chSavePath);
//取得所有图片资源并保存网页
if(CheckData(strTxt,strPath,chFilePath,Number,webpage) == FALSE)
SaveCharToFile(strTxt,chSavePath,TRUE);
else
SaveCharToFile(webpage.c_str(),chSavePath,TRUE);
BOOL ret = reg.WriteDWORD("ArticleNumber",++Number);
//释放内存
if(strTxt1)
delete[] strTxt1;
if(strTxt)
delete[] strTxt;
SysFreeString(bstrTxt1); // 用完释放
SysFreeString(bstrTxt); // 用完释放
}
}
}
} // else Internet Explorer is not running
::FreeLibrary( hInst );
} // else Active Accessibility is not installed
CoUninitialize();
return S_OK;
}
在取得选取网页内容之后,需要对HTML内容进行重新编辑以获得正确的显示,CheckData函数便是做这个工作的这个函数对HTML内容中图像信息的地址进行编辑,如果图像保存在本地则使用本地图像,如果图像没有下载保持原样。
BOOL CheckData(const LPCTSTR data,
const LPCTSTR host,
const LPCTSTR path,
DWORD Number,
std::string &outstring)
{
char chImgPath[1024];//图像路径 !:由于有些图像路径可能会很长所以申请内存多一点
char chImgSrc[MAX_PATH];//图像地址
char chDownLoadPath[MAX_PATH];//下载图像文件路径
char chWriteImgSrc[MAX_PATH];//图像文件路径
memset(chImgPath,0,1024);
memset(chImgSrc,0,MAX_PATH);
memset(chDownLoadPath,0,MAX_PATH);
memset(chWriteImgSrc,0,MAX_PATH);
char dirname[20];//根目录名
ltoa(Number,dirname,10);
try{
//查找是否有<img标记
const char *p1 = FindString(data,"<img");
const char *p2 = FindString(p1,"src=\"");
const char *p3 = FindString(p2+5,"\"");
const char *p4 = FindString(p1,">");
if(p1 == NULL||p2 == NULL||p3 == NULL||p4 == NULL)
return FALSE;
//找到一个图像文件标记
//拷贝图像链接之前的文字
int n = p4-p1+1;
strncpy(chImgPath,p1,n);
outstring.append(data,p2-data+5);
//提取图像链接
n = p3-p2-5;
strncpy(chImgSrc,p2+5,n);
if(FindString(chImgSrc,"http://") == NULL){
if(FindString(chImgSrc,".."))
strcpy(chImgSrc,&chImgSrc[2]);
sprintf(chDownLoadPath,"%s%s",host,chImgSrc);
sprintf(chWriteImgSrc,"%s//%s%s",path,dirname,chImgSrc);
}else{
strcpy(chDownLoadPath,chImgSrc);
const char *p5 = FindString(chImgSrc+7,"/");
sprintf(chWriteImgSrc,"%s\\%s%s",path,dirname,&chImgSrc[p5-chImgSrc]);
}
char Output[MAX_PATH];
sprintf(Output,"图像地址:%s\r\n存盘地址:%s\r\n主机地址:%s\r\n",chImgSrc,chWriteImgSrc,host);
SaveCharToFile(Output,debugpath);
n = strlen(chWriteImgSrc);
for(int i=0;i<n;i++){
if(chWriteImgSrc[i] == ''''/'''')
chWriteImgSrc[i] = ''''\\'''';
}
//在下载之前先建立保存图像文件的路径
CreateAllDirectory(chWriteImgSrc);
//下载图像文件
HRESULT hr = URLDownloadToFile( NULL, chDownLoadPath, chWriteImgSrc, 0, NULL);
if( SUCCEEDED(hr))//
{
const char *p6 = FindString(chDownLoadPath+7,"/");
sprintf(chWriteImgSrc,"%s%s",dirname,&chDownLoadPath[p6-chDownLoadPath]);
//将存盘的路径保存进字符串
}else{
strcpy(chWriteImgSrc,chDownLoadPath);
//没有下载成功将原始路径保存进
}
outstring.append(chWriteImgSrc);
outstring.append(p3,p4-p3+1);
BOOL ret = CheckData(p4+1,host,path,Number,outstring);
if(!ret){
outstring.append(p4+1);
}
}catch(...){
return FALSE;
}
return TRUE;
}
其他几个辅助函数这里简单介绍,详细内容请参看源代码
函数:void SaveCharToFile
功能:将字符串保存为给定文件名
const LPCTSTR data,//[IN] 给定待保存的数据
const LPCTSTR saveFileName,//[IN]给定文件名
BOOL flag = FALSE//[IN]追加写入还是覆盖标志
函数:const char *FindString
功能:在给定字符串中查找给定字符串
const LPCTSTR source,//[IN] 给定源字符串的数据
const LPCTSTR key)//[IN]待查字符串
函数:void CreateAllDirectory
功能:创建给定路径的所有未建目录
const char* AllPath)//[IN] 需要创建的详细目录
例如 输入为:"d:\a\b\c\d"则在D盘下创建相应a,b,c,d相应目录
好了,到此为止,我们已经做成一个取得网页选取内容的并保存在本地的程序,你可以举一反三,将它应用到更有趣更有用的领域。
这个程序是我研究了一周才写完的,其中取得domument的方法我觉得很笨,可我现在没有找到好办法,希望能够抛砖引玉,有更好的方法别忘了告诉我,我的Email:lgtest@sina.com欢迎来信讨论。
赞助商链接