Python怎么将图片链接多进程下载?

一个函数下载,一个函数创建文件夹并保存,但是一张张图片下载太慢了。
尝试通过Queue put get执行两个函数,但是put那里会卡住不执行get部分的下载函数。

# 一张张下载
def get_geturl():
  for row_i in range(row_num):
    for line_i in range(line_num):
      pic_url = get(origin_pic_url, headers=req_header)
      if pic_url.status_code == 200:
        pic_url = get(origin_pic_url, headers=req_header).json()['result']
        pic_name = pic_url
        save_pic(pic_url, pic_name, id, device_name)
      elif pic_url.status_code == 400:
        print('token过期,重新操作')
        get_token()
        pic_url = get(origin_pic_url, headers=req_header).json()['result']
        save_pic(pic_url, pic_name, id, device_name)
      else:
        print('获取下载链失败')

def save_pic(pic_url, pic_name, id, device_name):
  #判断目录是否存在,不存在建立

  down_pic = f"{down_path}/{id}/{device_name}/{pic_name}"
  res = get(pic_url)
  with open(f'{down_pic}.jpg', 'wb') as f:
  f.write(res.content)
  f.close()
# 尝试多进程下载但是失败的
def get_geturl(q):
  for row_i in range(row_num):
    for line_i in range(line_num):
      pic_url = get(origin_pic_url, headers=req_header)
      if pic_url.status_code == 200:
        pic_url = get(origin_pic_url, headers=req_header).json()['result']
        pic_name = pic_url
        q.put(pic_url)
        q.put(pic_name)
        q.put(id)
        q.put(device_name)
      elif pic_url.status_code == 400:
        print('token过期,重新操作')
        get_token()
        pic_url = get(origin_pic_url, headers=req_header).json()['result']
        q.put(pic_url)
        q.put(pic_name)
        q.put(id)
        q.put(device_name)
      else:
        print('获取下载链失败')

def save_pic(q):
  while not q.empty():
    pic_url = q.get()
    pic_name = q.get()
    id = q.get()
    device_name = q.get()
    #判断目录是否存在,不存在建立

    down_pic = f"{down_path}/{id}/{device_name}/{pic_name}"
    res = get(pic_url)
    with open(f'{down_pic}.jpg', 'wb') as f:
    f.write(res.content)
    f.close()

if __name__ == "__main__":
    q = Queue(6)
    p1 = Process(target=get_geturl, args=(q,))
    p2 = Process(target=save_pic, args=(q,))
    p3 = Process(target=save_pic, args=(q,))
    p4 = Process(target=save_pic, args=(q,))
    p5 = Process(target=save_pic, args=(q,))
    p6 = Process(target=save_pic, args=(q,))
    p1.start()
    p2.start()
    p3.start()
    p4.start()
    p5.start()
    p6.start()
    p1.join()
    p2.join()
    p3.join()
    p4.join()
    p5.join()
    p6.join()

望采纳!!点击回答右侧采纳即可采纳!!!我帮你看看代码

python多线程下载图片,请采纳哦!!1!!1:

import urllib.parse as parse
from urllib import request
import random
from queue import Queue
import threading
import time
import json
import os



def get_time():  # 时间戳处理
    str_time = str(time.time())
    str_time = str_time[:str_time.find('.')] + str_time[str_time.find('.') + 1:str_time.find('.') + 4]
    time.sleep(1.25)  # 没得到一个时间戳,休眠1.25秒
    return str_time

def get_url():
    keyword = input('请输入你想下载的图片类型:')
    key_word = parse.urlencode({'q': keyword})
    num=int(input('请输入你想下载的图片数量:'))//100
    headers = {
        "Referer": "https://www.quanjing.com/search.aspx?%s" % (key_word),
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.25 Safari/537.36 Core/1.70.3741.400 QQBrowser/10.5.3863.400"}
    url='https://www.quanjing.com/Handler/SearchUrl.ashx?t=%s&callback=searchresult&%s&stype=1&pagesize=100&pagenum=%s&imageType=2&imageColor=&brand=&imageSType=&fr=1&sortFlag=1&imageUType=&btype=&authid=&_=%s'

    list_url=[]
    for i in range(1,num+1):
        str_1 = str(random.random())
        random_1 = str_1[str_1.find('.') + 1:str_1.find('.') + 5]
        time_1=get_time()
        url_1=url%(random_1,key_word,i,time_1)
        list_url.append(url_1)

    return list_url,headers,keyword




tuple_1=get_url()
list_url,headers,keyword=tuple_1[0],tuple_1[1],tuple_1[2]
queue_url = Queue(len(list_url)*100+5)
queue_img = Queue(len(list_url)*100+5)

try:  # 防止因为没有该图片类型而报错
    num=1
    for i in range(len(list_url)):
        request_1=request.Request(url=list_url[i],headers=headers)
        content=request.urlopen(request_1)

        str_1 = content.read().decode('utf-8')  # 得到的数据字符串类型
        str_1 = str_1[str_1.find('(') + 1:str_1.rfind(')')]
        dict_1 = json.loads(str_1)
        images_list = dict_1['imglist']

        for j in range(len(images_list)):
            print('【{}】-{}'.format(num, images_list[j]['caption']))
            queue_url.put(images_list[j]['imgurl'])
            queue_img.put(images_list[j]['caption'])
            num+=1

    def Downlad(queue_url: Queue, queue_img: Queue):
        path_1 = './' + keyword
        try:
            os.mkdir(path_1)
        except:
            pass
        finally:
            while True:
                if queue_url.empty():
                    break
                image_name = queue_img.get()
                request.urlretrieve(url=queue_url.get(), filename=path_1 + '/【{}】-{}.png'.format(random.random()*1000,image_name))  # 下载图片
                # 为了防止出现图片名相同的情况,对于图片命名添加一个随机数
                print('线程{}正在下载【{}】'.format(threading.current_thread().getName(), image_name))
                time.sleep(0.25)  # 每下载一张图片,休眠0.25秒

    threading_list = []
    print('开始下载!')
    time.sleep(5)
    for i in range(len(list_url)*5):  # 根据用户的输入创建相应多的线程
        threading_1 = threading.Thread(target=Downlad, args=(queue_url, queue_img,))
        threading_1.start()
        threading_list.append(threading_1)

    for i in threading_list:
        i.join()

    print('------------------------下载完毕!当前线程为', threading.current_thread().getName())


except Exception as e:
    print(e,'没有搜到该图片或者今日访问次数过多!')

你应该使用多线程下载才符合你的要求。
在 Python 中,你可以使用多线程来下载图片,来加快图片的下载速度。
首先,你需要安装 Python 的多线程库 threading。你可以使用 pip 命令来安装:

pip install threading

然后,你可以使用 Python 的 requests 库来下载图片。requests 库是一个简单易用的 HTTP 库,可以帮助你发送 HTTP 请求并获取响应。你可以使用 pip 命令来安装:

pip install requests

接下来,你可以使用以下代码来实现多线程下载图片:

import threading
import requests

def download_image(image_url, image_name):
    response = requests.get(image_url)
    with open(image_name, 'wb') as f:
        f.write(response.content)

# 创建多个线程
threads = []
for i in range(10):
    image_url = "http://example.com/image" + str(i) + ".jpg"
    image_name = "image" + str(i) + ".jpg"
    t = threading.Thread(target=download_image, args=(image_url, image_name))
    threads.append(t)

# 启动多个线程
for t in threads:
    t.start()

# 等待所有线程完成
for t in threads:
    t.join


采纳

1、可以使用 Queue.Queue() 来替换 Queue() ,因为 Queue() 已经不被推荐使用了。
2、在使用多进程下载时,为了更好地利用 CPU,可以考虑使用进程池(process pool)。进程池可以让预先启动若干个进程,然后将任务分发给这些进程。这样可以减少进程创建的时间开销。

from multiprocessing import Pool

def download_image(pic_url, pic_name, id, device_name):
    # 判断目录是否存在,不存在建立
    down_pic = f"{down_path}/{id}/{device_name}/{pic_name}"
    res = get(pic_url)
    with open(f'{down_pic}.jpg', 'wb') as f:
        f.write(res.content)
        f.close()

if __name__ == "__main__":
    with Pool(4) as p:
        for row_i in range(row_num):
            for line_i in range(line_num):
                pic_url = get(origin_pic_url, headers=req_header)
                if pic_url.status_code == 200:
                    pic_url = get(origin_pic_url, headers=req_header).json()['result']
                    pic_name = pic_url
                    p.apply_async(download_image, (pic_url, pic_name, id, device_name))
                elif pic_url.status_code == 400:
                    print('token过期,重新操作')
                    get_token()
                    pic_url = get(origin_pic_url, headers=req_header).json()['result']
                    p.apply_async(download_image, (pic_url, pic_name, id, device_name))
                else:
                    print('获取下载链失败')
        p.close()
        p.join()

仅供参考,望采纳,谢谢。

参考下该实例【利用Python多线程实现图片下载器】,链接:https://www.jb51.net/article/242296.htm

要将图片链接使用多进程下载可以使用 Python 的 multiprocessing 模块。这个模块提供了多种方法来创建和管理子进程,可以方便地实现多进程编程。

下面是一个简单的例子,展示了如何使用 multiprocessing 模块将图片链接下载到本地:

import multiprocessing
import requests

def download_image(url, filename):
    response = requests.get(url)
    if response.status_code == 200:
        with open(filename, 'wb') as f:
            f.write(response.content)

if __name__ == '__main__':
    # 定义要下载的图片链接列表
    urls = [
        'https://example.com/image1.jpg',
        'https://example.com/image2.jpg',
        'https://example.com/image3.jpg',
        'https://example.com/image4.jpg',
        'https://example.com/image5.jpg',
    ]
    # 创建进程池,并使用 map 方法将下载任务提交到进程池中执行
    with multiprocessing.Pool() as pool:
        pool.starmap(download_image, [(url, f'image_{i}.jpg') for i, url in enumerate(urls)])

使用 multiprocessing.Pool 类创建了一个进程池,并使用 map 方法将下载任务提交到进程池中执行。每个进程将调用 download_image 函数来下载一个图片链接。

在使用多进程下载图片时,还可以使用 multiprocessing.Queue 类来创建一个任务队列,并使用多个进程从队列中取出任务来执行。这样可以使程序更加灵活,可以动态地添加和移除任务。

import multiprocessing
import requests

def download_image(url, filename, queue):
    response = requests.get(url)
    if response.status_code == 200:
        with open(filename, 'wb') as f:
            f.write(response.content)
    queue.put((url, filename))

if __name__ == '__main__':
    # 创建任务队

题主应该是没有注意到Queue调用的安全
首先当get_url调用后立即进入save_pic若get_url种还未来得及put那么save_pic显然是不能运行的直接跳出了
而假设get_url中神速put了,save_pic没有跳出,那么第二个问题又来了,六个save_pic同时抢资源那么前后4个数据就会被六马分尸当然无法正常下载了
解决方法也很简单,添加进程锁,修改好的代码稍后,望采纳


 一、页面抓取

  #coding=utf-8

  import urllib

  def getHtml(url):

  page = urllib.urlopen(url)

  html = page.read()

  return html

  html = getHtml("https://tieba.baidu.com/p/5582243679")

  print html

  页面数据抓取过程定义了getHtml()函数,其作用是给getHtml()传递一个网址,最终进行整个页面的下载。

  二、页面数据筛选

  import re

  import urllib

  def getHtml(url):

  page = urllib.urlopen(url)

  html = page.read()

  return html

  def getImg(html):

  reg = r'src="(.+?\.jpg)" pic_ext'

  imgre = re.compile(reg)

  imglist = re.findall(imgre,html)

  return imglist

  html = getHtml("https://tieba.baidu.com/p/5582243679")

  print getImg(html)

  页面数据筛选中,定义了一个新的函数getImg(),该函数的功能是筛选出.jpg格式的图片地址。

  三、图片下载

  #coding=utf-8

  import urllib

  import re

  def getHtml(url):

  page = urllib.urlopen(url)

  html = page.read()

  return html

  def getImg(html):

  reg = r'src="(.+?\.jpg)" pic_ext'

  imgre = re.compile(reg)

  imglist = re.findall(imgre,html)

  x = 0

  for imgurl in imglist:

  urllib.urlretrieve(imgurl,'%s.jpg' % x)

  x+=1

  html = getHtml("https://tieba.baidu.com/p/5582243679")

  print getImg(html)

你可以使用 Python 的多进程库 concurrent.futures 来实现图片的多进程下载。这个库提供了一个简单的 ProcessPoolExecutor 类,可以使用它来提交多个图片下载任务。

这里是一个示例代码,你可以用它来参考:

import concurrent.futures
import requests

def download_image(url, path):
    response = requests.get(url)
    with open(path, 'wb') as f:
        f.write(response.content)

def main():
    urls = [
        'https://www.example.com/image1.jpg',
        'https://www.example.com/image2.jpg',
        'https://www.example.com/image3.jpg',
        # ...
    ]
    with concurrent.futures.ProcessPoolExecutor() as executor:
        for url, future in zip(urls, executor.map(download_image, urls)):
            print(f'{url} was downloaded')

if __name__ == '__main__':
    main()

这里,我们首先定义了一个 download_image(url, path) 函数,它可以用来下载指定的图片并将其保存到磁盘上。在主程序中,我们创建了一个 ProcessPoolExecutor 对象并使用它来提交多个下载任务。每个下载任务都由 download_image() 函数执行,并且参数是一个要下载的图片的 URL。

这个程序会在多个进程中并行下载图片,因此它可能比单进程下载图片快得多。

对于下载图片并保存到文件夹的部分,可以考虑在 download_image() 函数中,通过 os.path 模块获取文件夹路径,比如你要下载的图片在 '/images/' 下,然后利用 os.path.exists 和 os.makedirs 来判断文件夹是否存在,如果不存在就新建文件夹并下载图片。


import os
def download_image(url, filename):
    folder = '/images/'
    if not os.path.exists(folder):
        os.makedirs(folder)
    path = os.path.join(folder, filename)
    response = requests.get(url)
    with open(path, 'wb') as f:
        f.write(response.content)

同时因为下载图片是IO密集型,因此可以考虑使用 Python asyncio 或 gevent 进行异步下载,这样也能提升下载的速度。如果使用 asyncio 库,可以使用 asyncio.gather 来并发下载多张图片。可以将多个 download_image() 任务放入一个列表中,然后使用 asyncio.gather 并发执行这些任务。

import asyncio

async def download_image(url, filename):
    folder = '/images/'
    if not os.path.exists(folder):
        os.makedirs(folder)
    path = os.path.join(folder, filename)
    async with aiohttp.ClientSession() as session:
        async with session.get(url) as resp:
            with open(path, 'wb') as f:
                while True:
                    chunk = await resp.content.read(1024)
                    if not chunk:
                        break
                    f.write(chunk)

async def main():
    urls = [
        'https://www.example.com/image1.jpg',
        'https://www.example.com/image2.jpg',
        'https://www.example.com/image3.jpg',
        # ...
    ]
    tasks = []
    for url in urls:
        task = download_image(url, f'{url.split("/")[-1]}')
        tasks.append(task)
    await asyncio.gather(*tasks)
    
if __name__ == '__main__':
    asyncio.run(main())

或者使用gevent 库

import gevent
from gevent import monkey

monkey.patch_all()

def download_image(url, filename):
    folder = '/images/'
    if not os.path.exists(folder):
        os.makedirs(folder)
    path = os.path.join(folder, filename)
    response = requests.get(url)
    with open(path, 'wb') as f:
        f.write(response.content)

def main():
    urls = [
        'https://www.example.com/image1.jpg',
        'https://www.example.com/image2.jpg',
        'https://www.example.com/image3.jpg',
        # ...
    ]
    tasks = [gevent.spawn(download_image, url, f'{url.split("/")[-1]}') for url in urls]
    gevent.joinall(tasks)

if __name__ == '__main__':
    main()

使用上述两种库中的一种来实现异步下载,应该可以大大提升下载图片的速度。

需要注意的是,使用 asyncio 与 gevent 来实现异步编程,还需要满足如下条件:

你需要在运行程序之前安装 aiohttp (如果是asyncio) 以及 gevent
你需要在程序中导入对应的库
你需要在 main 或 main() 函数运行asyncio.run(main())或 gevent.joinall(tasks)
如果你不确定哪种方式更适合你的项目,可以尝试多种方法并对比它们的性能。如果你遇到任何问题,请随时给我留言。望采纳。

提供一个多进程的demo,可以参考一下:

from multiprocessing import Process
from threading import Thread


def func():
    for i in range(1000):
        print("子进程", i)


if __name__ == '__main__':
    p = Process(target=func)
    p.start()
    for i in range(10000):
        print("主进程", i)

也可以用进程池,因为看到你代码中有多达6个进程:

# 线程池: 一次性开辟一些线程. 我们用户直接给线程池子提交任务. 线程任务的调度交给线程池来完成
from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor


def fn(name):
    for i in range(1000):
        print(name, i)


if __name__ == '__main__':
    # 创建进程池
    with ProcessPoolExecutor(50) as t:
        for i in range(100):
            t.submit(fn, name=f"进程{i}")
    # 等待进程池中的任务全部执行完毕. 才继续执行
    print("123")

如果有补充,可以留言继续讨论。

不知道你解决了没?
如果没有解决的话,我们可以聊聊。

前面的回答,估计你有点懵,没有回答到你的问题核心点上。