日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > python >内容正文

python

python爬虫的一些技巧

發(fā)布時間:2024/1/17 python 29 豆豆
生活随笔 收集整理的這篇文章主要介紹了 python爬虫的一些技巧 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

用python寫爬蟲程序,入門很快,要進階從“能用”提升到“用的省心省事”有很多方面需要改進 下面是一些技巧總結。
Gzip/deflate支持
現(xiàn)在的網(wǎng)頁普遍支持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):

req.add_header("Accept-Encoding", "gzip, deflate") return req

# decode
def http_response(self, req, resp):

old_resp = resp # gzip if resp.headers.get("content-encoding") == "gzip":gz = GzipFile(fileobj=StringIO(resp.read()),mode="r")resp = urllib2.addinfourl(gz, old_resp.headers, old_resp.url, old_resp.code)resp.msg = old_resp.msg # deflate if resp.headers.get("content-encoding") == "deflate":gz = StringIO( deflate(resp.read()) )resp = urllib2.addinfourl(gz, old_resp.headers, old_resp.url, old_resp.code) # 'class to add info() andresp.msg = old_resp.msg return 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:

return zlib.decompress(data, -zlib.MAX_WBITS)

except zlib.error:

return zlib.decompress(data)

然后就簡單了,

encoding_support = ContentEncodingProcessor
opener = urllib2.build_opener( encoding_support, urllib2.HTTPHandler )

直接用opener打開網(wǎng)頁,如果服務器支持gzip/defalte則自動解壓縮

content = opener.open(url).read()

更方便地多線程

  • 用twisted進行異步I/O抓取
  • 事實上更高效的抓取并非一定要用多線程,也可以使用異步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):

    req.add_header("Accept-Encoding", "gzip, deflate") return req

    # decode
    def http_response(self, req, resp):

    old_resp = resp # gzip if resp.headers.get("content-encoding") == "gzip":gz = GzipFile(fileobj=StringIO(resp.read()),mode="r")resp = urllib2.addinfourl(gz, old_resp.headers, old_resp.url, old_resp.code)resp.msg = old_resp.msg # deflate if resp.headers.get("content-encoding") == "deflate":gz = StringIO( deflate(resp.read()) )resp = urllib2.addinfourl(gz, old_resp.headers, old_resp.url, old_resp.code) # 'class to add info() andresp.msg = old_resp.msg return 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:

    return zlib.decompress(data, -zlib.MAX_WBITS)

    except zlib.error:

    return zlib.decompress(data)

    然后就簡單了,

    encoding_support = ContentEncodingProcessor
    opener = urllib2.build_opener( encoding_support, urllib2.HTTPHandler )

    直接用opener打開網(wǎng)頁,如果服務器支持gzip/defalte則自動解壓縮

    content = opener.open(url).read()

  • 設計一個簡單的多線程抓取類
  • 還是覺得在urllib之類python“本土”的東東里面折騰去來更舒服。試想一下,如果有個Fetcher類,你可以這么調(diào)用
    f = Fetcher(threads=10) #設定下載線程數(shù)為10
    for url in urls:

    f.push(url) #把所有url推入下載隊列

    while f.taskleft(): #若還有未完成下載的線程

    content = f.pop() #從下載完成隊列中取出結果

    do_with(content) # 處理content內(nèi)容

    這么個多線程調(diào)用簡單明了,那么就這么設計吧,首先要有兩個對列,用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 spam

    if 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)

    一些瑣碎的經(jīng)驗
    1.連接池:
    Opener.open和urllib2.urlopen一樣,都會新建一個http請求。通常情況下這不是什么問題,因為線性環(huán)境下,一秒鐘可能也就新生成一個請求;然而在多線程環(huán)境下,每秒可能使幾十上百個請求,這么干只要幾分鐘,正常的有理智的服務器一定會封禁你的。
    然而在正常的html請求時,保持同時和服務器十幾個鏈接又是很正常的一件事,所以完全可以手動維護一個HttpConnection的池,然后每次抓取是從連接里面選鏈接進行鏈接即可。
    這里有一個取巧的方法,就是利用squid做代理服務器來進行抓取,則squid會自動問你維護連接池,還附帶數(shù)據(jù)緩存功能,而且squid本來就是我每個服務器上面必須裝的東東,何必再自找麻煩寫連接池呢。
    2.設定線程的棧大小
    棧大小的設定講非常顯著地影響python的內(nèi)存占用,python多線程不設置這個值會導致程序占用大量內(nèi)存,這對openvz的vps來說非常致命。Stack_size必須大于32768,實際上應該總要32768*2以上
    from threading import stack_size
    stack_size(32768*16)
    3.設置失敗后自動重試

    def get(self,req,retries=3):try:response = self.opener.open(req)data = response.read()except Exception , what:print what,reqif retries>0:return self.get(req,retries-1)else:print 'GET Failed',reqreturn ''return data

    4.設置超時
    import socket
    socket.setdefaulttimeout(10) #設置10秒后連接超時
    5.登陸
    登陸更加簡化了,首先build_opener中要加入cookie支持,參考“總結”一文;如要登陸VeryCD,給fetcher新增一個空方法login,并在_init
    _()中調(diào)用,然后繼承Fetcher類并override login方法:
    def login(self,username,password):

    import urllib data=urllib.urlencode({'username':username,'password':password,'continue':'http://www.verycd.com/','login_submit':u'登錄'.encode('utf-8'),'save_cookie':1,}) url = 'http://www.verycd.com/signin' self.opener.open(url,data).read()

    于是在Fetcher初始化時便會自動登陸VeryCD網(wǎng)站。

    如此,把上述所有小技巧都糅合起來就可以顯著的改善python爬蟲,它支持多線程,gzip/deflate壓縮,超時設置,自動重試,設置棧大小,自動登陸等功能;代碼簡單,使用方便 性能也不俗。

    創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎勵來咯,堅持創(chuàng)作打卡瓜分現(xiàn)金大獎

    總結

    以上是生活随笔為你收集整理的python爬虫的一些技巧的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。