python下載夏目友人帳

公號_python學習開發發表於2019-02-28

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 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直接執行的話是下面的效果

完全不一樣的效果,個人感覺還是在pycharm裡看著舒服,後面併發的時候看著也方便。
好了下面我們就開啟我們的檔案看看結果如何:


可以發現這個視訊被成功的下載下來,怎麼樣激不動激不動啊。

對於單檔案的下載我們就完成,但是對於夏目友人帳這個動漫來說不只有一集,如果我們下載一個系列的話,我們就得使用併發了,這裡我使用aiohttp把上面的程式碼改成併發的版本。

使用aiohttp進行併發下載

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,方便演示效果,好了下面我們就來看效果。


可以發現開始併發的下載了。
到這裡我們的教程就結束了,怎麼樣是不是迫不及待想試試呢。另外再說句題外話夏目友人帳劇場版,3月7日在中國上映了,喜歡治癒系動漫的小夥伴不要錯過哦。

更多python內容歡迎關注微信公眾號:python學習開發



相關文章