Python快速下载M3U8电影

宅在家里的这段时间,本想安心看个电影,无耐网卡得厉害,看一会卡顿一会,搞得火大。

直接拿起我的万能武器“Python”,写了个下载电影的小脚本,给大家分享一下!

有很多电影站的电影资源,是以m3u8的格式存在的,这种格式有的进行了加密,有了没有加密,程序对这两者进行了兼容处理。

源代码使用python2.7开发,主要用到了这几个包:requests,threadpool,Crypto

(1)导入包

import threadpool
import requests
import os
from Crypto.Cipher import AES

from requests.packages.urllib3.exceptions import InsecureRequestWarning
requests.packages.urllib3.disable_warnings(InsecureRequestWarning)

上面最后一行代码,主要是为了取消https的请求警告,警告虽然没啥影响,但看着闹心,所以用这行代码就给去掉了。

(2)读取m3u8

#读取m3u8文件
def readm3u8(m3u8):
    global key,base_url
    arr = []
    with open(m3u8,'r') as f:
        while True:
            line = f.readline()
            if not line :
                break;
            #如果是加密格式,提取密钥
            if '#EXT-X-KEY' in line:
                URI = line.split(',')[1]
                uri_key = (URI.split('=')[1].replace('"','')).strip()
                url0 = base_url + uri_key
                r = requests.get(url0,verify=False)
                key = r.content
            if line[0]!='#':
                arr.append(line[0:-1])
    return arr

将m3u8文件读到数组中备用。如果检测到m3u8中指定了密钥,还要把密钥下载下来,放到变量key中,备用

(3)下载单个ts文件。

#下载单个TS文件
def downts(ts_path):
    global key
    url = ts_path
    ts = os.path.basename(url)
    if os.path.exists('tmp/' +ts):
        return
    print ts
    r = requests.get(url,verify=False)
    cnt = r.content
    
    with open('tmp/' +ts,'wb+') as f:
        if len(key)>0:
            cryptor = AES.new(key, AES.MODE_CBC, key) 
            f.write(cryptor.decrypt(cnt))
        else:
            f.write(cnt)

m3u8本身是一个文本文件,把一个大的视频文件,拆分成多个ts文件,以索引的形式记录到这个文件中。这段代码把每个文件下载下来,保存到本地;如果是加密格式,使用key进行解密后保存到本地。

(4)合并ts成一个mp4文件

#合并ts文件
def merge(arr,filmpath):
    with open(filmpath,'wb') as f1:
        for ts in arr:
            try:
                f_ts = open('tmp/'+ts,'rb')
                f1.write(f_ts.read())
                f_ts.close()
            except Exception:
                continue

下载完成后,tmp文件夹下有一大堆的ts文件,虽然播放器能播放,但一个文件就几秒种,看着更闹心,赶紧合并吧。执行以上代码后,久违的MP4电影就有了!

(5)主函数

def down(url,filmname):
    global base_url
    base_url,m3u8 = os.path.split(url)
    base_url = base_url + '/'
    r = requests.get(url,verify=False)
    cnt = r.content
    with open(m3u8,'w+') as f:
        f.write(cnt)
    arr = readm3u8(m3u8)
    #线程池,使用20个线程
    pool = threadpool.ThreadPool(20)
    #构造 参数列表
    var2 = [base_url + ts for ts in arr]
    reqs = threadpool.makeRequests(downts, var2)
    [pool.putRequest(req) for req in reqs]    
    #等待执行完毕
    pool.wait()
    #执行完毕合并成一个Mp4文件
    merge(arr, 'tmp/' + filmname)

这是一个入口函数,主要是调度之前的几个功能函数,同时,在这个主函数中使用的线程池,极大地提高了下载速度,这也是快速下载的核心代码。你可以试试不用线程池的效果,那个速度会让你感到绝望的!

  • 完整代码:
#coding=utf8
'''
Created on 2020年1月9日
根据m3u8链接下载电影
@author: Administrator
'''
import threadpool
import requests
import os
from Crypto.Cipher import AES

from requests.packages.urllib3.exceptions import InsecureRequestWarning
requests.packages.urllib3.disable_warnings(InsecureRequestWarning)

global key
key=''
def down(url,filmname):
    global base_url
    base_url,m3u8 = os.path.split(url)
    base_url = base_url + '/'
    r = requests.get(url,verify=False)
    cnt = r.content
    with open(m3u8,'w+') as f:
        f.write(cnt)
    arr = readm3u8(m3u8)
    #线程池,使用20个线程
    pool = threadpool.ThreadPool(20)
    #构造 参数列表
    var2 = [base_url + ts for ts in arr]
    reqs = threadpool.makeRequests(downts, var2)
    [pool.putRequest(req) for req in reqs]    
    #等待执行完毕
    pool.wait()
    #执行完毕合并成一个Mp4文件
    merge(arr, 'tmp/' + filmname)

#合并ts文件
def merge(arr,filmpath):
    with open(filmpath,'wb') as f1:
        for ts in arr:
            try:
                f_ts = open('tmp/'+ts,'rb')
                f1.write(f_ts.read())
                f_ts.close()
            except Exception:
                continue
#下载单个TS文件
def downts(ts_path):
    global key
    url = ts_path
    ts = os.path.basename(url)
    if os.path.exists('tmp/' +ts):
        return
    print ts
    r = requests.get(url,verify=False)
    cnt = r.content
    
    with open('tmp/' +ts,'wb+') as f:
        if len(key)>0:
            cryptor = AES.new(key, AES.MODE_CBC, key) 
            f.write(cryptor.decrypt(cnt))
        else:
            f.write(cnt)
#读取m3u8文件
def readm3u8(m3u8):
    global key,base_url
    arr = []
    with open(m3u8,'r') as f:
        while True:
            line = f.readline()
            if not line :
                break;
            #如果是加密格式,提取密钥
            if '#EXT-X-KEY' in line:
                URI = line.split(',')[1]
                uri_key = (URI.split('=')[1].replace('"','')).strip()
                url0 = base_url + uri_key
                r = requests.get(url0,verify=False)
                key = r.content
            if line[0]!='#':
                arr.append(line[0:-1])
    return arr
if __name__ == '__main__':
    #无加密,西游记
    url = 'https://xxxxxxxxx.com/20200220/5zpOS6bB14nsUXgM/index.m3u8'
    #有加密,狄仁杰
    #url = 'https://xxxxxxxxxx.com/20190425/xiqWxfle/950kb/hls/index.m3u8'
    down(url,'xyj.mp4')

声明:代码中的示例链接来采集于网络,仅用于技术的学习交流,不直接发布到文章里,如有需要可以留言。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章