需求描述
需求:结合多进程和线程池,下载一个图片网站某一页的全部图片 将上述需求分为两个进程执行:
- 进程1、获取图片下载地址;
- 进程2、根据图片下载地址下载图片(下载图片的时候使用线程池)
代码实现
import requests
from urllib import parse
from lxml import etree
# 多进程中的队列用于在不同的进程中传递信息
from multiprocessing import Process, Queue
from concurrent.futures import ThreadPoolExecutor
def get_img_url(q):
"""
从图片详情页获取图片下载地址
"""
# 图片首页网页链接
url = 'https://m.mms591.com/filter.php?q=dongwu_zhiwu-0-0-755-2'
resp_img = requests.get(url)
# print(resp_img.text)
# 用xpath解析网页,获取每个图片详情页的url
tree = etree.HTML(resp_img.text)
hrefs = tree.xpath('//div[@class="am-list-news-bd"]//a/@href')
for href in hrefs: # 每一个href是图片详情页的链接
# print(href)
# 拼接图片详情页链接
href = parse.urljoin(url, href)
# print(href)
# 向图片详情页url发送请求,获取图片的下载地址
child_resp = requests.get(href)
child_tree = etree.HTML(child_resp.text)
# 从HTML页面中解析出图片下载地址
img_src = child_tree.xpath('/html/body/div[3]/div[2]/article/div[1]/a/img/@src')[0]
# print(img_src)
# 将图片下载地址放入到队列中
q.put(img_src)
print(f'获取图片地址{img_src}成功')
# 向队列中添加一个表示结束的标志,否则下载图片的进程将会一直处于阻塞状态,不能结束程序
q.put('over!')
def downloading(img_src):
# 下载图片
name = img_src.split("/")[-1] # 图片名称
with open(f'./img/{name}', mode='wb') as f:
resp = requests.get(img_src)
f.write(resp.content)
print(f'{name}下载完成')
def download_img(q):
# 用线程池下载图片
with ThreadPoolExecutor(3) as t:
while True:
# 从队列中取出图片下载地址
# 如果队列为空,就会一直阻塞,直到队列中有值
img_src = q.get()
if img_src == 'over!': # 图片已经全部下载完成,结束程序运行
break
# 提交任务
t.submit(downloading, img_src)
if __name__ == '__main__':
# 由于进程之间的内存、资源是相互隔离的,所以两个进程之间不能直接通信,要通过队列等方式才能传递信息
q = Queue()
p1 = Process(target=get_img_url, args=(q,))
p2 = Process(target=download_img, args=(q,))
p1.start()
p2.start()