爬蟲速度優化
-
優化硬盤存儲:每個網頁大概多大,加起來以后會有多大,需不需要壓縮存儲
-
優化內存,url去重:減少所有url放在一起去重時,內存不夠用情況,使用bloomFilter算法,查詢效率高
-
反抓取訪問頻率限制:
- 研究網站的反爬策略
- 多ip抓取:IP代理池和adsl撥號
- IP代理池:比較貴
- ADSL撥號:便宜,但速度可能稍微慢一些
-
網絡性能,抓取技術細節調優
- 開多個線程,探索多長時間/多少頻率切換撥號ip最優:
- 需要對網站的反爬策略進行測試。先開一個線程,一直抓到ip被屏蔽,記錄下抓取耗時,總抓取詞數和成功抓取詞數。再開兩個線程/4個線程,重復上面的步驟,統計抓取極限和細節調優的表格。
- 開線程由於有全局鎖的存在,其實還是串行的,所以速度上沒有太大的優勢。使用多進程。
- 單個ip、單個cookies多測幾次,得到一個大概的值,再進行優化。
- requests請求優化:設置超時放棄時間requests.get(url, timeout=(5, 10)),二次請求可能比一直在等待的抓取效率高一些
- 優化ADSL撥號等待時間:
- 每次斷開撥號后,要等待幾秒鍾再撥號,太短時間內撥號有可能又撥到上一個ip或者失敗,所以要等待幾秒鍾再撥。
- 撥完號后,需要檢測一下外網是否連通,使用ping功能或requests檢測外網的連通性以及代理的可用性。
- 開多個線程,探索多長時間/多少頻率切換撥號ip最優:
-
注意事項:
- 要計算對方的帶寬壓力,不要抓取的太過分,以致於影響對方網站的正常運轉。
需要了解的一些東西
- 多線程和多進程分別是什么?有什么的區別?有什么優缺點?分別適用於什么場景?怎么用?
- 多線程和多進程是不一樣的!一個是 thread 庫,一個是 multiprocessing 庫
- 多線程:
- GIL的全稱是Global Interpreter Lock(全局解釋器鎖), 某個線程想要執行,必須先拿到GIL,我們可以把GIL看作是“通行證”,並且在一個python進程中,GIL只有一個。拿不到通行證的線程,就不允許進入CPU執行。而每次釋放GIL鎖,線程進行鎖競爭、切換線程,會消耗資源。並且由於GIL鎖存在,python里一個進程永遠只能同時執行一個線程(拿到GIL的線程才能執行),這就是為什么在多核CPU上,python的多線程效率並不高。在Python2.x里,GIL的釋放邏輯是當前線程遇見IO操作或者ticks計數達到100(ticks可以看作是Python自身的一個計數器,專門做用於GIL,每次釋放后歸零,這個計數可以通過 sys.setcheckinterval 來調整),進行釋放。
- CPU密集型代碼(各種循環處理、計數等等),在這種情況下,由於計算工作多,ticks計數很快就會達到閾值,然后觸發GIL的釋放與再競爭(多個線程來回切換當然是需要消耗資源的),所以python下的多線程對CPU密集型代碼並不友好。
- IO密集型代碼(文件處理、網絡爬蟲等),多線程能夠有效提升效率(單線程下有IO操作會進行IO等待,造成不必要的時間浪費,而開啟多線程能在線程A等待時,自動切換到線程B,可以不浪費CPU的資源,從而能提升程序執行效率)。所以python的多線程對IO密集型代碼比較友好。
- 而在python3.x中,GIL不使用ticks計數,改為使用計時器(執行時間達到閾值后,當前線程釋放GIL),這樣對CPU密集型程序更加友好,但依然沒有解決GIL導致的同一時間只能執行一個線程的問題,所以效率依然不盡如人意。
- 多核多線程比單核多線程更差,原因是單核下多線程,每次釋放GIL,喚醒的那個線程都能獲取到GIL鎖,所以能夠無縫執行,但多核下,CPU0釋放GIL后,其他CPU上的線程都會進行競爭,但GIL可能會馬上又被CPU0拿到,導致其他幾個CPU上被喚醒后的線程會醒着等待到切換時間后又進入待調度狀態,這樣會造成線程顛簸(thrashing),導致效率更低
- 多進程
- 每個進程有各自獨立的GIL,互不干擾,這樣就可以真正意義上的並行執行,所以在python中,多進程的執行效率優於多線程(僅僅針對多核CPU而言)。所以在這里說結論:多核下,想做並行提升效率,比較通用的方法是使用多進程,能夠有效提高執行效率。
- 總結
- 多線程適用於單核機器、IO密集型代碼;多核下,想要並行提高效率,一般用多進程。
- 自己沒有做過的是URL降重和爬取質量的檢測,這個之后要完善一下。
資料
- 使用adsl撥號服務器搭建代理池、github精簡操作地址、另外一篇adsl參考
- 知乎關於反爬蟲內容,主要看那個猿人學寫的 adsl撥號方法獲取ip啟蒙文章。
- 進程與線程:
- 多進程問題:
- 捕捉進程中出現的異常get方法是可以將異常捕捉到,但是加了get()方法,程序就變成了阻塞的了,進程之間需要串行了,不是我們想要的結果
- get獲取pool.apply_ssync()的結果的過程,最好放在進程池回收之后進行,避免阻塞后面的語句但是這樣處理會使得異常在所有的進程都運行完了以后處理。這種辦法適用於需要對多個進程的結果進行處理(加減乘除之類的),而不適用於處理異常,將程序停下來這種需求。
- multiprocessing pool 源碼分析異常處理過程
- 最好的解決辦法應該是,明確為什么這一步會出異常,並進行處理,進程不能在半路上停下來,在最終所有任務跑完以后再一起處理。
-
其他
-
[requests 庫用法](https://cuiqingcai.com/2556.html)
-
[ubuntu安裝與測試redis](https://www.cnblogs.com/wxjnew/p/9189191.html)
-
異常處理
- 抓到異常應該把它拋到調用方法中去解決不是所有異常你都可以處理的,許多異常你就應該把它拋出到調用方去,如果你捕獲住一個異常不往外拋,你就等於告訴調用方前面的過程沒問題,可以繼續往下走,但如果真的出現了問題,繼續做下去往往會導致更嚴重的后果,通常都要比通過異常中止整個過程要糟糕。
redis 操作
sudo service redis-server restart
redis-cli
service redis status
vim 操作
- i 插入
- esc 推出編輯狀態
- :q 退出;:wq 保存后退出;:q! 退出且強制不保存
- G 最后一行;nG 移動到第n行;0 這一行最前面的字符;$ 這一行最后面的字符;
- 搜索功能:/word 向下查找word這個單詞;n 接着往下查找下一個word;N 反向查找word單詞
Todo
- 更優雅的異常信息,使用logging,代替print