寻找最快的图片下载方式

首先因为工作原因,批量下载很常见,之前一直也没太关注这一块,但是闲下来对这个块的确有点感兴趣,
于是在实验中试着进行一些操作,本文主要探讨,异步,单线程,多线程,以及多进程的情况下,图片
下载速度的问题。

代码部分使用的python,如果有什么不对的地方欢迎指出,一同进步。

首先准备了100个图片链接的列表,这里因为数据源的问题就不透露了。

单线程示例:

最普通的方式,没什么好说的。

def main():
    targetDir = os.path.join(os.path.dirname(os.path.abspath(__file__)), "test\\renran")
    if not os.path.isdir(targetDir):  # 不存在创建路径
        os.makedirs(targetDir)
    num = 0
    for i in _list:
        downloadImage(i, targetDir, num)
        num = num + 1

def downloadImage(img_url,fileDir,fileName):
    r = requests.get(img_url, headers)
    t = os.path.join(fileDir, str(fileName) + '.jpg')  # 指定目录
    fw = open(t, 'wb')  # 指定绝对路径
    fw.write(r.content)  # 保存图片到本地指定目录

if __name__ == '__main__':
    start = time.clock()
    main()
    end = time.clock()
    print("程序运行时间:" + str(end - start))

多线程(第一版)

最普通的多线程,一个请求一个线程。

def main():
    targetDir = os.path.join(os.path.dirname(os.path.abspath(__file__)), "test\\renran")
    if not os.path.isdir(targetDir):  # 不存在创建路径
        os.makedirs(targetDir)
    num = 0
    for i in _list:
        t =threading.Thread(target=downloadImage, args=(i, targetDir, num))
        t.start()
        t.join()
        num = num + 1

def downloadImage(img_url,fileDir,fileName):
    r = requests.get(img_url, headers)
    t = os.path.join(fileDir, str(fileName) + '.jpg')  # 指定目录
    fw = open(t, 'wb')  # 指定绝对路径
    fw.write(r.content)  # 保存图片到本地指定目录

if __name__ == '__main__':
    start = time.clock()
    main()
    end = time.clock()
    print("程序运行时间:" + str(end - start))

多线程(第二版)

使用了线程池的方式.好像明白了为什么很少人这么玩。

def main():
    targetDir = os.path.join(os.path.dirname(os.path.abspath(__file__)), "test\\renran")
    if not os.path.isdir(targetDir):  # 不存在创建路径
        os.makedirs(targetDir)
    num = 0
    pool = threadpool.ThreadPool(10)
    for i in _list:
        dict_vars_1 = {'img_url': i, 'fileDir': targetDir, 'fileName': num}
        func_var = [(None, dict_vars_1)]
        requests = threadpool.makeRequests(downloadImage, func_var)
        [pool.putRequest(req) for req in requests]
        pool.wait()
        num = num + 1

def downloadImage(img_url,fileDir,fileName):
    r = requests.get(img_url, headers)
    t = os.path.join(fileDir, str(fileName) + '.jpg')  # 指定目录
    fw = open(t, 'wb')  # 指定绝对路径
    fw.write(r.content)  # 保存图片到本地指定目录

if __name__ == '__main__':
    start = time.clock()
    main()
    end = time.clock()
    print("程序运行时间:" + str(end - start))

多进程(第一版)

一次下载起一个进程,起进程开销大实则体验不好。

def main():
    targetDir = os.path.join(os.path.dirname(os.path.abspath(__file__)), "test\\renran")
    if not os.path.isdir(targetDir):  # 不存在创建路径
        os.makedirs(targetDir)
    num = 0
    for i in _list:
        p = Process(target=downloadImage, args=(i,targetDir,num))
        p.start()
        p.join()
        num = num + 1

def downloadImage(img_url,fileDir,fileName):
    r = requests.get(img_url, headers)
    t = os.path.join(fileDir, str(fileName) + '.jpg')  # 指定目录
    fw = open(t, 'wb')  # 指定绝对路径
    fw.write(r.content)  # 保存图片到本地指定目录

if __name__ == '__main__':
    start = time.clock()
    main()
    end = time.clock()
    print("程序运行时间:" + str(end - start))

多进程第二版

可以说,非常快了,只起了四个进程,减少了很多开销,速度的确得到了飞升。

def main():
    targetDir = os.path.join(os.path.dirname(os.path.abspath(__file__)), "test\\renran")
    if not os.path.isdir(targetDir):  # 不存在创建路径
        os.makedirs(targetDir)
    num = 0
    p = Pool(4)
    for i in _list:
        p.apply_async(downloadImage, args=(i,targetDir,num,))
        num = num + 1
    p.close()
    p.join()

def downloadImage(img_url,fileDir,fileName):
    r = requests.get(img_url, headers)
    t = os.path.join(fileDir, str(fileName) + '.jpg')  # 指定目录
    fw = open(t, 'wb')  # 指定绝对路径
    fw.write(r.content)  # 保存图片到本地指定目录

if __name__ == '__main__':
    start = time.clock()
    main()
    end = time.clock()
    print("程序运行时间:" + str(end - start))

异步

基础的异步,没啥好介绍的

def main():
    targetDir = os.path.join(os.path.dirname(os.path.abspath(__file__)), "test\\renran")
    if not os.path.isdir(targetDir):  # 不存在创建路径
        os.makedirs(targetDir)
    num = 0
    loop = asyncio.get_event_loop()
    for i in _list:
        loop.run_until_complete(downloadImage(i, targetDir, num))
        num = num + 1

async def downloadImage(img_url,fileDir,fileName):
    r = requests.get(img_url, headers)
    t = os.path.join(fileDir, str(fileName) + '.jpg')  # 指定目录downloadImage
    fw = open(t, 'wb')  # 指定绝对路径
    fw.write(r.content)  # 保存图片到本地指定目录

if __name__ == '__main__':
    start = time.clock()
    main()
    end = time.clock()
    print("程序运行时间:" + str(end - start))

异步(第二版)

有大佬指出上面的写法,不标准,于是加了一个版本。

async def main():
    targetDir = os.path.join(os.path.dirname(os.path.abspath(__file__)), "test\\renran")
    if not os.path.isdir(targetDir):  # 不存在创建路径
        os.makedirs(targetDir)
    num = 0
    for i in _list:
        await downloadImage(i, targetDir, num)
        num = num + 1

async def downloadImage(img_url,fileDir,fileName):
    r = requests.get(img_url, headers)
    t = os.path.join(fileDir, str(fileName) + '.jpg')  # 指定目录downloadImage
    fw = open(t, 'wb')  # 指定绝对路径
    fw.write(r.content)  # 保存图片到本地指定目录

if __name__ == '__main__':
    start = time.clock()
    loop = asyncio.get_event_loop()
    tasks = [main()]
    loop.run_until_complete(asyncio.wait(tasks))
    loop.close()
    end = time.clock()
    print("程序运行时间:" + str(end - start))

测验总结

版本 数量 时间/s 点评
单线程 1 58.6002 中规中矩的玩法,可能是大部分人程序的速度。
多线程 100 14.4361 看的出来的优化速度,经常能看到的同事写法。
多线程(线程池) 4 12.7496 这个玩法写的人很少,但是速度的确比开全量线程要快一丢丢(然并卵)。
_ 10 18.5659
_ 50 21.3403
多进程 100 72.7953 由此可见,进程开销是真的很大,某种程度还不如单线程呢(强行优化真的会打脸)。
多进程(进程池) 4 5.6596 这是什么神仙速度,如果你体验了你会爱上它.在控制一定的数量下,它会表现更加优异!
_ 10 2.9305
_ 50 7.6447
异步 1 23.1643 老实说,异步了也还是单线程的本质,不过这个效率已经比较满意了。
异步(第二版) 1 13.0175 这种异步的方式,会更快了一些。

福利时间:

image.png

好了,今天的内容就到这里啦,如果你有更好的测验想法,欢迎在下方评论,留言。
如果有错误的地方欢迎指正,谢谢!

以上完毕!

你可能感兴趣的:(寻找最快的图片下载方式)