python爬虫的一些技巧
用python寫爬蟲程序,入門很快,要進階從“能用”提升到“用的省心省事”有很多方面需要改進 下面是一些技巧總結。
Gzip/deflate支持
現在的網頁普遍支持gzip壓縮,這往往可以解決大量傳輸時間,以VeryCD的主頁為例,未壓縮版本247k,壓縮了以后45k,為原來的1/5。這就意味著抓取速度會快5倍。
然后python的urllib/urllib2默認都不支持壓縮,要返回壓縮格式,必須在request的headar里面寫明’accept-encoding’ 然后讀取response后更要檢查header查看是否有’content-encoding’一項來判斷是否需要解碼,很繁瑣瑣碎。如何讓urllib2自動支持gzip,defalte呢?
其實可以繼承BaseHanlder類,然后build_opener的方式來處理:
import urllib2
from gzip import GzipFile
from StringIO import StringIO
class ContentEncodingProcessor(urllib2.BaseHandler):
"""A handler to add gzip capabilities to urllib2 requests """
# add headers to requests
def http_request(self, req):
# decode
def http_response(self, req, resp):
deflate support
import zlib
def deflate(data): # zlib only provides the zlib compress format, not the deflate format;
try: # so on top of all there's this workaround:
except zlib.error:
return zlib.decompress(data)然后就簡單了,
encoding_support = ContentEncodingProcessor
opener = urllib2.build_opener( encoding_support, urllib2.HTTPHandler )
直接用opener打開網頁,如果服務器支持gzip/defalte則自動解壓縮
content = opener.open(url).read()
更方便地多線程
事實上更高效的抓取并非一定要用多線程,也可以使用異步I/O法:直接用twisted的getPage方法,然后分別加上異步I/O結束時的callback和errback方法即可。例如可以這么干:
import urllib2
from gzip import GzipFile
from StringIO import StringIO
class ContentEncodingProcessor(urllib2.BaseHandler):
"""A handler to add gzip capabilities to urllib2 requests """
# add headers to requests
def http_request(self, req):
# decode
def http_response(self, req, resp):
deflate support
import zlib
def deflate(data): # zlib only provides the zlib compress format, not the deflate format;
try: # so on top of all there's this workaround:
except zlib.error:
return zlib.decompress(data)然后就簡單了,
encoding_support = ContentEncodingProcessor
opener = urllib2.build_opener( encoding_support, urllib2.HTTPHandler )
直接用opener打開網頁,如果服務器支持gzip/defalte則自動解壓縮
content = opener.open(url).read()
還是覺得在urllib之類python“本土”的東東里面折騰去來更舒服。試想一下,如果有個Fetcher類,你可以這么調用
f = Fetcher(threads=10) #設定下載線程數為10
for url in urls:
while f.taskleft(): #若還有未完成下載的線程
content = f.pop() #從下載完成隊列中取出結果do_with(content) # 處理content內容
這么個多線程調用簡單明了,那么就這么設計吧,首先要有兩個對列,用Queue搞定,多線程的基本架構也和“技巧總結”一文類似,push方法和pop方法都比較好處理,都是直接用Queue的方法,taskleft則是如果有“正在運行的任務”或者“隊列中的任務”則為是,也好辦,于是代碼如下:
import urllib2
from threading import Thread,Lock
from Queue import Queue
import time
class Fetcher:
def __init__(self,threads):self.opener = urllib2.build_opener(urllib2.HTTPHandler)self.lock = Lock() #線程鎖self.q_req = Queue() #任務隊列self.q_ans = Queue() #完成隊列self.threads = threadsfor i in range(threads):t = Thread(target=self.threadget)t.setDaemon(True)t.start()self.running = 0def __del__(self): #解構時需等待兩個隊列完成time.sleep(0.5)self.q_req.join()self.q_ans.join()def taskleft(self):return self.q_req.qsize()+self.q_ans.qsize()+self.runningdef push(self,req):self.q_req.put(req)def pop(self):return self.q_ans.get()def threadget(self):while True:req = self.q_req.get()with self.lock: #要保證該操作的原子性,進入critical areaself.running += 1try:ans = self.opener.open(req).read()except Exception, what:ans = ''print whatself.q_ans.put((req,ans))with self.lock:self.running -= 1self.q_req.task_done()time.sleep(0.1) # don't spamif name == "__main__":
links = [ 'http://www.verycd.com/topics/%d/'%i for i in range(5420,5430) ] f = Fetcher(threads=10) for url in links:f.push(url) while f.taskleft():url,content = f.pop()print url,len(content)一些瑣碎的經驗
1.連接池:
Opener.open和urllib2.urlopen一樣,都會新建一個http請求。通常情況下這不是什么問題,因為線性環境下,一秒鐘可能也就新生成一個請求;然而在多線程環境下,每秒可能使幾十上百個請求,這么干只要幾分鐘,正常的有理智的服務器一定會封禁你的。
然而在正常的html請求時,保持同時和服務器十幾個鏈接又是很正常的一件事,所以完全可以手動維護一個HttpConnection的池,然后每次抓取是從連接里面選鏈接進行鏈接即可。
這里有一個取巧的方法,就是利用squid做代理服務器來進行抓取,則squid會自動問你維護連接池,還附帶數據緩存功能,而且squid本來就是我每個服務器上面必須裝的東東,何必再自找麻煩寫連接池呢。
2.設定線程的棧大小
棧大小的設定講非常顯著地影響python的內存占用,python多線程不設置這個值會導致程序占用大量內存,這對openvz的vps來說非常致命。Stack_size必須大于32768,實際上應該總要32768*2以上
from threading import stack_size
stack_size(32768*16)
3.設置失敗后自動重試
4.設置超時
import socket
socket.setdefaulttimeout(10) #設置10秒后連接超時
5.登陸
登陸更加簡化了,首先build_opener中要加入cookie支持,參考“總結”一文;如要登陸VeryCD,給fetcher新增一個空方法login,并在_init
_()中調用,然后繼承Fetcher類并override login方法:
def login(self,username,password):
于是在Fetcher初始化時便會自動登陸VeryCD網站。
如此,把上述所有小技巧都糅合起來就可以顯著的改善python爬蟲,它支持多線程,gzip/deflate壓縮,超時設置,自動重試,設置棧大小,自動登陸等功能;代碼簡單,使用方便 性能也不俗。
創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎總結
以上是生活随笔為你收集整理的python爬虫的一些技巧的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: node.js 使用----相关常用命令
- 下一篇: Sequelize 4.42.1 发布,