五、用多線程下載網絡資源
一般來說,網絡蜘蛛都是使用多線程來下載網絡資源的。至於如何使用多線程來下載,各個版本的網絡蜘蛛不盡相同。為了方便和容易理解,本文所討論的網絡蜘蛛采用了每一個線程負責將網絡資源下載到一個屬於自己的目錄中,也就是說,每一個線程對應一個目錄。而在當前目錄中下載的網絡資源達到一定的數目後(如5000),這個線程就會再建立一個新目錄,並從0開始計數繼續下載網絡資源。在本節中將介紹一個用於下載網絡資源的線程類DownLoadThread。這個類的主要功能就是從下載隊列中獲得一定數量的URL,並進行下載和分析。在DownLoadThread類中涉及到很多其他重要的類,這些類將在後面的部分介紹。在這裡我們先看一下DownLoadThread類的實現代碼。
DownLoadThread類的代碼
class DownLoadThread : MyThread
{
// ParseResource類用於下載和分析網絡資源
private ParseResource pr = new ParseResource();
private int currentCount = 0; // 當前下載目錄中的網頁數
// 用於向每個線程目錄中的index.txt中寫當前目錄的URL
private FileIO fileIO = new FileIO();
private string path; // 當前的下載目錄(後面帶“\")
private string[] patterns; // 線程不下載符合patterns中的正則表達式的URL
public bool stop = false; // stop為true,線程退出
public int threadID; // 當前線程的threadID,用於區分其他的線程
public DownLoadThread(string[] patterns)
{
pr.findUrl += findUrl; // 為findUrl事件賦一個方法
this.patterns = patterns;
}
// 這是一個事件方法,每獲得一個URL時發生
private void findUrl(string url)
{
Common.addUrl(url); // 將獲得的URL加到下載隊列中
}
private void openFile() // 打開下載目錄中的index.txt文件
{
fileIO.CloseWriteFile();
fileIO.OpenWriteFile(path + Common.indexFile);
}
public override void run() // 線程運行方法
{
LinkedList urls = new LinkedList();
path = Common.getDir(); // 獲得下載目錄
openFile();
while (!stop)
{
// 當下載隊列中沒有URL時,進行循環等待
while (!stop && urls.Count == 0)
{
Common.getUrls(urls, 20); // 從下載隊列中獲得20個url
if (urls.Count == 0) // 如果未獲得url
{
// 通知系統當前線程已處於等待狀態,
// 如果所有的線程都處於等待狀態,
// 說明所有的網絡資源都被下載完了
Common.threadWait(threadID);
sleep(5000); // 當前線程休眠5秒
}
}
StringBuilder sb = new StringBuilder();
foreach (string url in urls) // 循環對這20個url進行循環下載分析
{
if (stop) break;
// 如果當前下載目錄的資源文件數大於等於最大文件數目時,
// 建立一個新目錄,並繼續下載
if (currentCount >= Common.maxCount)
{
path = Common.getDir();
openFile();
currentCount = 0; // 目錄
}
// 每個下載資源文件名使用5位的順序號保存(沒有擴展名),
// 如00001、00002。下面的語句是格式化文件名
string s = string.Format("{0:D5}", currentCount + 1);
sb.Remove(0, sb.Length);
sb.Append(s);
sb.Append(":");
sb.Append(url);
try
{
// 下載和分析當前的url
pr.parse(url, path + s, patterns);
Common.Count++;
// 將當前的url寫入index.txt
fileIO.WriteLine(sb.ToString());
currentCount++;
}
catch (Exception e)
{
}
}
urls.Clear();
}
}
}
}