尋找最快的圖片下載方式

首先因為工作原因,批量下載很常見,之前一直也沒太關注這一塊,但是閑下來對這個塊的確有點感興趣,
于是在實驗中試著進行一些操作,本文主要探討,異步,單線程,多線程,以及多進程的情況下,圖片
下載速度的問題。

代碼部分使用的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

好了,今天的內容就到這里啦,如果你有更好的測驗想法,歡迎在下方評論,留言。
如果有錯誤的地方歡迎指正,謝謝!

以上完畢!

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。