當前位置:
首頁 > 知識 > python如何創建一個分散式網路爬蟲

python如何創建一個分散式網路爬蟲

這是我的數據分析項目要處理的數據集,但它的記錄有一個很大的問題:許多欄位缺失,很多欄位要麼格式不一致或者過時了。換句話說,我的數據集非常臟。

但對於我這個業餘數據科學家來說還是有點希望的-至少對於缺失和過時的欄位來說。大多數記錄包含至少一個到外部網站的超鏈接,在那裡我可能找到我需要的信息。因此,這看起來像一個完美的網路爬蟲的用例。

在這篇文章中,你將了解我是如何構建和擴展分散式網路爬蟲的,特別是我如何處理隨之而來的技術挑戰。

初始需求

創建網路爬蟲的想法令人興奮。因為,你知道,爬蟲很酷,對吧?

但我很快意識到,我的要求比我想像的要複雜得多:


  1. 給定指定 URL,爬蟲程序需要自動發現特定記錄中缺失欄位的值。因此,如果一個網頁不包含我正在尋找的信息,爬蟲程序需要跟蹤出站鏈接,直到找到該信息。

  2. 它需要是某種爬蟲和抓取的混合功能,因為它必須同時跟蹤出站鏈接並從網頁中提取特定信息。

  3. 整個程序需要分散式處理,因為有可能有數億個URL需要訪問。

  4. 抓取的數據需要存儲在某處,很可能是在資料庫中。

  5. 爬蟲程序需要7*24小時不間斷工作,所以不能在我的筆記本電腦上運行它。

  6. 我不希望在雲服務上花費太多 1。

  7. 需要用Python編碼,這是我選擇的語言。

好吧,我曾經在以前的工作中寫過很多爬蟲,但從沒有這麼大的規模。所以對我來說這是個全新的領域。

初始設計

我最開始的設計是這樣的:

python如何創建一個分散式網路爬蟲

主要組件包括:

  1. 一個爬蟲調度器,負責把URL分派給 m 個爬蟲主控制器,並從它們收集結果(欄位)。

  2. m個爬蟲主控制器,負責管理 n 個子進程。這些子過程執行實際的爬取操作。為方便起見,我把他們稱為爬蟲。

  3. 一個資料庫伺服器,負責存儲初始URL和提取的欄位。

這樣我最終會有

m*n個爬蟲,從而將負載分布在許多節點上。例如,4個主控制器,每個包含8個子進程的話,就相當於32個爬蟲。

另外,所有進程間通信都將使用隊列。 所以在理論上,它將很容易擴展。 我可以添加更多的主控制器,爬網率 - 一個性能指標- 會相應增加。

初始實現

現在我有一個看起來不錯的設計,我需要選擇使用哪些技術。

但別誤會我的意思:我的目標不是提出一個完美的技術棧。 相反,我主要把它看作是一個學習的機會,也是一個挑戰 - 所以如果需要,我更願意提出自製的解決方案。

1. 雲託管

我可以選擇AWS,但是我對DigitalOcean更熟悉,恰好它是更便宜的。 所以我用了幾個5美元每月的虛擬機(很省錢啦)。

2. HTTP 庫

requests庫是Python里處理HTTP請求的不二選擇。

3. ETL 管道

當然,我需要從每個訪問過的網頁中提取所有的超鏈接。但我也需要在一些頁面抓取具體數據。

因此,我構建了自己的ETL管道,以便能夠以我所需的數據格式提取數據並進行轉換。

它可以通過配置文件進行定製,如下所示:

{
"name": "gravatar",
"url_patterns": [
{
"type": "regex",
"pattern": "^https?:\/\/(?:(?:www|\w{2})\.)?gravatar\.com\/(?!avatar|support|site|connect)\w+\/?$"
}
],
"url_parsers": [
{
"description": "URLs in the "Find Me Online" section.",
"processors": [
{
"type": "xpath",
"parameters": {
"expression": "//h3[contains(text(),"Find Me Online")]/following-sibling::ul[@class="list-details"][1]//a/@href"
} }
] },
{
"description": "URLs in the "Websites" section.",
"processors": [
{
"type": "xpath",
"parameters": {
"expression": "//ul[@class="list-sites"]//a/@href"
} }
] }
],
"fields": [
{
"name": "name",
"processors": [
{
"type": "xpath",
"parameters": {
"expression": "//div[@class="profile-description"]/h2[@class="fn"]/a/text()"
} },
{
"type": "trim",
"parameters": {
} }
] },
{
"name": "location",
"processors": [
{
"type": "xpath",
"parameters": {
"expression": "//div[@class="profile-description"]/p[@class="location"]/text()"
} },
{
"type": "trim",
"parameters": {
} }
] }
]}

你在上面看到的是一個Gravatar 用戶個人資料頁面的映射。它告訴爬蟲程序應該從這些頁面中抓取什麼數據以及如何抓取:

  1. url_patterns 定義了與當前頁URL 進行試探性匹配的模式。如果有一個匹配,那麼當前頁面確實是Gravatar的用戶配置文件。

  2. url_parsers 定義了能夠在頁面中抓取特定URL的解析器,比如那些指向用戶的個人網站或社交媒體資料的URL。

  3. fields 欄位定義了要從頁面抓取的數據。在Gravatar的用戶配置文件里,我想抓取用戶的全名和位置信息。

url_parsers 和 fields 都包含了一系列針對 web 頁面 HTML 數據的處理器。它們執行轉換(XPath,JSONPath,查找和替換,等等)以獲取所需的確切數據,並轉成我想要的格式。因此,數據在存儲在其它地方之前被規範化,這是特別有用的,因為所有網站都是不同的,並且它們表示數據的方式各不相同。

手動創建所有這些映射花費了我很多時間,因為相關網站的列表非常長(數百個)。


4. 消息處理

最初,我想知道RabbitMQ是否適合。 但是我決定,我不想要單獨的伺服器來管理隊列。 我想要的一切都要如閃電般快速而且要獨立運行。

所以我用了ZeroMQ的push/pull隊列,我把它們加到了queuelib的FifoDiskQueue上,以便將數據保存到磁碟,以防系統崩潰。 另外,使用push/pull隊列可以確保使用輪轉調度演算法將URL分派給主控制器。

了解ZeroMQ如何工作和理解其幾個極端案例花了我一段時間。 但是學習如何實現自己的消息傳遞真的很有趣,最終是值得的,尤其是性能方面。


5. 存儲處理

一個好的關係資料庫可以完成這項工作。 但是我需要存儲類似對象的結果(欄位),所以我選了MongoDB。

加分項:MongoDB相當容易使用和管理。

6. 日誌記錄和監控

我使用了 Python 的日誌模塊,加上一個 RotatingFileHandler,每個進程生成一個日誌文件。這對於管理由每個主控制器管理的各個爬蟲進程的日誌文件特別有用。這也有助於調試。

為了監視各種節點,我沒有使用任何花哨的工具或框架。我只是每隔幾個小時使用 MongoChef連接到 MongoDB 伺服器,按照我的計算, 檢查已經處理好的記錄的平均數。如果數字變小了,很可能意味著某件事情 (壞的) 正在發生,比如一個進程崩潰了或其他別的什麼事情。

當然,你知道的-所有的血,汗水和眼淚都在這裡。


7. 管理已經爬過的URLs

Web爬蟲很可能會不止一次碰到同一個URL。但是你通常不想重新抓取它,因為網頁可能沒有改變。

為了避免這個問題,我在爬蟲程序調度器上使用了一個本地SQLite資料庫來存儲每個已爬過的URL,以及與其抓取日期相對應的時間戳。因此,每當新的URL出現時,調度程序會在SQLite資料庫中搜索該URL,以查看是否已經被爬過。如果沒有,則執行爬取。否則,就忽略掉。

我選擇SQLite是因為它的快速和易於使用。每個爬取URL附帶的時間戳對調試和事件回溯都非常有用,萬一有人對我的爬蟲提出投訴的話。


8. URL過濾

我的目標不是抓取整個網路。相反,我想自動發現我感興趣的網址,並過濾掉那些沒用的網址。

利用前面介紹的ETL配置,我感興趣的URL被列入白名單。為了過濾掉我不想要的網址,我使用Alexa的100萬頂級網站列表中的前20K個網站。

這個概念很簡單:任何出現在前20K的網站有很大的可能性是無用的,如youtube.com或amazon.com。然而,根據我自己的分析,那些20K以外的網站更有可能有與我的分析相關,比如個人網站和博客等。


9. 安全

我不希望任何人篡改我的 DigitalOcean 虛擬機,所以:


  1. 我關閉了每個虛擬機上使用 iptables的所有埠。我選擇性地打開了我絕對需要的埠(80、443、22、27017等)。

  2. 我在 MongoDB 上啟用了 SSL 身份驗證,因此只有擁有適當證書的用戶才能登錄。

  3. 我在所有虛擬機上都使用了加密的磁碟。

  4. 我在每個虛擬機上都啟用了fail2ban,以阻止多次失敗的登錄請求。

  5. 我在所有虛擬機上都配置了基於SSH密鑰的身份驗證。

  6. 我在 ZeroMQ 中啟用了 SSL身份驗證。

好吧,也許我對安全有點過分了:) 但我是故意的:這不僅是一個很好的學習機會,而且也是保護我數據的一種非常有效的方法。


10. 內存

一個每月5美元的DigitalOcean 虛擬機只有512MB的內存,所以它可做的相當有限。 經過多次測試運行,我確定我的所有節點都應該有1GB的內存。 所以我在每個虛擬機上創建了一個512MB的交換文件。

喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 青峰科技 的精彩文章:

12個你未必知道的CSS小知識
Spring5源碼解析-Spring框架中的事件和監聽器
巧用Unity 2D功能:只需六步開發簡單的2D UFO遊戲
Go語言:成長的十年

TAG:青峰科技 |