【实现一个网络爬虫】一、爬虫程序的扫描框架

应挚友之邀,给他写一个爬虫性质的程序,主要的功能是建立一个网站列表(主要是一些新闻网站),然后定期地去扫描这些主页面上包含的所有子页面,分析这些子页面上面是包含有设置过的关键字。

PS:几天前天始写这个程序,虽然有个大致的结构,可是写出来之后发现有太多地方不统一,功能重叠。于是想起来软件工程思想的伟大,决定从界面写起。而且有一个好的地方,就是我写的程序已经分好模块,因而即使换一个方向来写,之前的工作也没有白费,很不错。

这款软件考虑到给别人用的时候装环境的话比较麻烦,因此放弃使用Java,改用VS开发,下面介绍一下软件的大致流程。需要定义两个线程,管理线程AdminThread和爬虫线程。作用如下:

 

管理线程AdminThread

原则:管理线程由界面按钮启动,用来管理不同的爬虫线程。

在系统允许的最大线程个数,如50之内,创建爬虫线程CrawlerThread,并为每一个爬虫线程分配一个网站地址首页地址让它去爬,爬虫线程完成后会自动销毁,管理线程监测到子线程终止之后会再次为其分配一个url地址,除非所有的url都已经扫描完毕并且还没有到规定时间间隔。当再到设定的时间间隔后,再次执行这些流程。

注意一点,在管理线程执行的过程当中,界面如果有命令,比如说想让你停止扫描或暂停扫描,这时如果你还在等,那界面命令就响应不了了。所以等待下次设定的时候把时间分开来,每5秒检查一次,如果有新命令了就去执行。

Implementation:

HANDLE *pHandle = new HANDLE[50];

While(1)

{

  If(noMore)//没有新网址了,说明已经扫完一轮

  {

    If(getTickCount()-StareTime<扫描间隔时间) //时间没到,等待

      Sleep(5000);

    Else //到了,初始化,开始

      resetGetUrl(); //从头开始

      noMore = false;

      StartTime = getTickCount(); //重新记录开始时间

}else

  For(i=0->50)

  {

    If(pHandle[i]不为空)

      //跳过,看一下个线程

    Else //这个是空的,为它分配新的任务并启动

      getNextIndexUrl;

      If(noMoreUrl) noMore=true;

      pHandle[i].start(nextUrl);

   }

  CheckParameters();//检查界面是否发布了新命令,若有,在此处理

}

 

爬虫线程CrawlerThread

原则:由管理线程启动,一个爬虫只爬一个网站,这里只能是网站首页。

爬虫线程分析网站首页之后得到其所有子页面,并分别去分析其子页面是否包含关键字信息。

若有,爬虫线程直接使用窗口指针发出警告。

若无,终止线程,销毁所有变量。返回

Implementation

lParam通过参数,得到当前要扫描的网站主页

//用于控制的对象可以新建,而不用从父对象中传递

getSubsitesFromPage(); //从当前主页当中分析得到所有的子页面

getSubsiteFromLog(); //从日志文件当中得到当前网站所有扫描过的子页面

ResetLog(); //将站点日志当中的记录清空,以记录新日志

For(all: subSite from page)

{

writeSubsite(); //将该页标记为已经扫描

If(log当中不包含该子页面)

Bool b = analyseSubSite(); //对比子页面

If(b) new Messager.Warn(subsite);  //当前页面有敏感词,调用函数报警

}

ExitInstance() //销毁当前线程,正常返回

以上只是实现的伪伪代码(其实都算不上),具体怎样用程序代码实现,你等会儿,我现在就去写...

 

  1 //爬虫线程
  2 DWORD WINAPI CrawlerThread(LPVOID  lParam)
  3 {
  4     CString indexUrl = (LPCTSTR)lParam;
  5     TRACE("爬虫收到地址url:%s\r\n", indexUrl);
  6     CCrawler *pCrawler = new CCrawler();
  7     CSitesKeys *pSiteKey = new CSitesKeys();
  8     CString subsitesFromPage = pCrawler->GetSubsitesFromPage(pCrawler->GetPageStr(indexUrl));
  9     CString subsitesFromLog = pSiteKey->GetSubsitesFromLog(indexUrl);
 10     pSiteKey->ResetIndexLog(indexUrl);
 11     CString keywords = pSiteKey->GetKeywrods();
 12 
 13     int posa=-1, posb=-1;
 14     while((posa=subsitesFromPage.Find(' ', posb+1)) !=-1)
 15     {
 16         CString site = subsitesFromPage.Mid(posb+1, posa - posb -1).Trim();
 17         pSiteKey->Log(indexUrl, site);
 18         if (subsitesFromLog.Find(site)!=-1)
 19         {
 20             CString subsiteUrl = pCrawler->GenerateUrl(indexUrl, site);
 21             CString keyword = pCrawler->CheckContent(pCrawler->PurifyContent(pCrawler->GetPageStr(subsiteUrl)), keywords);
 22             if (keyword.GetLength() > 0)
 23             {
 24                 CMsg *pMsg = new CMsg();
 25                 pMsg->Warn(subsiteUrl, keyword);
 26             }
 27         }
 28         posb = posa;
 29     }
 30     return 0;
 31 }
 32 
 33 
 34 //管理线程
 35 DWORD WINAPI AdminThread(LPVOID lParam)
 36 {
 37     CMyCrawlerDlg *pDlg = (CMyCrawlerDlg*)lParam;
 38     CSitesKeys *pSiteKey = new CSitesKeys();
 39     BOOL noMore = true;
 40     int realThdCount = pDlg->nThdNum; //实际的线程数量,防止在线程运行期间,用户改变了线程数量,造成运行出错
 41     HANDLE *pHandle = new HANDLE[pDlg->nThdNum];
 42     DWORD mStartTime = 0;
 43     
 44     while(true)
 45     {
 46         switch(pDlg->nMsgFlag) //处理界面消息
 47         {
 48         case FLAG_EXIT:
 49             for (int i=0; i<realThdCount; i++)
 50                 TerminateThread(pHandle[i], NULL); //结束所子管理的子线程
 51             delete pHandle;
 52             ExitThread(0); //结束本线程
 53             break;
 54         case FLAG_STOP:
 55             for (int i=0; i<realThdCount; i++)
 56                 TerminateThread(pHandle[i], NULL); //结束所子管理的子线程
 57             delete pHandle;
 58             pDlg->nMsgFlag = FLAG_NOMSG; 
 59             break;
 60         case FLAG_SUSPEND:
 61             for (int i=0; i<realThdCount; i++)
 62                 SuspendThread(pHandle[i]); 
 63             pDlg->nMsgFlag = FLAG_NOMSG; 
 64             break;
 65         case FLAG_RESUME:
 66             for (int i=0; i<realThdCount; i++)
 67                 ResumeThread(pHandle[i]); 
 68             pDlg->nMsgFlag = FLAG_NOMSG; 
 69             break;
 70         case FLAG_NOMSG:
 71             break;
 72         } 
 73         
 74 
 75         if (noMore)
 76         {
 77             if (GetCurrentTime()-mStartTime < pDlg->intervMinute*60*1000) 
 78             {
 79                 Sleep(2000);   //如果还没到时间,等2秒钟
 80             }
 81             else{
 82                 pSiteKey->ResetUrlGeneration();
 83                 noMore = FALSE;
 84                 mStartTime = GetCurrentTime();    
 85                 if(pHandle != NULL) delete pHandle;
 86                 pHandle = new HANDLE[pDlg->nThdNum];
 87                 realThdCount = pDlg->nThdNum;
 88             }
 89         }else
 90             for (int i=0; i<realThdCount; i++)
 91             {
 92                 DWORD hExitCode;
 93                 GetExitCodeThread(pHandle[i], &hExitCode);
 94                 if (hExitCode != STILL_ACTIVE) //线程未启动
 95                 {    
 96                     CString  indexUrl = pSiteKey->NextIndexUrl(); 
 97                     if(indexUrl.GetLength() == 0) 
 98                     {
 99                         noMore = TRUE; 
100                         break;
101                     }else
102                     {
103                         DWORD thdID;
104                         pHandle[i] = CreateThread(NULL, NULL, CrawlerThread, indexUrl.GetBuffer(indexUrl.GetLength()), 0, &thdID); //运行子线程,启动
105                     }
106                     
107                 }
108             }
109     }
110 }
posted @ 2012-10-27 14:03  byan  阅读(1817)  评论(0编辑  收藏  举报