【实现一个网络爬虫】一、爬虫程序的扫描框架
应挚友之邀,给他写一个爬虫性质的程序,主要的功能是建立一个网站列表(主要是一些新闻网站),然后定期地去扫描这些主页面上包含的所有子页面,分析这些子页面上面是包含有设置过的关键字。
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 }