您好,登錄后才能下訂單哦!
利用Python怎么樣實現(xiàn)一個并發(fā)爬蟲?很多新手對此不是很清楚,為了幫助大家解決這個難題,下面小編將為大家詳細講解,有這方面需求的人可以來學(xué)習(xí)下,希望你能有所收獲。
一.順序抓取
順序抓取是最最常見的抓取方式,一般初學(xué)爬蟲的朋友就是利用這種方式,下面是一個測試代碼,順序抓取8個url,我們可以來測試一下抓取完成需要多少時間:
HEADERS = {'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9', 'Accept-Language': 'zh-CN,zh;q=0.8', 'Accept-Encoding': 'gzip, deflate',} URLS = ['http://www.cnblogs.com/moodlxs/p/3248890.html', 'https://www.zhihu.com/topic/19804387/newest', 'http://blog.csdn.net/yueguanghaidao/article/details/24281751', 'https://my.oschina.net/visualgui823/blog/36987', 'http://blog.chinaunix.net/uid-9162199-id-4738168.html', 'http://www.tuicool.com/articles/u67Bz26', 'http://rfyiamcool.blog.51cto.com/1030776/1538367/', 'http://itindex.net/detail/26512-flask-tornado-gevent'] #url為隨機獲取的一批url def func(): """ 順序抓取 """ import requests import time urls = URLS headers = HEADERS headers['user-agent'] = "Mozilla/5.0+(Windows+NT+6.2;+WOW64)+AppleWebKit/537" \ ".36+(KHTML,+like+Gecko)+Chrome/45.0.2454.101+Safari/537.36" print(u'順序抓取') starttime= time.time() for url in urls: try: r = requests.get(url, allow_redirects=False, timeout=2.0, headers=headers) except: pass else: print(r.status_code, r.url) endtime=time.time() print(endtime-starttime) func()
我們直接采用內(nèi)建的time.time()來計時,較為粗略,但可以反映大概的情況。下面是順序抓取的結(jié)果計時:
可以從圖片中看到,顯示的順序與urls的順序是一模一樣的,總共耗時為7.763269901275635秒,一共8個url,平均抓取一個大概需要0.97秒??傮w來看,還可以接受。
二.多線程抓取
線程是python內(nèi)的一種較為不錯的并發(fā)方式,我們也給出相應(yīng)的代碼,并且為每個url創(chuàng)建了一個線程,一共8線程并發(fā)抓取,下面的代碼:
下面是我們運行8線程的測試代碼:
HEADERS = {'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9', 'Accept-Language': 'zh-CN,zh;q=0.8', 'Accept-Encoding': 'gzip, deflate',} URLS = ['http://www.cnblogs.com/moodlxs/p/3248890.html', 'https://www.zhihu.com/topic/19804387/newest', 'http://blog.csdn.net/yueguanghaidao/article/details/24281751', 'https://my.oschina.net/visualgui823/blog/36987', 'http://blog.chinaunix.net/uid-9162199-id-4738168.html', 'http://www.tuicool.com/articles/u67Bz26', 'http://rfyiamcool.blog.51cto.com/1030776/1538367/', 'http://itindex.net/detail/26512-flask-tornado-gevent'] def thread(): from threading import Thread import requests import time urls = URLS headers = HEADERS headers['user-agent'] = "Mozilla/5.0+(Windows+NT+6.2;+WOW64)+AppleWebKit/537.36+" \ "(KHTML,+like+Gecko)+Chrome/45.0.2454.101+Safari/537.36" def get(url): try: r = requests.get(url, allow_redirects=False, timeout=2.0, headers=headers) except: pass else: print(r.status_code, r.url) print(u'多線程抓取') ts = [Thread(target=get, args=(url,)) for url in urls] starttime= time.time() for t in ts: t.start() for t in ts: t.join() endtime=time.time() print(endtime-starttime) thread()
多線程抓住的時間如下:
可以看到相較于順序抓取,8線程的抓取效率明顯上升了3倍多,全部完成只消耗了2.154秒??梢钥吹斤@示的結(jié)果已經(jīng)不是urls的順序了,說明每個url各自完成的時間都是不一樣的。線程就是在一個進程中不斷的切換,讓每個線程各自運行一會,這對于網(wǎng)絡(luò)io來說,性能是非常高的。但是線程之間的切換是挺浪費資源的。
三.gevent并發(fā)抓取
gevent是一種輕量級的協(xié)程,可用它來代替線程,而且,他是在一個線程中運行,機器資源的損耗比線程低很多。如果遇到了網(wǎng)絡(luò)io阻塞,會馬上切換到另一個程序中去運行,不斷的輪詢,來降低抓取的時間
下面是測試代碼:
HEADERS = {'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9', 'Accept-Language': 'zh-CN,zh;q=0.8', 'Accept-Encoding': 'gzip, deflate',} URLS = ['http://www.cnblogs.com/moodlxs/p/3248890.html', 'https://www.zhihu.com/topic/19804387/newest', 'http://blog.csdn.net/yueguanghaidao/article/details/24281751', 'https://my.oschina.net/visualgui823/blog/36987', 'http://blog.chinaunix.net/uid-9162199-id-4738168.html', 'http://www.tuicool.com/articles/u67Bz26', 'http://rfyiamcool.blog.51cto.com/1030776/1538367/', 'http://itindex.net/detail/26512-flask-tornado-gevent'] def main(): """ gevent并發(fā)抓取 """ import requests import gevent import time headers = HEADERS headers['user-agent'] = "Mozilla/5.0+(Windows+NT+6.2;+WOW64)+AppleWebKit/537.36+" \ "(KHTML,+like+Gecko)+Chrome/45.0.2454.101+Safari/537.36" urls = URLS def get(url): try: r = requests.get(url, allow_redirects=False, timeout=2.0, headers=headers) except: pass else: print(r.status_code, r.url) print(u'基于gevent的并發(fā)抓取') starttime= time.time() g = [gevent.spawn(get, url) for url in urls] gevent.joinall(g) endtime=time.time() print(endtime - starttime) main()
協(xié)程的抓取時間如下:
正常情況下,gevent的并發(fā)抓取與多線程的消耗時間差不了多少,但是可能是我網(wǎng)絡(luò)的原因,或者機器的性能的原因,時間有點長......,請各位小主在自己電腦進行跑一下看運行時間
四.基于tornado的coroutine并發(fā)抓取
tornado中的coroutine是python中真正意義上的協(xié)程,與python3中的asyncio幾乎是完全一樣的,而且兩者之間的future是可以相互轉(zhuǎn)換的,tornado中有與asyncio相兼容的接口。
下面是利用tornado中的coroutine進行并發(fā)抓取的代碼:
利用coroutine編寫并發(fā)略顯復(fù)雜,但這是推薦的寫法,如果你使用的是python3,強烈建議你使用coroutine來編寫并發(fā)抓取。
下面是測試代碼:
HEADERS = {'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9', 'Accept-Language': 'zh-CN,zh;q=0.8', 'Accept-Encoding': 'gzip, deflate',} URLS = ['http://www.cnblogs.com/moodlxs/p/3248890.html', 'https://www.zhihu.com/topic/19804387/newest', 'http://blog.csdn.net/yueguanghaidao/article/details/24281751', 'https://my.oschina.net/visualgui823/blog/36987', 'http://blog.chinaunix.net/uid-9162199-id-4738168.html', 'http://www.tuicool.com/articles/u67Bz26', 'http://rfyiamcool.blog.51cto.com/1030776/1538367/', 'http://itindex.net/detail/26512-flask-tornado-gevent'] import time from tornado.gen import coroutine from tornado.ioloop import IOLoop from tornado.httpclient import AsyncHTTPClient, HTTPError from tornado.httpclient import HTTPRequest #urls與前面相同 class MyClass(object): def __init__(self): #AsyncHTTPClient.configure("tornado.curl_httpclient.CurlAsyncHTTPClient") self.http = AsyncHTTPClient() @coroutine def get(self, url): #tornado會自動在請求首部帶上host首部 request = HTTPRequest(url=url, method='GET', headers=HEADERS, connect_timeout=2.0, request_timeout=2.0, follow_redirects=False, max_redirects=False, user_agent="Mozilla/5.0+(Windows+NT+6.2;+WOW64)+AppleWebKit/537.36+\ (KHTML,+like+Gecko)+Chrome/45.0.2454.101+Safari/537.36",) yield self.http.fetch(request, callback=self.find, raise_error=False) def find(self, response): if response.error: print(response.error) print(response.code, response.effective_url, response.request_time) class Download(object): def __init__(self): self.a = MyClass() self.urls = URLS @coroutine def d(self): print(u'基于tornado的并發(fā)抓取') starttime = time.time() yield [self.a.get(url) for url in self.urls] endtime=time.time() print(endtime-starttime) if __name__ == '__main__': dd = Download() loop = IOLoop.current() loop.run_sync(dd.d)
抓取的時間如下:
可以看到總共花費了128087秒,而這所花費的時間恰恰就是最后一個url抓取所需要的時間,tornado中自帶了查看每個請求的相應(yīng)時間。我們可以從圖中看到,最后一個url抓取總共花了1.28087秒,相較于其他時間大大的增加,這也是導(dǎo)致我們消耗時間過長的原因。那可以推斷出,前面的并發(fā)抓取,也在這個url上花費了較多的時間。
看完上述內(nèi)容是否對您有幫助呢?如果還想對相關(guān)知識有進一步的了解或閱讀更多相關(guān)文章,請關(guān)注億速云行業(yè)資訊頻道,感謝您對億速云的支持。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。