本發(fā)明的范圍。此外,在以下說明中,省略了對公知結構和技術的描述,以避免不必要地混淆本發(fā)明的概念。
[0024]實施例:
如圖1所示,一種基于多線程的基于多線程的網絡爬蟲系統(tǒng),包括URL處理模塊、網頁爬取模塊、網頁分析模塊和網頁存儲模塊。
[0025]URL處理模塊,通過URL類處理獲得每個URL的主機名,端口號,文件名。
[0026]URL的一般形式是:〈URL的訪問方式>://〈主機>:〈端口 >/〈路徑>。在這個程序當中能使得變得簡單,就設計了一存放URL的類,在這個類中包含了Host(主機名)
口),File(文件路徑),Fname(這個是給這個網頁叫的名字h下面的代碼是URL類的所有的成員和它的成員函數:class URL{
public:
URLO {}
void SetHost(const string& host) { Host = host; } string GetHostO { return Host; } void SetPort(int port) { Port = port; } int GetPortO { return Port; }
void SetFile(const string& file) { File = file; } string GetFileO { return File; }
void SetFname(const string& fname) { Fname = fname; } string GetFnameO { return Fname; }
?URLO {}private:
string Host; int Port; string File; string Fname;
};
對URL的處理:
除此之外,在我們看到的網頁源代碼中,統(tǒng)一資源定位符URL有的時候是絕對路徑,有的時候卻是相對路徑,當然還有其他的情況,還有一些URL中會有一些特殊字符,像‘#,,多個‘///’等等。
[0027]所以,應該將這些URL進行特別的處理,最終可以獲得每個URL的主機名,端口號,文件名。因此需要對URL的特殊處理如下:
a)其中URL的文件成員在最后不能是‘/’,如果有最后應該是‘\0’;如果這個文件成員真的沒有路徑,那么應該讓路徑變成這樣可以讓URL排重變得更加簡單。
[0028]b)如果在URL的文件成員中包含‘#’這樣的的特殊字符,那么就可以把這個字符前面的所有當做File,而從這個字符到以后所有的字符刪掉。
[0029]c)如果在URL中沒有很明顯的表示端口號,那么就要指定Port成員的值為80。
[0030]d)如果URL沒有host的屬性,那么就把Host成員的值默認為初始URL的主機名。
[0031]網頁爬取模塊,對網頁內容進行分塊抓取,并將抓取的網頁保存至暫存模塊。
[0032]在能夠進行分析網頁的前面,首先要進行的是網頁的抓取,并且要進行分析,再需要將數據從服務器下載下來然后把它保存到本地。
[0033]第一部分:在分析之前,在讀取數據信息的時候,在最開始的一部分信息是和網頁本身無關的,就像某些網頁沒有被找到的時候,就會得到404 not found等這樣的產生的服務信息,這像這樣的信息一樣,每一種情況就會得到相關聯的數據信息,所以,對照著某些服務的信息,就不應該這樣被記錄。應該從‘〉’這個字符開始讀取時,才算真正地開始可以獲得在網頁中的內容。
[0034]第二部分:定義一個名字叫tmp的字符數組,它的作用是暫時存放網頁,這些網頁是被抓取的,它的爬取的辦法與第一部分所提到的大概一致,全部都是采用read函數,不一樣的地方的是這里是一大塊一大塊的爬取,從效率的角度來看,這樣可以每一次抓取一個字節(jié)的情況效率要高。在下一步要分析網頁的時候,在這里面有一個統(tǒng)一資源定位符重定向的階段,所以程序還會去繼續(xù)修正這個名字為tmp的數組。假設是把這些爬取的網頁直接保存到文件的話,那么還需要再次打開文件的時候進行網頁分析,這樣很容易降低運行效率,因為在磁盤中執(zhí)行讀寫操作命令永遠都是比在內存中讀寫慢得很多很多。
[0035]網頁分析模塊,提取URL,對URL進行重定向,對URL進行判重處理,刪除重復的URL。
[0036]a) URL提取
在網頁當中,所有鏈接的形式是這樣的:〈a herf=”sss”>title〈/a>,因此本閥門根據string類將自己封裝的f ind函數,用于找到鏈接形式當中每一個”herf”所在的位置,把它分為三種情況。第一種情況是:URL可以表示為雙引號勾選起來的情況;第二種情況是:URL可以表示為單引號勾選起來的情況;第三種情況是:URL可以表示為沒有被引號勾選起來的情況。在這里需要使用了一個變量(flag)來標記,以此來區(qū)分以上的三種情況。
[0037]b) URL的重定向
在這以前,網絡爬蟲已經爬取了一些網頁將它們存儲在了文件夾中,這個文件夾的名字為Pages,而且在這里面的每一個網頁都做了了重命名的處理。所以不管從路徑上來說,還是文件名上,存儲在本地的URL都和原本的網頁中的鏈接都不是一一對應的,要想產生和原來一樣進行網頁跳轉的效果,必須重新修正每一個鏈接的herf值。
[0038]針對一開始的網頁鏈接〈a herf=”sss.htmr>title〈/a>,假設現在把這個鏈接網頁重新命名為new.html,可以采用覆蓋或者移動的辦法轉變成新的鏈接,使得兩個引號中直接的內容為new.html。在本文采用的辦法如下:
現在已經知道第一個引號的位置在pos_l,所以可以采用調用string類的insert方法來達到這樣的目的。調用后產生的效果如下:
<a herf=,’new.html,,,,xxx.html” >title</a>
經過這樣的處理,它不只是隱藏了”xxx.html”,也同樣可以減少刪除原本一些不必要的鏈接等操作的錯誤可能,在某種情況下反而可以提升代碼的效率。
[0039]c) URL判重
存儲在這個等待抓取的隊列里的URL不斷增多,而且里面的URL有很多是重復的。使用set的容器被提供(或者可以使用hash函數來進行判重)。
[0040]set是一種關聯容器,在這里面存放的元素每一個都是不一樣的,在這里面的每一個元素都叫做關鍵字,它的實現的依據是二叉搜索樹。
[0041]因為它的元素是單一的,并且性能很高,所以把它選擇作為URL判重的一種方法,同樣這也是一種hash的工具。本文代碼如下:
定義:set〈unsigned int>Set ;
使用:Set.1nsert (hashVal);
網頁存儲模塊,在存儲文件時判斷是否存在該文件,如果不存在,則直接爬取該文件;如果存在并且此次爬取網頁所獲得的內容比上一次爬取的多,則覆蓋原來的文件;否則,丟棄該文件。代碼如下:chdir(〃Pages〃);
int fd = open(url_t.GetFname0.c_str(), 0—CREAT|0—EXCL|0—RDWR, 00770);
/氺 check whether needs re-fetch 氺/if(fd < 0) {if(errno == EEXIST) {stat(url_t.GetFname().c_str(), &buf);int Ien = buf.st_size;if(len >= flen) goto NEXT;else {
fd = open(url_t.GetFname0.c_str(), 0_RDWR|0_TRUNC, 00770);if(fd < 0) {
perror(〃file open error,,);goto NEXT;
}
}
}
else {
perror(〃file open error,,);goto NEXT;
}
}
write(fd, HtmFile.c_str(),HtmFile.1engthO);
NEXT:
close(fd);
Il todo next
上面的所有文件操作的代碼是將已經被抓取網頁的內容保存到文件當中。其中受到網絡條件的限制,網絡爬蟲在爬取的過程中請求網頁會超時,或者爬取獲得的網頁內容不全面,所以使得爬取的網頁內容能夠得到完善,可以不斷地反復進行爬取。
[0042]在第一行代碼當中,使用了0