
網(wǎng)絡(luò )地址
Go語(yǔ)言網(wǎng)絡(luò )爬蟲(chóng)概述
采集交流 ? 優(yōu)采云 發(fā)表了文章 ? 0 個(gè)評論 ? 350 次瀏覽 ? 2020-05-13 08:03
簡(jiǎn)單來(lái)說(shuō),網(wǎng)絡(luò )爬蟲(chóng)是互聯(lián)網(wǎng)終端用戶(hù)的模仿者。它模仿的主要對象有兩個(gè),一個(gè)是坐在計算器前使用網(wǎng)絡(luò )瀏覽器訪(fǎng)問(wèn)網(wǎng)路內容的人類(lèi)用戶(hù),另一個(gè)就是網(wǎng)絡(luò )瀏覽器。
網(wǎng)絡(luò )爬蟲(chóng)會(huì )模仿人類(lèi)用戶(hù)輸入某個(gè)網(wǎng)站的網(wǎng)路地址,并企圖訪(fǎng)問(wèn)該網(wǎng)站上的內容,還會(huì )模仿網(wǎng)路瀏覽器按照給定的網(wǎng)路地址去下載相應的內容。這里所說(shuō)的內容可以是 HTML 頁(yè)面、圖片文件、音視頻數據流,等等。
在下載到對應的內容以后,網(wǎng)絡(luò )爬蟲(chóng)會(huì )按照預設的規則對它進(jìn)行剖析和篩選。這些篩選岀的部份會(huì )馬上得到特定的處理。與此同時(shí),網(wǎng)絡(luò )爬蟲(chóng)都會(huì )象人類(lèi)用戶(hù)點(diǎn)擊網(wǎng)頁(yè)中某個(gè)他感興趣的鏈接那樣,繼續訪(fǎng)問(wèn)和下載相關(guān)聯(lián)的其他內容,然后再重復上述步驟,直到滿(mǎn)足停止的條件。
如上所述,網(wǎng)絡(luò )爬蟲(chóng)應當按照使用者的意愿手動(dòng)下載、分析、篩選、統計以及儲存指定的網(wǎng)路內容。注意,這里的關(guān)鍵詞是“自動(dòng)”和“根據意愿”?!白詣?dòng)”的涵義是,網(wǎng)絡(luò )爬蟲(chóng)在啟動(dòng)后自己完成整個(gè)爬取過(guò)程而無(wú)需人工干預,并且能夠夠在過(guò)程結束以后手動(dòng)停止。而“根據意愿”則是說(shuō),網(wǎng)絡(luò )爬蟲(chóng)最大限度地準許使用者對其爬取過(guò)程進(jìn)行訂制。
乍一看,要做到手動(dòng)爬取其實(shí)并不困難。我們只需使網(wǎng)路爬蟲(chóng)依據相關(guān)的網(wǎng)路地址不斷地下載對應的內容即可。但是,窺探其中就可以發(fā)覺(jué),這里有很多細節須要我們進(jìn)行非常處理,如下所示。
在這種細節當中,有的是比較容易處理的,而有的則須要額外的解決方案。例如,我們都曉得,基于 HTML 的網(wǎng)頁(yè)中可以包含代表按鍵的 button 標簽。
讓網(wǎng)絡(luò )瀏覽器在終端用戶(hù)點(diǎn)擊按鍵的時(shí)侯加載并顯示另一個(gè)網(wǎng)頁(yè)可以有很多種方式,其中,非常常用的一種方式就是為該標簽添加 onclick 屬性并把一些 JavaScript 語(yǔ)言的代碼作為它的值。
雖然這個(gè)方式這么常用,但是我們要想使網(wǎng)絡(luò )爬蟲(chóng)可以從中提取出有效的網(wǎng)路地址卻是比較 困難的,因為這涉及對JavaScript程序的理解。JavaScript代碼的編撰方式繁雜,要想使 網(wǎng)絡(luò )爬蟲(chóng)完全理解它們,恐怕就須要用到某個(gè)JavaScript程序解析器的 Go語(yǔ)言實(shí)現了。
另一方面,由于互聯(lián)網(wǎng)對人們生活和工作的全面滲透,我們可以通過(guò)各類(lèi)途徑找到各式各樣的網(wǎng)路爬蟲(chóng)實(shí)現,它們幾乎都有著(zhù)復雜而又奇特的邏輯。這些復雜的邏輯主要針對如下幾個(gè)方面。
這些邏輯絕大多數都與網(wǎng)路爬蟲(chóng)使用者當時(shí)的意愿有關(guān)。換句話(huà)說(shuō),它們都與具體的使用目的有著(zhù)緊密的聯(lián)系。也許它們并不應當是網(wǎng)路爬蟲(chóng)的核心功能,而應當作為擴充功能或可訂制的功能存在。
因此,我想我們更應當編撰一個(gè)容易被訂制和擴充的網(wǎng)路爬蟲(chóng)框架,而非一個(gè)滿(mǎn)足特定爬取目的的網(wǎng)路爬蟲(chóng),這樣就能讓這個(gè)程序成為一個(gè)可適用于不同應用場(chǎng)景的通用工具。
既然這么,接下來(lái)我們就要搞清楚該程序應當或可以做什么事,這也就能使我們進(jìn)一步明晰它的功能、用途和意義。
功能需求和剖析概括來(lái)講,網(wǎng)絡(luò )爬蟲(chóng)框架會(huì )反復執行如下步驟直到觸遇到停止條件。
1) “下載器”下載與給定網(wǎng)路地址相對應的內容。其中,在下載“請求”的組裝方面,網(wǎng)絡(luò )爬蟲(chóng)框架為使用者盡量預留出訂制插口。使用者可以使用這種插口自定義“請求”的組裝方式。
2) “分析器”分析下載到的內容,并從中篩選出可用的部份(以下稱(chēng)為“條目”)和須要訪(fǎng)問(wèn)的新網(wǎng)路地址。其中,在用于剖析和篩選內容的規則和策略方面,應該由網(wǎng)路爬蟲(chóng)框架提供靈活的訂制插口。
換句話(huà)說(shuō),由于只有使用者自己才曉得她們真正想要的是哪些,所以應當準許她們對這種規則和策略進(jìn)行深入的訂制。網(wǎng)絡(luò )爬蟲(chóng)框架僅須要規定好訂制的方法即可。
3) “分析器”把篩選出的“條目”發(fā)送給“條目處理管線(xiàn)”。同時(shí),它會(huì )把發(fā)覺(jué)的新網(wǎng)路地址和其他一些信息組裝成新的下載“請求”,然后把這種懇求發(fā)送給“下載器”。在此步驟中,我們會(huì )過(guò)濾掉一些不符合要求的網(wǎng)路地址,比如忽視超出有效邊界的網(wǎng)路地址。
你可能早已注意到,在這幾個(gè)步驟中,我使用冒號突出展示了幾個(gè)名詞,即下載器、請求、分析器、條目和條目處理管線(xiàn),其中,請求和條目都代表了某類(lèi)數據,而其他 3 個(gè)名詞則代表了處理數據的子程序(可稱(chēng)為處理模塊或組件)。
它們與上面早已提及過(guò)的網(wǎng)路內容(或稱(chēng)對懇求的響應)共同描述了數據在網(wǎng)路爬蟲(chóng)程序中的流轉形式。下圖演示了起始于首次懇求的數據流程圖。
圖:起始于首次懇求的數據流程圖
從上圖中,我們可以清晰地看見(jiàn)每一個(gè)處理模塊才能接受的輸入和可以形成的輸出。實(shí)際上,我們即將編撰的網(wǎng)路爬蟲(chóng)框架都會(huì )借此為根據產(chǎn)生幾個(gè)相對獨立的子程序。
當然,為了維護它們的運行和協(xié)作的有效性,框架中都會(huì )存在其他一些子程序。關(guān)于它們,我會(huì )在旁邊相繼給以說(shuō)明。
這里,我再度指出一下網(wǎng)路爬蟲(chóng)框架與網(wǎng)路爬蟲(chóng)實(shí)現的區別。作為一個(gè)框架,該程序在每位處理模塊中給與使用者盡量多的訂制技巧,而不去涉及各個(gè)處理步驟的實(shí)現細節。
另外,框架更多地考慮使用者自定義的處理步驟在執行期間可能發(fā)生的各類(lèi)情況和問(wèn)題,并注意對這種問(wèn)題的處理方法,這樣就能在便于擴充的同時(shí)保證框架的穩定性。這方面的思索和策略會(huì )彰顯在該網(wǎng)路爬蟲(chóng)框架的各階段設計和編碼實(shí)現之中。
下面我就依照上述剖析對這一程序進(jìn)行總體設計。
總體設計通過(guò)上圖可知,網(wǎng)絡(luò )爬蟲(chóng)框架的處理模塊有 3 個(gè):下載器、分析器和條目處理管線(xiàn)。再加上調度和協(xié)調那些處理模塊運行的控制模塊,我們就可以明確該框架的模塊界定了。我把這兒提及的控制模塊稱(chēng)為調度器。下面是這 4 個(gè)模塊各自承當的職責。
1) 下載器接受懇求類(lèi)型的數據,并根據該懇求獲得 HTTP 請求;將 HTTP 請求發(fā)送至與指定的網(wǎng)路地址對應的遠程服務(wù)器;在 HTTP 請求發(fā)送完畢以后,立即等待相應的 HTTP 響應的到來(lái);在收到 HTTP 響應以后,將其封裝成響應并作為輸出返回給下載器的調用方。
其中,HTTP 客戶(hù)端程序可以由網(wǎng)路爬蟲(chóng)框架的使用方自行定義。另外,若在該子流程執行期間發(fā)生了錯誤,應該立刻以適當的方法告知使用方。對于其他模塊來(lái)講,也是這樣。
2) 分析器接受響應類(lèi)型的數據,并根據該響應獲得 HTTP 響應;對該 HTTP 響應的內容進(jìn)行檢測,并按照給定的規則進(jìn)行剖析、篩選以及生成新的懇求或條目;將生成的懇求或條目作為輸出返回給分析器的調用方。
在分析器的職責中,我可以想到的才能留給網(wǎng)路爬蟲(chóng)框架的使用方自定義的部份并不少。例如,對 HTTP 響應的前期檢測、對內容的篩選,以及生成懇求和條目的方法,等等。不過(guò),我在前面會(huì )對那些可以自定義的部份進(jìn)行一些抉擇。
3) 條目處理管線(xiàn)接受條目類(lèi)型的數據,并對其執行若干步驟的處理;條目處理管線(xiàn)中可以產(chǎn)出最終的數據;這個(gè)最終的數據可以在其中的某個(gè)處理步驟中被持久化(不論是本地儲存還是發(fā)送給遠程的儲存服務(wù)器)以備后用。
我們可以把這種處理步驟的具體實(shí)現留給網(wǎng)路爬蟲(chóng)框架的使用方自行定義。這樣,網(wǎng)絡(luò )爬蟲(chóng)框架就可以真正地與條目處理的細節脫離開(kāi)來(lái)。網(wǎng)絡(luò )爬蟲(chóng)框架絲毫不關(guān)心那些條目如何被處理和持久化,它僅僅負責控制整體的處理流程。我把負責單個(gè)處理步驟的程序稱(chēng)為條目處理器。
條目處理器接受條目類(lèi)型的數據,并把處理完成的條目返回給條目處理管線(xiàn)。條目處理管線(xiàn)會(huì )緊接著(zhù)把該條目傳遞給下一個(gè)條目處理器,直至給定的條目處理器列表中的每位條目處理器都處理過(guò)該條目為止。
4) 調度器調度器在啟動(dòng)時(shí)僅接受首次懇求,并且不會(huì )形成任何輸出。調度器的主要職責是調度各個(gè)處理模塊的運行。其中包括維護各個(gè)處理模塊的實(shí)例、在不同的處理模塊實(shí)例之間傳遞數據(包括懇求、響應和條目),以及監控所有那些被調度者的狀態(tài),等等。
有了調度器的維護,各個(gè)處理模塊得以保持其職責的簡(jiǎn)約和專(zhuān)情。由于調度器是網(wǎng)路爬蟲(chóng)框架中最重要的一個(gè)模塊,所以還須要再編撰出一些工具來(lái)支撐起它的功能。
在弄清楚網(wǎng)路爬蟲(chóng)框架中各個(gè)模塊的職責以后網(wǎng)絡(luò )爬蟲(chóng) 語(yǔ)言,你曉得它是以調度器為核心的。此外,為了并發(fā)執行的須要,除調度器之外的其他模塊都可以是多實(shí)例的,它們由調度器持有、維護和調用。反過(guò)來(lái)講,這些處理模塊的實(shí)例會(huì )從調度器那兒接受輸入,并在進(jìn)行相應的處理后將輸出返回給調度器。
最后,與另外兩個(gè)處理模塊相比,條目處理管線(xiàn)是比較特殊的。顧名思義,它是以流式處理為基礎的,其設計靈感來(lái)自于我之前講過(guò)的 Linux 系統中的管線(xiàn)。
我們可以不斷地向該管線(xiàn)發(fā)送條目,而該管線(xiàn)則會(huì )使其中的若干個(gè)條目處理器依次處理每一個(gè)條目。我們可以挺輕易地使用一些同步方式來(lái)保證條目處理管線(xiàn)的并發(fā)安全性,因此雖然調度器只持有該管線(xiàn)的一個(gè)實(shí)例,也不會(huì )有任何問(wèn)題。
下圖展示了調度器與各個(gè)處理模塊之間的關(guān)系,圖中加入了一個(gè)新的元素——工具箱網(wǎng)絡(luò )爬蟲(chóng) 語(yǔ)言,之前所說(shuō)的用于支撐調度器功能的這些工具就是工具箱的一部分。顧名思義,工具箱不是一個(gè)完整的模塊,而是一些工具的集合,這些工具是調度器與所有處理模塊之間的橋梁。
圖:調度器與各處理模塊的關(guān)系
至此,大家對網(wǎng)路爬蟲(chóng)框架的設計有了一個(gè)宏觀(guān)上的認識。不過(guò),我還未提到在這個(gè)總體設計之下包含的大量設計方法和決策。這些方法和決策不但與一些通用的程序設計原則有關(guān),還涉及好多依賴(lài)于 Go語(yǔ)言的編程風(fēng)格和形式技巧。
這也從側面說(shuō)明,由于幾乎所有語(yǔ)言都有著(zhù)十分鮮明的特征和比較擅長(cháng)的領(lǐng)域,所以在設計一個(gè)須要由特定語(yǔ)言實(shí)現的軟件或程序時(shí),多多少少會(huì )考慮到這門(mén)語(yǔ)言自身的特點(diǎn)。也就是說(shuō),軟件設計不是與具體的語(yǔ)言毫不相關(guān)的。反過(guò)來(lái)講,總會(huì )有一門(mén)或幾門(mén)語(yǔ)言十分適宜實(shí)現某一類(lèi)軟件或程序。 查看全部

簡(jiǎn)單來(lái)說(shuō),網(wǎng)絡(luò )爬蟲(chóng)是互聯(lián)網(wǎng)終端用戶(hù)的模仿者。它模仿的主要對象有兩個(gè),一個(gè)是坐在計算器前使用網(wǎng)絡(luò )瀏覽器訪(fǎng)問(wèn)網(wǎng)路內容的人類(lèi)用戶(hù),另一個(gè)就是網(wǎng)絡(luò )瀏覽器。
網(wǎng)絡(luò )爬蟲(chóng)會(huì )模仿人類(lèi)用戶(hù)輸入某個(gè)網(wǎng)站的網(wǎng)路地址,并企圖訪(fǎng)問(wèn)該網(wǎng)站上的內容,還會(huì )模仿網(wǎng)路瀏覽器按照給定的網(wǎng)路地址去下載相應的內容。這里所說(shuō)的內容可以是 HTML 頁(yè)面、圖片文件、音視頻數據流,等等。
在下載到對應的內容以后,網(wǎng)絡(luò )爬蟲(chóng)會(huì )按照預設的規則對它進(jìn)行剖析和篩選。這些篩選岀的部份會(huì )馬上得到特定的處理。與此同時(shí),網(wǎng)絡(luò )爬蟲(chóng)都會(huì )象人類(lèi)用戶(hù)點(diǎn)擊網(wǎng)頁(yè)中某個(gè)他感興趣的鏈接那樣,繼續訪(fǎng)問(wèn)和下載相關(guān)聯(lián)的其他內容,然后再重復上述步驟,直到滿(mǎn)足停止的條件。
如上所述,網(wǎng)絡(luò )爬蟲(chóng)應當按照使用者的意愿手動(dòng)下載、分析、篩選、統計以及儲存指定的網(wǎng)路內容。注意,這里的關(guān)鍵詞是“自動(dòng)”和“根據意愿”?!白詣?dòng)”的涵義是,網(wǎng)絡(luò )爬蟲(chóng)在啟動(dòng)后自己完成整個(gè)爬取過(guò)程而無(wú)需人工干預,并且能夠夠在過(guò)程結束以后手動(dòng)停止。而“根據意愿”則是說(shuō),網(wǎng)絡(luò )爬蟲(chóng)最大限度地準許使用者對其爬取過(guò)程進(jìn)行訂制。
乍一看,要做到手動(dòng)爬取其實(shí)并不困難。我們只需使網(wǎng)路爬蟲(chóng)依據相關(guān)的網(wǎng)路地址不斷地下載對應的內容即可。但是,窺探其中就可以發(fā)覺(jué),這里有很多細節須要我們進(jìn)行非常處理,如下所示。
在這種細節當中,有的是比較容易處理的,而有的則須要額外的解決方案。例如,我們都曉得,基于 HTML 的網(wǎng)頁(yè)中可以包含代表按鍵的 button 標簽。
讓網(wǎng)絡(luò )瀏覽器在終端用戶(hù)點(diǎn)擊按鍵的時(shí)侯加載并顯示另一個(gè)網(wǎng)頁(yè)可以有很多種方式,其中,非常常用的一種方式就是為該標簽添加 onclick 屬性并把一些 JavaScript 語(yǔ)言的代碼作為它的值。
雖然這個(gè)方式這么常用,但是我們要想使網(wǎng)絡(luò )爬蟲(chóng)可以從中提取出有效的網(wǎng)路地址卻是比較 困難的,因為這涉及對JavaScript程序的理解。JavaScript代碼的編撰方式繁雜,要想使 網(wǎng)絡(luò )爬蟲(chóng)完全理解它們,恐怕就須要用到某個(gè)JavaScript程序解析器的 Go語(yǔ)言實(shí)現了。
另一方面,由于互聯(lián)網(wǎng)對人們生活和工作的全面滲透,我們可以通過(guò)各類(lèi)途徑找到各式各樣的網(wǎng)路爬蟲(chóng)實(shí)現,它們幾乎都有著(zhù)復雜而又奇特的邏輯。這些復雜的邏輯主要針對如下幾個(gè)方面。
這些邏輯絕大多數都與網(wǎng)路爬蟲(chóng)使用者當時(shí)的意愿有關(guān)。換句話(huà)說(shuō),它們都與具體的使用目的有著(zhù)緊密的聯(lián)系。也許它們并不應當是網(wǎng)路爬蟲(chóng)的核心功能,而應當作為擴充功能或可訂制的功能存在。
因此,我想我們更應當編撰一個(gè)容易被訂制和擴充的網(wǎng)路爬蟲(chóng)框架,而非一個(gè)滿(mǎn)足特定爬取目的的網(wǎng)路爬蟲(chóng),這樣就能讓這個(gè)程序成為一個(gè)可適用于不同應用場(chǎng)景的通用工具。
既然這么,接下來(lái)我們就要搞清楚該程序應當或可以做什么事,這也就能使我們進(jìn)一步明晰它的功能、用途和意義。
功能需求和剖析概括來(lái)講,網(wǎng)絡(luò )爬蟲(chóng)框架會(huì )反復執行如下步驟直到觸遇到停止條件。
1) “下載器”下載與給定網(wǎng)路地址相對應的內容。其中,在下載“請求”的組裝方面,網(wǎng)絡(luò )爬蟲(chóng)框架為使用者盡量預留出訂制插口。使用者可以使用這種插口自定義“請求”的組裝方式。
2) “分析器”分析下載到的內容,并從中篩選出可用的部份(以下稱(chēng)為“條目”)和須要訪(fǎng)問(wèn)的新網(wǎng)路地址。其中,在用于剖析和篩選內容的規則和策略方面,應該由網(wǎng)路爬蟲(chóng)框架提供靈活的訂制插口。
換句話(huà)說(shuō),由于只有使用者自己才曉得她們真正想要的是哪些,所以應當準許她們對這種規則和策略進(jìn)行深入的訂制。網(wǎng)絡(luò )爬蟲(chóng)框架僅須要規定好訂制的方法即可。
3) “分析器”把篩選出的“條目”發(fā)送給“條目處理管線(xiàn)”。同時(shí),它會(huì )把發(fā)覺(jué)的新網(wǎng)路地址和其他一些信息組裝成新的下載“請求”,然后把這種懇求發(fā)送給“下載器”。在此步驟中,我們會(huì )過(guò)濾掉一些不符合要求的網(wǎng)路地址,比如忽視超出有效邊界的網(wǎng)路地址。
你可能早已注意到,在這幾個(gè)步驟中,我使用冒號突出展示了幾個(gè)名詞,即下載器、請求、分析器、條目和條目處理管線(xiàn),其中,請求和條目都代表了某類(lèi)數據,而其他 3 個(gè)名詞則代表了處理數據的子程序(可稱(chēng)為處理模塊或組件)。
它們與上面早已提及過(guò)的網(wǎng)路內容(或稱(chēng)對懇求的響應)共同描述了數據在網(wǎng)路爬蟲(chóng)程序中的流轉形式。下圖演示了起始于首次懇求的數據流程圖。

圖:起始于首次懇求的數據流程圖
從上圖中,我們可以清晰地看見(jiàn)每一個(gè)處理模塊才能接受的輸入和可以形成的輸出。實(shí)際上,我們即將編撰的網(wǎng)路爬蟲(chóng)框架都會(huì )借此為根據產(chǎn)生幾個(gè)相對獨立的子程序。
當然,為了維護它們的運行和協(xié)作的有效性,框架中都會(huì )存在其他一些子程序。關(guān)于它們,我會(huì )在旁邊相繼給以說(shuō)明。
這里,我再度指出一下網(wǎng)路爬蟲(chóng)框架與網(wǎng)路爬蟲(chóng)實(shí)現的區別。作為一個(gè)框架,該程序在每位處理模塊中給與使用者盡量多的訂制技巧,而不去涉及各個(gè)處理步驟的實(shí)現細節。
另外,框架更多地考慮使用者自定義的處理步驟在執行期間可能發(fā)生的各類(lèi)情況和問(wèn)題,并注意對這種問(wèn)題的處理方法,這樣就能在便于擴充的同時(shí)保證框架的穩定性。這方面的思索和策略會(huì )彰顯在該網(wǎng)路爬蟲(chóng)框架的各階段設計和編碼實(shí)現之中。
下面我就依照上述剖析對這一程序進(jìn)行總體設計。
總體設計通過(guò)上圖可知,網(wǎng)絡(luò )爬蟲(chóng)框架的處理模塊有 3 個(gè):下載器、分析器和條目處理管線(xiàn)。再加上調度和協(xié)調那些處理模塊運行的控制模塊,我們就可以明確該框架的模塊界定了。我把這兒提及的控制模塊稱(chēng)為調度器。下面是這 4 個(gè)模塊各自承當的職責。
1) 下載器接受懇求類(lèi)型的數據,并根據該懇求獲得 HTTP 請求;將 HTTP 請求發(fā)送至與指定的網(wǎng)路地址對應的遠程服務(wù)器;在 HTTP 請求發(fā)送完畢以后,立即等待相應的 HTTP 響應的到來(lái);在收到 HTTP 響應以后,將其封裝成響應并作為輸出返回給下載器的調用方。
其中,HTTP 客戶(hù)端程序可以由網(wǎng)路爬蟲(chóng)框架的使用方自行定義。另外,若在該子流程執行期間發(fā)生了錯誤,應該立刻以適當的方法告知使用方。對于其他模塊來(lái)講,也是這樣。
2) 分析器接受響應類(lèi)型的數據,并根據該響應獲得 HTTP 響應;對該 HTTP 響應的內容進(jìn)行檢測,并按照給定的規則進(jìn)行剖析、篩選以及生成新的懇求或條目;將生成的懇求或條目作為輸出返回給分析器的調用方。
在分析器的職責中,我可以想到的才能留給網(wǎng)路爬蟲(chóng)框架的使用方自定義的部份并不少。例如,對 HTTP 響應的前期檢測、對內容的篩選,以及生成懇求和條目的方法,等等。不過(guò),我在前面會(huì )對那些可以自定義的部份進(jìn)行一些抉擇。
3) 條目處理管線(xiàn)接受條目類(lèi)型的數據,并對其執行若干步驟的處理;條目處理管線(xiàn)中可以產(chǎn)出最終的數據;這個(gè)最終的數據可以在其中的某個(gè)處理步驟中被持久化(不論是本地儲存還是發(fā)送給遠程的儲存服務(wù)器)以備后用。
我們可以把這種處理步驟的具體實(shí)現留給網(wǎng)路爬蟲(chóng)框架的使用方自行定義。這樣,網(wǎng)絡(luò )爬蟲(chóng)框架就可以真正地與條目處理的細節脫離開(kāi)來(lái)。網(wǎng)絡(luò )爬蟲(chóng)框架絲毫不關(guān)心那些條目如何被處理和持久化,它僅僅負責控制整體的處理流程。我把負責單個(gè)處理步驟的程序稱(chēng)為條目處理器。
條目處理器接受條目類(lèi)型的數據,并把處理完成的條目返回給條目處理管線(xiàn)。條目處理管線(xiàn)會(huì )緊接著(zhù)把該條目傳遞給下一個(gè)條目處理器,直至給定的條目處理器列表中的每位條目處理器都處理過(guò)該條目為止。
4) 調度器調度器在啟動(dòng)時(shí)僅接受首次懇求,并且不會(huì )形成任何輸出。調度器的主要職責是調度各個(gè)處理模塊的運行。其中包括維護各個(gè)處理模塊的實(shí)例、在不同的處理模塊實(shí)例之間傳遞數據(包括懇求、響應和條目),以及監控所有那些被調度者的狀態(tài),等等。
有了調度器的維護,各個(gè)處理模塊得以保持其職責的簡(jiǎn)約和專(zhuān)情。由于調度器是網(wǎng)路爬蟲(chóng)框架中最重要的一個(gè)模塊,所以還須要再編撰出一些工具來(lái)支撐起它的功能。
在弄清楚網(wǎng)路爬蟲(chóng)框架中各個(gè)模塊的職責以后網(wǎng)絡(luò )爬蟲(chóng) 語(yǔ)言,你曉得它是以調度器為核心的。此外,為了并發(fā)執行的須要,除調度器之外的其他模塊都可以是多實(shí)例的,它們由調度器持有、維護和調用。反過(guò)來(lái)講,這些處理模塊的實(shí)例會(huì )從調度器那兒接受輸入,并在進(jìn)行相應的處理后將輸出返回給調度器。
最后,與另外兩個(gè)處理模塊相比,條目處理管線(xiàn)是比較特殊的。顧名思義,它是以流式處理為基礎的,其設計靈感來(lái)自于我之前講過(guò)的 Linux 系統中的管線(xiàn)。
我們可以不斷地向該管線(xiàn)發(fā)送條目,而該管線(xiàn)則會(huì )使其中的若干個(gè)條目處理器依次處理每一個(gè)條目。我們可以挺輕易地使用一些同步方式來(lái)保證條目處理管線(xiàn)的并發(fā)安全性,因此雖然調度器只持有該管線(xiàn)的一個(gè)實(shí)例,也不會(huì )有任何問(wèn)題。
下圖展示了調度器與各個(gè)處理模塊之間的關(guān)系,圖中加入了一個(gè)新的元素——工具箱網(wǎng)絡(luò )爬蟲(chóng) 語(yǔ)言,之前所說(shuō)的用于支撐調度器功能的這些工具就是工具箱的一部分。顧名思義,工具箱不是一個(gè)完整的模塊,而是一些工具的集合,這些工具是調度器與所有處理模塊之間的橋梁。

圖:調度器與各處理模塊的關(guān)系
至此,大家對網(wǎng)路爬蟲(chóng)框架的設計有了一個(gè)宏觀(guān)上的認識。不過(guò),我還未提到在這個(gè)總體設計之下包含的大量設計方法和決策。這些方法和決策不但與一些通用的程序設計原則有關(guān),還涉及好多依賴(lài)于 Go語(yǔ)言的編程風(fēng)格和形式技巧。
這也從側面說(shuō)明,由于幾乎所有語(yǔ)言都有著(zhù)十分鮮明的特征和比較擅長(cháng)的領(lǐng)域,所以在設計一個(gè)須要由特定語(yǔ)言實(shí)現的軟件或程序時(shí),多多少少會(huì )考慮到這門(mén)語(yǔ)言自身的特點(diǎn)。也就是說(shuō),軟件設計不是與具體的語(yǔ)言毫不相關(guān)的。反過(guò)來(lái)講,總會(huì )有一門(mén)或幾門(mén)語(yǔ)言十分適宜實(shí)現某一類(lèi)軟件或程序。
Go語(yǔ)言網(wǎng)絡(luò )爬蟲(chóng)概述
采集交流 ? 優(yōu)采云 發(fā)表了文章 ? 0 個(gè)評論 ? 350 次瀏覽 ? 2020-05-13 08:03
簡(jiǎn)單來(lái)說(shuō),網(wǎng)絡(luò )爬蟲(chóng)是互聯(lián)網(wǎng)終端用戶(hù)的模仿者。它模仿的主要對象有兩個(gè),一個(gè)是坐在計算器前使用網(wǎng)絡(luò )瀏覽器訪(fǎng)問(wèn)網(wǎng)路內容的人類(lèi)用戶(hù),另一個(gè)就是網(wǎng)絡(luò )瀏覽器。
網(wǎng)絡(luò )爬蟲(chóng)會(huì )模仿人類(lèi)用戶(hù)輸入某個(gè)網(wǎng)站的網(wǎng)路地址,并企圖訪(fǎng)問(wèn)該網(wǎng)站上的內容,還會(huì )模仿網(wǎng)路瀏覽器按照給定的網(wǎng)路地址去下載相應的內容。這里所說(shuō)的內容可以是 HTML 頁(yè)面、圖片文件、音視頻數據流,等等。
在下載到對應的內容以后,網(wǎng)絡(luò )爬蟲(chóng)會(huì )按照預設的規則對它進(jìn)行剖析和篩選。這些篩選岀的部份會(huì )馬上得到特定的處理。與此同時(shí),網(wǎng)絡(luò )爬蟲(chóng)都會(huì )象人類(lèi)用戶(hù)點(diǎn)擊網(wǎng)頁(yè)中某個(gè)他感興趣的鏈接那樣,繼續訪(fǎng)問(wèn)和下載相關(guān)聯(lián)的其他內容,然后再重復上述步驟,直到滿(mǎn)足停止的條件。
如上所述,網(wǎng)絡(luò )爬蟲(chóng)應當按照使用者的意愿手動(dòng)下載、分析、篩選、統計以及儲存指定的網(wǎng)路內容。注意,這里的關(guān)鍵詞是“自動(dòng)”和“根據意愿”?!白詣?dòng)”的涵義是,網(wǎng)絡(luò )爬蟲(chóng)在啟動(dòng)后自己完成整個(gè)爬取過(guò)程而無(wú)需人工干預,并且能夠夠在過(guò)程結束以后手動(dòng)停止。而“根據意愿”則是說(shuō),網(wǎng)絡(luò )爬蟲(chóng)最大限度地準許使用者對其爬取過(guò)程進(jìn)行訂制。
乍一看,要做到手動(dòng)爬取其實(shí)并不困難。我們只需使網(wǎng)路爬蟲(chóng)依據相關(guān)的網(wǎng)路地址不斷地下載對應的內容即可。但是,窺探其中就可以發(fā)覺(jué),這里有很多細節須要我們進(jìn)行非常處理,如下所示。
在這種細節當中,有的是比較容易處理的,而有的則須要額外的解決方案。例如,我們都曉得,基于 HTML 的網(wǎng)頁(yè)中可以包含代表按鍵的 button 標簽。
讓網(wǎng)絡(luò )瀏覽器在終端用戶(hù)點(diǎn)擊按鍵的時(shí)侯加載并顯示另一個(gè)網(wǎng)頁(yè)可以有很多種方式,其中,非常常用的一種方式就是為該標簽添加 onclick 屬性并把一些 JavaScript 語(yǔ)言的代碼作為它的值。
雖然這個(gè)方式這么常用,但是我們要想使網(wǎng)絡(luò )爬蟲(chóng)可以從中提取出有效的網(wǎng)路地址卻是比較 困難的,因為這涉及對JavaScript程序的理解。JavaScript代碼的編撰方式繁雜,要想使 網(wǎng)絡(luò )爬蟲(chóng)完全理解它們,恐怕就須要用到某個(gè)JavaScript程序解析器的 Go語(yǔ)言實(shí)現了。
另一方面,由于互聯(lián)網(wǎng)對人們生活和工作的全面滲透,我們可以通過(guò)各類(lèi)途徑找到各式各樣的網(wǎng)路爬蟲(chóng)實(shí)現,它們幾乎都有著(zhù)復雜而又奇特的邏輯。這些復雜的邏輯主要針對如下幾個(gè)方面。
這些邏輯絕大多數都與網(wǎng)路爬蟲(chóng)使用者當時(shí)的意愿有關(guān)。換句話(huà)說(shuō),它們都與具體的使用目的有著(zhù)緊密的聯(lián)系。也許它們并不應當是網(wǎng)路爬蟲(chóng)的核心功能,而應當作為擴充功能或可訂制的功能存在。
因此,我想我們更應當編撰一個(gè)容易被訂制和擴充的網(wǎng)路爬蟲(chóng)框架,而非一個(gè)滿(mǎn)足特定爬取目的的網(wǎng)路爬蟲(chóng),這樣就能讓這個(gè)程序成為一個(gè)可適用于不同應用場(chǎng)景的通用工具。
既然這么,接下來(lái)我們就要搞清楚該程序應當或可以做什么事,這也就能使我們進(jìn)一步明晰它的功能、用途和意義。
功能需求和剖析概括來(lái)講,網(wǎng)絡(luò )爬蟲(chóng)框架會(huì )反復執行如下步驟直到觸遇到停止條件。
1) “下載器”下載與給定網(wǎng)路地址相對應的內容。其中,在下載“請求”的組裝方面,網(wǎng)絡(luò )爬蟲(chóng)框架為使用者盡量預留出訂制插口。使用者可以使用這種插口自定義“請求”的組裝方式。
2) “分析器”分析下載到的內容,并從中篩選出可用的部份(以下稱(chēng)為“條目”)和須要訪(fǎng)問(wèn)的新網(wǎng)路地址。其中,在用于剖析和篩選內容的規則和策略方面,應該由網(wǎng)路爬蟲(chóng)框架提供靈活的訂制插口。
換句話(huà)說(shuō),由于只有使用者自己才曉得她們真正想要的是哪些,所以應當準許她們對這種規則和策略進(jìn)行深入的訂制。網(wǎng)絡(luò )爬蟲(chóng)框架僅須要規定好訂制的方法即可。
3) “分析器”把篩選出的“條目”發(fā)送給“條目處理管線(xiàn)”。同時(shí),它會(huì )把發(fā)覺(jué)的新網(wǎng)路地址和其他一些信息組裝成新的下載“請求”,然后把這種懇求發(fā)送給“下載器”。在此步驟中,我們會(huì )過(guò)濾掉一些不符合要求的網(wǎng)路地址,比如忽視超出有效邊界的網(wǎng)路地址。
你可能早已注意到,在這幾個(gè)步驟中,我使用冒號突出展示了幾個(gè)名詞,即下載器、請求、分析器、條目和條目處理管線(xiàn),其中,請求和條目都代表了某類(lèi)數據,而其他 3 個(gè)名詞則代表了處理數據的子程序(可稱(chēng)為處理模塊或組件)。
它們與上面早已提及過(guò)的網(wǎng)路內容(或稱(chēng)對懇求的響應)共同描述了數據在網(wǎng)路爬蟲(chóng)程序中的流轉形式。下圖演示了起始于首次懇求的數據流程圖。
圖:起始于首次懇求的數據流程圖
從上圖中,我們可以清晰地看見(jiàn)每一個(gè)處理模塊才能接受的輸入和可以形成的輸出。實(shí)際上,我們即將編撰的網(wǎng)路爬蟲(chóng)框架都會(huì )借此為根據產(chǎn)生幾個(gè)相對獨立的子程序。
當然,為了維護它們的運行和協(xié)作的有效性,框架中都會(huì )存在其他一些子程序。關(guān)于它們,我會(huì )在旁邊相繼給以說(shuō)明。
這里,我再度指出一下網(wǎng)路爬蟲(chóng)框架與網(wǎng)路爬蟲(chóng)實(shí)現的區別。作為一個(gè)框架,該程序在每位處理模塊中給與使用者盡量多的訂制技巧,而不去涉及各個(gè)處理步驟的實(shí)現細節。
另外,框架更多地考慮使用者自定義的處理步驟在執行期間可能發(fā)生的各類(lèi)情況和問(wèn)題,并注意對這種問(wèn)題的處理方法,這樣就能在便于擴充的同時(shí)保證框架的穩定性。這方面的思索和策略會(huì )彰顯在該網(wǎng)路爬蟲(chóng)框架的各階段設計和編碼實(shí)現之中。
下面我就依照上述剖析對這一程序進(jìn)行總體設計。
總體設計通過(guò)上圖可知,網(wǎng)絡(luò )爬蟲(chóng)框架的處理模塊有 3 個(gè):下載器、分析器和條目處理管線(xiàn)。再加上調度和協(xié)調那些處理模塊運行的控制模塊,我們就可以明確該框架的模塊界定了。我把這兒提及的控制模塊稱(chēng)為調度器。下面是這 4 個(gè)模塊各自承當的職責。
1) 下載器接受懇求類(lèi)型的數據,并根據該懇求獲得 HTTP 請求;將 HTTP 請求發(fā)送至與指定的網(wǎng)路地址對應的遠程服務(wù)器;在 HTTP 請求發(fā)送完畢以后,立即等待相應的 HTTP 響應的到來(lái);在收到 HTTP 響應以后,將其封裝成響應并作為輸出返回給下載器的調用方。
其中,HTTP 客戶(hù)端程序可以由網(wǎng)路爬蟲(chóng)框架的使用方自行定義。另外,若在該子流程執行期間發(fā)生了錯誤,應該立刻以適當的方法告知使用方。對于其他模塊來(lái)講,也是這樣。
2) 分析器接受響應類(lèi)型的數據,并根據該響應獲得 HTTP 響應;對該 HTTP 響應的內容進(jìn)行檢測,并按照給定的規則進(jìn)行剖析、篩選以及生成新的懇求或條目;將生成的懇求或條目作為輸出返回給分析器的調用方。
在分析器的職責中,我可以想到的才能留給網(wǎng)路爬蟲(chóng)框架的使用方自定義的部份并不少。例如,對 HTTP 響應的前期檢測、對內容的篩選,以及生成懇求和條目的方法,等等。不過(guò),我在前面會(huì )對那些可以自定義的部份進(jìn)行一些抉擇。
3) 條目處理管線(xiàn)接受條目類(lèi)型的數據,并對其執行若干步驟的處理;條目處理管線(xiàn)中可以產(chǎn)出最終的數據;這個(gè)最終的數據可以在其中的某個(gè)處理步驟中被持久化(不論是本地儲存還是發(fā)送給遠程的儲存服務(wù)器)以備后用。
我們可以把這種處理步驟的具體實(shí)現留給網(wǎng)路爬蟲(chóng)框架的使用方自行定義。這樣,網(wǎng)絡(luò )爬蟲(chóng)框架就可以真正地與條目處理的細節脫離開(kāi)來(lái)。網(wǎng)絡(luò )爬蟲(chóng)框架絲毫不關(guān)心那些條目如何被處理和持久化,它僅僅負責控制整體的處理流程。我把負責單個(gè)處理步驟的程序稱(chēng)為條目處理器。
條目處理器接受條目類(lèi)型的數據,并把處理完成的條目返回給條目處理管線(xiàn)。條目處理管線(xiàn)會(huì )緊接著(zhù)把該條目傳遞給下一個(gè)條目處理器,直至給定的條目處理器列表中的每位條目處理器都處理過(guò)該條目為止。
4) 調度器調度器在啟動(dòng)時(shí)僅接受首次懇求,并且不會(huì )形成任何輸出。調度器的主要職責是調度各個(gè)處理模塊的運行。其中包括維護各個(gè)處理模塊的實(shí)例、在不同的處理模塊實(shí)例之間傳遞數據(包括懇求、響應和條目),以及監控所有那些被調度者的狀態(tài),等等。
有了調度器的維護,各個(gè)處理模塊得以保持其職責的簡(jiǎn)約和專(zhuān)情。由于調度器是網(wǎng)路爬蟲(chóng)框架中最重要的一個(gè)模塊,所以還須要再編撰出一些工具來(lái)支撐起它的功能。
在弄清楚網(wǎng)路爬蟲(chóng)框架中各個(gè)模塊的職責以后網(wǎng)絡(luò )爬蟲(chóng) 語(yǔ)言,你曉得它是以調度器為核心的。此外,為了并發(fā)執行的須要,除調度器之外的其他模塊都可以是多實(shí)例的,它們由調度器持有、維護和調用。反過(guò)來(lái)講,這些處理模塊的實(shí)例會(huì )從調度器那兒接受輸入,并在進(jìn)行相應的處理后將輸出返回給調度器。
最后,與另外兩個(gè)處理模塊相比,條目處理管線(xiàn)是比較特殊的。顧名思義,它是以流式處理為基礎的,其設計靈感來(lái)自于我之前講過(guò)的 Linux 系統中的管線(xiàn)。
我們可以不斷地向該管線(xiàn)發(fā)送條目,而該管線(xiàn)則會(huì )使其中的若干個(gè)條目處理器依次處理每一個(gè)條目。我們可以挺輕易地使用一些同步方式來(lái)保證條目處理管線(xiàn)的并發(fā)安全性,因此雖然調度器只持有該管線(xiàn)的一個(gè)實(shí)例,也不會(huì )有任何問(wèn)題。
下圖展示了調度器與各個(gè)處理模塊之間的關(guān)系,圖中加入了一個(gè)新的元素——工具箱網(wǎng)絡(luò )爬蟲(chóng) 語(yǔ)言,之前所說(shuō)的用于支撐調度器功能的這些工具就是工具箱的一部分。顧名思義,工具箱不是一個(gè)完整的模塊,而是一些工具的集合,這些工具是調度器與所有處理模塊之間的橋梁。
圖:調度器與各處理模塊的關(guān)系
至此,大家對網(wǎng)路爬蟲(chóng)框架的設計有了一個(gè)宏觀(guān)上的認識。不過(guò),我還未提到在這個(gè)總體設計之下包含的大量設計方法和決策。這些方法和決策不但與一些通用的程序設計原則有關(guān),還涉及好多依賴(lài)于 Go語(yǔ)言的編程風(fēng)格和形式技巧。
這也從側面說(shuō)明,由于幾乎所有語(yǔ)言都有著(zhù)十分鮮明的特征和比較擅長(cháng)的領(lǐng)域,所以在設計一個(gè)須要由特定語(yǔ)言實(shí)現的軟件或程序時(shí),多多少少會(huì )考慮到這門(mén)語(yǔ)言自身的特點(diǎn)。也就是說(shuō),軟件設計不是與具體的語(yǔ)言毫不相關(guān)的。反過(guò)來(lái)講,總會(huì )有一門(mén)或幾門(mén)語(yǔ)言十分適宜實(shí)現某一類(lèi)軟件或程序。 查看全部

簡(jiǎn)單來(lái)說(shuō),網(wǎng)絡(luò )爬蟲(chóng)是互聯(lián)網(wǎng)終端用戶(hù)的模仿者。它模仿的主要對象有兩個(gè),一個(gè)是坐在計算器前使用網(wǎng)絡(luò )瀏覽器訪(fǎng)問(wèn)網(wǎng)路內容的人類(lèi)用戶(hù),另一個(gè)就是網(wǎng)絡(luò )瀏覽器。
網(wǎng)絡(luò )爬蟲(chóng)會(huì )模仿人類(lèi)用戶(hù)輸入某個(gè)網(wǎng)站的網(wǎng)路地址,并企圖訪(fǎng)問(wèn)該網(wǎng)站上的內容,還會(huì )模仿網(wǎng)路瀏覽器按照給定的網(wǎng)路地址去下載相應的內容。這里所說(shuō)的內容可以是 HTML 頁(yè)面、圖片文件、音視頻數據流,等等。
在下載到對應的內容以后,網(wǎng)絡(luò )爬蟲(chóng)會(huì )按照預設的規則對它進(jìn)行剖析和篩選。這些篩選岀的部份會(huì )馬上得到特定的處理。與此同時(shí),網(wǎng)絡(luò )爬蟲(chóng)都會(huì )象人類(lèi)用戶(hù)點(diǎn)擊網(wǎng)頁(yè)中某個(gè)他感興趣的鏈接那樣,繼續訪(fǎng)問(wèn)和下載相關(guān)聯(lián)的其他內容,然后再重復上述步驟,直到滿(mǎn)足停止的條件。
如上所述,網(wǎng)絡(luò )爬蟲(chóng)應當按照使用者的意愿手動(dòng)下載、分析、篩選、統計以及儲存指定的網(wǎng)路內容。注意,這里的關(guān)鍵詞是“自動(dòng)”和“根據意愿”?!白詣?dòng)”的涵義是,網(wǎng)絡(luò )爬蟲(chóng)在啟動(dòng)后自己完成整個(gè)爬取過(guò)程而無(wú)需人工干預,并且能夠夠在過(guò)程結束以后手動(dòng)停止。而“根據意愿”則是說(shuō),網(wǎng)絡(luò )爬蟲(chóng)最大限度地準許使用者對其爬取過(guò)程進(jìn)行訂制。
乍一看,要做到手動(dòng)爬取其實(shí)并不困難。我們只需使網(wǎng)路爬蟲(chóng)依據相關(guān)的網(wǎng)路地址不斷地下載對應的內容即可。但是,窺探其中就可以發(fā)覺(jué),這里有很多細節須要我們進(jìn)行非常處理,如下所示。
在這種細節當中,有的是比較容易處理的,而有的則須要額外的解決方案。例如,我們都曉得,基于 HTML 的網(wǎng)頁(yè)中可以包含代表按鍵的 button 標簽。
讓網(wǎng)絡(luò )瀏覽器在終端用戶(hù)點(diǎn)擊按鍵的時(shí)侯加載并顯示另一個(gè)網(wǎng)頁(yè)可以有很多種方式,其中,非常常用的一種方式就是為該標簽添加 onclick 屬性并把一些 JavaScript 語(yǔ)言的代碼作為它的值。
雖然這個(gè)方式這么常用,但是我們要想使網(wǎng)絡(luò )爬蟲(chóng)可以從中提取出有效的網(wǎng)路地址卻是比較 困難的,因為這涉及對JavaScript程序的理解。JavaScript代碼的編撰方式繁雜,要想使 網(wǎng)絡(luò )爬蟲(chóng)完全理解它們,恐怕就須要用到某個(gè)JavaScript程序解析器的 Go語(yǔ)言實(shí)現了。
另一方面,由于互聯(lián)網(wǎng)對人們生活和工作的全面滲透,我們可以通過(guò)各類(lèi)途徑找到各式各樣的網(wǎng)路爬蟲(chóng)實(shí)現,它們幾乎都有著(zhù)復雜而又奇特的邏輯。這些復雜的邏輯主要針對如下幾個(gè)方面。
這些邏輯絕大多數都與網(wǎng)路爬蟲(chóng)使用者當時(shí)的意愿有關(guān)。換句話(huà)說(shuō),它們都與具體的使用目的有著(zhù)緊密的聯(lián)系。也許它們并不應當是網(wǎng)路爬蟲(chóng)的核心功能,而應當作為擴充功能或可訂制的功能存在。
因此,我想我們更應當編撰一個(gè)容易被訂制和擴充的網(wǎng)路爬蟲(chóng)框架,而非一個(gè)滿(mǎn)足特定爬取目的的網(wǎng)路爬蟲(chóng),這樣就能讓這個(gè)程序成為一個(gè)可適用于不同應用場(chǎng)景的通用工具。
既然這么,接下來(lái)我們就要搞清楚該程序應當或可以做什么事,這也就能使我們進(jìn)一步明晰它的功能、用途和意義。
功能需求和剖析概括來(lái)講,網(wǎng)絡(luò )爬蟲(chóng)框架會(huì )反復執行如下步驟直到觸遇到停止條件。
1) “下載器”下載與給定網(wǎng)路地址相對應的內容。其中,在下載“請求”的組裝方面,網(wǎng)絡(luò )爬蟲(chóng)框架為使用者盡量預留出訂制插口。使用者可以使用這種插口自定義“請求”的組裝方式。
2) “分析器”分析下載到的內容,并從中篩選出可用的部份(以下稱(chēng)為“條目”)和須要訪(fǎng)問(wèn)的新網(wǎng)路地址。其中,在用于剖析和篩選內容的規則和策略方面,應該由網(wǎng)路爬蟲(chóng)框架提供靈活的訂制插口。
換句話(huà)說(shuō),由于只有使用者自己才曉得她們真正想要的是哪些,所以應當準許她們對這種規則和策略進(jìn)行深入的訂制。網(wǎng)絡(luò )爬蟲(chóng)框架僅須要規定好訂制的方法即可。
3) “分析器”把篩選出的“條目”發(fā)送給“條目處理管線(xiàn)”。同時(shí),它會(huì )把發(fā)覺(jué)的新網(wǎng)路地址和其他一些信息組裝成新的下載“請求”,然后把這種懇求發(fā)送給“下載器”。在此步驟中,我們會(huì )過(guò)濾掉一些不符合要求的網(wǎng)路地址,比如忽視超出有效邊界的網(wǎng)路地址。
你可能早已注意到,在這幾個(gè)步驟中,我使用冒號突出展示了幾個(gè)名詞,即下載器、請求、分析器、條目和條目處理管線(xiàn),其中,請求和條目都代表了某類(lèi)數據,而其他 3 個(gè)名詞則代表了處理數據的子程序(可稱(chēng)為處理模塊或組件)。
它們與上面早已提及過(guò)的網(wǎng)路內容(或稱(chēng)對懇求的響應)共同描述了數據在網(wǎng)路爬蟲(chóng)程序中的流轉形式。下圖演示了起始于首次懇求的數據流程圖。

圖:起始于首次懇求的數據流程圖
從上圖中,我們可以清晰地看見(jiàn)每一個(gè)處理模塊才能接受的輸入和可以形成的輸出。實(shí)際上,我們即將編撰的網(wǎng)路爬蟲(chóng)框架都會(huì )借此為根據產(chǎn)生幾個(gè)相對獨立的子程序。
當然,為了維護它們的運行和協(xié)作的有效性,框架中都會(huì )存在其他一些子程序。關(guān)于它們,我會(huì )在旁邊相繼給以說(shuō)明。
這里,我再度指出一下網(wǎng)路爬蟲(chóng)框架與網(wǎng)路爬蟲(chóng)實(shí)現的區別。作為一個(gè)框架,該程序在每位處理模塊中給與使用者盡量多的訂制技巧,而不去涉及各個(gè)處理步驟的實(shí)現細節。
另外,框架更多地考慮使用者自定義的處理步驟在執行期間可能發(fā)生的各類(lèi)情況和問(wèn)題,并注意對這種問(wèn)題的處理方法,這樣就能在便于擴充的同時(shí)保證框架的穩定性。這方面的思索和策略會(huì )彰顯在該網(wǎng)路爬蟲(chóng)框架的各階段設計和編碼實(shí)現之中。
下面我就依照上述剖析對這一程序進(jìn)行總體設計。
總體設計通過(guò)上圖可知,網(wǎng)絡(luò )爬蟲(chóng)框架的處理模塊有 3 個(gè):下載器、分析器和條目處理管線(xiàn)。再加上調度和協(xié)調那些處理模塊運行的控制模塊,我們就可以明確該框架的模塊界定了。我把這兒提及的控制模塊稱(chēng)為調度器。下面是這 4 個(gè)模塊各自承當的職責。
1) 下載器接受懇求類(lèi)型的數據,并根據該懇求獲得 HTTP 請求;將 HTTP 請求發(fā)送至與指定的網(wǎng)路地址對應的遠程服務(wù)器;在 HTTP 請求發(fā)送完畢以后,立即等待相應的 HTTP 響應的到來(lái);在收到 HTTP 響應以后,將其封裝成響應并作為輸出返回給下載器的調用方。
其中,HTTP 客戶(hù)端程序可以由網(wǎng)路爬蟲(chóng)框架的使用方自行定義。另外,若在該子流程執行期間發(fā)生了錯誤,應該立刻以適當的方法告知使用方。對于其他模塊來(lái)講,也是這樣。
2) 分析器接受響應類(lèi)型的數據,并根據該響應獲得 HTTP 響應;對該 HTTP 響應的內容進(jìn)行檢測,并按照給定的規則進(jìn)行剖析、篩選以及生成新的懇求或條目;將生成的懇求或條目作為輸出返回給分析器的調用方。
在分析器的職責中,我可以想到的才能留給網(wǎng)路爬蟲(chóng)框架的使用方自定義的部份并不少。例如,對 HTTP 響應的前期檢測、對內容的篩選,以及生成懇求和條目的方法,等等。不過(guò),我在前面會(huì )對那些可以自定義的部份進(jìn)行一些抉擇。
3) 條目處理管線(xiàn)接受條目類(lèi)型的數據,并對其執行若干步驟的處理;條目處理管線(xiàn)中可以產(chǎn)出最終的數據;這個(gè)最終的數據可以在其中的某個(gè)處理步驟中被持久化(不論是本地儲存還是發(fā)送給遠程的儲存服務(wù)器)以備后用。
我們可以把這種處理步驟的具體實(shí)現留給網(wǎng)路爬蟲(chóng)框架的使用方自行定義。這樣,網(wǎng)絡(luò )爬蟲(chóng)框架就可以真正地與條目處理的細節脫離開(kāi)來(lái)。網(wǎng)絡(luò )爬蟲(chóng)框架絲毫不關(guān)心那些條目如何被處理和持久化,它僅僅負責控制整體的處理流程。我把負責單個(gè)處理步驟的程序稱(chēng)為條目處理器。
條目處理器接受條目類(lèi)型的數據,并把處理完成的條目返回給條目處理管線(xiàn)。條目處理管線(xiàn)會(huì )緊接著(zhù)把該條目傳遞給下一個(gè)條目處理器,直至給定的條目處理器列表中的每位條目處理器都處理過(guò)該條目為止。
4) 調度器調度器在啟動(dòng)時(shí)僅接受首次懇求,并且不會(huì )形成任何輸出。調度器的主要職責是調度各個(gè)處理模塊的運行。其中包括維護各個(gè)處理模塊的實(shí)例、在不同的處理模塊實(shí)例之間傳遞數據(包括懇求、響應和條目),以及監控所有那些被調度者的狀態(tài),等等。
有了調度器的維護,各個(gè)處理模塊得以保持其職責的簡(jiǎn)約和專(zhuān)情。由于調度器是網(wǎng)路爬蟲(chóng)框架中最重要的一個(gè)模塊,所以還須要再編撰出一些工具來(lái)支撐起它的功能。
在弄清楚網(wǎng)路爬蟲(chóng)框架中各個(gè)模塊的職責以后網(wǎng)絡(luò )爬蟲(chóng) 語(yǔ)言,你曉得它是以調度器為核心的。此外,為了并發(fā)執行的須要,除調度器之外的其他模塊都可以是多實(shí)例的,它們由調度器持有、維護和調用。反過(guò)來(lái)講,這些處理模塊的實(shí)例會(huì )從調度器那兒接受輸入,并在進(jìn)行相應的處理后將輸出返回給調度器。
最后,與另外兩個(gè)處理模塊相比,條目處理管線(xiàn)是比較特殊的。顧名思義,它是以流式處理為基礎的,其設計靈感來(lái)自于我之前講過(guò)的 Linux 系統中的管線(xiàn)。
我們可以不斷地向該管線(xiàn)發(fā)送條目,而該管線(xiàn)則會(huì )使其中的若干個(gè)條目處理器依次處理每一個(gè)條目。我們可以挺輕易地使用一些同步方式來(lái)保證條目處理管線(xiàn)的并發(fā)安全性,因此雖然調度器只持有該管線(xiàn)的一個(gè)實(shí)例,也不會(huì )有任何問(wèn)題。
下圖展示了調度器與各個(gè)處理模塊之間的關(guān)系,圖中加入了一個(gè)新的元素——工具箱網(wǎng)絡(luò )爬蟲(chóng) 語(yǔ)言,之前所說(shuō)的用于支撐調度器功能的這些工具就是工具箱的一部分。顧名思義,工具箱不是一個(gè)完整的模塊,而是一些工具的集合,這些工具是調度器與所有處理模塊之間的橋梁。

圖:調度器與各處理模塊的關(guān)系
至此,大家對網(wǎng)路爬蟲(chóng)框架的設計有了一個(gè)宏觀(guān)上的認識。不過(guò),我還未提到在這個(gè)總體設計之下包含的大量設計方法和決策。這些方法和決策不但與一些通用的程序設計原則有關(guān),還涉及好多依賴(lài)于 Go語(yǔ)言的編程風(fēng)格和形式技巧。
這也從側面說(shuō)明,由于幾乎所有語(yǔ)言都有著(zhù)十分鮮明的特征和比較擅長(cháng)的領(lǐng)域,所以在設計一個(gè)須要由特定語(yǔ)言實(shí)現的軟件或程序時(shí),多多少少會(huì )考慮到這門(mén)語(yǔ)言自身的特點(diǎn)。也就是說(shuō),軟件設計不是與具體的語(yǔ)言毫不相關(guān)的。反過(guò)來(lái)講,總會(huì )有一門(mén)或幾門(mén)語(yǔ)言十分適宜實(shí)現某一類(lèi)軟件或程序。