python之路 -- 爬蟲 -- 高性能相關
高性能爬蟲方案:
多進程
多線程
利用“異步非阻塞”模塊實現單線程並發請求。
本質
1 sk = socket() 2 # 阻塞 3 sk.connect((‘www.cnblogs.com‘,80)) 4 5 sk.sendall(b"GET /wupeiqi http1.1\r\n.....\r\n\r\n") 6 sk.sendall(b"POST /wupeiqi http1.1\r\n.....\r\n\r\nuser=alex&pwd=123") 7 8 # 阻塞 9 data = sk.recv(8096) 10 11 sk.close()
IO多路復用:
監聽多個socket是否發生變化
IO多路復用的作用:
1.select,內部循環檢測socket是否發生變化;最多只能檢測1024個socket
2.poll,內部循環檢測socket是否發生變化;檢測socket數不限
3.epoll,通過回調的方式檢測socket是否發生變化;檢測socket數不限
什麽是異步非阻塞?
非阻塞:
不等待(可能會報錯,捕捉異常)
代碼:
sk = socket.socket()
sk.setblocking(False)
異步:
回調,當達到某個指定的狀態之後,自動調用特定函數。
如何自定義異步非阻塞模塊?
本質:socket+IO多路復用
基於socket設置setblocking和IO多路復用來實現。
爬蟲發送Http請求本質創建socket對象;
IO多路復用"循環"監聽socket是否發生變化,一旦發生變化, 我們可以自定義操作(觸發某個函數的執行)
什麽是協程?
1. 是“微線程”,不存在;是由程序員人為創造出來並控制程序:先執行某段代碼、再跳到某處執行某段代碼。
2.如果遇到非IO請求來回切換:性能更低。
3. 如果遇到IO(耗時)請求來回切換:性能高、實現並發(本質上利用IO等待的過程,再去幹一些其他的事)
通過yield實現一個協程:
def func1(): print(‘adsfasdf‘) print(‘adsfasdf‘) print(‘adsfasdf‘) yield 1 print(‘adsfasdf‘) print(‘adsfasdf‘) print(‘adsfasdf‘) yield 2 yield 3 yield 4 def func2(): print(‘adsfasdf‘) print(‘adsfasdf‘) print(‘adsfasdf‘) yield 11 yield 12 yield 19 g1=func1() g2=func2() g1.send(None) g1.send(None) g2.send(None)View Code
通過greenlet模塊實現一個協程:
from greenlet import greenlet def test1(): print 12 gr2.switch() print 34 gr2.switch() def test2(): print 56 gr1.switch() print 78 gr1 = greenlet(test1) gr2 = greenlet(test2) gr1.switch()View Code
Python內置以及第三方模塊提供異步IO請求模塊,使用簡便大大提高效率,而對於異步IO請求的本質則是【非阻塞Socket】+【IO多路復用】:
常用的3種:
import asyncio import requests @asyncio.coroutine def fetch_async(func, *args): loop = asyncio.get_event_loop() future = loop.run_in_executor(None, func, *args) response = yield from future print(response.url, response.content) tasks = [ fetch_async(requests.get, ‘http://www.cnblogs.com/wupeiqi/‘), fetch_async(requests.get, ‘http://dig.chouti.com/pic/show?nid=4073644713430508&lid=10273091‘) ] loop = asyncio.get_event_loop() results = loop.run_until_complete(asyncio.gather(*tasks)) loop.close()asyncio+requests
import gevent import requests from gevent import monkey monkey.patch_all() def fetch_async(method, url, req_kwargs): print(method, url, req_kwargs) response = requests.request(method=method, url=url, **req_kwargs) print(response.url, response.content) # ##### 發送請求 ##### gevent.joinall([ gevent.spawn(fetch_async, method=‘get‘, url=‘https://www.python.org/‘, req_kwargs={}), gevent.spawn(fetch_async, method=‘get‘, url=‘https://www.yahoo.com/‘, req_kwargs={}), gevent.spawn(fetch_async, method=‘get‘, url=‘https://github.com/‘, req_kwargs={}), ]) # ##### 發送請求(協程池控制最大協程數量) ##### # from gevent.pool import Pool # pool = Pool(None) # gevent.joinall([ # pool.spawn(fetch_async, method=‘get‘, url=‘https://www.python.org/‘, req_kwargs={}), # pool.spawn(fetch_async, method=‘get‘, url=‘https://www.yahoo.com/‘, req_kwargs={}), # pool.spawn(fetch_async, method=‘get‘, url=‘https://www.github.com/‘, req_kwargs={}), # ]) 4.gevent + requestsgevent+requests
from twisted.web.client import getPage, defer from twisted.internet import reactor def all_done(arg): reactor.stop() def callback(contents): print(contents) d_list = [] url_list = [‘http://www.bing.com‘, ‘http://www.baidu.com‘, ] for url in url_list: d = getPage(bytes(url, encoding=‘utf8‘)) d.addCallback(callback) d_list.append(d) # 用於檢查是否頁面已經全部下載完成,如果已下載完成那麽,就停止循環。 dlist = defer.DeferredList(d_list) dlist.addBoth(all_done) # reactor.run()Twisted示例
python之路 -- 爬蟲 -- 高性能相關