1. 程式人生 > >python之路 -- 爬蟲 -- 高性能相關

python之路 -- 爬蟲 -- 高性能相關

3-0 第三方 進程 Nid end cnblogs ide blocking aps

高性能爬蟲方案:

  多進程

  多線程

  利用“異步非阻塞”模塊實現單線程並發請求。

本質

 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 + requests
gevent+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之路 -- 爬蟲 -- 高性能相關