一、前言
你知道的视频下载“姿势”,有哪些吗?
本文绝对有你意想不到的玩法!
PS:文中出现的所有代码,均可在我的 Github 上下载:点击查看
二、陈年往事
视频下载,跟图片下载其实并无差别,甚至更简单。
玩过视频下载的,应该对「you-get」并不陌生。
「you-get」支持各大视频网站的视频下载,国内外加起来近 80 家。像国内的爱奇艺、腾讯视频、抖音、快手、B站、A站,国外的 Youtube、Twitter、TED、Instagram等等。
你只需要一行命令,就可以轻松下载想要的视频。
1 | you-get https://www.bilibili.com/video/BV1NZ4y1j7nw |
比如,下载“黑人抬棺原版视频”。
简单好用!
URL:https://github.com/soimort/you-get
有了「you-get」就“无敌”了吗?
并非如此,「you-get」对于短视频下载较好。对于爱奇艺、腾讯视频里面的长视频内容,支持就有些局限。比如 VIP 付费内容,只能下载个 6 分钟时长的片头。
付费的 VIP 内容,也可以下载吗?
当然可以。
早在 2 年前,我就搭建过视频解析网站。
甭管是爱奇艺、腾讯、还是优酷,也甭管是免费视频还是付费视频,只要给个视频网址链接,分分钟解析播放。
用自己搭建的这个解析网站,白嫖了半年的视频。后来,用我这个网站看视频的人越来越多,索性就给它停了。
说实话,自己白嫖就算了,还带着大家一起白嫖,就有些过分了。
后来付费意识逐渐增强,不想再做白嫖党,也就办了爱奇艺的年度会员。
这种视频解析网站现在仍然有很多,但大多数都早已不能用了。
在网络上,有个叫「视频解析接口」的东西。
这些大大小小的视频解析网站,其实就是调用的这些第三方「视频解析接口」实现的。
背后的原理也并不复杂,就是拿着一些 VIP 会员账号,在后台解析视频,然后分享给别人看的。
随着版权意识、版权管理的加强,以及技术的更新迭代,这类「视频解析接口」也就逐渐失效了。
在网络上,还有个叫「资源采集网」的东西。
「视频解析接口」大量失效,那帮搞「灰产」的人另辟蹊径,既然视频无法解析,那就搜索资源吧。
现在,与其叫它们「视频解析接口」,不如叫它们「视频搜索接口」。
这些「视频搜索接口」拿着视频地址,去「资源采集网」匹配资源。
「资源采集网」其实就是一个大型网络爬虫,它会采集网络上的视频资源,并将它们整合到一起。
这里的说头,太多了。扯远了,回归正题。
三、视频下载
背景讲完了,那就进入今天的主题,从「资源采集网」下载我们想要的视频。
以「越狱第一季」为例,越狱算是带我入坑的美剧之一,Miller 太帅了。
本文要爬的网站名叫「ok 资源网」,长这样:
思路也不复杂,搜索、解析、下载。
1、搜索
想要下载视频,首先得找到资源,采用站内搜索很简单。
抓搜索的请求包也特别简单,教一个小技巧:
第一步输入要搜索的内容,第二步打开并清理 Network,第三步点击搜索按钮。
Network 里第一个弹出的就是搜索的请求包。
可以看到,这是一个 POST 请求。
之前的实战内容,无论是下载小说,还是下载图片,都是 GET 请求。
GET 请求,就是字面意思,从服务器获取数据。POST 请求,也是字面意思,给服务器发送数据。
因为我们是搜索嘛,得告诉服务器,咱搜索啥,给服务器传递数据,就是通过 POST。
能 POST 的数据有很多,可以是简单的字符串、也可以是一张图片。
POST 啥数据,那就看服务器要啥数据,要啥给啥,就这么简单。
看一下搜索的结果页,不难发现,搜索结果存储在 class 属性为 xing_vb4 的 span 标签里。
没有难度啊,直接搞起。根据浏览器抓包结果,填写 data 信息即可。由于网站也有简单的 Header 反爬虫,所以一些必要的 Headers 信息也要填写。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | import requests from bs4 import BeautifulSoup search_keyword = '越狱第一季' search_url = 'http://www.jisudhw.com/index.php' serach_params = { 'm': 'vod-search' } serach_headers = { 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.122 Safari/537.36', 'Referer': 'http://www.jisudhw.com/', 'Origin': 'http://www.jisudhw.com', 'Host': 'www.jisudhw.com' } serach_datas = { 'wd': search_keyword, 'submit': 'search' } r = requests.post(url=search_url, params=serach_params, headers=serach_headers, data=serach_datas) r.encoding = 'utf-8' server = 'http://www.jisudhw.com' search_html = BeautifulSoup(r.text, 'lxml') search_spans = search_html.find_all('span', class_='xing_vb4') for span in search_spans: url = server + span.a.get('href') name = span.a.string print(name) print(url) |
顺利拿到详情页地址:
2、解析
解析详情页,获得下载链接,我们先看下详情页都有啥。
URL:http://www.jisudhw.com/?m=vod-detail-id-15409.html
视频详情页,至少有两种播放类型,一种是 m3u8 ,一种是 kuyun 。
拿到其中一种链接即可,我们以 m3u8 为例进行说明。
可以看到,视频链接都放在了 input 标签里,直接匹配:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | import requests from bs4 import BeautifulSoup detail_url = 'http://www.jisudhw.com/?m=vod-detail-id-15409.html' r = requests.get(url = detail_url) r.encoding = 'utf-8' detail_bf = BeautifulSoup(r.text, 'lxml') num = 1 serach_res = {} for each_url in detail_bf.find_all('input'): if 'm3u8' in each_url.get('value'): url = each_url.get('value') if url not in serach_res.keys(): serach_res[url] = num print('第%03d集:' % num) print(url) num += 1 |
将结果都存放到了字典里,这样方便后续视频链接和视频名称的一一对应。
3、下载
视频下载,一般而言,无非两种情况。
一种,链接明确是以 mp4、mkv、rmvb 这类视频格式后缀为结尾的链接,这种下载很简单,跟图片下载方法一样,就是视频文件要比图片大而已。
另一种,链接是以 m3u8 这类分段视频后缀结尾的链接。啥是分段视频?
抓包看一下就知道了,打开视频播放链接:
http://youku.com-youku.net/share/f0d48bde60d407c45af7ca00d1ef927b
现在很多视频都是分段存储的,你看视频的时候,其实是在加载一个个 ts 视频片段,一个片段是几秒钟的视频。
这种视频要怎么下载?怎么将 ts 视频片段组合成一个视频?
其实,如果知道方法,就很简单。
m3u8 这种格式的视频,就是由一个个 ts 视频片段组成的。
一个 m3u8 文件并不大,你可以把它理解为链表,每个 ts 视频片段文件,都有下一个时序的 ts 视频片段的地址。
记住一点,解决音频和视频的一些问题,可以看看 FFmpeg,它的中文名叫多媒体视频处理工具。
FFmpeg 有非常强大的功能包括视频采集、视频格式转换、视频抓图、给视频加水印等功能。
这种 ts 视频片段合成,格式转换问题,交给 FFmpeg 就好了。
要使用 FFmpeg,需要先安装配置一番。
下载地址:https://ffmpeg.zeranoe.com/builds/
安装好后,记得配置环境变量。
FFmpeg 也有 Python API 接口,名字叫 ffmpy3,安装好 FFmpeg 后,可以直接通过 pip 安装。
1 | pip install ffmpy3 |
都弄好,就可以愉快的玩耍了。
比如想要下载 m3u8 文件。
URL:http://youku.com-youku.net/20180614/11920_4c9e1cc1/index.m3u8
可以在命令行输入如下指令:
1 | ffmpeg -i "http://youku.com-youku.net/20180614/11920_4c9e1cc1/index.m3u8" "第001集.mp4" |
视频会保存为「第001集.mp4」。
如果用 Python 接口,也只需要两行代码:
1 2 | import ffmpy3 ffmpy3.FFmpeg(inputs={'http://youku.com-youku.net/20180614/11920_4c9e1cc1/index.m3u8': None}, outputs={'第001集.mp4':None}).run() |
FFmpeg 自动整合 ts 分段视频,并保存为 mp4 格式的视频。
是不是很简单?
四、整合代码
代码整合在一起,开始下载电视剧。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 | import os import ffmpy3 import requests from bs4 import BeautifulSoup from multiprocessing.dummy import Pool as ThreadPool search_keyword = '越狱第一季' search_url = 'http://www.jisudhw.com/index.php' serach_params = { 'm': 'vod-search' } serach_headers = { 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.122 Safari/537.36', 'Referer': 'http://www.jisudhw.com/', 'Origin': 'http://www.jisudhw.com', 'Host': 'www.jisudhw.com' } serach_datas = { 'wd': search_keyword, 'submit': 'search' } video_dir = '' r = requests.post(url=search_url, params=serach_params, headers=serach_headers, data=serach_datas) r.encoding = 'utf-8' server = 'http://www.jisudhw.com' search_html = BeautifulSoup(r.text, 'lxml') search_spans = search_html.find_all('span', class_='xing_vb4') for span in search_spans: url = server + span.a.get('href') name = span.a.string print(name) print(url) video_dir = name if name not in os.listdir('./'): os.mkdir(name) detail_url = url r = requests.get(url = detail_url) r.encoding = 'utf-8' detail_bf = BeautifulSoup(r.text, 'lxml') num = 1 serach_res = {} for each_url in detail_bf.find_all('input'): if 'm3u8' in each_url.get('value'): url = each_url.get('value') if url not in serach_res.keys(): serach_res[url] = num print('第%03d集:' % num) print(url) num += 1 def downVideo(url): num = serach_res[url] name = os.path.join(video_dir, '第%03d集.mp4' % num) ffmpy3.FFmpeg(inputs={url: None}, outputs={name:None}).run() # 开8个线程池 pool = ThreadPool(8) results = pool.map(downVideo, serach_res.keys()) pool.close() pool.join() |
由于视频太大了,要是一个一个地下载,有些慢,适当开个线程,8 线程下载。
来吧,一起看「越狱」吧!
五、最后
本文仅是探讨一些关于视频下载的技术,切勿滥用。
支持正版,从我做起。
关注微信公众号,公众号文章评论区留言,获赞数前2名的读者,将获得爱奇艺1个月黄金 VIP 会员。
2020年5月13日 上午10:47 沙发
直接在页面的地址栏里键入about:version,进去页面后,用户代理后面就是UserAgent
2020年5月13日 下午4:58 1层
@sugar good!
2020年5月15日 下午3:37 板凳
想问大佬,怎么查看下载速度,下半天才不到2m
2020年5月15日 下午5:12 1层
@99ing 你那个应该是卡了,这个尴尬就尴尬在无法看进度,想有进度,可以接迅雷,我这个为了降低难度,就没接迅雷。windows有接口python接口可以调用。
2020年5月19日 上午11:47 地板
最后开多线程的时候出错,根据报错查了好一会儿发现要放到 if __name__ == ‘__main__’ : 下才能运行,但依然不太明白原理,现在的代码修改为:if __name__ == ‘__main__’:
ThreadPool(8, downVideo(m3u8))
2020年5月19日 下午10:26 1层
@Vince_94 不知道你这是什么情况~
2020年6月3日 下午7:42 4楼
博主你好,我想问一个问题,我用你的代码下载那种链接里只有一个视频链接,像电影这种东西,它会报错: ‘NoneType’ object has no attribute ‘decode’,我知道是 ffmpy3.FFmpeg(inputs={url: None}, outputs={name:None}).run()这个语句出现问题了,我想知道怎么解决,,,,,
2020年6月4日 上午10:26 1层
@锟斤拷锟斤拷 这个NoneType就是没有找对链接,链接送进去的不对。
2020年6月4日 下午3:28 2层
@Jack Cui 好的,谢谢你
2020年6月15日 下午2:29 1层
@锟斤拷锟斤拷 发现了一样的问题,请问你是怎样解决的?
2020年7月18日 下午4:46 5楼
大佬,迅雷要怎么接
2020年7月19日 下午4:53 1层
@OMG 你搜下 windows 调用迅雷,应该有教程。
2020年7月19日 下午6:05 2层
@Jack Cui 好的谢谢大佬
2020年11月13日 下午3:10 6楼
大神,我也报’NoneType’ object has no attribute ‘decode’这个错误了,我也检查了inputs的值了,为啥呀
2021年4月10日 上午12:02 7楼
大佬,这个章节的内容失效了,什么时间能重新补一下
2021年4月25日 上午12:07 8楼
您好,ok资源网(http://www.jisudhw.com/)失效了,请问是否有别的资源采集网?
2021年6月3日 上午6:32 1层
@ouuqqo 我也想知道……