看教程的時候看到一個,生產者跟消費者的概念比較有意思,但是給的代碼有問題無法正常運行,于是我就搗鼓了一下。
基本概念就是:
? ?? ?? ?? ?? ?? ?? ?? ?生產者:? ???一個進程獲取網頁沒頁的圖片連接(主進程)
? ?? ?? ?? ?? ?? ?? ?? ?消費者:? ?? ???一個進程下載圖片,不同的是每個頁面有20張圖片,所以在進程中又開了二十線程(子進程)
嗯,概念就是這樣接下來看代碼
# -*- coding: utf-8 -*-
# @Time :2023/9/30 22:46
# [url=home.php?mod=space&uid=686208]@AuThor[/url] :
# @FileName :進程池.線程池配合使用與進程共用變量的使用.py
# [url=home.php?mod=space&uid=2097534]@IED[/url] :PyCharm""""""
import multiprocessing
import requests
import os
from concurrent.futures import ThreadPoolExecutor
from lxml import etreepath = r'D:\img'
headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) ''AppleWebKit/537.36 (KHTML, like Gecko) ''Chrome/117.0.0.0 Safari/537.36'
}# 獲取圖片地址
def get_url(url, que):resp = requests.get(url, headers=headers)resp.encoding = resp.apparent_encodingdate = resp.texttree = etree.HTML(date)list_url = tree.xpath('//ul[@class="clearfix"]/li/a')for i in list_url:url_img = i.xpath('./img/@src')que.put('https://pic.netbian.com' + str(*url_img)) # put寫入進程變量隊列中resp.close()# 下載并保存圖片
def download_ove(url, name):try:resp = requests.get(url, headers=headers)path_img = os.path.join(path, f'{name.value}.jpg')with open(path_img, 'wb') as f:f.write(resp.content)print(f'保存成功{name.value}.jpg')name.value += 1 # 數值型進程變量自增except Exception as ex:print('下載出錯', ex)# 獲取進程隊列中的url并啟用線程池下載保存圖片
def download_img(que, name):# 創建線程池,指定20個線程處理數據with ThreadPoolExecutor(20) as t:while True:try:s = que.get(timeout=3) # 獲取進程隊列中的數據,等待3秒若是還沒獲取到數據拋出異常t.submit(download_ove, s, name) # 添加進線程池# t.submit(download_ove, s).add_done_callback(err_call_back) # 獲取線程池異常except Exception as ec:print(ec)break# 接收進程池與線程池異常的回調函數
def err_call_back(err):print(f'出錯啦~ error:{str(err)}')if __name__ == '__main__':if not os.path.exists(path):os.mkdir(path)que = multiprocessing.Manager().Queue() # 創建一個進程之間共享的隊列變量name = multiprocessing.Manager().Value('i', 0) # 創建一個進程之間共享的數值型變量,'i'表示整型數字,0表示從0開始for i in range(1, 4):if i != 1:url = f'https://pic.netbian.com/4kdongman/index_{i}.html'else:url = 'https://pic.netbian.com/4kdongman/'get_url(url, que)# 創建進程池,并將函數添加到進程池內,指定最多開辟1個進程處理數據(開多了怕把網址玩崩)with multiprocessing.Pool(1) as pool:pool.apply(download_img, args=(que, name)) # 將函數以同步的方式添加進進程池# apply_async異步方式無法使用不知道為啥,不報異常也不運行download_img函數# pool.apply_async(download_img, args=(que, name), error_callback=err_call_back)pool.close() # 結束進程池,不在往內添加數據pool.join() # 等待進程池運行結束print('結束')
本代碼僅作為練習所用,大家別把人家網站搞崩了。