https://blog.csdn.net/liujiayu2/article/details/86083400
# -*- coding:utf-8 -*-import osimport sysimport requestsimport datetimefrom Crypto.Cipher import AESfrom binascii import b2a_hex, a2b_hex## pip3 install -i https://pypi.tuna.tsinghua.edu.cn/simple pycryptodomedef download(url):download_path = os.getcwd() + "\download"if not os.path.exists(download_path):os.mkdir(download_path)#新建日期文件夹download_path = os.path.join(download_path, datetime.datetime.now().strftime('%Y%m%d_%H%M%S'))#print download_pathos.mkdir(download_path)all_content = requests.get(url).text # 获取第一层M3U8文件内容if "#EXTM3U" not in all_content:raise BaseException("非M3U8的链接")if "EXT-X-STREAM-INF" in all_content: # 第一层file_line = all_content.split("\n")for line in file_line:if '.m3u8' in line:url = url.rsplit("/", 1)[0] + "/" + line # 拼出第二层m3u8的URLall_content = requests.get(url).textfile_line = all_content.split("\n")unknow = Truekey = ""for index, line in enumerate(file_line): # 第二层if "#EXT-X-KEY" in line: # 找解密Keymethod_pos = line.find("METHOD")comma_pos = line.find(",")method = line[method_pos:comma_pos].split('=')[1]print ("Decode Method: ", method)uri_pos = line.find("URI")quotation_mark_pos = line.rfind('"')key_path = line[uri_pos:quotation_mark_pos].split('"')[1]key_url = url.rsplit("/", 1)[0] + "/" + key_path # 拼出key解密密钥URLres = requests.get(key_url)key = res.contentprint ("key_url: " , key_url)print ("key: " , key)if "EXTINF" in line: # 找ts地址并下载unknow = Falsepd_url = url.rsplit("/", 1)[0] + "/" + file_line[index + 1] # 拼出ts片段的URL#print pd_urlres = requests.get(pd_url)c_fule_name = file_line[index + 1].rsplit("/", 1)[-1]if len(key): # AES 解密cryptor = AES.new(key, AES.MODE_CBC, key)with open(os.path.join(download_path, c_fule_name + ".mp4"), 'ab') as f:f.write(cryptor.decrypt(res.content))else:with open(os.path.join(download_path, c_fule_name), 'ab') as f:f.write(res.content)f.flush()if unknow:raise BaseException("未找到对应的下载链接")else:print ("下载完成")merge_file(download_path)def merge_file(path):os.chdir(path)cmd = "copy /b * new.tmp"os.system(cmd)os.system('del /Q *.ts')os.system('del /Q *.mp4')os.rename("new.tmp", "new.mp4")if __name__ == '__main__':_url = "https://streamobs.yunxuetang.cn/knowledgefiles/13601042801/videos/202006/96be6f1183ca4434804ac154ce0a8b27_0302042302_720p_enc.m3u8"download(_url)
m3u8
'''站点地址https://www.sanjieke.cn/free.html课程列表jsonhttps://class.sanjieke.cn/course/class_content_with_checkpoint?cid=3003359加载第一个视频页面https://class.sanjieke.cn/course/section_content?cid=3003359§ion_id=1673672https://class.sanjieke.cn/course/class_content_with_checkpoint?cid=17301420加载第一个视频https://service.sanjieke.cn/video/master/1673668.m3u8?class_id=3003359从加载页中取出m3u8文件地址https://service.sanjieke.cn/video/media/1673668/608p.m3u8?class_id=3003359获取key地址https://service.sanjieke.cn/video/key/1673668?class_id=3003359'''print('''三节课m3u8下载器v21、需要先报名这个课程2、输入课程的id就能下载3、时间久了可能需要更新一下cookies''')import osimport reimport requestsfrom Crypto.Cipher import AESheadrer = {'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.96 Safari/537.36','Cookie':'acw_tc=276aedd715843843846276228e7237cfd3f2985ec8423b6b72e1f786d0f2f0; sajssdk_2015_cross_new_user=1; PHPSESSID=8c8i2ohp56ah69jr6i8jp07pa5; sjk_jwt=eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJhdWQiOiIyIiwianRpIjoiNDE4ZDViOWRjOTQ3YjFmYzNjMGM5MzhmODgxMzU0NmMzZDgwMGQ5NWVkM2JlMjlhNjQ4MmFkODAyMTZlNGU0ZTdiMDY1OWMwNjA3ZDJlMDkiLCJpYXQiOjE1ODQzODQ0MTcsIm5iZiI6MTU4NDM4NDQxNywiZXhwIjoxNTg3MDYyODE3LCJzdWIiOiIyMDc5NTY2NiIsInNjb3BlcyI6W119.bykO1OWL9_mqZKhI6qEpwwrbTEpEk25Xc-bwFXG47lktpRfKLBPw22WOUqYRfl0qi-WkkItBgcEe9TVU8jfFcg; sensorsdata2015jssdkcross=%7B%22distinct_id%22%3A%2220795666%22%2C%22%24device_id%22%3A%22170e4ab2004b55-0f846e6d4f2e96-4313f6a-3686400-170e4ab2005aa9%22%2C%22props%22%3A%7B%22%24latest_referrer%22%3A%22https%3A%2F%2Fwww.baidu.com%2Fs%22%2C%22%24latest_traffic_source_type%22%3A%22%E8%87%AA%E7%84%B6%E6%90%9C%E7%B4%A2%E6%B5%81%E9%87%8F%22%2C%22%24latest_search_keyword%22%3A%22%E4%B8%89%E8%8A%82%E8%AF%BE%22%7D%2C%22first_id%22%3A%22170e4ab2004b55-0f846e6d4f2e96-4313f6a-3686400-170e4ab2005aa9%22%7D; Hm_lvt_85148c078fe4635816b80e5a36990313=1584384385,1584384418; Hm_lpvt_85148c078fe4635816b80e5a36990313=1584384436'}class_id =input('请输入课程编号:')# 获取这个课程所有课时的IDclass_url = 'https://class.sanjieke.cn/course/class_content_with_checkpoint?cid='+class_idclass_contect = requests.get(class_url,headers=headrer).text.encode('utf-8').decode('unicode-escape')course_title = re.compile('"course_title":"(.*?)"',re.S).findall(class_contect)if len(course_title)==0:print('课程不在有效期内或未报名')raise IndexErrorcourse_title = course_title[0]course_id_name_list = re.compile('"node_id":(\d*),.*?"title":"(.*?)"',re.S).findall(class_contect) # 课时ID,名称# 创建与课程同名文件夹course_title = re.sub(u"([^\u4e00-\u9fa5\u0030-\u0039\u0041-\u005a\u0061-\u007a])", "", course_title)if not os.path.exists(r'./' + r'./' + course_title): # 判断文件夹是否存在os.makedirs(r'./' + r'./' + course_title) # 如果不存在则创建文件夹# 获取m3u8文件的ID以及对应的名称i = 1for course_id_name in course_id_name_list:course_url = 'https://class.sanjieke.cn/course/section_content?cid='+class_id+'§ion_id='+course_id_name[0]course_contect = requests.get(course_url,headers=headrer)course_contect = course_contect.text.replace('\\/','/').encode('utf-8','ignore').decode('unicode-escape','ignore')course_m3u8_id = re.compile('"id":"(\d*)"', re.S).findall(course_contect)if len(course_m3u8_id) == 0:course_m3u8_id = re.compile('"id":(\d*)', re.S).findall(course_contect)# 读取每个m3u8文件的内容if not len(course_m3u8_id) == 0:# 获取m3u8文件下载地址course_m3u8_url = 'https://service.sanjieke.cn/video/master/'+course_m3u8_id[0]+'.m3u8?class_id='+class_id[0]course_m3u8_contect = requests.get(course_m3u8_url,headers=headrer).text.replace('\\/','/')course_m3u8_contect = course_m3u8_contect.encode('utf-8','ignore').decode('unicode-escape','ignore')course_m3u8_down_url = re.compile('http.*?\.m3u8',re.S).findall(course_m3u8_contect)[0]# 读取m3u8文件原始内容m3u8_res = requests.get(course_m3u8_down_url,headers=headrer).textm3u8_res = m3u8_res.encode('utf-8','ignore').decode('unicode-escape','ignore')URI = 'https://service.sanjieke.cn/video/key/'+course_m3u8_id[0]key = requests.get(URI,headers=headrer).contentiv = re.compile('IV=0x(.*)').findall(m3u8_res)ts_url = re.compile('(https://vcdn.*)').findall(m3u8_res)file_url = r'./' + course_title + r'./ ' + str(i).rjust(2, '0') + ' ' + course_id_name[1] + '.mp4' #构造文件下载地址for k in range(len(iv)):# 获取ts文件的内容ts_contrect = requests.get(ts_url[k])# 解密ts文件cryptor = AES.new(key, AES.MODE_CBC, bytes.fromhex(iv[k]))# 下载ts文件with open(file_url, 'ab') as f:f.write(cryptor.decrypt(ts_contrect.content))print(' 正在下载片段'+str(k).rjust(2, '0') + ' ' + course_id_name[1] + '.MP4')print(str(i).rjust(2, '0') + ' ' + course_id_name[1] + '.MP4' + ' 下载成功!')i = i + 1print(course_title +'下载完成!')
