1. 背景
因为不希望儿子太多使用平板并联网看视频,所以都是把视频下载到盒子中并用一个十几年的康佳小电视看,话说回来,康佳电视品质不错。如果有类似需要,比如说希望把QQ视频下载到存储卡里离线看,可参考本文。
本文使用Python等一系列工具,感觉非程序员勿入。如果你是有QQ视频会员的程序员,希望对你有参考意义。说视频会员还有一个好处,选集切换时没有广告干扰,画质也可以选择高清。
2. 下载视频的基本原理(M3U8)
QQ视频网站使用M3U8 格式传输流媒体。M3U8是 Unicode 版本的 M3U,用 UTF-8 编码。M3U有一个长长的名字,MP3 URL or Moving Picture Experts Group Audio Layer 3 Uniform Resource Locator。简单地说,这是一种类似于播放列表的文件格式,只不过,像QQ视频这些地方会把视频拆成大约10秒一部分的一个.ts文件。打个比方,看了一个一分钟的视频,可能对应一个M3U,其中包括了第一个片段到第六个片段的URL信息。这些流媒体片段会在你在网页版QQ视频观看过程中自动下载并有序播放,当然你感觉不到多个片段之间的跳变。所以想要下载完整视频,也就是说要根据M3U8中的列表信息,下载这些片段,并自动将其合并成单个视频文件。
3.手工下载的做法
手工下载我之前就在用,也因此还经常要坐在电脑前一集一集手工下载。QQ视频不能直接下载,需要借助一些工具,比如说专门的M3U8下载器。对照过一些,不是每个都那么好用或者说能下载到质量符合期望的视频。如果想尝试手工下载,建议阅读本节和相关的内容。如果想直接尝试自动的,建议跳过本节。
我们需要一个M3U8的下载器。下载器不仅下载这些流媒体片段,也包括把这些片段合并起来。既然要下载,就要有下载的URL。使用下面的JS代码,就可以“挖”出一个页面中的视频URL。简单地说,页面中有一个名为PLAYER的JS对象,其中暴露了当前视频的URL,当然也可以用于获取当前选集的名称等。
javascript:var a=prompt(PLAYER._DownloadMonitor.context.dataset.title,PLAYER._DownloadMonitor.context.dataset.ckc?PLAYER._DownloadMonitor.context.dataset.currentVideoUrl:PLAYER._DownloadMonitor.context.dataset.currentVideoUrl.replace(/:.*qq.com/g,"://defaultts.tc.qq.com/defaultts.tc.qq.com"));
以动画片 舒克贝塔第一季 第01集 舒克为例,页面链接如下:
01_舒克贝塔第一季_1080P在线观看平台_腾讯视频
其中的视频URL则是下面这个。M3U8下载工具就是需要这个URL。
https://defaultts.tc.qq.com/defaultts.tc.qq.com/AKN1YRgZqO53AVS38t9P2KGlizFAV17d__SoanbV_uFc/uwMROfz2r5xgoaQXGdGnC2df64gVTKzl5C_X6A3JOVT0QIb-/_imPHrjJpsBsZ6YIV35kAY8FQpmcS5kYLswcH6AGxxBXXOcVPQ8bwgc7SZf8Xt1qJTKGZENJuayuY3gt7xTa5C3zGADpP1mckFZVCBqQwItemfBm4L17wV_zoUkPeuPDdqjZlpsAoHeT15q0EesRXSlYh6j78xNJ2pkiptc60wc/v0032sn0o61.321004.ts.m3u8?ver=4
确实想要尝试的话,下面引用其它网友提到的手动获取QQ视频下载URL的说明,并附上M3U8下载工具的链接(链接未亲测),可供参考。
腾讯视频M3U8获取下载MP4 - 玩友资源网www.wyp88.com
https://www.wyp88.com/post/2129
m3u8下载工具.zip - 蓝奏云www.lanzoux.com
https://www.lanzoux.com/i1Y2de4r7uh
4.自动下载的做法
4.1自动下载的原理和工具集
要自动下载,需要Python脚本来实现自动化的编程。同时也需要一些Python第三方库,甚至需要其它一些工具软件(组件)。
实现自动地批量下载多集视频的Python脚本是这样考虑的。
(1)用浏览器而不是requests组件,因为我们需要执行脚本,而不是简单地获取和解析HTML内容。要切换选集,也可以在浏览器中模拟人的单击选集操作。自动化浏览器的操作,需要使用Selenium这个Python第三方库。当然,Selenium是很有名的工具,很多语言都可以用,不只是Python。要使用Selenium组件,除了安装这个Python库外,还需要搭配对应的浏览器驱动。我使用的是Google Chrome浏览器,所以下载了chromedriver。只不过,chromedriver下载和使用需要注意两点:一个是它不能直接从官方下载了,可以使用镜像下载(https://npm.taobao.org/mirrors/chromedriver/),另一个是要检查它支持的Chrome版本和你正在使用的Chrome是否兼容。
(2)还需要一个名为m3u8downloader的Python库。请使用命令pip install m3u8downloader安装,这个库装上好,提供了CLI界面,也就是说,可以使用命令行调用它。类似的组件还有其它,本文并不探讨和对比哪个更好,这个组件用的时候也发现一些问题,暂时先通过修改源码(后面会说)处理了,修改后实测还算好用。该组件的详细内容,见以下网址。
https://pypi.org/project/m3u8downloader/
(3)m3u8downloader在做下载视频后的合并时,又需要另一个工具,ffmpeg。可以在https://ffmpeg.org/download.html#build-windows 网址下载并解压到适当目录。建议把这个下载的ffmpeg和前面下载的chromedriver都放在一起,并需要设置PATH变量,让这些命令可以直接使用。
4.2脚本及说明
脚本如下,供参考:
import argparse
import os
from time import sleep
from selenium import webdriver
from selenium.common.exceptions import NoSuchElementException
parser = argparse.ArgumentParser(prog='autoseleniumvqq',
description="download video at QQ Video")
parser.add_argument('--destdir', default='D:\\', help='specify Destination Directory')
parser.add_argument('url', metavar='URL', help='the m3u8 url')
args = parser.parse_args()
browser = webdriver.Chrome()
browser.get(args.url)
input('登录成功并切换到高清后,请按回车键:')
while True:
sleep(1)
title = browser.execute_script("return $('.item_detail_half.current').text().trim().replace(' ', '')")
videoURLJS = 'PLAYER._DownloadMonitor.context.dataset.ckc?PLAYER._DownloadMonitor.context.dataset.currentVideoUrl:PLAYER._DownloadMonitor.context.dataset.currentVideoUrl.replace(/http.*:.*qq.com/g,"http://defaultts.tc.qq.com/defaultts.tc.qq.com")'
videoURL = browser.execute_script(f'return {videoURLJS}')
outDir = args.destdir
fileName = title + '.mp4'
print('downloading ', fileName)
os.system(f'downloadm3u8 -o {outDir}{fileName} {videoURL}')
try:
# break
next = browser.find_element_by_css_selector('.item_detail_half.current + span a')
next.click()
except NoSuchElementException:
browser.execute_script('console.log("DONE")')
break
对脚本的说明如下:
(1)使用argparse获取可选参数--destdir(目标路径),也就是下载的MP4文件保存的位置,默认保存在D盘根目录。获取必选参数url。这个URL给定多集中的第一集的URL。注意URL就是浏览器地址栏里的URL。
(2)程序打开Chrome浏览器并打开(1)中给定的URL对应的页面。之后暂时等待输入回车。在浏览器中登录QQ会员并切换到高清后再回车。
(3)程序进入循环。在循环中程序执行浏览器中的JS脚本得到当前选集的标题(例如第一集的标题是“01舒克”),得到视频URL(本质上讲视频的M3U8文件的URL),并通过执行带参数的downloadm3u8命令(这个命令来自于前面提到的m3u8downloader的安装)下载当前选集的视频到指定的目录中。
(4)除了下载当前选集外,循环的最后,通过对CSS选择器找到下一集的超链接并模拟单击操作,直到把播放列表中找不到下一集时循环结束,程序完成下载任务。
(5)补充说明:在Windows 10上的实际运行效果来看,ffmpeg命令不能适应太长的路径,会导致不能正常运行合并视频片段为完整视频。究其原因,是因为QQ视频的真实URL太长,会导致形成一个名称多达171个字的目录。不得已,我只有修改m3u8downloader的源码,避免这种过长的目录名称。具体地修改在
\Python\Python38-32\Lib\site-packages\m3u8downloader\main.py文件中,对get_local_file_for_url()函数做了调整,在函数最后一句 return os.path.normpath(os.path.join(tempdir, path))之前加上了这几句:
# QQ视频的路径太长,ffmpeg不正常工作
TRUNC_LEN = 30
parts = path.split('/')
if len(parts[-2]) > TRUNC_LEN:
parts[-2] = parts[-2][0:TRUNC_LEN]
path = ''.join(parts)
最后运行脚本如下:
python autoseleniumvqq.py https://v.qq.com/x/cover/ean263o58zvxwpm/v0032sn0o61.html
5.结语
这篇文章是在学习Python时想到的尝试和实践基础上形成的,让我看到了Python语言在自动化方面的独到优势。想当年在爬虫一词还没有流行时,我还曾经用传统语言用下载了别的网站的连载漫画图片(给朋友帮忙),现在再看Python手段果然更为简洁方便。
有这个脚本,我下载整部动画片就不用像之前那样,手工去获取视频URL,手工去下载,手工去改文件名了。现在只要启动脚本,几十集动画片就等着它下载完成了。
第一次写,写得有点啰嗦,脚本本身也没有什么特别之处,请见谅。另外,软件可能也会随着版本升级发生变化,QQ视频也可能,所以说文章内容希望更多的交流经验,不希望复制粘贴地用。如果觉得还可供参考,更加欢迎。碰到问题,欢迎交流。