1. 程式人生 > 實用技巧 >辣條君寫爬蟲5【戀愛綜藝《心動的訊號》視訊下載】

辣條君寫爬蟲5【戀愛綜藝《心動的訊號》視訊下載】

最近辣條君看到一部綜藝《心動的訊號》,打算看一看,學一學年輕人該怎樣談戀愛。可是,某訊視訊要會員,從小艱苦樸素的辣條君當然選擇白嫖了。於是乎,發現有線上資源哎~

Tip:本文僅供學習與交流,切勿用於非法用途!!!請大家支援正版!!!

分析與思路

今天爬取的頁面是http://www.ikanmv.com/k/39050.html。底下一共有10集。

我們隨便開啟一集,點選network,發現視訊是m3u8格式的。有兩個m3u8格式檔案,第一個是一些限制條件,真正的m3u8列表。後面就是列表中逐條下載的ts檔案。

我們的目標是,把一集的所有ts檔案下載下來,通過ffmpeg軟體進行拼接、轉化,最終生成一個.mp4檔案。ffmpeg

從官網下載較慢,如有需要請戳下面連線(windows10版本)。

連結: https://pan.baidu.com/s/1_1htbaRQP_6y_G2xHOmWBw
提取碼: wivw

其實直接用ffmpeg也可以根據m3u8列表連線直接下載,但是速度較慢,咱們程式碼中使用多程序,加速下載。

不過還有個問題,經過測試,ffmpeg合併ts檔案,一次性最多合併4、5百個(每集ts檔案大概在1500~2000個之間),再多就會報錯,所以我們先以300個ts檔案為1組,將他們合併一下,得到幾個大個的ts檔案,再將這幾個大個的ts檔案合併成一集一個整個的ts檔案,再轉化mp4。

所以我們的程式碼流程為:下載某集所有ts檔案->合併成幾個大個的ts檔案->合併成一個ts檔案->轉化成mp4->刪除沒用的ts檔案

程式碼實現

首先建立一個資料夾D:\ts用於存放ts檔案。

根據上節的程式碼流程,我們可以寫出主函式,幾個方法逐步實現:

if __name__ == '__main__':
    urls = [
       'https://zy.kubozy-youku-163-aiqi.com/20190710/13975_9273f090/1000k/hls/index.m3u8',
       'https://zy.kubozy-youku-163-aiqi.com/20190717/14563_0eca69d7/1000k/hls/index.m3u8',
       'https://zy.kubozy-youku-163-aiqi.com/20190724/15218_7487f9e5/1000k/hls/index.m3u8'
    ]
    # 開始的集數
    index = 4
    for url in urls:
        # 獲取某集需要下載的ts列表
        get_list = get_m3u8(url)
        # 下載ts檔案到本地
        download('D:\\ts', get_list, url[0:-10])
        # 合併
        merge_ts('D:\\ts', str(index)+'.mp4')
        # 刪除沒用的 ts
        delete_ts('D:\\ts')
        # 下一集
        index = index + 1

我們模擬下載4~6集(由於沒找到規律,需要手動獲取幾集的第二個m3u8請求連線)

隨便點選兩個ts連線就會發現,某一集的ts檔案請求前面都是一樣的,最後拼接上xxxxx00x.ts,然後get請求就可以得到某個ts視訊資源了。首先獲取某集所有ts檔案字尾,就是xxxxx00x.ts部分,實現一下get_m3u8方法

# 獲取某集需要下載的ts列表
def get_m3u8(url):
    result = requests.get(url)
    lines = result.text.split('\n')
    all_ts = []
    for line in lines:
        if line.endswith('.ts'):
            all_ts.append(line)
    return all_ts

然後下載到本地

# 下載一個ts檔案
def download_ts(ts, root, url):
    res = requests.get(url+ts)
    with open(root+"\\" + ts, 'ab') as f:
        f.write(res.content)
        f.flush()
        print(ts+'下載完畢!')

# 下載ts檔案到本地
def download(root, get_list, url):
    # 已經下載過的檔案
    had_list = []
    # 還有沒下載完的就繼續
    while len(list(set(get_list).difference(set(had_list)))) > 0:
        had_list = os.listdir(root)
        # 還需要下載的
        needs = list(set(get_list).difference(set(had_list)))
        print('還需要下載個數為:'+str(len(needs)))
        pool = Pool(10)
        group = ([ts for ts in needs])
        pool.map(partial(download_ts, root=root, url=url), group)
        pool.close()
        pool.join()

download方法,即主函式中使用的方法。第一個引數root是ts視訊下載的地址,第二個引數get_list是某集所有ts檔案字尾(xxxxx00x.ts部分),第三個引數url是某個ts請求字首。首先這裡用到了多程序,往期部落格介紹過,這裡照抄就好。

值得一提的時,這邊有個while迴圈。因為多程序下載某集所有ts檔案過程中,我發現可能會造成某些ts檔案的丟失未下載,所以每次下載完成,我們獲取已下載完成的檔名稱(檔名稱就是ts字尾),和某集所有的ts檔案字尾,做差集。繼續下載這些丟失的,知道差集為空,就證明全部下載完畢。

接下來需要合併。

# 合併ts,由於一次無法合併那麼多,合併兩次
def merge_ts(root, name):
    os.chdir(root)
    all_ts = os.listdir(root)
    # 計數器
    counter = 0
    big_list = []
    per_list = []
    # 將多個ts檔名,300為一組拼接在一起
    for ts in all_ts:
        if counter > 300:
            counter = 0
            big_list.append('+'.join(per_list))
            per_list = []
        else:
            counter = counter+1
            per_list.append(ts)

    batch = 0
    batch_ts = []
    for big in big_list:
        batch = batch + 1
        batch_ts.append(str(batch)+'.ts')
        os.system('copy /b ' + big + f' batch{batch}.ts')
    shell_str = '+'.join(batch_ts)
    # 第二次拼接
    os.system('copy /b '+shell_str+' '+name)

這裡需要兩次合併,第二次合併和轉mp4格式是一個指令碼操作。這裡使用os.system呼叫系統方法,由於我們的ffmpeg已經放入到環境變數中,所以這裡就是直接操作ffmpeg軟體了。

生成某集的mp4檔案之後,我們需要刪除根目錄下所有的.ts檔案,一來節約硬碟空間,二來為下一集下載.ts檔案清除干擾。

def delete_ts(root):
    os.chdir(root)
    all_file = os.listdir(root)
    for file in all_file:
        if file.endswith('.ts'):
            os.remove(root+'\\'+file)

以上我們就可以下載到喜歡看的視訊了。喜歡的小夥伴快去試試吧~