程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> VC >> 關於VC++ >> 采集網頁選定部分全攻略

采集網頁選定部分全攻略

編輯:關於VC++

在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編程,注冊表操作。

1,用ATL COM模板創建一個工程

2,加入一個接口testa

3,加入Urlmon.Lib 和 #include <urlmon.h>

4,加入接口函數GetHtmlText()

實現如下:

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:[email protected]歡迎來信討論。

本文配套源碼

  1. 上一頁:
  2. 下一頁:
欄目導航
Copyright © 程式師世界 All Rights Reserved