摘要:繼續(xù)看上一段循環(huán)的代碼,是遍歷,將已經(jīng)掛了的線程去除掉,那么在這個中線程什么情況下會死掉就是類中的方法中的這段代碼如果為空會循環(huán),此時對應(yīng)的線程會死掉。此時主函數(shù)的循環(huán)將死掉的線程去除,在線程數(shù)不足個的情況下,接下來的循環(huán)繼續(xù)制造新的線程。
上一篇文章: 從0開始寫一個多線程爬蟲(1)
我們用繼承Thread類的方式來改造多線程爬蟲,其實主要就是把上一篇文章的代碼寫到線程類的run方法中,代碼如下:
import re import requests from threading import Thread class BtdxMovie(Thread): # 初始化時傳入3個list,含義見上文,并為當(dāng)前線程取個名字 def __init__(self, total_url_list, used_url_list, movie_url_list, thread_name="MyThread"): super(BtdxMovie, self).__init__() self.all_url = total_url_list self.used_url = used_url_list self.movie_url = movie_url_list self.name = thread_name def run(self): while 1: # 從all_url中獲取第一條url,如果all_url為空則break,這會導(dǎo)致線程死掉(is_alive()為False) try: url = self.all_url.pop(0) except IndexError: break # 如果url是電影詳情頁,則將其加入到movie_url中 if re.match("https://www.btdx8.com/torrent/.*?html", url): if url not in self.movie_url: self.movie_url.append(url) try: html = requests.get(url).text new_url = re.findall("href="(https://.*?)"", html) for u in new_url: # 只要同一個域名下的url if not re.match("https://.*?btdx8.com", u): continue # "#"在url中是代表網(wǎng)頁位置的,這里處理一下,避免url重復(fù) if "#" in u: u = u.split("#")[0] if u in self.used_url or u in self.all_url: continue self.all_url.append(u) except: pass self.used_url.append(url) # 每次循環(huán)打印當(dāng)前線程id和各個list的長度 curr_thread = "[{}]".format(self.name) info = "ALL: {}, USED: {}, MOV: {}".format(len(self.all_url), len(self.used_url), len(self.movie_url)) print(curr_thread + ": " + info)
此時線程類就已經(jīng)寫好了,接下來要做的就是生成多個實例,并開啟線程,繼續(xù)追加如下代碼:
# 網(wǎng)站首頁 base_url = r"https://www.btdx8.com/" # 爬取到的新url會繼續(xù)加入到這個list里 total_url_list = [base_url] # 存放已經(jīng)爬取過的url used_url_list = [] # 存放是電影詳情頁的url movie_url_list = [] # 存入線程對象的list thread_list = [] thread_id = 0 while total_url_list or thread_list: for t in thread_list: if not t.is_alive(): thread_list.remove(t) while len(thread_list) < 5 and total_url_list: thread_id += 1 thread_name = "Thread-{}".format(str(thread_id).zfill(2)) t = BtdxMovie(total_url_list, used_url_list, movie_url_list, thread_name) t.start() thread_list.append(t)
此時運行腳本,就可以以多線程的方式抓取url了,運行之后print的信息如下:
[Thread-04]: ALL: 2482, USED: 84, MOV: 55 [Thread-01]: ALL: 2511, USED: 85, MOV: 56 [Thread-02]: ALL: 2518, USED: 86, MOV: 57 [Thread-05]: ALL: 2555, USED: 87, MOV: 58 [Thread-03]: ALL: 2587, USED: 88, MOV: 59 [Thread-01]: ALL: 2595, USED: 89, MOV: 60 [Thread-04]: ALL: 2614, USED: 90, MOV: 61 [Thread-05]: ALL: 2644, USED: 91, MOV: 62 [Thread-03]: ALL: 2686, USED: 92, MOV: 63
我們來解釋一下while循環(huán)里的代碼,先看內(nèi)嵌的while循環(huán),是當(dāng)total_url_list不為空,并且thread_list長度小于5的時候執(zhí)行,利用thread_id獲得thread_name,實例化一個線程實例t,并用t.start()開啟線程,然后將其加入到thread_list中,因此很容易可以理解這段代碼,就是確保當(dāng)前運行的線程數(shù)為5,并且給每個新線程一個從1開始自增長的id。
繼續(xù)看上一段for循環(huán)的代碼,是遍歷thread_list,將已經(jīng)掛了的線程去除掉,那么在這個case中線程什么情況下會死掉?就是BtdxMovie類中的run方法中的這段代碼:
try: url = self.all_url.pop(0) except IndexError: break
如果all_url為空會break循環(huán),此時對應(yīng)的線程會死掉。這里可能很容易誤以為所有的url都已經(jīng)爬取完了導(dǎo)致線程退出,實際上,目前的代碼沒有對爬取的url深度做控制,可能永遠都不會爬完,當(dāng)all_url為空時候,很大可能是all_url里的url被線程取走了,但還沒來得及把爬取到新的url加入到all_url中,所以很容易理解這種情況會在程序剛開始運行的時候發(fā)生,因為一開始all_url中只有一個url,被第一個線程取走,在第一個線程還沒返回結(jié)果的時候,后續(xù)的線程去取url都會導(dǎo)致循環(huán)break,然后線程死掉。此時主函數(shù)的for循環(huán)將死掉的線程去除,在線程數(shù)不足5個的情況下,接下來的while循環(huán)繼續(xù)制造新的線程。
那么外層的while循環(huán)的條件也很容易就明白了,不能在total_url_list為空的時候退出,要在total_url_list和thread_list都為空的時候才能退出。如果就是在total_url_list為空的時候退出會發(fā)生什么?程序會在第一個url被取走導(dǎo)致total_url_list為空的時候退出循環(huán)并結(jié)束嗎?嚴(yán)格來說是的,我們可以在程序的末尾加入一個print語句,就可以驗證修改while條件之后,while循環(huán)就退出了,但這個時候是主線程結(jié)束了,新增的線程并沒有結(jié)束,此時還有一個線程在不斷的運行和爬取url,這個線程就是獲取了第一個url的線程,線程可以設(shè)置成隨主線程一起停止,也可以讓主線程掛起等待其余線程運行完成,默認(rèn)情況下是我們這種,主線程運行完成并停止,而其余線程繼續(xù)運行。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/42294.html
摘要:最近發(fā)現(xiàn)有個電影下載網(wǎng)站叫做比特大雄,下了幾部電影之后,打算寫個爬蟲把網(wǎng)站的電影信息都爬取下來。結(jié)果我就發(fā)現(xiàn),速度太慢了因為決定將其改成多線程爬蟲,歡迎繼續(xù)閱讀后續(xù)的此系列文章。 最近發(fā)現(xiàn)有個電影下載網(wǎng)站叫做比特大雄,下了幾部電影之后,打算寫個爬蟲把網(wǎng)站的電影信息都爬取下來。 一開始思路是這樣的,從首頁開始,解析首頁的所有鏈接,如果這個鏈接是電影詳情頁的鏈接,就將其html解析成想要...
摘要:一般用進程池維護,的設(shè)為數(shù)量。多線程爬蟲多線程版本可以在單進程下進行異步采集,但線程間的切換開銷也會隨著線程數(shù)的增大而增大。異步協(xié)程爬蟲引入了異步協(xié)程語法。 Welcome to the D-age 對于網(wǎng)絡(luò)上的公開數(shù)據(jù),理論上只要由服務(wù)端發(fā)送到前端都可以由爬蟲獲取到。但是Data-age時代的到來,數(shù)據(jù)是新的黃金,毫不夸張的說,數(shù)據(jù)是未來的一切?;诮y(tǒng)計學(xué)數(shù)學(xué)模型的各種人工智能的出現(xiàn)...
摘要:所以與多線程相比,線程的數(shù)量越多,協(xié)程性能的優(yōu)勢越明顯。值得一提的是,在此過程中,只有一個線程在執(zhí)行,因此這與多線程的概念是不一樣的。 真正有知識的人的成長過程,就像麥穗的成長過程:麥穗空的時候,麥子長得很快,麥穗驕傲地高高昂起,但是,麥穗成熟飽滿時,它們開始謙虛,垂下麥芒。 ——蒙田《蒙田隨筆全集》 上篇論述了關(guān)于python多線程是否是雞肋的問題,得到了一些網(wǎng)友的認(rèn)可,當(dāng)然也有...
摘要:以下這些項目,你拿來學(xué)習(xí)學(xué)習(xí)練練手。當(dāng)你每個步驟都能做到很優(yōu)秀的時候,你應(yīng)該考慮如何組合這四個步驟,使你的爬蟲達到效率最高,也就是所謂的爬蟲策略問題,爬蟲策略學(xué)習(xí)不是一朝一夕的事情,建議多看看一些比較優(yōu)秀的爬蟲的設(shè)計方案,比如說。 (一)如何學(xué)習(xí)Python 學(xué)習(xí)Python大致可以分為以下幾個階段: 1.剛上手的時候肯定是先過一遍Python最基本的知識,比如說:變量、數(shù)據(jù)結(jié)構(gòu)、語法...
閱讀 3591·2021-11-04 16:06
閱讀 3589·2021-09-09 11:56
閱讀 854·2021-09-01 11:39
閱讀 906·2019-08-29 15:28
閱讀 2300·2019-08-29 15:18
閱讀 837·2019-08-29 13:26
閱讀 3338·2019-08-29 13:22
閱讀 1051·2019-08-29 12:18