首先因為工作原因,批量下載很常見,之前一直也沒太關注這一塊,但是閑下來對這個塊的確有點感興趣,
于是在實驗中試著進行一些操作,本文主要探討,異步,單線程,多線程,以及多進程的情況下,圖片
下載速度的問題。
代碼部分使用的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
好了,今天的內容就到這里啦,如果你有更好的測驗想法,歡迎在下方評論,留言。
如果有錯誤的地方歡迎指正,謝謝!
以上完畢!