通常狀況下咱們使用爬蟲更多的應該是爬數據或者圖片吧,今天在這裏和你們分享一下關於使用爬蟲技術來進行視頻下載的方法,不只能夠方便的下載一些體積小的視頻,針對大容量的視頻下載一樣試用。
python
這裏咱們使用的是python的requests模塊做爲例子,須要獲取文本的時候咱們會使用response.text獲取文本信息,使用response.content獲取字節流,好比下載圖片保存到一個文件,而對於大個的文件咱們就要採起分塊讀取的方式了,bash
第一步,咱們須要設置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、迅雷等)實現多線程下載的核心所在。
session
range是請求資源的部份內容(不包括響應頭的大小),單位是byte,即字節,從0開始.
若是服務器可以正常響應的話,服務器會返回 206 Partial Content 的狀態碼及說明.
若是不能處理這種Range的話,就會返回整個資源以及響應狀態碼爲 200 OK .(這個要注意,要分段下載時,要先判斷這個)多線程
Range: bytes=start-end複製代碼
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]異步
下面咱們經過具體的代碼去進一步瞭解一些細節。async
import requestsimport 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://v11-tt.ixigua.com/7da2b219bc734de0f0d04706a9629b61/5c77ed4b/video/m/220d4f4e99b7bfd49efb110892d892bea9011612eb3100006b7bebf69d81/?rc=am12NDw4dGlqajMzNzYzM0ApQHRAbzU6Ojw8MzQzMzU4NTUzNDVvQGgzdSlAZjN1KWRzcmd5a3VyZ3lybHh3Zjc2QHFubHBfZDJrbV8tLTYxL3NzLW8jbyMxLTEtLzEtLjMvLTUvNi06I28jOmEtcSM6YHZpXGJmK2BeYmYrXnFsOiMzLl4%3D"download_from_url(url, "夏目友人賬第一集.mp4")複製代碼
在命令行中運行代碼以後看到效果以下
徹底不同的效果,我的感受仍是在pycharm裏看着舒服,後面併發的時候看着也方便。
好了下面咱們就打開咱們的文件看看結果如何:
import aiohttpimport asynciofrom tqdm import tqdmasync def fetch(session, url, dst, pbar=None, headers=None): if headers: async with session.get(url, headers=headers) as req: with(open(dst, 'ab')) as f: while True: chunk = await req.content.read(1024) if not chunk: break f.write(chunk) pbar.update(1024) pbar.close() else: async with session.get(url) as req: return reqasync def async_download_from_url(url, dst): '''異步''' async with aiohttp.connector.TCPConnector(limit=300, force_close=True, enable_cleanup_closed=True) as tc: async with aiohttp.ClientSession(connector=tc) as session: req = await fetch(session, url, dst) file_size = int(req.headers['content-length']) print(f"獲取視頻總長度:{file_size}") if os.path.exists(dst): first_byte = os.path.getsize(dst) else: first_byte = 0 if first_byte >= file_size: 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) await fetch(session, url, dst, pbar=pbar, headers=header)複製代碼
上面的代碼功能和咱們的同步代碼同樣的,不一樣的是這裏是異步的。
咱們首先要拿到MP4的連接,而後進行下面的代碼便可
task = [asyncio.ensure_future(async_download_from_url(url, f"{i}.mp4")) for i in range(1, 12)] loop = asyncio.get_event_loop() loop.run_until_complete(asyncio.wait(task)) loop.close()複製代碼
這裏我同時下載了11次上面的那個視頻,命令爲1-11,方便演示效果,好了下面咱們就來看效果。
更多python內容歡迎關注微信公衆號:python學習開發