欧美三区_成人在线免费观看视频_欧美极品少妇xxxxⅹ免费视频_a级毛片免费播放_鲁一鲁中文字幕久久_亚洲一级特黄

python爬蟲(chóng)第11關(guān) 協(xié)程

系統(tǒng) 1616 0

協(xié)程是什么

我們已經(jīng)做過(guò)不少爬蟲(chóng)項(xiàng)目,不過(guò)我們爬取的數(shù)據(jù)都不算太大,如果我們想要爬取的是成千上萬(wàn)條的數(shù)據(jù),那么就會(huì)遇到一個(gè)問(wèn)題:因?yàn)槌绦蚴且恍幸恍幸来螆?zhí)行的緣故,要等待很久,我們才能拿到想要的數(shù)據(jù)。

既然一個(gè)爬蟲(chóng)爬取大量數(shù)據(jù)要爬很久,那我們能不能讓多個(gè)爬蟲(chóng)一起爬取?

這樣無(wú)疑能提高爬取的效率,就像一個(gè)人干不完的活兒,組個(gè)團(tuán)隊(duì)一起干,活一下被干完了。

這是一個(gè)很好的思路——讓多個(gè)爬蟲(chóng)幫我們干活。但具體怎么用Python實(shí)現(xiàn)這事呢?

我們可以先別急著想怎么實(shí)現(xiàn)這件事,后面我會(huì)跟你說(shuō)。

現(xiàn)在,你先跟我想象一個(gè)情景:
python爬蟲(chóng)第11關(guān) 協(xié)程_第1張圖片
相信你肯定會(huì)這么做:把三部電影都點(diǎn)擊下載。看哪一部先下載好了,就先看哪一部,讓還沒(méi)有下載完的電影持續(xù)保持下載狀態(tài)。

如果用計(jì)算機(jī)里的概念來(lái)解釋這件事的話:在一個(gè)任務(wù)未完成時(shí),就可以執(zhí)行其他多個(gè)任務(wù),彼此不受影響(在看第一部下載好的電影時(shí),其他電影繼續(xù)保持下載狀態(tài),彼此之間不受影響),叫異步。

有異步的概念,那應(yīng)該也有同步的概念吧?是的,同步就是一個(gè)任務(wù)結(jié)束才能啟動(dòng)下一個(gè)(類(lèi)比你看完一部電影,才能去看下一部電影)。
顯然,異步執(zhí)行任務(wù)會(huì)比同步更加節(jié)省時(shí)間,因?yàn)樗軠p少不必要的等待。如果你需要對(duì)時(shí)間做優(yōu)化,異步是一個(gè)很值得考慮的方案。
如果我們把同步與異步的概念遷移到網(wǎng)絡(luò)爬蟲(chóng)的場(chǎng)景中,那我們之前學(xué)的爬蟲(chóng)方式都是同步的。

爬蟲(chóng)每發(fā)起一個(gè)請(qǐng)求,都要等服務(wù)器返回響應(yīng)后,才會(huì)執(zhí)行下一步。而很多時(shí)候,由于網(wǎng)絡(luò)不穩(wěn)定,加上服務(wù)器自身也需要響應(yīng)的時(shí)間,導(dǎo)致爬蟲(chóng)會(huì)浪費(fèi)大量時(shí)間在等待上。這也是爬取大量數(shù)據(jù)時(shí),爬蟲(chóng)的速度會(huì)比較慢的原因。
怎樣才能實(shí)現(xiàn)異步的爬蟲(chóng)方式,提高爬蟲(chóng)的效率呢?要回答這個(gè)問(wèn)題的話,得了解一點(diǎn)點(diǎn)計(jì)算機(jī)的歷史小知識(shí)。

我們知道每臺(tái)計(jì)算機(jī)都靠著CPU(中央處理器)干活。在過(guò)去,單核CPU的計(jì)算機(jī)在處理多任務(wù)時(shí),會(huì)出現(xiàn)一個(gè)問(wèn)題:每個(gè)任務(wù)都要搶占CPU,執(zhí)行完了一個(gè)任務(wù)才開(kāi)啟下一個(gè)任務(wù)。CPU畢竟只有一個(gè),這會(huì)讓計(jì)算機(jī)處理的效率很低。

python爬蟲(chóng)第11關(guān) 協(xié)程_第2張圖片
為了解決這樣的問(wèn)題,一種非搶占式的異步技術(shù)被創(chuàng)造了出來(lái),這種方式叫多協(xié)程(在此,多是多個(gè)的意思)。

它的原理是:一個(gè)任務(wù)在執(zhí)行過(guò)程中,如果遇到等待,就先去執(zhí)行其他的任務(wù),當(dāng)?shù)却Y(jié)束,再回來(lái)繼續(xù)之前的那個(gè)任務(wù)。在計(jì)算機(jī)的世界,這種任務(wù)來(lái)回切換得非常快速,看上去就像多個(gè)任務(wù)在被同時(shí)執(zhí)行一樣。

這就好比當(dāng)你要做一桌飯菜,你可以在等電飯煲蒸飯的時(shí)候去炒菜。而不是等飯做好,再去炒菜。你還是那個(gè)你,但工作時(shí)間就這樣被縮短了。多協(xié)程能夠縮短工作時(shí)間的原理,也是如此。

所以,要實(shí)現(xiàn)異步的爬蟲(chóng)方式的話,需要用到多協(xié)程。在它的幫助下,我們能實(shí)現(xiàn)前面提到的“讓多個(gè)爬蟲(chóng)替我們干活”。
那么,新的問(wèn)題來(lái)了——怎么使用多協(xié)程?

多協(xié)程的用法

gevent庫(kù)
python爬蟲(chóng)第11關(guān) 協(xié)程_第3張圖片
所以,接下來(lái)我會(huì)帶你了解gevent的用法,和實(shí)操一個(gè)多協(xié)程案例:爬取8個(gè)網(wǎng)站(包括百度、新浪、搜狐、騰訊、網(wǎng)易、愛(ài)奇藝、天貓、鳳凰)。

我們先用之前同步的爬蟲(chóng)方式爬取這8個(gè)網(wǎng)站,然后等下再和gevent異步爬取做一個(gè)對(duì)比。

            
              import requests,time
#導(dǎo)入requests和time
start = time.time()
#記錄程序開(kāi)始時(shí)間

url_list = ['https://www.baidu.com/',
'https://www.sina.com.cn/',
'http://www.sohu.com/',
'https://www.qq.com/',
'https://www.163.com/',
'http://www.iqiyi.com/',
'https://www.tmall.com/',
'http://www.ifeng.com/']
#把8個(gè)網(wǎng)站封裝成列表

for url in url_list:
#遍歷url_list
    r = requests.get(url)
    #用requests.get()函數(shù)爬取網(wǎng)站
    print(url,r.status_code)
    #打印網(wǎng)址和抓取請(qǐng)求的狀態(tài)碼

end = time.time()
#記錄程序結(jié)束時(shí)間
print(end-start)
#end-start是結(jié)束時(shí)間減去開(kāi)始時(shí)間,就是最終所花時(shí)間。
#最后,把時(shí)間打印出來(lái)。



            
          

python爬蟲(chóng)第11關(guān) 協(xié)程_第4張圖片
程序運(yùn)行后,你會(huì)看到同步的爬蟲(chóng)方式,是依次爬取網(wǎng)站,并等待服務(wù)器響應(yīng)(狀態(tài)碼為200表示正常響應(yīng))后,才爬取下一個(gè)網(wǎng)站。比如第一個(gè)先爬取了百度的網(wǎng)址,等服務(wù)器響應(yīng)后,再去爬取新浪的網(wǎng)址,以此類(lèi)推,直至全部爬取完畢。

為了讓你能更直觀地看到爬蟲(chóng)完成任務(wù)所需的時(shí)間,我導(dǎo)入了time模塊,記錄了程序開(kāi)始和結(jié)束的時(shí)間,最后打印出來(lái)的就是爬蟲(chóng)爬取這8個(gè)網(wǎng)站所花費(fèi)的時(shí)間。
如果我們用了多協(xié)程來(lái)爬取會(huì)有什么不同?

            
              from gevent import monkey
monkey.patch_all()
import gevent,time,requests

start = time.time()

url_list = ['https://www.baidu.com/',
'https://www.sina.com.cn/',
'http://www.sohu.com/',
'https://www.qq.com/',
'https://www.163.com/',
'http://www.iqiyi.com/',
'https://www.tmall.com/',
'http://www.ifeng.com/']

def crawler(url):
    r = requests.get(url)
    print(url,time.time()-start,r.status_code)

tasks_list = []

for url in url_list:
    task = gevent.spawn(crawler,url)
    tasks_list.append(task)
gevent.joinall(tasks_list)
end = time.time()
print(end-start)



            
          

python爬蟲(chóng)第11關(guān) 協(xié)程_第5張圖片
通過(guò)每個(gè)請(qǐng)求運(yùn)行的時(shí)間,我們能知道:爬蟲(chóng)用了異步的方式抓取了8個(gè)網(wǎng)站,因?yàn)槊總€(gè)請(qǐng)求完成的時(shí)間并不是按著順序來(lái)的。比如在我測(cè)試運(yùn)行這個(gè)代碼的時(shí)候,最先爬取到的網(wǎng)站是搜狐,接著是鳳凰,并不是百度和新浪。

且每個(gè)請(qǐng)求完成時(shí)間之間的間隔都非常短,你可以看作這些請(qǐng)求幾乎是“同時(shí)”發(fā)起的。

通過(guò)對(duì)比同步和異步爬取最終所花的時(shí)間,用多協(xié)程異步的爬取方式,確實(shí)比同步的爬蟲(chóng)方式速度更快。
其實(shí),我們案例爬取的數(shù)據(jù)量還比較小,不能直接體現(xiàn)出更大的速度差異。如果爬的是大量的數(shù)據(jù),運(yùn)用多協(xié)程會(huì)有更顯著的速度優(yōu)勢(shì)。

比如我做了一個(gè)測(cè)試:把爬取8個(gè)網(wǎng)站變成爬取80個(gè)網(wǎng)站,用同步的爬取方式大概需要花17.3秒,但用多協(xié)程異步爬取只需大概4.5秒,整個(gè)爬取效率提升了280%+。
python爬蟲(chóng)第11關(guān) 協(xié)程_第6張圖片
現(xiàn)在,我們一行行來(lái)看剛剛用了gevent的代碼。

提醒:導(dǎo)入gevent庫(kù)前,得先安裝它。(安裝方法:window電腦:在終端輸入命令:pip install gevent,按下enter鍵;mac電腦:在終端輸入命令:pip3 install gevent,按下enter鍵)
再來(lái)看一遍代碼:

            
              from gevent import monkey
#從gevent庫(kù)里導(dǎo)入monkey模塊。
monkey.patch_all()
#monkey.patch_all()能把程序變成協(xié)作式運(yùn)行,就是可以幫助程序?qū)崿F(xiàn)異步。
import gevent,time,requests
#導(dǎo)入gevent、time、requests。

start = time.time()
#記錄程序開(kāi)始時(shí)間。

url_list = ['https://www.baidu.com/',
'https://www.sina.com.cn/',
'http://www.sohu.com/',
'https://www.qq.com/',
'https://www.163.com/',
'http://www.iqiyi.com/',
'https://www.tmall.com/',
'http://www.ifeng.com/']
#把8個(gè)網(wǎng)站封裝成列表。

def crawler(url):
#定義一個(gè)crawler()函數(shù)。
    r = requests.get(url)
    #用requests.get()函數(shù)爬取網(wǎng)站。
    print(url,time.time()-start,r.status_code)
    #打印網(wǎng)址、請(qǐng)求運(yùn)行時(shí)間、狀態(tài)碼。

tasks_list = [ ]
#創(chuàng)建空的任務(wù)列表。

for url in url_list:
#遍歷url_list。
    task = gevent.spawn(crawler,url)
    #用gevent.spawn()函數(shù)創(chuàng)建任務(wù)。
    tasks_list.append(task)
    #往任務(wù)列表添加任務(wù)。
gevent.joinall(tasks_list)
#執(zhí)行任務(wù)列表里的所有任務(wù),就是讓爬蟲(chóng)開(kāi)始爬取網(wǎng)站。
end = time.time()
#記錄程序結(jié)束時(shí)間。
print(end-start)
#打印程序最終所需時(shí)間。



            
          

第1、3行代碼:從gevent庫(kù)里導(dǎo)入了monkey模塊,這個(gè)模塊能將程序轉(zhuǎn)換成可異步的程序。monkey.patch_all(),它的作用其實(shí)就像你的電腦有時(shí)會(huì)彈出“是否要用補(bǔ)丁修補(bǔ)漏洞或更新”一樣。它能給程序打上補(bǔ)丁,讓程序變成是異步模式,而不是同步模式。它也叫“猴子補(bǔ)丁”。
我們要在導(dǎo)入其他庫(kù)和模塊前,先把monkey模塊導(dǎo)入進(jìn)來(lái),并運(yùn)行monkey.patch_all()。這樣,才能先給程序打上補(bǔ)丁。你也可以理解成這是一個(gè)規(guī)范的寫(xiě)法。

第5行代碼:我們導(dǎo)入了gevent庫(kù)來(lái)幫我們實(shí)現(xiàn)多協(xié)程,導(dǎo)入了time模塊來(lái)幫我們記錄爬取所需時(shí)間,導(dǎo)入了requests模塊幫我們實(shí)現(xiàn)爬取8個(gè)網(wǎng)站。
python爬蟲(chóng)第11關(guān) 協(xié)程_第7張圖片
第21、23、25行代碼:我們定義了一個(gè)crawler函數(shù),只要調(diào)用這個(gè)函數(shù),它就會(huì)執(zhí)行【用requests.get()爬取網(wǎng)站】和【打印網(wǎng)址、請(qǐng)求運(yùn)行時(shí)間、狀態(tài)碼】這兩個(gè)任務(wù)。
python爬蟲(chóng)第11關(guān) 協(xié)程_第8張圖片
第33行代碼:因?yàn)間event只能處理gevent的任務(wù)對(duì)象,不能直接調(diào)用普通函數(shù),所以需要借助gevent.spawn()來(lái)創(chuàng)建任務(wù)對(duì)象。
這里需要注意一點(diǎn):gevent.spawn()的參數(shù)需為要調(diào)用的函數(shù)名及該函數(shù)的參數(shù)。比如,gevent.spawn(crawler,url)就是創(chuàng)建一個(gè)執(zhí)行crawler函數(shù)的任務(wù),參數(shù)為crawler函數(shù)名和它自身的參數(shù)url。
python爬蟲(chóng)第11關(guān) 協(xié)程_第9張圖片
第35行代碼:用append函數(shù)把任務(wù)添加到tasks_list的任務(wù)列表里。
第37行代碼:調(diào)用gevent庫(kù)里的joinall方法,能啟動(dòng)執(zhí)行所有的任務(wù)。gevent.joinall(tasks_list)就是執(zhí)行tasks_list這個(gè)任務(wù)列表里的所有任務(wù),開(kāi)始爬取。
python爬蟲(chóng)第11關(guān) 協(xié)程_第10張圖片
總結(jié)一下用gevent實(shí)現(xiàn)多協(xié)程爬取的重點(diǎn):
python爬蟲(chóng)第11關(guān) 協(xié)程_第11張圖片
到這里,用gevent實(shí)操抓取8個(gè)網(wǎng)站我們已經(jīng)完成,gevent的基礎(chǔ)語(yǔ)法我們也大致了解。

那如果我們要爬的不是8個(gè)網(wǎng)站,而是1000個(gè)網(wǎng)站,我們可以怎么做?

用我們剛剛學(xué)的gevent語(yǔ)法,我們可以用gevent.spawn()創(chuàng)建1000個(gè)爬取任務(wù),再用gevent.joinall()執(zhí)行這1000個(gè)任務(wù)。

但這種方法會(huì)有問(wèn)題:執(zhí)行1000個(gè)任務(wù),就是一下子發(fā)起1000次請(qǐng)求,這樣子的惡意請(qǐng)求,會(huì)拖垮網(wǎng)站的服務(wù)器。
python爬蟲(chóng)第11關(guān) 協(xié)程_第12張圖片
既然這種直接創(chuàng)建1000個(gè)任務(wù)的方式不可取,那我們能不能只創(chuàng)建成5個(gè)任務(wù),但每個(gè)任務(wù)爬取200個(gè)網(wǎng)站?

假設(shè)我們有1000個(gè)任務(wù),那創(chuàng)建5個(gè)任務(wù),每個(gè)任務(wù)爬取200個(gè)網(wǎng)站的代碼可以寫(xiě)成如下的樣子(此代碼僅做展示,并不可運(yùn)行):

            
              from gevent import monkey
monkey.patch_all()
import gevent,time,requests

start = time.time()
url_list = ['https://www.baidu.com/',
'https://www.sina.com.cn/',
'http://www.sohu.com/',
'https://www.qq.com/',
'https://www.163.com/',
'http://www.iqiyi.com/',
'https://www.tmall.com/',
'http://www.ifeng.com/'
……
#假設(shè)有1000個(gè)網(wǎng)址
]

def crawler(url_list):
#定義一個(gè)crawler()函數(shù)。
    for url in url_list:
        r = requests.get(url)
        print(url,time.time()-start,r.status_code)

tasks_list = [ ]
#創(chuàng)建空的任務(wù)列表。
for i in range(5):
    task = gevent.spawn(crawler,url_list[i*200:(i+1)*200])
    #用gevent.spawn()函數(shù)創(chuàng)建5個(gè)任務(wù)。
    tasks_list.append(task)
    #往任務(wù)列表添加任務(wù)。

gevent.joinall(tasks_list)
end = time.time()
print(end-start)



            
          

遺憾地告訴你,這么做也還是會(huì)有問(wèn)題的。就算我們用gevent.spawn()創(chuàng)建了5個(gè)分別執(zhí)行爬取200個(gè)網(wǎng)站的任務(wù),這5個(gè)任務(wù)之間是異步執(zhí)行的,但是每個(gè)任務(wù)(爬取200個(gè)網(wǎng)站)內(nèi)部是同步的。
這意味著:如果有一個(gè)任務(wù)在執(zhí)行的過(guò)程中,它要爬取的一個(gè)網(wǎng)站一直在等待響應(yīng),哪怕其他任務(wù)都完成了200個(gè)網(wǎng)站的爬取,它也還是不能完成200個(gè)網(wǎng)站的爬取。
python爬蟲(chóng)第11關(guān) 協(xié)程_第13張圖片
這個(gè)方法也不行,那還有什么方法呢?

這時(shí)我們可以從實(shí)際生活的案例中得到啟發(fā)。想想銀行是怎么在一天內(nèi)辦理1000個(gè)客戶(hù)的業(yè)務(wù)的。

銀行會(huì)開(kāi)設(shè)辦理業(yè)務(wù)的多個(gè)窗口,讓客戶(hù)取號(hào)排隊(duì),由銀行的叫號(hào)系統(tǒng)分配客戶(hù)到不同的窗口去辦理業(yè)務(wù)。

在gevent庫(kù)中,也有一個(gè)模塊可以實(shí)現(xiàn)這種功能——queue模塊。

queue模塊
當(dāng)我們用多協(xié)程來(lái)爬蟲(chóng),需要?jiǎng)?chuàng)建大量任務(wù)時(shí),我們可以借助queue模塊。

queue翻譯成中文是隊(duì)列的意思。我們可以用queue模塊來(lái)存儲(chǔ)任務(wù),讓任務(wù)都變成一條整齊的隊(duì)列,就像銀行窗口的排號(hào)做法。因?yàn)閝ueue其實(shí)是一種有序的數(shù)據(jù)結(jié)構(gòu),可以用來(lái)存取數(shù)據(jù)。

這樣,協(xié)程就可以從隊(duì)列里把任務(wù)提取出來(lái)執(zhí)行,直到隊(duì)列空了,任務(wù)也就處理完了。就像銀行窗口的工作人員會(huì)根據(jù)排號(hào)系統(tǒng)里的排號(hào),處理客人的業(yè)務(wù),如果已經(jīng)沒(méi)有新的排號(hào),就意味著客戶(hù)的業(yè)務(wù)都已辦理完畢。
python爬蟲(chóng)第11關(guān) 協(xié)程_第14張圖片
接下來(lái),我們來(lái)實(shí)操看看,可以怎么用queue模塊和協(xié)程配合,依舊以抓取8個(gè)網(wǎng)站為例。

            
              from gevent import monkey
monkey.patch_all()
import gevent,time,requests
from gevent.queue import Queue

start = time.time()

url_list = ['https://www.baidu.com/',
'https://www.sina.com.cn/',
'http://www.sohu.com/',
'https://www.qq.com/',
'https://www.163.com/',
'http://www.iqiyi.com/',
'https://www.tmall.com/',
'http://www.ifeng.com/']

work = Queue()
for url in url_list:
    work.put_nowait(url)

def crawler():
    while not work.empty():
        url = work.get_nowait()
        r = requests.get(url)
        print(url,work.qsize(),r.status_code)

tasks_list  = [ ]

for x in range(2):
    task = gevent.spawn(crawler)
    tasks_list.append(task)
gevent.joinall(tasks_list)

end = time.time()
print(end-start)



            
          

python爬蟲(chóng)第11關(guān) 協(xié)程_第15張圖片
網(wǎng)址后面的數(shù)字指的是隊(duì)列里還剩的任務(wù)數(shù),比如第一個(gè)網(wǎng)址后面的數(shù)字6,就是此時(shí)隊(duì)列里還剩6個(gè)抓取其他網(wǎng)址的任務(wù)。

現(xiàn)在,我們把剛剛運(yùn)行的代碼拆成4部分來(lái)講解,第1部分是導(dǎo)入模塊。

            
              from gevent import monkey
#從gevent庫(kù)里導(dǎo)入monkey模塊。
monkey.patch_all()
#monkey.patch_all()能把程序變成協(xié)作式運(yùn)行,就是可以幫助程序?qū)崿F(xiàn)異步。
import gevent,time,requests
#導(dǎo)入gevent、time、requests
from gevent.queue import Queue
#從gevent庫(kù)里導(dǎo)入queue模塊


            
          

因?yàn)間event庫(kù)里就帶有queue,所以我們用【from gevent.queue import Queue】就能把queue模塊導(dǎo)入。其他模塊和代碼我們?cè)谥v解gevent時(shí)已經(jīng)講解過(guò)了,相信你能懂。

第2部分,是如何創(chuàng)建隊(duì)列,以及怎么把任務(wù)存儲(chǔ)進(jìn)隊(duì)列里。

            
              start = time.time()
#記錄程序開(kāi)始時(shí)間

url_list = ['https://www.baidu.com/',
'https://www.sina.com.cn/',
'http://www.sohu.com/',
'https://www.qq.com/',
'https://www.163.com/',
'http://www.iqiyi.com/',
'https://www.tmall.com/',
'http://www.ifeng.com/']

work = Queue()
#創(chuàng)建隊(duì)列對(duì)象,并賦值給work。
for url in url_list:
#遍歷url_list
    work.put_nowait(url)
    #用put_nowait()函數(shù)可以把網(wǎng)址都放進(jìn)隊(duì)列里。



            
          

用Queue()能創(chuàng)建queue對(duì)象,相當(dāng)于創(chuàng)建了一個(gè)不限任何存儲(chǔ)數(shù)量的空隊(duì)列。如果我們往Queue()中傳入?yún)?shù),比如Queue(10),則表示這個(gè)隊(duì)列只能存儲(chǔ)10個(gè)任務(wù)。

創(chuàng)建了queue對(duì)象后,我們就能調(diào)用這個(gè)對(duì)象的put_nowait方法,把我們的每個(gè)網(wǎng)址都存儲(chǔ)進(jìn)我們剛剛建立好的空隊(duì)列里。

work.put_nowait(url)這行代碼就是把遍歷的8個(gè)網(wǎng)站,都存儲(chǔ)進(jìn)隊(duì)列里。

第3部分,是定義爬取函數(shù),和如何從隊(duì)列里提取出剛剛存儲(chǔ)進(jìn)去的網(wǎng)址。

            
              def crawler():
    while not work.empty():
    #當(dāng)隊(duì)列不是空的時(shí)候,就執(zhí)行下面的程序。
        url = work.get_nowait()
        #用get_nowait()函數(shù)可以把隊(duì)列里的網(wǎng)址都取出。
        r = requests.get(url)
        #用requests.get()函數(shù)抓取網(wǎng)址。
        print(url,work.qsize(),r.status_code)
        #打印網(wǎng)址、隊(duì)列長(zhǎng)度、抓取請(qǐng)求的狀態(tài)碼。



            
          

這里定義的crawler函數(shù),多了三個(gè)你可能看不懂的代碼:1.while not work.empty():;2.url = work.get_nowait();3.work.qsize()。

這三個(gè)代碼涉及到queue對(duì)象的三個(gè)方法:empty方法,是用來(lái)判斷隊(duì)列是不是空了的;get_nowait方法,是用來(lái)從隊(duì)列里提取數(shù)據(jù)的;qsize方法,是用來(lái)判斷隊(duì)列里還剩多少數(shù)量的。

當(dāng)然,queue對(duì)象的方法還不止這幾種,比如有判斷隊(duì)列是否為空的empty方法,對(duì)應(yīng)也有判斷隊(duì)列是否為滿的full方法。

你是不是覺(jué)得queue對(duì)象這么多方法,一下子記不住?其實(shí),這些不需要你死記硬背的,附上一張queue對(duì)象的方法表,你只需要在用到的時(shí)候,查查表就好。
python爬蟲(chóng)第11關(guān) 協(xié)程_第16張圖片
代碼的前3部分,我們講解完了。如果你能明白隊(duì)列怎么創(chuàng)建、數(shù)據(jù)怎么存儲(chǔ)進(jìn)隊(duì)列,以及怎么從隊(duì)列里提取出的數(shù)據(jù),就說(shuō)明queue模塊的重點(diǎn)內(nèi)容你都掌握了。

python爬蟲(chóng)第11關(guān) 協(xié)程_第17張圖片
接在第3部分代碼的后面,就是讓爬蟲(chóng)用多協(xié)程執(zhí)行任務(wù),爬取隊(duì)列里的8個(gè)網(wǎng)站的代碼(重點(diǎn)看有注釋的代碼)。

            
              def crawler():
    while not work.empty():
        url = work.get_nowait()
        r = requests.get(url)
        print(url,work.qsize(),r.status_code)

tasks_list  = [ ]
#創(chuàng)建空的任務(wù)列表
for x in range(2):
#相當(dāng)于創(chuàng)建了2個(gè)爬蟲(chóng)
    task = gevent.spawn(crawler)
    #用gevent.spawn()函數(shù)創(chuàng)建執(zhí)行crawler()函數(shù)的任務(wù)。
    tasks_list.append(task)
    #往任務(wù)列表添加任務(wù)。
gevent.joinall(tasks_list)
#用gevent.joinall方法,執(zhí)行任務(wù)列表里的所有任務(wù),就是讓爬蟲(chóng)開(kāi)始爬取網(wǎng)站。
end = time.time()
print(end-start)



            
          

python爬蟲(chóng)第11關(guān) 協(xié)程_第18張圖片
我們創(chuàng)建了兩只可以異步爬取的爬蟲(chóng)。它們會(huì)從隊(duì)列里取走網(wǎng)址,執(zhí)行爬取任務(wù)。一旦一個(gè)網(wǎng)址被一只爬蟲(chóng)取走,另一只爬蟲(chóng)就取不到了,另一只爬蟲(chóng)就會(huì)取走下一個(gè)網(wǎng)址。直至所有網(wǎng)址都被取走,隊(duì)列為空時(shí),爬蟲(chóng)就停止工作。

用協(xié)程技術(shù)和隊(duì)列爬取8個(gè)網(wǎng)站的完整代碼如下:

            
              from gevent import monkey#gevent從庫(kù)里導(dǎo)入monkey模塊
monkey.patch_all()#能把程序變成協(xié)作式運(yùn)行,就是可以幫助程序?qū)崿F(xiàn)異步
import gevent,time,requests
from gevent.queue import Queue#gevent從庫(kù)里導(dǎo)入queue模塊

start=time.time()

url_list=['https://www.baidu.com/',
'https://www.sina.com.cn/',
'http://www.sohu.com/',
'https://www.qq.com/',
'https://www.163.com/',
'http://www.iqiyi.com/',
'https://www.tmall.com/',
'http://www.ifeng.com/'
]

work=Queue()#創(chuàng)建隊(duì)列對(duì)象,并賦值給work

for url in url_list:
    work.put_nowait(url)#用put_nowait()函數(shù)可以把網(wǎng)址都放進(jìn)隊(duì)列里。

def crawler():
    while not work.empty():#當(dāng)隊(duì)列不是空的時(shí)候,就執(zhí)行下面的程序
        url=work.get_nowait()#get_nowait()函數(shù)可以把隊(duì)列里的網(wǎng)址都取出。
        res=requests.get(url)#用requests.get()函數(shù)抓取網(wǎng)址。
        print(url,work.qsize(),res.status_code)#打印網(wǎng)址、隊(duì)列長(zhǎng)度、抓取請(qǐng)求的狀態(tài)碼

tasks_list=[]#創(chuàng)建空的任務(wù)列表

for x in range(2):#相當(dāng)于創(chuàng)建了兩個(gè)爬蟲(chóng)
    task=gevent.spawn(crawler)#用gevent.spawn()函數(shù)創(chuàng)建執(zhí)行crawler()函數(shù)的任務(wù)
    tasks_list.append(task)

gevent.joinall(tasks_list)#用gevent.joinall方法,執(zhí)行任務(wù)列表里的所有任務(wù),就是讓爬蟲(chóng)開(kāi)始爬取網(wǎng)站。

end=time.time()
print(end-start)



            
          

動(dòng)手總會(huì)有收獲的。恭喜你,這一關(guān)的核心知識(shí),實(shí)現(xiàn)多協(xié)程的gevent庫(kù)和Queue模塊,你都學(xué)完了!

拓展復(fù)習(xí)

不過(guò),我還想和你拓展一點(diǎn)新的知識(shí)。

同樣是要做飯菜,我們已經(jīng)知道比先做飯?jiān)僮霾烁玫姆绞绞牵却鲲埖倪^(guò)程中去做菜。但其實(shí)還有更快的方案:讓一個(gè)人負(fù)責(zé)做飯,一個(gè)人負(fù)責(zé)做菜。

繼續(xù)說(shuō)我們的計(jì)算機(jī)歷史小知識(shí):在后來(lái),我們的CPU從單核終于進(jìn)化到了多核,每個(gè)核都能夠獨(dú)立運(yùn)作。計(jì)算機(jī)開(kāi)始能夠真正意義上同時(shí)執(zhí)行多個(gè)任務(wù)(術(shù)語(yǔ)叫并行執(zhí)行),而不是在多個(gè)任務(wù)之間來(lái)回切換(術(shù)語(yǔ)叫并發(fā)執(zhí)行)。

比如你現(xiàn)在打開(kāi)瀏覽器看著爬蟲(chóng)課程的同時(shí),可以打開(kāi)音樂(lè)播放器聽(tīng)歌,還可以打開(kāi)Excel。對(duì)于多核CPU而言,這些任務(wù)就都是同時(shí)運(yùn)行的。

時(shí)至今日,我們電腦一般都會(huì)是多核CPU。多協(xié)程,其實(shí)只占用了CPU的一個(gè)核運(yùn)行,沒(méi)有充分利用到其他核。利用CPU的多個(gè)核同時(shí)執(zhí)行任務(wù)的技術(shù),我們把它叫做“多進(jìn)程”。

所以,真正大型的爬蟲(chóng)程序不會(huì)單單只靠多協(xié)程來(lái)提升爬取速度的。比如,百度搜索引擎,可以說(shuō)是超大型的爬蟲(chóng)程序,它除了靠多協(xié)程,一定還會(huì)靠多進(jìn)程,甚至是分布式爬蟲(chóng)。

多進(jìn)程爬蟲(chóng)和分布式爬蟲(chóng)不屬于我們這個(gè)課程的知識(shí)范疇,我這里不會(huì)多講。或許接下來(lái)我們會(huì)開(kāi)設(shè)爬蟲(chóng)進(jìn)階課程,到時(shí)再和大家分享。

最后,是這一關(guān)的復(fù)習(xí)。
同步與異步——
python爬蟲(chóng)第11關(guān) 協(xié)程_第19張圖片
多協(xié)程,是一種非搶占式的異步方式。使用多協(xié)程的話,就能讓多個(gè)爬取任務(wù)用異步的方式交替執(zhí)行。
python爬蟲(chóng)第11關(guān) 協(xié)程_第20張圖片
python爬蟲(chóng)第11關(guān) 協(xié)程_第21張圖片
python爬蟲(chóng)第11關(guān) 協(xié)程_第22張圖片
python爬蟲(chóng)第11關(guān) 協(xié)程_第23張圖片
python爬蟲(chóng)第11關(guān) 協(xié)程_第24張圖片


更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主

微信掃碼或搜索:z360901061

微信掃一掃加我為好友

QQ號(hào)聯(lián)系: 360901061

您的支持是博主寫(xiě)作最大的動(dòng)力,如果您喜歡我的文章,感覺(jué)我的文章對(duì)您有幫助,請(qǐng)用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點(diǎn)擊下面給點(diǎn)支持吧,站長(zhǎng)非常感激您!手機(jī)微信長(zhǎng)按不能支付解決辦法:請(qǐng)將微信支付二維碼保存到相冊(cè),切換到微信,然后點(diǎn)擊微信右上角掃一掃功能,選擇支付二維碼完成支付。

【本文對(duì)您有幫助就好】

您的支持是博主寫(xiě)作最大的動(dòng)力,如果您喜歡我的文章,感覺(jué)我的文章對(duì)您有幫助,請(qǐng)用微信掃描上面二維碼支持博主2元、5元、10元、自定義金額等您想捐的金額吧,站長(zhǎng)會(huì)非常 感謝您的哦!!!

發(fā)表我的評(píng)論
最新評(píng)論 總共0條評(píng)論
主站蜘蛛池模板: 欧美日韩在线免费观看 | 伊人久久大杳蕉综合大象 | 无码乱人伦一区二区亚洲 | 一道本不卡一区 | 五月综合久久 | 大开眼界 无删减 | 亚洲精品国产成人 | 久久草在线看 | 亚洲 欧美 日韩在线 | 国产精品原创巨作av | 欧美男女网站 | 国产伦精品一区二区三区精品视频 | 一区二区精品 | 97精品国产综合久久 | 性福利影院 | 亚洲综合久久久久久中文字幕 | 青青99| 党涛| 国产原创91 | 婷婷色综合| 国产精品一区二区三区免费 | 浮力影院在线 | 成人年鲁鲁在线观看视频 | 日本一本视频 | 天天干天天舔天天操 | 中国一级特黄毛片大片 | 蜜芽亚洲| 91久久精品一区二区二区 | 新婚少妇小倩给老许泄火 | 久久久久av| 欧美精品一区二区三区免费播放 | 成人小片magnet | 久热在 | 久久精品中文 | 精品一区二区久久久久久久网站 | 色中色资源 | 精品欧美一区视频在线观看 | 日本天天操 | 丁香婷婷综合五月六月 | 久久久久久久久99精品 | 国产伊人网|