注:源內容來自公眾號【python學習開發】
一般情況下我們使用爬蟲更多的是對數據或者圖片進行爬取,今天在這里和大家分享一下關于使用爬蟲技術來進行視頻下載的方法,不僅可以方便的下載一些體積小的視頻,針對大容量的視頻下載同樣試用。
接下來我們來介紹此次爬取視頻過程中用到的模塊和方法。
requests模塊的iter_content方法
這里我們使用的是python的requests模塊作為例子,我們使用response.text獲取文本信息,使用response.content獲取字節流,比如下載圖片保存到一個文件,而對于大個的文件我們需要采取分塊讀取的方式。
requests.get方法的stream
第一步,我們需要設置requests.get的stream參數為True。
默認情況下是stream的值為false,它會立即開始下載文件并存放到內存當中,倘若文件過大就會導致內存不足的情況.
當把get函數的stream參數設置成True時,它不會立即開始下載,當你使用iter_content或iter_lines遍歷內容或訪問內容屬性時才開始下載。需要注意一點:文件沒有下載之前,它也需要保持連接。
iter_content:一塊一塊的遍歷要下載的內容
iter_lines:一行一行的遍歷要下載的內容
使用上面兩個函數下載大文件可以防止占用過多的內存,因為每次只下載小部分數據。
示例代碼:
r?=?requests.get(url_file,?stream=True)
f?=?open("file_path",?"wb")
for?chunk?in?r.iter_content(chunk_size=512):
if?chunk:
f.write(chunk)
上面的代碼表示請求了url_file,這個url_file是一個大文件,所以開啟了stream模式,然后通過迭代r對象的iter_content方法,同時指定chunk_size=512(即每次讀取512個字節)來進行讀取。但是如果僅僅是迭代是不行,如果下載中途出現問題我們之前的努力就白費了,所以我們需要做到一個斷點續傳的功能。
斷點續傳
所謂斷點續傳,也就是要從文件已經下載的地方開始繼續下載。在以前版本的 HTTP 協議是不支持斷點的,HTTP/1.1 開始就支持了。一般斷點下載時會用到 header請求頭的Range字段,這也是現在許多多線程下載工具(如 FlashGet、迅雷等)實現多線程下載的核心所在。
如何在代碼中實現用呢?
HTTP請求頭Range
range是請求資源的部分內容(不包括響應頭的大小),單位是byte,即字節,從0開始.
如果服務器能夠正常響應的話,服務器會返回 206 Partial Content 的狀態碼及說明.
如果不能處理這種Range的話,就會返回整個資源以及響應狀態碼為 200 OK .(這個要注意,要分段下載時,要先判斷這個)
Range請求頭格式
Range:?bytes=start-end
Range頭域
Range頭域可以請求實體的一個或者多個子范圍。例如,
表示頭500個字節:bytes=0-499
表示第二個500字節:bytes=500-999
表示最后500個字節:bytes=-500
表示500字節以后的范圍:bytes=500-
第一個和最后一個字節:bytes=0-0,-1
同時指定幾個范圍:bytes=500-600,601-999
例如
Range:?bytes=10-?:第10個字節及最后個字節的數據
Range:?bytes=40-100?:第40個字節到第100個字節之間的數據.
注意,這個表示[start,end],即是包含請求頭的start及end字節的,所以,下一個請求,應該是上一個請求的[end+1, nextEnd]
下載實例
下面我們通過具體的代碼去進一步了解一些細節。
import?requests
import?tqdm
def?download_from_url(url,?dst):
response?=?requests.get(url,?stream=True)?#(1)
file_size?=?int(response.headers['content-length'])?#(2)
if?os.path.exists(dst):
first_byte?=?os.path.getsize(dst)?#(3)
else:
first_byte?=?0
if?first_byte?>=?file_size:?#(4)
return?file_size
header?=?{"Range":?f"bytes={first_byte}-{file_size}"}
pbar?=?tqdm(
total=file_size,?initial=first_byte,
unit='B',?unit_scale=True,?desc=dst)
req?=?requests.get(url,?headers=header,?stream=True)?#(5)
with(open(dst,?'ab'))?as?f:
for?chunk?in?req.iter_content(chunk_size=1024):?#(6)
if?chunk:
f.write(chunk)
pbar.update(1024)
pbar.close()
return?file_size
下面我們開始解讀標有注釋的代碼:
tqdm是一個可以顯示進度條的包,具體的用法可以參考官網文檔:https://pypi.org/project/tqdm/
(1)設置stream=True參數讀取大文件。
(2)通過header的content-length屬性可以獲取文件的總容量。
(3)獲取本地已經下載的部分文件的容量,方便繼續下載,當然需要判斷文件是否存在,如果不存在就從頭開始下載。
(4)本地已下載文件的總容量和網絡文件的實際容量進行比較,如果大于或者等于則表示已經下載完成,否則繼續。
(5)開始請求視頻文件了
(6)循環讀取每次讀取一個1024個字節,當然你也可以設置512個字節
效果演示
首先調用上面的方法并傳入參數。視頻我找的是南京大學的一個新聞視頻。
url="http://news.nju.edu.cn/uploadfiles/media/49debf11202e95e696120c74020ff658.mp4"
download_from_url(url, "NanDa.mp4")
效果如下圖所示:爬取NanDa新聞視頻
我們打開文件來看下視頻如何:爬取的視頻
可以發現這個視頻被成功的下載下來。
對于單文件的下載我們就完成,但是如果爬取的是多集的連續劇,我們下載一個系列的話,我們就得使用并發了,將在下一章講述。
Github地址:https://github.com/muzico425/mp4download