在上週寫完用scrapy爬去知乎用戶信息的爬蟲以後,github上star個數一下就在公司小組內部排的上名次了,我還信誓旦旦的跟上級吹牛皮說若是再寫一個,都很差意思和你再提star了,怕大家傷心。上級不屑的說,那就寫一個爬蟲爬一爬github,找一找python大牛,公司也正好在找人。臨危受命,格外激動,當天就去研究github網站,琢磨怎麼解析頁面以及爬蟲的運行策略。意外的發現github提供了很是nice的API以及文檔文檔,讓我對github的愛已經深刻骨髓。python
說了這麼多廢話,講講真題吧。我須要下載github用戶還有他們的reposities數據,展開方式也很簡單,根據一個用戶的following以及follower關係,遍歷整個用戶網就能夠下載全部的數據了,據說github註冊用戶才幾百萬,一下就把全部的數據爬下來想一想還有點小激動呢,下面是流程圖:git
這是我根據這個流程實現的代碼,網址:https://github.com/LiuRoy/github_spider程序員
看到這麼簡單的流程,心裏的第一想法就是先簡單的寫一個遞歸實現唄,要是性能差再慢慢優化,因此初版代碼很快就完成了(在目錄recursion下)。數據存儲使用mongo,重複請求判斷使用的redis,寫mongo數據採用celery的異步調用,須要rabbitmq服務正常啓動,在settings.py正確配置後,使用下面的步驟啓動:github
celery -A github_spider.worker worker loglevel=info
啓動異步任務python github_spider/recursion/main.py
啓動爬蟲由於每一個請求延時很高,爬蟲運行效率很慢,訪問了幾千個請求以後拿到了部分數據,這是按照查看數降序排列的python項目: redis
這是按粉絲數降序排列的用戶列表 併發
做爲一個有追求的程序員,固然不能由於一點小成就知足,總結一下遞歸實現的幾個缺陷:異步
針對這種I/O耗時的問題,解決方法也就那幾種,要麼多併發,要麼走異步訪問,要麼左右開弓。針對上面的問題2,我最開始的解決方式是異步請求API。由於最開始寫代碼的時候考慮到了這點,代碼對調用方法已經作過優化,很快就改好了,實現方式使用了grequests。這個庫和requests是同一個做者,代碼也很是的簡單,就是講request請求用gevent作了一個簡單的封裝,能夠非阻塞的請求數據。scrapy
可是當我運行以後,發現程序很快運行結束,一查發現公網IP被github封掉了,當時心中千萬只草泥馬奔騰而過,沒辦法只能祭出爬蟲的終極殺器--代理。又專門寫了一個輔助腳本從網上爬取免費的HTTPS代理存放在redis中,路徑proxy/extract.py,每次請求的時候都帶上代理,運行錯誤重試自動更換代理並把錯誤代理清楚。原本網上免費的HTTPS代理就不多,並且不少還不能用,因爲大量的報錯重試,訪問速度不只沒有原來快,並且比原來慢一大截,此路不通只能走多併發實現了。ide
採起廣度優先的遍歷的方式,能夠把要訪問的網址存放在隊列中,再套用生產者消費者的模式就能夠很容易的實現多併發,從而解決上面的問題2。若是某段時間內一直失敗,只須要將數據再仍會隊列就能夠完全解決問題3。不只如此,這種方式還能夠支持中斷後繼續運行,程序流程圖以下:性能
爲了實現多級部署(雖然我就只有一臺機器),消息隊列使用了rabbitmq,須要建立名爲github,類型是direct的exchange,而後建立四個名稱分別爲user, repo, follower, following的隊列,詳細的綁定關係見下圖:
詳細的啓動步驟以下:
celery -A github_spider.worker worker loglevel=info
啓動異步任務python github_spider/proxy/extract.py
更新代理python github_spider/queue/main.py
啓動腳本隊列狀態圖: