最近几年 Python 实在是太火了,网上的课程层出不穷。一般 Forece 都是在B站找视频看的。最近有个朋友问我,他加入了个 Python 群,每周都有课。用的平台是网易云课堂微专业直播。课程可以回放,想问我能不能帮他下载一下网易云课堂的视频。安排!
1. 抓包分析
首先打开直播网址,然后 F12 打开开发者工具,进行抓包。发现有类似如下的 GET 请求。
https://jdvodluwytr3t.vod.126.net/jdvodluwytr3t/nos/hls/2020/06/04/1216859039_01eca46abd424762a92e7a7e26a35b3d_hd0.ts https://jdvodluwytr3t.vod.126.net/jdvodluwytr3t/nos/hls/2020/06/04/1216859039_01eca46abd424762a92e7a7e26a35b3d_hd1.ts https://jdvodluwytr3t.vod.126.net/jdvodluwytr3t/nos/hls/2020/06/04/1216859039_01eca46abd424762a92e7a7e26a35b3d_hd2.ts https://jdvodluwytr3t.vod.126.net/jdvodluwytr3t/nos/hls/2020/06/04/1216859039_01eca46abd424762a92e7a7e26a35b3d_hd3.ts https://jdvodluwytr3t.vod.126.net/jdvodluwytr3t/nos/hls/2020/06/04/1216859039_01eca46abd424762a92e7a7e26a35b3d_hd4.ts ......
下载下来看了一下,发现只有几秒的视频。继续查看抓包情况。在加载这些视频之前,发现有个 M3U8 的文件。科普一下,这个文件其实是一个索引文件,我们可以把它当做一个 playlist。下载回来用文本工具打开
#EXTM3U #EXT-X-VERSION:3 #EXT-X-TARGETDURATION:13 #EXT-X-MEDIA-SEQUENCE:0 #EXTINF:12.500000, 1216859039_01eca46abd424762a92e7a7e26a35b3d_hd0.ts #EXTINF:12.500000, 1216859039_01eca46abd424762a92e7a7e26a35b3d_hd1.ts #EXTINF:12.500000, 1216859039_01eca46abd424762a92e7a7e26a35b3d_hd2.ts #EXTINF:12.500000, 1216859039_01eca46abd424762a92e7a7e26a35b3d_hd3.ts #EXTINF:12.500000, 1216859039_01eca46abd424762a92e7a7e26a35b3d_hd4.ts #EXTINF:12.500000, 1216859039_01eca46abd424762a92e7a7e26a35b3d_hd5.ts #EXTINF:12.500000, 1216859039_01eca46abd424762a92e7a7e26a35b3d_hd6.ts #EXTINF:12.500000, 1216859039_01eca46abd424762a92e7a7e26a35b3d_hd7.ts #EXTINF:12.500000, 1216859039_01eca46abd424762a92e7a7e26a35b3d_hd8.ts #EXTINF:12.500000, 1216859039_01eca46abd424762a92e7a7e26a35b3d_hd9.ts #EXTINF:12.500000, ...... #EXTINF:2.900000, 1216859039_01eca46abd424762a92e7a7e26a35b3d_hd596.ts #EXT-X-ENDLIST
可以看出这个整个视频被分隔成了597份,然后除了最后一段2.9s,每段只有12.5s的时间。有了地址,有了索引,下载下来,然后用 os.command 合并就可以了。
2. 代码
import requests import os # 手动添加了分割数目(有能力的同学自己完善一下) for i in range(596): url = 'https://jdvodluwytr3t.vod.126.net/jdvodluwytr3t/nos/hls/2020/06/04/1216859039_01eca46abd424762a92e7a7e26a35b3d_hd%s.ts' % i headers = {'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36'} response = requests.get(url, headers=headers) content = response.content with open(r"./163/%s.ts" % os.path.basename(url), 'wb') as fp: fp.write(content) # 利用 cmd 中的 copy 命令合并文件 command = r'copy /b 163\*.ts 163\new.ts' os.system(command)
发现问题:
发现用 copy 命令会出现文件顺序问题。自己重新生成了一下文件列表,然后直接用 write 方法拼接了文件。
file_list = [] for i in range(596): file_list.append("./163/1216859039_01eca46abd424762a92e7a7e26a35b3d_hd%s.ts" % i) with open("./163/new.ts", 'wb+') as fp: for i in range(len(file_list)): fp.write(open(file_list[i], 'rb').read())
其实解决方法很多,你也可以将文件下载的时候就直接弄好编号,然文件顺序排列,比如补位0之类的。或者在做下载的时候,下载完一个视频就追加到合并视频当中。
完善了一下代码,从M3U8自动获取课程分割视频数目,直接以添加方式合并文件
import requests import re import os url = 'https://jdvodluwytr3t.vod.126.net/jdvodluwytr3t/nos/hls/2020/07/21/1217041902_b0c26b5a8ff44633aeaa7c20089d07f7_hd.m3u8' headers = {'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36'} response = requests.get(url, headers=headers) m3u8 = response.content.decode('utf-8') pattern = '\d*?_.*?_hd\d*.ts' file_index = re.findall(pattern, m3u8) base = os.path.dirname(url) for i in file_index: file_url = base + '/' + str(i) response = requests.get(file_url, headers=headers) content = response.content with open(r"./163/new.ts", 'ab') as fp: fp.write(content) print(i + '下载完成')
未完善
1. 自动获取课程所有课程M3U8
2. 异步下载课程