?? 如何用c#語(yǔ)言構(gòu)造網(wǎng)絡(luò)蜘蛛程序1.htm
字號(hào):
<html>
<head>
<title>如何用C#語(yǔ)言構(gòu)造網(wǎng)絡(luò)蜘蛛程序|搜索引擎,網(wǎng)絡(luò)蜘蛛,程序,算法,C#-中國(guó)源碼網(wǎng)</title>
<meta http-equiv="Content-Type" content="text/html; charset=gb2312">
<meta http-equiv="Refresh" content=600>
<meta name="keywords" content="搜索引擎,網(wǎng)絡(luò)蜘蛛,程序,算法,C#">
<meta name="description" content="如何用C#語(yǔ)言構(gòu)造網(wǎng)絡(luò)蜘蛛程序">
<link rel="stylesheet" href="/templates/yobo/style/default/style.css" type="text/css" media="all" />
</head><body oncontextmenu='return false' ondragstart='return false' onselectstart ='return false' onselect='document.selection.empty()' oncopy='document.selection.empty()' onbeforecopy='return false'>
<a name="top"></a>
<!--頁(yè)面頭部信息-->
<div id="header">
<div id="topmenu">
<div class="left">
<div id="topdate"><a href="http://www.yuanma.org/rss.php" target="_blank" title="中國(guó)源碼 RSS 訂閱 "><img src="/images/rss.gif" border="0"></a>=>中國(guó)源碼:全球著名開源項(xiàng)目大本營(yíng)</div>
</div>
<div class="right">
<div id="topnavlist">
<ul>
<li><a href="/member.php?action=reg" title="注冊(cè)會(huì)員">注冊(cè)會(huì)員</a></li>
<li><a href="/member.php?action=login" title="會(huì)員登錄">會(huì)員登錄</a></li>
<li><a href="/member.php?action=manage" title="控制面板">控制面板</a></li>
<li><a href="#" onClick="this.style.behavior= 'url(#default#homepage)';this.setHomePage ('http://www.yuanma.org');">設(shè)為首頁(yè)</a></li>
<li><a href="#" onClick="window.external.addFavorite('http://www.yuanma.org','中國(guó)源碼-全球著名開放源代碼大本營(yíng)')">加入收藏</a></li>
<li><a href="http://cgi.alexa.com/client/mail_this_site/cgi-bin/mail_this_site.cgi?url=yuanma.org" title="推薦本站" target=_blank>推薦本站</a></li>
</ul>
</div>
</div>
</div>
<div id="maintop">
<div id="LogoSide1">
<a href="/blog/"><img src="/images/blog.gif" alt="博客" /></a>
<a href="/download/"><img src="/images/download.gif" alt="下載" /></a><br />
博客 下載
</div>
<div id="Logo"><a href="/"><img src="/images/logo.gif" alt="中國(guó)源碼 首頁(yè)" /></a></div>
<div id="LogoSide2">
<a href="/bbs/"><img src="/images/forum.gif" alt="論壇" /></a>
<a href="/lilina/" target=_blank><img src="/images/rss_reader.gif" alt="最新開源項(xiàng)目" /></a><br />
論壇 開源項(xiàng)目
</div>
</div>
<div class="navline"> </div>
<div class="navup">
<div id="nav-up-left"><a href="/" class="mainmenu">首頁(yè)</a> ·<a href='/data/osupdate/' target='_self' title='開源社區(qū)最新動(dòng)態(tài)、新聞' class="mainmenu">開源動(dòng)態(tài)</a>
·<a href='/data/osprojects/' target='_self' title='世界知名開源社區(qū)著名開源項(xiàng)目、軟件介紹和使用文章' class="mainmenu">應(yīng)用軟件</a>
·<a href='/data/osprojectsos/' target='_self' title='開放源代碼操作系統(tǒng) Linux FreeBSD OpenBSD..' class="mainmenu">開源操作系統(tǒng)</a>
·<a href='/data/osprojectssd/' target='_self' title='介紹程序設(shè)計(jì)方法、事例、教程
software development' class="mainmenu">程序設(shè)計(jì)</a>
·<a href='/data/osarticles/' target='_self' title='開放源代碼社區(qū)文章、評(píng)論' class="mainmenu">開源文章</a>
·<a href='/data/yuanmaprotocol/' target='_self' title='介紹網(wǎng)絡(luò)協(xié)議、RFC及安全' class="mainmenu">網(wǎng)絡(luò)協(xié)議與安全</a>
·<a href="/data/special/" class="mainmenu">專題</a>·<a href="/mypage.php?action=picture" class="mainmenu">圖片文章</a>·<a href="/search.php" title="搜索" class="mainmenu">搜索</a>· <a href="/contribute.php" title="投稿" class="mainmenu">投稿</a> </div>
</div>
<div class="navline"> </div>
</div>
<SCRIPT language=javascript type=text/javascript>
var currentpos,timer;
function initialize()
{
timer=setInterval("scrollwindow()",2);
}
function sc(){
clearInterval(timer);
}
function scrollwindow()
{
currentpos=document.body.scrollTop;
window.scroll(0,++currentpos);
if (currentpos != document.body.scrollTop)
sc();
}
document.onmousedown=sc
document.ondblclick=initialize
</SCRIPT>
<script language="JavaScript">
function fontZoom(size)
{
document.getElementById('fontzoom').style.fontSize=size+'px'
}
function doCheck(){
// 檢測(cè)表單的有效性
// 如:標(biāo)題不能為空,內(nèi)容不能為空,等等....
if (myform.username.value=="") {
alert("請(qǐng)輸入姓名!");
myform.username.focus();
return false;
}
if (myform.content.value=="") {
alert("請(qǐng)輸入評(píng)論內(nèi)容!");
myform.content.focus();
return false;
}
if (myform.checkcode.value=="") {
alert("請(qǐng)輸入驗(yàn)證碼!");
myform.checkcode.focus();
return false;
}
return true;
}
</script>
<div class="mainline"> </div>
<div class="maincolumn">
<div class="navadimg">
<script type="text/javascript"><!--
google_ad_client = "pub-4357927283882197";
google_ad_width = 728;
google_ad_height = 90;
google_ad_format = "728x90_as";
google_ad_type = "image";
google_ad_channel = "";
//--></script>
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script>
</div>
</div>
<div class="mainline"> </div>
<div class="maincolumn">
<div id="flist">
<div class="ftitleico"> </div>
<div class="ftitlebg">
<div class="ftitlename">當(dāng)前位置: <a href='/' class='position'>首頁(yè)</a> >> <a href='/data/osprojectssd/' class='position'>程序設(shè)計(jì)</a> >> 如何用C#語(yǔ)言構(gòu)造網(wǎng)絡(luò)蜘蛛程序</div>
</div>
<div class="clear"> </div>
<div id="newsview">
<div class="title">
<h1>如何用C#語(yǔ)言構(gòu)造網(wǎng)絡(luò)蜘蛛程序</h1>
<h2><strong>作者:</strong><a href="/member.php?action=show&username=webmaster" target="_blank"></a> <strong>來(lái)源:</strong><a href="http://blog.csdn.net/smildlzj/archive/2006/09/21/1260758.aspx" target="_blank">zz</a> <strong>發(fā)表時(shí)間:</strong>2006-09-21
<strong>瀏覽次數(shù):</strong>
<script language=Javascript src="/view.php?articleid=1570"></script>
<strong>字號(hào):</strong><a href="javascript:fontZoom(16)" class=black>大</a> <a href="javascript:fontZoom(14)" class=black>中</a> <a href="javascript:fontZoom(12)" class=black>小</a></h2>
</div>
<div id="fontzoom" class="content">
<p>如何用C#語(yǔ)言構(gòu)造蜘蛛程序 <BR> "蜘蛛"(Spider)是Internet上一種很有用的程序,搜索引擎利用蜘蛛程序?qū)eb頁(yè)面收集到數(shù)據(jù)庫(kù),企業(yè)利用蜘蛛程序監(jiān)視競(jìng)爭(zhēng)對(duì)手的網(wǎng)站并跟蹤變動(dòng),個(gè)人用戶用蜘蛛程序下載Web頁(yè)面以便脫機(jī)使用,開發(fā)者利用蜘蛛程序掃描自己的Web檢查無(wú)效的鏈接……對(duì)于不同的用戶,蜘蛛程序有不同的用途。那么,蜘蛛程序到底是怎樣工作的呢? <BR><BR> 蜘蛛是一種半自動(dòng)的程序,就象現(xiàn)實(shí)當(dāng)中的蜘蛛在它的Web(蜘蛛網(wǎng))上旅行一樣,蜘蛛程序也按照類似的方式在Web鏈接織成的網(wǎng)上旅行。蜘蛛程序之所以是半自動(dòng)的,是因?yàn)樗偸切枰粋€(gè)初始鏈接(出發(fā)點(diǎn)),但此后的運(yùn)行情況就要由它自己決定了,蜘蛛程序會(huì)掃描起始頁(yè)面包含的鏈接,然后訪問這些鏈接指向的頁(yè)面,再分析和追蹤那些頁(yè)面包含的鏈接。從理論上看,最終蜘蛛程序會(huì)訪問到Internet上的每一個(gè)頁(yè)面,因?yàn)镮nternet上幾乎每一個(gè)頁(yè)面總是被其他或多或少的頁(yè)面引用。 <BR><BR> 本文介紹如何用C#語(yǔ)言構(gòu)造一個(gè)蜘蛛程序,它能夠把整個(gè)網(wǎng)站的內(nèi)容下載到某個(gè)指定的目錄,程序的運(yùn)行界面如圖一。你可以方便地利用本文提供的幾個(gè)核心類構(gòu)造出自己的蜘蛛程序。 <BR> C#特別適合于構(gòu)造蜘蛛程序,這是因?yàn)樗呀?jīng)內(nèi)置了HTTP訪問和多線程的能力,而這兩種能力對(duì)于蜘蛛程序來(lái)說(shuō)都是非常關(guān)鍵的。下面是構(gòu)造一個(gè)蜘蛛程序要解決的關(guān)鍵問題: <BR><BR> ⑴ HTML分析:需要某種HTML解析器來(lái)分析蜘蛛程序遇到的每一個(gè)頁(yè)面。 <BR><BR> ⑵ 頁(yè)面處理:需要處理每一個(gè)下載得到的頁(yè)面。下載得到的內(nèi)容可能要保存到磁盤,或者進(jìn)一步分析處理。 <BR><BR> ⑶ 多線程:只有擁有多線程能力,蜘蛛程序才能真正做到高效。 <BR><BR> ⑷ 確定何時(shí)完成:不要小看這個(gè)問題,確定任務(wù)是否已經(jīng)完成并不簡(jiǎn)單,尤其是在多線程環(huán)境下。 <BR><BR> 一、HTML解析 <BR><BR> C#語(yǔ)言本身不包含解析HTML的能力,但支持XML解析;不過,XML有著嚴(yán)格的語(yǔ)法,為XML設(shè)計(jì)的解析器對(duì)HTML來(lái)說(shuō)根本沒用,因?yàn)镠TML的語(yǔ)法要寬松得多。為此,我們需要自己設(shè)計(jì)一個(gè)HTML解析器。本文提供的解析器是高度獨(dú)立的,你可以方便地將它用于其它用C#處理HTML的場(chǎng)合。 <BR><BR> 本文提供的HTML解析器由ParseHTML類實(shí)現(xiàn),使用非常方便:首先創(chuàng)建該類的一個(gè)實(shí)例,然后將它的Source屬性設(shè)置為要解析的HTML文檔: <BR><BR>ParseHTML parse = new ParseHTML();parse.Source = "
<P>Hello World</P>";<BR><BR><BR><BR> 接下來(lái)就可以利用循環(huán)來(lái)檢查HTML文檔包含的所有文本和標(biāo)記。通常,檢查過程可以從一個(gè)測(cè)試Eof方法的while循環(huán)開始: <BR><BR>while(!parse.Eof()){char ch = parse.Parse();<BR><BR><BR><BR> Parse方法將返回HTML文檔包含的字符--它返回的內(nèi)容只包含那些非HTML標(biāo)記的字符,如果遇到了HTML標(biāo)記,Parse方法將返回0值,表示現(xiàn)在遇到了一個(gè)HTML標(biāo)記。遇到一個(gè)標(biāo)記之后,我們可以用GetTag()方法來(lái)處理它。 <BR><BR>if(ch==0){HTMLTag tag = parse.GetTag();}<BR><BR><BR><BR> 一般地,蜘蛛程序最重要的任務(wù)之一就是找出各個(gè)HREF屬性,這可以借助C#的索引功能完成。例如,下面的代碼將提取出HREF屬性的值(如果存在的話)。 <BR><BR>Attribute href = tag["HREF"];string link = href.Value;<BR><BR><BR><BR> 獲得Attribute對(duì)象之后,通過Attribute.Value可以得到該屬性的值。 <BR><BR>二、處理HTML頁(yè)面 <BR><BR> 下面來(lái)看看如何處理HTML頁(yè)面。首先要做的當(dāng)然是下載HTML頁(yè)面,這可以通過C#提供的HttpWebRequest類實(shí)現(xiàn): <BR><BR>HttpWebRequest request = (HttpWebRequest)WebRequest.Create(m_uri);response = request.GetResponse();stream = response.GetResponseStream();<BR><BR><BR><BR> 接下來(lái)我們就從request創(chuàng)建一個(gè)stream流。在執(zhí)行其他處理之前,我們要先確定該文件是二進(jìn)制文件還是文本文件,不同的文件類型處理方式也不同。下面的代碼確定該文件是否為二進(jìn)制文件。 <BR><BR>if( !response.ContentType.ToLower().StartsWith("text/") ){SaveBinaryFile(response);return null;}string buffer = "",line;<BR><BR><BR><BR> 如果該文件不是文本文件,我們將它作為二進(jìn)制文件讀入。如果是文本文件,首先從stream創(chuàng)建一個(gè)StreamReader,然后將文本文件的內(nèi)容一行一行加入緩沖區(qū)。 <BR><BR>reader = new StreamReader(stream);while( (line = reader.ReadLine())!=null ){buffer+=line+"\r\n";}<BR><BR><BR><BR> 裝入整個(gè)文件之后,接著就要把它保存為文本文件。 <BR><BR>SaveTextFile(buffer);<BR><BR><BR><BR> 下面來(lái)看看這兩類不同文件的存儲(chǔ)方式。 <BR><BR> 二進(jìn)制文件的內(nèi)容類型聲明不以"text/"開頭,蜘蛛程序直接把二進(jìn)制文件保存到磁盤,不必進(jìn)行額外的處理,這是因?yàn)槎M(jìn)制文件不包含HTML,因此也不會(huì)再有需要蜘蛛程序處理的HTML鏈接。下面是寫入二進(jìn)制文件的步驟。 <BR><BR> 首先準(zhǔn)備一個(gè)緩沖區(qū)臨時(shí)地保存二進(jìn)制文件的內(nèi)容。 byte []buffer = new byte[1024];<BR><BR><BR><BR> 接下來(lái)要確定文件保存到本地的路徑和名稱。如果要把一個(gè)myhost.com網(wǎng)站的內(nèi)容下載到本地的c:\test文件夾,二進(jìn)制文件的網(wǎng)上路徑和名稱是http://myhost.com/images/logo.gif,則本地路徑和名稱應(yīng)當(dāng)是c:\test\images\logo.gif。與此同時(shí),我們還要確保c:\test目錄下已經(jīng)創(chuàng)建了images子目錄。這部分任務(wù)由convertFilename方法完成。 <BR><BR>string filename = convertFilename( response.ResponseUri );<BR><BR><BR><BR> convertFilename方法分離HTTP地址,創(chuàng)建相應(yīng)的目錄結(jié)構(gòu)。確定了輸出文件的名字和路徑之后就可以打開讀取Web頁(yè)面的輸入流、寫入本地文件的輸出流。 <BR><BR>Stream outStream = File.Create( filename );Stream inStream = response.GetResponseStream();<BR><BR><BR><BR> 接下來(lái)就可以讀取Web文件的內(nèi)容并寫入到本地文件,這可以通過一個(gè)循環(huán)方便地完成。 <BR><BR>int l;do{l = inStream.Read(buffer,0,buffer.Length);if(l>0)outStream.Write(buffer,0,l);} while(l>0);<BR><BR><BR><BR> 寫入整個(gè)文件之后,關(guān)閉輸入流、輸出流。 <BR><BR>outStream.Close();inStream.Close();<BR><BR><BR><BR> 比較而言,下載文本文件更容易一些。文本文件的內(nèi)容類型總是以"text/"開頭。假設(shè)文件已被下載并保存到了一個(gè)字符串,這個(gè)字符串可以用來(lái)分析網(wǎng)頁(yè)包含的鏈接,當(dāng)然也可以保存為磁盤上的文件。下面代碼的任務(wù)就是保存文本文件。 <BR><BR>string filename = convertFilename( m_uri );StreamWriter outStream = new StreamWriter( filename );outStream.Write(buffer);outStream.Close();<BR><BR><BR><BR> 在這里,我們首先打開一個(gè)文件輸出流,然后將緩沖區(qū)的內(nèi)容寫入流,最后關(guān)閉文件。 <BR><BR> 三、多線程 <BR><BR> 多線程使得計(jì)算機(jī)看起來(lái)就象能夠同時(shí)執(zhí)行一個(gè)以上的操作,不過,除非計(jì)算機(jī)包含多個(gè)處理器,否則,所謂的同時(shí)執(zhí)行多個(gè)操作僅僅是一種模擬出來(lái)的效果--靠計(jì)算機(jī)在多個(gè)線程之間快速切換達(dá)到"同時(shí)"執(zhí)行多個(gè)操作的效果。一般而言,只有在兩種情況下多線程才能事實(shí)上提高程序運(yùn)行的速度。第一種情況是計(jì)算機(jī)擁有多個(gè)處理器,第二種情況是程序經(jīng)常要等待某個(gè)外部事件。 <BR><BR> 對(duì)于蜘蛛程序來(lái)說(shuō),第二種情況正是它的典型特征之一,它每發(fā)出一個(gè)URL請(qǐng)求,總是要等待文件下載完畢,然后再請(qǐng)求下一個(gè)URL。如果蜘蛛程序能夠同時(shí)請(qǐng)求多個(gè)URL,顯然能夠有效地減少總下載時(shí)間。 <BR><BR> 為此,我們用DocumentWorker類封裝所有下載一個(gè)URL的操作。每當(dāng)一個(gè)DocumentWorker的實(shí)例被創(chuàng)建,它就進(jìn)入循環(huán),等待下一個(gè)要處理的URL。下面是DocumentWorker的主循環(huán): <BR><BR>while(!m_spider.Quit ){m_uri = m_spider.ObtainWork();m_spider.SpiderDone.WorkerBegin();string page = GetPage();if(page!=null)ProcessPage(page);m_spider.SpiderDone.WorkerEnd();}<BR><BR><BR><BR> 這個(gè)循環(huán)將一直運(yùn)行,直至Quit標(biāo)記被設(shè)置成了true(當(dāng)用戶點(diǎn)擊"Cancel"按鈕時(shí),Quit標(biāo)記就被設(shè)置成true)。在循環(huán)之內(nèi),我們調(diào)用ObtainWork獲取一個(gè)URL。ObtainWork將一直等待,直到有一個(gè)URL可用--這要由其他線程解析文檔并尋找鏈接才能獲得。Done類利用WorkerBegin和WorkerEnd方法來(lái)確定何時(shí)整個(gè)下載操作已經(jīng)完成。 <BR><BR> 從圖一可以看出,蜘蛛程序允許用戶自己確定要使用的線程數(shù)量。在實(shí)踐中,線程的最佳數(shù)量受許多因素影響。如果你的機(jī)器性能較高,或者有兩個(gè)處理器,可以設(shè)置較多的線程數(shù)量;反之,如果網(wǎng)絡(luò)帶寬、機(jī)器性能有限,設(shè)置太多的線程數(shù)量其實(shí)不一定能夠提高性能。 <BR><BR> 四、任務(wù)完成了嗎? <BR><BR> 利用多個(gè)線程同時(shí)下載文件有效地提高了性能,但也帶來(lái)了線程管理方面的問題。其中最復(fù)雜的一個(gè)問題是:蜘蛛程序何時(shí)才算完成了工作?在這里我們要借助一個(gè)專用的類Done來(lái)判斷。 <BR><BR> 首先有必要說(shuō)明一下"完成工作"的具體含義。只有當(dāng)系統(tǒng)中不存在等待下載的URL,而且所有工作線程都已經(jīng)結(jié)束其處理工作時(shí),蜘蛛程序的工作才算完成。也就是說(shuō),完成工作意味著已經(jīng)沒有等待下載和正在下載的URL。 <BR><BR> Done類提供了一個(gè)WaitDone方法,它的功能是一直等待,直到Done對(duì)象檢測(cè)到蜘蛛程序已完成工作。下面是WaitDone方法的代碼。 <BR><BR>public void WaitDone(){Monitor.Enter(this);while ( m_activeThreads>0 ){Monitor.Wait(this);}Monitor.Exit(this);}<BR><BR><BR><BR> WaitDone方法將一直等待,直到不再有活動(dòng)的線程。但必須注意的是,下載開始的最初階段也沒有任何活動(dòng)的線程,所以很容易造成蜘蛛程序一開始就立即停止的現(xiàn)象。為解決這個(gè)問題,我們還需要另一個(gè)方法WaitBegin來(lái)等待蜘蛛程序進(jìn)入"正式的"工作階段。一般的調(diào)用次序是:先調(diào)用WaitBegin,再接著調(diào)用WaitDone,WaitDone將等待蜘蛛程序完成工作。下面是WaitBegin的代碼: <BR><BR>public void WaitBegin(){Monitor.Enter(this);while ( !m_started ){Monitor.Wait(this);}Monitor.Exit(this);}<BR><BR><BR><BR> WaitBegin方法將一直等待,直到m_started標(biāo)記被設(shè)置。m_started標(biāo)記是由WorkerBegin方法設(shè)置的。工作線程在開始處理各個(gè)URL之時(shí),會(huì)調(diào)用WorkerBegin;處理結(jié)束時(shí)調(diào)用WorkerEnd。WorkerBegin和WorkerEnd這兩個(gè)方法幫助Done對(duì)象確定當(dāng)前的工作狀態(tài)。下面是WorkerBegin方法的代碼: <BR><BR>public void WorkerBegin(){Monitor.Enter(this);m_activeThreads++;m_started = true;Monitor.Pulse(this);Monitor.Exit(this);}<BR><BR><BR><BR> WorkerBegin方法首先增加當(dāng)前活動(dòng)線程的數(shù)量,接著設(shè)置m_started標(biāo)記,最后調(diào)用Pulse方法以通知(可能存在的)等待工作線程啟動(dòng)的線程。如前所述,可能等待Done對(duì)象的方法是WaitBegin方法。每處理完一個(gè)URL,WorkerEnd方法會(huì)被調(diào)用: <BR><BR>public void WorkerEnd(){Monitor.Enter(this);m_activeThreads--;Monitor.Pulse(this);Monitor.Exit(this);}<BR><BR><BR><BR><BR><BR> WorkerEnd方法減小m_activeThreads活動(dòng)線程計(jì)數(shù)器,調(diào)用Pulse釋放可能在等待Done對(duì)象的線程--如前所述,可能在等待Done對(duì)象的方法是WaitDone方法。 <BR><BR> 結(jié)束語(yǔ):本文介紹了開發(fā)Internet蜘蛛程序的基礎(chǔ)知識(shí),下面提供的源代碼將幫助你進(jìn)一步深入理解本文的主題。這里提供的代碼非常靈活,你可以方便地將它用于自己的程序。<BR><BR>創(chuàng)建智能網(wǎng)絡(luò)蜘蛛——如何使用Java網(wǎng)絡(luò)對(duì)象和HTML對(duì)象(翻譯)<BR>作者:Mark O. Pendergast<BR><BR>原文:http://www.javaworld.com/javaworld/jw-11-2004/jw-1101-spider.html<BR><BR>摘要<BR>你是否想過創(chuàng)建自己的符合特定標(biāo)準(zhǔn)的網(wǎng)站數(shù)據(jù)庫(kù)呢?網(wǎng)絡(luò)蜘蛛,有時(shí)也稱為網(wǎng)絡(luò)爬蟲,是一些根據(jù)網(wǎng)絡(luò)鏈接從一個(gè)網(wǎng)站到另外一個(gè)網(wǎng)站,檢查內(nèi)容和記錄位置的程序。商業(yè)搜索站點(diǎn)使用網(wǎng)絡(luò)蜘蛛豐富它們的數(shù)據(jù)庫(kù),研究人員可以使用蜘蛛獲得相關(guān)的信息。創(chuàng)建自己的蜘蛛搜索的內(nèi)容、主機(jī)和網(wǎng)頁(yè)特征,比如文字密度和內(nèi)置的多媒體內(nèi)容。這篇文章將告訴你如何使用Java的HTML和網(wǎng)絡(luò)類來(lái)創(chuàng)建你自己的功能強(qiáng)大的網(wǎng)絡(luò)蜘蛛。<BR><BR>這篇文章將介紹如何在標(biāo)準(zhǔn)Java網(wǎng)絡(luò)對(duì)象的基礎(chǔ)上創(chuàng)建一個(gè)智能的網(wǎng)絡(luò)蜘蛛。蜘蛛的核心是一個(gè)基于關(guān)鍵字/短語(yǔ)標(biāo)準(zhǔn)和網(wǎng)頁(yè)特征進(jìn)行深入網(wǎng)絡(luò)搜索的遞歸程序。搜索過程在圖形上類似于JTree結(jié)構(gòu)。我主要介紹的問題,例如處理相關(guān)的URL,防止循環(huán)引用和監(jiān)視內(nèi)存/堆棧使用。另外,我將介紹再訪問和分解遠(yuǎn)程網(wǎng)頁(yè)中如何正確是用Java網(wǎng)絡(luò)對(duì)象。<BR><BR>● 蜘蛛示例程序<BR><BR>示例程序包括用戶界面類SpiderControl、網(wǎng)絡(luò)搜索類Spider,兩個(gè)用作創(chuàng)建JTree顯示結(jié)果的類UrlTreeNode和UrlNodeRenderer,和兩個(gè)幫助驗(yàn)證用戶界面中數(shù)字輸入的類IntegerVerifier和VerifierListener。文章末尾的資源中有完整代碼和文檔的璉接。<BR><BR>SpiderControl界面由三個(gè)屬性頁(yè)組成,一個(gè)用來(lái)設(shè)置搜索參數(shù),另一個(gè)顯示結(jié)果搜索樹(JTree),第三個(gè)顯示錯(cuò)誤和狀態(tài)信息,如圖1<BR><BR><BR><BR><BR>圖1 搜索參數(shù)屬性頁(yè)<BR><BR><BR>搜索參數(shù)包括訪問網(wǎng)站的最大數(shù)量,搜索的最大深度(鏈接到鏈接到鏈接),關(guān)鍵字/短語(yǔ)列表,搜索的頂級(jí)主機(jī),起始網(wǎng)站或者門戶。一旦用戶輸入了搜索參數(shù),并按下開始按鈕,網(wǎng)絡(luò)搜索將開始,第二個(gè)屬性頁(yè)將顯示搜索的進(jìn)度。<BR><BR><BR>圖2 搜索樹<BR><BR>一個(gè)Spider類的實(shí)例以獨(dú)立進(jìn)程的方式執(zhí)行網(wǎng)絡(luò)搜索。獨(dú)立進(jìn)程的使用是為了SpiderControl模塊可以不斷更新搜索樹顯示和處理停止搜索按鈕。當(dāng)Spider運(yùn)行時(shí),它不斷在第二個(gè)屬性頁(yè)中為JTree增加節(jié)點(diǎn)(UrlTreeNode)。包含關(guān)鍵字和短語(yǔ)的搜索樹節(jié)點(diǎn)以藍(lán)色顯示(UrlNodeRenderer)。<BR><BR>當(dāng)搜索完成以后,用戶可以查看站點(diǎn)的統(tǒng)計(jì),還可以用外部瀏覽器(默認(rèn)是位于Program Files目錄的Internet Explorer)查看站點(diǎn)。統(tǒng)計(jì)包括關(guān)鍵字出現(xiàn)次數(shù),總字符數(shù),總圖片數(shù)和總鏈接數(shù)。<BR><BR>● Spider類<BR><BR>Spider類負(fù)責(zé)搜索給出起點(diǎn)(入口)的網(wǎng)絡(luò),一系列的關(guān)鍵字和主機(jī),和搜索深度和大小的限制。Spider繼承了Thread,所以可以以獨(dú)立線程運(yùn)行。這允許SpiderControl模塊不斷更新搜索樹顯示和處理停止搜索按鈕。<BR><BR>構(gòu)造方法接受包含對(duì)一個(gè)空的JTree和一個(gè)空的JtextArea引用的搜索參數(shù)。JTree被用作創(chuàng)建一個(gè)搜索過程中的分類站點(diǎn)記錄。這樣為用戶提供了可見的反饋,幫助跟蹤Spdier循環(huán)搜索的位置。JtextArea顯示錯(cuò)誤和過程信息。<BR><BR>構(gòu)造器將參數(shù)存放在類變量中,使用UrlNodeRenderer類初始化顯示節(jié)點(diǎn)的JTree。直到SpiderControl調(diào)用run()方法搜索才開始。<BR><BR>run()方法以獨(dú)立的線程開始執(zhí)行。它首先判斷入口站點(diǎn)是否是一個(gè)Web引用(以http,ftp或者www開始)或是一個(gè)本地文件引用。它接著確認(rèn)入口站點(diǎn)是否具有正確的符號(hào),重置運(yùn)行統(tǒng)計(jì),接著調(diào)用searchWeb()開始搜索:<BR><BR>public void run()<BR>{<BR>DefaultTreeModel treeModel = (DefaultTreeModel)searchTree.getModel(); // get our model<BR>DefaultMutableTreeNode root = (DefaultMutableTreeNode)treeModel.getRoot();<BR>String urllc = startSite.toLowerCase();<BR>if(!urllc.startsWith("http://") && !urllc.startsWith("ftp://") &&<BR>!urllc.startsWith("www."))<BR><BR>{<BR>startSite = "file:///"+startSite; // Note you must have 3 slashes !<BR><BR>}<BR>else // Http missing ?<BR>if(urllc.startsWith("www."))<BR>{<BR>startSite = "http://"+startSite; // Tack on http:// <BR>}<BR><BR>startSite = startSite.replace('\\', '/'); // Fix bad slashes<BR>sitesFound = 0;<BR>sitesSearched = 0;<BR>updateStats();<BR>searchWeb(root,startSite); // Search the Web<BR>messageArea.append("Done!\n\n");<BR>}<BR><BR>searchWeb()是一個(gè)接受搜索樹父節(jié)點(diǎn)和搜索Web地址參數(shù)的遞歸方法。searchWeb()首先檢查給出的站點(diǎn)是否已被訪問和未被執(zhí)行的搜索深度和站點(diǎn)。SearchWeb()接著允許SpiderControl運(yùn)行(更新界面和檢查停止搜索按鈕是否按下)。如果所有正常,searchWeb()繼續(xù),否則返回。<BR><BR>在searchWeb()開始讀和解析站點(diǎn)以前,它首先檢驗(yàn)基于站點(diǎn)創(chuàng)建的URL對(duì)象是否具有正確的類型和主機(jī)。URL協(xié)議被檢查來(lái)確認(rèn)它是一個(gè)HTML地址或者一個(gè)文件地址(不必搜索mailt和其他協(xié)議)。接著檢查文件擴(kuò)展名(如果當(dāng)前有)來(lái)確認(rèn)它是一個(gè)HTML文件(不必解析pdf或者gif文件)。一旦這些工作完成,通過isDomainOk()方法檢查根據(jù)用戶指定的列表檢查主機(jī):<BR><BR>...URL url = new URL(urlstr); // Create the URL object from a string.<BR>String protocol = url.getProtocol(); // Ask the URL for its protocol<BR>if(!protocol.equalsIgnoreCase("http") && !protocol.equalsIgnoreCase("file"))<BR>{<BR>messageArea.append(" Skipping : "+urlstr+" not a http site\n\n");<BR>return;<BR>}<BR><BR>String path = url.getPath(); // Ask the URL for its path<BR>int lastdot = path.lastIndexOf("."); // Check for file extension<BR>if(lastdot > 0)<BR>{<BR>String extension = path.substring(lastdot); // Just the file extension<BR>if(!extension.equalsIgnoreCase(".html") && !extension.equalsIgnoreCase(".htm"))<BR>return; // Skip everything but html files<BR>}<BR><BR>if(!isDomainOk(url))<BR><BR>{<BR><BR>messageArea.append(" Skipping : "+urlstr+" not in domain list\n\n");<BR>return;<BR>}<BR></p>
</div>
<div class="content">
<p align="center"><b>[1]</b> [<a href=/data/2006/0921/article_1570_1.htm>2</a>] </p>
</div>
<div class="content">
<p align="right">責(zé)任編輯 webmaster</p>
</div>
<div class="mainline"> </div>
<div class="maincolumn">
<div class="navadimg">
<script type="text/javascript"><!--
google_ad_client = "pub-4357927283882197";
google_ad_width = 728;
google_ad_height = 90;
google_ad_format = "728x90_as";
google_ad_type = "image";
google_ad_channel = "";
//--></script>
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script>
</div>
</div>
<div class="content">
<p align="left"><b>相關(guān)鏈接</b><br>
?? 快捷鍵說(shuō)明
復(fù)制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號(hào)
Ctrl + =
減小字號(hào)
Ctrl + -