方法一:
使用 urllib 模塊提供的 urlretrieve() 函數。urlretrieve() 方法直接將遠程數據下載到本地。
urlretrieve(url, [filename=None, [reporthook=None, [data=None]]])
- 請求url不可為空: urlretrieve(url)
- 下載后的文件保存路徑可為空: urlretrieve(url, [filename=None])
- 傳輸到服務器的數據data可為空: urlretrieve(url, [filename=None, [reporthook=None, [data=None]]])
說明:
-
參數 finename 指定了保存本地路徑(如果參數未指定,urllib會生成一個臨時文件保存數據。)
-
參數 reporthook 是一個回調函數,當連接上服務器、以及相應的數據塊傳輸完畢時會觸發該回調,我們可以利用這個回調函數來顯示當前的下載進度。
-
參數 data 指 post 到服務器的數據,該方法返回一個包含兩個元素的(filename, headers)元組,filename 表示保存到本地的路徑,header 表示服務器的響應頭。
實例:
from urllib import request load_url = 'http://docs.python-requests.org/zh_CN/latest/_static/requests-sidebar.png' def Schedule(a,b,c): ''' a:已經下載的數據塊 b:數據塊的大小 c:遠程文件的大小 ''' per = 100.0 * a * b / c if per > 100 : per = 100 print('%.2f%%' % per) request.urlretrieve(url=load_url,filename='d:\\python.png',reporthook=Schedule)
方法二:
使用requests模塊
使用該方法下載大文件可以防止占用過多的內存,因為每次只下載小部分數據。
import requests load_url = 'http://docs.python-requests.org/zh_CN/latest/_static/requests-sidebar.png' r = requests.get(url=load_url,stream=True) with open("D:\\python.png","wb") as f: for chunk in r.iter_content(chunk_size=1024): f.write(chunk)
-
stream參數默認為False,它會立即開始下載文件並放到內存中,如果文件過大,有可能導致內存不足。
-
stream參數設置成True時,它不會立即開始下載,當你使用iter_content或iter_lines遍歷內容或訪問內容屬性時才開始下載。
-
iter_content:一塊一塊的遍歷要下載的內容
-
iter_lines:一行一行的遍歷要下載的內容
關於requests請求時的stream參數詳解:
默認情況下,當你進行網絡請求后,響應體會立即被下載。(當響應體數據量過大時,立即下載會占用系統大量內存)【當響應體數據量並不是很大時,可以不用考慮內存,即可以直接下載】
你可以通過 stream
參數覆蓋這個行為,推遲下載響應體直到訪問 Response.content
屬性:(請求后不立即下載響應體,而是先下載響應頭)【當訪問r.content字節碼的時候才會下載響應體】
tarball_url = 'https://github.com/kennethreitz/requests/tarball/master' r = requests.get(tarball_url, stream=True)
此時僅有響應頭被下載下來了,連接保持打開狀態,因此允許我們根據條件獲取內容:
if int(r.headers['content-length']) < TOO_LONG: content = r.content # 此時開始下載響應體 ...
解釋:
歸功於 urllib3,同一會話內的持久連接是完全自動處理的!同一會話內你發出的任何請求都會自動復用恰當的連接。
只有所有的響應體數據被讀取完畢連接才會被釋放為連接池;以下是釋放連接池的兩種情況:
- 所以當requests請求時設置的參數stream為False時【requests請求時,stream默認為False】,所有的響應體數據被讀取完之后立即釋放連接池。
- 或者當requests請求時設置的參數stream為True時,當訪問r.content屬性且讀取下載響應體數據之后也會立即釋放連接池。
當然:如果你在請求中把 stream 設為 True 的時候,且不訪問任何方法,那么Requests 無法將連接釋放回連接池,除非你消耗了所有的數據,或者調用了 Response.close 。 否則這樣會帶來連接效率低下的問題。
關於Response.iter_content的詳解
Response.iter_content是可迭代對象。
一塊一塊的遍歷要下載的內容【可以理解為一塊一塊的下載服務器返回的數據量過大的響應體數據】,然后通過對文件流的操作按塊逐一寫入指定的路徑文件中。
在請求中設置stream = True時,可以避免將內容一次性讀入內存以獲得較大的響應。塊大小是它應該循環每次讀入內存的字節數【即chunk_size=1024]。