7 三種方式爬取斗魚主播照片
gitbook鏈接:用python帶你進入AI中的深度學習技術(shù)領(lǐng)域https://www.gitbook.com/book/scrappyzhang/python_to_deeplearn/details
github鏈接:https://github.com/ScrappyZhang/python_web_Crawler_DA_ML_DL
在第4章到第6章幻枉,我們分別學習了解了單線程迈喉、多線程副硅、多進程和協(xié)程,本章將通過一個抓取斗魚美女主播照片的案例來實際感受不同模式的實際區(qū)別傅寡。本章結(jié)合第7章的HTTP協(xié)議写隶,以python3的urllib.request(訪問網(wǎng)頁模塊) 和 re(正則表達式)兩個模塊為輔助來進行不同版本的抓取程序編寫陨献。
7.1 單線程抓取
首先爪膊,我們可以先通過瀏覽器查看到斗魚美女主播首頁的相關(guān)信息:[https://www.douyu.com/directory/game/yz]。
通過查看網(wǎng)頁源代碼缰揪,我們會發(fā)現(xiàn)每一張主播圖片都對應(yīng)著一個url陨享。因此我們可以想到,通過打開首頁網(wǎng)頁源代碼中所有的該url就可以將首頁的所有美女主播的圖片獲取到钝腺。(https://www.douyu.com/directory/game/yz抛姑。通過查看網(wǎng)頁源代碼,我們會發(fā)現(xiàn)每一張主播圖片都對應(yīng)著一個url艳狐。因此我們可以想到定硝,通過打開首頁網(wǎng)頁源代碼中所有的該url就可以將首頁的所有美女主播的圖片獲取到。)
如何實現(xiàn)全部下載呢毫目?單線程就是一個下載完再下載另一個蔬啡,直至所有的都下載。本節(jié)通過將首頁網(wǎng)頁源代碼中所有的url存入一個列表镀虐,然后再通過for循環(huán)一個個去下載美女主播圖片箱蟆。主要的代碼思路如下。
時間大約統(tǒng)計
import time
start = time.time() # 程序大約的開始時間
end = time.time() # 程序大約的結(jié)束時間
print('耗時:', end - start)
打開并獲取首頁網(wǎng)頁內(nèi)容
home = """https://www.douyu.com/directory/game/yz?page=1&isAjax=1""" # 首頁地址
# 請求的時候需要帶上頭部 可以防止初步的反爬措施
headers = {
"Host":"www.douyu.com",
"User-Agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) "
"Chrome/62.0.3202.94 Safari/537.36"
}
# 構(gòu)造好請求對象 將請求提交到服務(wù)器 獲取的響應(yīng)就是到首頁的html代碼
request = urllib.request.Request(url=home, headers=headers)
# urlopen函數(shù)可以直接傳入url網(wǎng)址 也可以指定好一個請求對象
response = urllib.request.urlopen(request)
# 將收到的響應(yīng)對象中數(shù)據(jù)的bytes數(shù)據(jù)讀出出來 并且解碼
html_data = response.read().decode()
正則解析美女主播首頁的內(nèi)容
img_list = re.findall(r"https://.*?\.(?:jpg)", html_data)
單線程下載照片
for img_url in img_list:
down_img(img_url)
下載照片函數(shù)
max_retry_count = 3
def down_img(url):
"""
下載圖片
https://rpic.douyucdn.cn/live-cover/appCovers/2017/10/24/12017.jpg
"""
for i in range(max_retry_count):
try:
response = urllib.request.urlopen(url)
# bytes
data = response.read()
# 從url中得到文件名
file_name = url[url.rfind('/')+1:]
# 打開文件用以寫入
with open("img/"+ file_name, "wb") as file:
file.write(data)
except Exception as e:
print("出錯 %s 正在重試" % e)
else:
break
完整代碼
import urllib.request
import re
import time
max_retry_count = 3
def down_img(url):
for i in range(max_retry_count):
try:
response = urllib.request.urlopen(url)
data = response.read()
file_name = url[url.rfind('/')+1:]
with open("img/"+ file_name, "wb") as file:
file.write(data)
except Exception as e:
print("出錯 %s 正在重試" % e)
else:
break
if __name__ == '__main__':
start = time.time() # 程序大約的開始時間
home = """https://www.douyu.com/directory/game/yz?page=1&isAjax=1""" # 首頁地址
# 請求的時候需要帶上頭部 可以防止初步的反爬措施
headers = {
"Host":"www.douyu.com",
"User-Agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) "
"Chrome/62.0.3202.94 Safari/537.36"
}
request = urllib.request.Request(url=home, headers=headers)
response = urllib.request.urlopen(request)
html_data = response.read().decode()
img_list = re.findall(r"https://.*?\.(?:jpg)", html_data)
# 下載美女主播圖片
for img_url in img_list:
down_img(img_url)
end = time.time()
print('耗時:', end - start)
實現(xiàn)結(jié)果
我們成功下載到114張照片(不同場景可能略有差異),并將所有的照片存入./img/文件夾刮便,耗時56.19秒空猜。
小結(jié)
7.2 多線程抓取
根據(jù)之前我們第4章所學習的知識,我們只需將for循環(huán)抓取替代為多線程創(chuàng)建抓取即可恨旱。替換代碼(完整代碼見net07_douyu_threading.py)如下:
# 下載美女主播圖片
for img_url in img_list:
td = threading.Thread(target=down_img, args=(img_url,))
td.start()
# 阻塞程序直到所有線程運行完畢
while True:
length = len(threading.enumerate())
if length == 1:
break
我們共花費了22.84秒辈毯。
7.3 多進程抓取
根據(jù)之前我們第5章所學習的進程知識,我們只需將for循環(huán)抓取替代為多進程創(chuàng)建抓取即可搜贤。替換代碼(完整代碼見net07_douyu_multiprocessing.py)如下:
# 下載美女主播圖片
for img_url in img_list:
p = multiprocessing.Process(target=down_img, args=(img_url,))
p.start()
# 阻塞程序直到所有進程運行完畢
while True:
length = len(multiprocessing.active_children()) # 存活的子進程的數(shù)量
if length == 0:
break
我們共花費了7.23秒谆沃。
7.4 協(xié)程抓取
根據(jù)之前我們第6章所學習的進程知識,我們只需將for循環(huán)抓取替代為協(xié)程創(chuàng)建抓取即可入客。替換代碼(完整代碼見net07_douyu_gevent.py)如下:
# 下載美女主播圖片
gevent_list = []
for img_url in img_list:
g1 = gevent.spawn(down_img, img_url)
gevent_list.append(g1)
gevent.joinall(gevent_list) # 待所有協(xié)程執(zhí)行完畢再向下執(zhí)行
同樣是單線程的協(xié)程管毙,我們共花費了5.46秒。