大家都知道"巧婦難為無米之炊",同樣對于搜索引擎而言魁蒜,沒有了輸入內(nèi)容這個“米”囊扳,“炊”自然也是無從談起吩翻。不過首先我們來看看搜索引擎作為一個系統(tǒng),它大概長什么樣子宪拥。
這個圖展示了搜索引擎的框架仿野,其實大部分的檢索系統(tǒng)都采用類似的邏輯架構(gòu),所以我們可以把搜索引擎看作是一個超大型圖書文獻檢索系統(tǒng)她君。(插播一個八卦:百度的李彥宏在大學時候的專業(yè)是圖書館系古典文學編目脚作。)如圖,系統(tǒng)通過“數(shù)據(jù)獲取”模塊從互聯(lián)網(wǎng)上汲取網(wǎng)頁信息缔刹,然后發(fā)送給后臺“數(shù)據(jù)加工”模塊進行索引再存入數(shù)據(jù)庫球涛。在右邊面向用戶端,系統(tǒng)通過搜索框入口獲取用戶需要檢索的信息校镐,并轉(zhuǎn)發(fā)給檢索服務器亿扁,檢索服務器按照一定規(guī)則再從數(shù)據(jù)庫中抽取匹配信息返還給用戶。從頂層設計上來看鸟廓,搜索引擎不是很復雜从祝,而我們要做的就是實現(xiàn)其中每一個模塊的技術(shù)細節(jié)。
本篇的重點我將談談圖中的“數(shù)據(jù)獲取”模塊引谜,即如何處理搜索引擎的輸入源數(shù)據(jù)牍陌,也就是解決“米”的問題。對于站內(nèi)搜索引擎而言员咽,這個的問題是比較容易解決的毒涧。原因是網(wǎng)站可以直接索引站點自身的內(nèi)容或者是用戶提交的內(nèi)容,這些對于網(wǎng)站的設計者而言都是可控的贝室。比如網(wǎng)站設計者可以在每提交一個頁面到服務器前契讲,先把頁面輸入到站內(nèi)搜索引擎,等搜索引擎索引完頁面后再進行發(fā)布滑频。又比如當用戶提交的內(nèi)容需要被檢索的時候捡偏,網(wǎng)站首先將這些內(nèi)容提交給站內(nèi)搜索引擎,等搜索引擎索引完后再提交用戶的內(nèi)容到數(shù)據(jù)庫中误趴。對于如何恰當?shù)仄胶庥脩趔w驗與系統(tǒng)索引耗時之間的關系是一門有趣的藝術(shù)霹琼,不同的網(wǎng)站設計者可能有不同的看法。
從技術(shù)上來說凉当,獲取一個網(wǎng)頁是相當容易的枣申,無論你使用哪種語言作為開發(fā)工具,它們一般都會提供類似http.get()這樣的方法看杭。讀者甚至也可以直接使用socket套接字忠藤。http協(xié)議從本質(zhì)上來說,只是使用socket套接字發(fā)送的一串文本而已楼雹∧:ⅲ可以任選如下2種方式來驗證這一點:
方法1:
手工使用socket套接字在本地搭建一個server尖阔,監(jiān)聽在3000端口,偽代碼:
main()
{
???????? socket_handle=socket('0.0.0.0','3000').bind();
???????? listen(socket_handle);
???????? new_socket = accept(socket_handle, data);
???????? printf(data);
???????? return();
}
使用你的編程工具完成上述的編碼后榨咐,運行一下介却。打開瀏覽器,在地址欄里輸入"http://127.0.0.1:3000"块茁,回車齿坷,看一下server程序在屏幕上的輸出。
方法2:
如果你有l(wèi)inux操作系統(tǒng)数焊,你可以使用netcat這個神奇的命令行工具永淌。netcat也提供windows的版本,但是需要額外下載佩耳。筆者使用suse的linux遂蛀,不同的linux在參數(shù)上可能略有差別。
在linux桌面模式下調(diào)出控制臺并輸入:
??? netcat -l -p 3000
然后打開瀏覽器干厚,在地址欄里輸入"http://127.0.0.1:3000"李滴,回車,看一下server程序在屏幕上的輸出蛮瞄。
如果你嘗試了上面任何一種方式悬嗓,會在控制臺上看到一個http的請求頭,一般它會是這樣的:
我不在這里過多地討論http協(xié)議的細節(jié)裕坊,讀者可以直接使用firefox或者chrome自帶的web開發(fā)者工具來看看瀏覽器發(fā)送到其他網(wǎng)站的實際報文頭。由于大部分現(xiàn)代的編程語言都會提供http協(xié)議的庫或者方法燕酷,我們就直接忽略它的具體實現(xiàn)籍凝,而把更多的精力放到更重要的內(nèi)容上,比如下載前系統(tǒng)要做些什么準備工作或者下載完成后系統(tǒng)接著該干些什么苗缩,諸如此類的饵蒂。
通常我們需要提供一些url種子鏈接作為搜索引擎的初始下載目標,之后要做的就是從下載到的網(wǎng)頁中解析出有價值的鏈接繼續(xù)新的下載酱讶,同時對有價值的文本并做進一步的索引處理退盯。這里主要涉及到如下幾個需要解決的問題:
1:互聯(lián)網(wǎng)上的網(wǎng)站數(shù)以千萬計,搜索引擎在搜索網(wǎng)站時泻肯,是應該以深度優(yōu)先呢渊迁,還是以廣度優(yōu)先?換句話說灶挟,是應該把一個網(wǎng)站的內(nèi)容全部檢索完后再進入下一個網(wǎng)站琉朽,還是應該盡量檢索不同的網(wǎng)站,在這個基礎上再考慮檢索深度的問題稚铣。
2:網(wǎng)站上的網(wǎng)頁箱叁,特別是中文網(wǎng)站上的網(wǎng)頁墅垮,除了包含自身的主題信息外,常常還含有大量的廣告信息耕漱,這些廣告對于用戶來說是毫無意義的算色。比如一個關于兒童皮膚問題的網(wǎng)頁上可能含有女性服裝的廣告,如果系統(tǒng)不能區(qū)分這個網(wǎng)頁是關于討論兒童皮膚的問題螟够,在用戶檢索女性服飾時灾梦,就會出現(xiàn)不相關的兒童皮膚問題的網(wǎng)頁鏈接。如何剔除網(wǎng)頁垃圾信息齐鲤,獲取有用的主題信息斥废,是一個好的搜索引擎必須考慮的事情。
3:當我們打算提供中文搜索服務時给郊,需要額外面對一個英語世界中不存在的問題牡肉,或者說英語的世界里這個問題是可以被忽略的,它就是“中文分詞”淆九。英語中語義是按單個字來表示的统锤,一個字表示一個意思,那么很自然地可以在系統(tǒng)中按單個字進行拆分索引炭庙。而中文常常是使用多個字的組合來表示一個意思饲窿,單個字本身并不能代表這個詞組的意思。比如“中文”這個詞組指的是中國人使用的文字或者語言焕蹄。單個字“中”和“文”逾雄,分別對應地理方位的正中間和文章,他們與“中文”的含義相去甚遠腻脏。所以我們在做搜索引擎的時候鸦泳,需要考慮如何來實現(xiàn)按詞組來構(gòu)建語義的中文分詞系統(tǒng)。
4:我們從網(wǎng)頁中獲取url鏈接時永品,難免會遇到已經(jīng)獲取過的鏈接做鹰,如果系統(tǒng)不能辨識這些冗余鏈接地址,就會造成重復勞動鼎姐,不僅浪費計算資源钾麸,最差情況下,系統(tǒng)會陷入無限循環(huán)處理同一個資源集合的深淵中炕桨。當鏈接地址上億級的時候饭尝,發(fā)現(xiàn)重復鏈接就變成了一個挑戰(zhàn)。
這幾個問題我會在后面專門的專題中進行講述谋作,現(xiàn)在假設這些問題都已經(jīng)解決了芋肠。我們來看看“數(shù)據(jù)獲取”模塊的實際實現(xiàn)。
“HTTP進程調(diào)度”占據(jù)整個模塊的心臟地位遵蚜,它負責從“URL池管理器”處獲取有效URL鏈接帖池,然后調(diào)度起一個“http通信”進程去互聯(lián)網(wǎng)上拿URL對應的網(wǎng)頁奈惑,之后再把下載到的網(wǎng)頁發(fā)送給“網(wǎng)頁解析器”∷冢“網(wǎng)頁解析器”的主要工作是抽取網(wǎng)頁中的有效文字信息和有效URL鏈接地址肴甸,并分別發(fā)送給下一級的“數(shù)據(jù)加工”模塊和“URL池管理器”∏舭停“URL池管理器”解決我們前面提到的問題1和問題4原在。“網(wǎng)頁解析器”則是解決問題2彤叉。問題3則是留給下一級的“數(shù)據(jù)加工”去處理了庶柿。