網路爬蟲專案

qq_18973645發表於2022-01-29

傳智課程學習筆記。


搜尋引擎後臺的第一個子系統,

爬蟲,機器人,軟體形式的,



暗網,儘管連在網際網路上,但是是私有的,


內部網,


整站下載工具,就是一種爬蟲,


索引系統,從抓到的網站上提取關鍵字,建立索引,



使用者產生的資料我們暫時不管,


網際網路產生的資料,

大資料,

內容很多,沒有歸類,

首先是抓取,


微博推薦名人啊,

背後是什麼,資料探勘系統,

微博官方提供的有一些api,但是有限制,比如訪問請求速度,

第三方通過http開發的介面,限制就相對少很多,其背後還是爬蟲,

爬蟲簡單說就是一個網頁下載器,

灌水機,刷票系統,也是爬蟲在背後不停地抓取,

還有淘寶秒殺,


專案,

需求,設計,編碼,測試,整合,


設計:業務設計和技術設計,

比如客戶去銀行存取錢,

對銀行工作人員來說,就是要辦一套 業務,

對軟體工程師來說,我們的系統要完成這項工作,要經歷 哪些步驟,


技術設計,關於框架的流程,


程式設計師,

往技術方向,要關注框架,

往管理方向,是專案管理,

需求一變,業務流程就會變,進而底層的實現也會跟著變,


需求設計的時間大於程式碼實現的實現,

測試的時間和程式碼實現差不多,

測試,系統可靠性的一個檢測,


當要 支援新的特性或者功能的時候,

冷更新,機器關掉,重新編譯原始碼,重新啟動,

動態更新,通過外掛的方式,



軟體開發思想,

模組思想,

漸進式開發,好比房地產的一期,二期,



需求,

爬蟲,自動抓取網路資源,太廣泛了,

具體呢?

1,資源?網頁,圖片,音樂,視訊等,

2,自動?一旦執行,我們就不用管了,


生成需求說明文件,

對需求中的不明確或不完善的方面進行解釋,



mysql,oracle,這種資料庫,不適合儲存網頁,


種子,最初爬取的url,

下載種子url中的資源,

解析資源,

從已經下載的資源中獲取更多url,

對頁面做持久化操作,

根據提取的url再進行下載操作,

重複上面的操作,繼續下載,


google,百度,屬於廣域的搜尋引擎,


垂直型別的,比如只抓取新聞,



系統結構圖,




控制器的設計,因為它是核心,

輸入是配置檔案,輸出是指令,指令用於控制其它模組,


控制器由三個模組組成:

1、  配置檔案處理模組:從配置檔案中讀取配置項,提供配置項的提取介面

2、  URL維護模組:負責維護URL庫,提供如下功能

a)        輸入新的URL

b)        輸出一個未被抓取的URL

c)        負責維護URL的抓取狀態

3、  任務排程模組

a)        負責協調控制器的流程

b)        負責呼叫其他系統模組完成工作

4、  維護URL列表資料結構



下載器,

網頁是資料來源,url才是輸入,

下載url的工具有很多,在ubuntu中的wget命令,curl命令, 

使用wget XX,在當前目錄下會生成一個index.html的檔案,

按理說我們可以通過複用的方式,直接使用現成的工具,

但是由於我們還要實現一個其它功能的支援,所以我們還是得自己來寫,

但是還是可以參考這兩個命令的實現,


http,

get,post指令,



解析器,

提取頁面中的url,

html解析模組,

有的是用xml來寫的,考慮到擴充套件性,所以單獨寫出來,



持久化器,輸入是資料流,輸出是檔案,

頁面儲存,涉及到編碼,

uft-8,

GB2312,

GBK,

圖片儲存模組,

視訊流儲存模組,

音樂流儲存模組,

總之編碼的種類非常多,




設計好業務流程,

每一個部分,對應一個物件,用於進行相關處理,

說道物件,你有沒有想到當初學習C++的時候,企業對第三方產品的支援問題,比如不同廠商的加密產品,

哈哈,連起來了,


關於測試,

專門的測試團隊,是測試系統整合以後,

程式碼的質量,還是靠程式設計師自己,小模組的測試,還是程式設計師自己來做,




控制器設計,

配置檔案解析模組,

配置檔案是以檔案的形式儲存程式執行時必要的引數,

併發任務數,

url種子,

抓取深度,

允許抓取資源型別,

。。。 


配置檔案的值,

我們需要讀取到程式內部,

具體怎麼做呢?

建構函式自動做了,

class ConfigParser

{

public:

ConfigParser();

load();

getValue( char *key);

}


C++向下相容C,

但是混合寫的時候,容易產生一些麻煩,

我們需要做一些類的介面匯出,

用C++引用C的模組,不是很麻煩,

用C引用C++的模組,就比較麻煩,

所以怎麼解決呢?

避免呼叫,,我感覺這不算解決。。

用傳引數的方式,


單例模式,

1,建構函式私有,

2,需要一個方法得到這個類的例項,

3,需要一個類變數,指向這個類例項本身,


讀取配置檔案,

需要操作字串,

需要分隔符作為讀取的標誌,用於生成鍵值對,

按行處理,

過濾掉註釋,

job_num=10,如果等號前後有空格,我們也需要考慮到,做法就是過來掉空格,


技術實現,(先想,有沒有現成的函式已經實現)

1,fgets,讀取整行,

2,分割字串,

3,消除註釋,

4,消除空格

技術點講了之後,具體實現自己去敲,

框架程式碼老師帶著敲,



URL維護模組,

URL格式,http://192.168.0.22/docs/linuxdex.html

http://,是協議名稱,還有可以有ftp等,

域名或ip地址,路徑,檔名,

設計URL的資料結構,

完整的URL,

協議型別,

域名,

資源路徑,

檔名,

當前url處理狀態,

當前url深度,

資源型別,


頁面抓取處理流程,

1,得到url,

2,url進入抓取佇列等待抓取,

3,從佇列中得到一個url,把其分配給一個下載器例項,

4,得到下載器的處理狀態,

5,得到當前頁面中存在的下一級URL列表,


URL維護模組操作(對外介面):

 

1、新增新URL

2、使URL進入抓取佇列

3、從抓取佇列中移除一個URL

4、修改URL庫中某一個URL的值

5、新增新URL的列表

 

class Url

{

public:

         //初始化

//set…

//get…

private:

//

url

protocal

Sitename

Path

Filename

state

Deep

Filetype

}

class UrlManager

{

public: //外部介面

         addUrl();

addUrlList();

getUrlForQuque();

removeUrlForQuque();

//setUrlValue();

private: //內部介面

         findUrl();

 

private:

         list<Url> Urls;

map<string, Url*> UrlMap;

quque<Url*>Urlquque;

}



http協議請求頁面時的流程,

1,輸入網址,

2,向DNS傳送解析請求,

3,DNS返回給我們一個對應的ip地址,

4,通過ip地址向資源所在的主機傳送請求,

5,如果資源存在,主機返回200狀態,同時返回資料部分,

6,本地http客戶端接收資料,

7,得到資源,





任務排程模組,

儘可能把可拆分的功能封裝成獨立函式進行呼叫。

 

定義主程式框架的處理流程:

1程式執行時先處理命令列引數,根據引數跳轉到相應分支或呼叫對應的函式

2檢測是否按照守護程式模式執行(控制選項從命令列引數中得到)

3初始化環境

         a讀取配置檔案,提取配置檔案中的引數

         b根據守護程式模式的標記將當前程式轉變為守護程式

         c載入程式模組的動態庫

4開始程式的主處理流程

a檢測種子是否存在,把種子交給URL管理器

b分析種子,得到種子url的IP地址(DNS解析)

c根據種子URL得到第一個頁面

d對頁面進行處理(複雜流程,由其他模組實現細節)

e從URL管理器中取出一個URL

f啟動一個處理任務(先檢測是否達到最大任務數量,功能封裝到一個獨立函式中)

g監控任務處理數量,如果任務維護池中有空閒任務,那麼重複e步驟

h回收資源,準備結束程式或當前任務



多路複用框架:

1、為什麼不用select而是用epoll

具體的筆記,如果想看,去看文件,



日誌工具, 

方便除錯,以及程式碼維護,


日誌輸出資訊設計,

日期時間+除錯資訊,

日誌輸出等級設計

0,除錯,debug,僅用於除錯,

1,普通訊息,可以讓使用者瞭解一些資訊,

2,警告資訊,意味著程式中出現了錯誤,但是並不嚴重,

3,錯誤資訊,意味著程式中發生了嚴重錯誤,根據實際情況可選擇使程式繼續執行或終止,

4,程式崩潰,程式無法繼續執行了,



日誌呼叫介面設計,

SPIDER_LOG(日誌等級標記,日誌輸出資訊);
注意,配置檔案中的日誌輸出登等級段和介面中的日誌等級標記不是一個概念
日誌等級標記,純粹是一個標記,體現在輸出的日誌字串中
配置檔案中的日誌輸出等級欄位用來控制那些日誌被輸出


介面內部的處理流程:
1得到控制日誌輸出等級的標記,用來控制當前日誌是否要輸出
2得到呼叫日誌介面的時間
3得到日誌輸出資訊並進行日誌字串的拼接
4把日誌資訊輸出到指定的裝置




底層通過socket,


使用epoll,只能管理io相關的,

PPC 或TPC除了上面,還能管理資料包處理




靜態檔案,是通過.h檔案,將編譯好的程式碼嵌入到我們的程式碼中,

動態的.so,也就是下面的模組,外掛,

模組,外掛,

設計入口函式指標原型,

int (*handle)(void *);


設計初始化函式指標原型,

int (*int)(Module *);


模組管理器設計,

1載入模組的操作

Int Load (char* path, char*name);

Module* getModule(char* name);

 

載入模組操作的處理流程:

1通過路徑找到模組檔案(.so)

2呼叫dlopen開啟動態庫(.so)

3使用動態庫

4關閉動態庫檔案







下載器設計,

下載器模組分為socket功能封裝與http功能模組,

 

這裡有大篇幅的http處理的介紹,




頁面解析器設計,


html文件,標記語言,

對頁面解析,得到頁面中存在的下級url,存在於 a標籤中,

但是這裡a標籤是在太多了,用字串提取不太方便,

因此用正規表示式,


頁面解析的處理流程,

1,得到下載的頁面,

2,得到頁面對應的url結構體,

3,使用正規表示式得到頁面中所有url列表,

4,處理url中的相對路徑,

5,一個細節:把當前頁面深度加一,生成並填充URL結構體6把得到的URL列表回寫到URL管理器中(生成列表資料,以返回值形式回傳給上層程式碼)一個細節:把當前頁面深度加一,生成並填充URL結構體6把得到的URL列表回寫到URL管理器中(生成列表資料,以返回值形式回傳給上層程式碼)



持久化器設計,

處理流程,

1,得到頁面的資料流或在記憶體緩衝區的資料,

2,得到當前頁面的url描述結構體,

3,生成儲存目錄,

4,把檔案按照指定模式寫入磁碟系統,

5,向主處理器流程傳送一個反饋,表示當前頁面處理的進度,




系統的核心程式碼:

1、  系統主處理框架

2、  Epoll框架的呼叫

3、  外掛框架的完整實現

4、  Soket功能封裝

5、  http協議頭解析

6、  html解析並提取URL列表

7、  URL管理器實現

 

後續工作:如何進行單元測試 cunit。

把軟體做成系統服務,需要shell指令碼。

整合測試。




專案中用到的一個開源庫,

Libevent,

雖然都是it,但是像一些領域,比如通訊,http,它裡面的一些細節是非常的複雜,

不是專門去做的,不是那麼容易理解,

但是開源庫使用也要注意,

因為有些庫可能編譯不了,或者使用不穩定,


開源庫有很多版本,怎麼選擇呢?

release版本是成熟的穩定的,一般採用它,

beta版,測試版,一般不採用,

RC版,比beta高,一版也不採用,

最後一次提交版本,非常不穩定,


DNS查詢使用的就是這個庫中的,


這個庫,需要在原始碼級別進行安裝,


tar -zxf  xxxxx

z代表格式,

x代表解壓縮,

f代表按照後面的目錄結構來解壓,

v可以看到解壓過程,


動態庫,需要載入到動態庫的配置檔案中,

有點像path,


系統中可以被開源庫替換的部分,

下載器,wget,curl,

用wget,使用fork+exec,但是無法使用epoll,因為得不到控制程式碼,

用curl,可以使用,因為這個有庫,



對程式碼進行單元測試,

1,程式碼產生bug的原因,

低階錯誤,編譯器可以幫我們檢查大部分,

一些編譯器檢查不到的錯誤, if( var == 1) 寫成 if( var = 1),

邏輯上的錯誤,

容錯,比如開啟檔案應該判斷是否成功開啟,



測試用例,

事先做好的一個

正常的輸入,預期輸出是什麼,

非正常的輸入,預期輸出是什麼,

然後編寫測試程式,


方法,

1,斷言,#include<assert.h>,

斷言是除錯工具,不能有程式處理流程,

因為程式釋出的時候,斷言是要遮蔽掉的,因此裡面若有業務處理流程,那麼業務流程豈不是少了,

2,使用單元測試工具CUNIT,

得有測試用例,,編寫測試程式碼,


大系統測試,

業務邏輯,處理流程,壓力測試,


對程式設計師來說,測試是保證程式碼質量的,

一定要掌握容錯,否則後果非常嚴重,

斷言,知道一點就行,

而像CUNIT,高階的功能,就屬於專門的測試人員的範疇了,我們不必那麼深入,




分析程式碼的方法,

1,找到程式的入口,

找與程式名稱相同的,相似的,

找main()函式,

查說明檔案,

2,弄清楚主處理流程,不要果早陷入到細節中去,

一邊分析,一邊加註釋, 

ps:我們這個專案主要關注的是,網路資源的抓取,所以一些資料結構直接使用STL模板,


相關文章