一、需求
当我们浏览视频网页时,有可能想要下载下来某个视频,或者想要跳过臭长的广告(慎),但是现在的网站往往不提供直接下载mp4或其他格式的视频文件的功能。我们通过使用Python
配合Chrome
浏览器,可以获取到腾讯视频
的视频真实地址,直接下载视频文件。
- 为什么使用
Python
:很简单,专注自己需要的事情,同样的功能,使用其他语言,可能要自己写http
实现,写几百行代码,但是用Python
,20多行代码就可以了。
二、实现
以下以小猪佩奇
某一集为例,看看如何得到视频地址。
https://v.qq.com/x/cover/bzfkv5se8qaqel2/b0020buglwx.html
有一种思路是:使用Chrome
浏览器的开发者工具,监控网页加载过程中发出的每个请求和服务器的回复,从中查找关于视频信息的蛛丝马迹。当发现可疑的request
时,查看该请求发出的所有参数内容,使用Python
拼装相应的参数,并构造虚假的http请求头,模拟成用户通过网页的点击,向服务器发出请求,获取服务器回复内容,然后从回复内容中解析出视频地址。
根据以前的经验,这些网站的开发者,在移动端的功能实现会相对简单一些,我们更容易识别和抓取,因此我们解析的时候使用浏览器模拟成移动端,如图:
我们用
Python
,拼接出图中方框Request的URL地址,填入参数,发出GET
请求,就可以得到一个包含视频信息的json
字符串。
GET请求的URL是:
http://h5vv.video.qq.com/getinfo?
问号后面由很多包含参数名称和值的组合构成,例如otype=json&platform=11&defnpayver=1&appver=3.4.40
其中一个重要的参数vid
,就是视频地址本身提供的:
当我们发出get请求,得到的
json
数据类似这样:
{
"dltype": 1,
"exem": 0,
"fl": {
"cnt": 4,
"fi": [
{
"id": 10203,
"name": "sd",
"lmt": 0,
"sb": 1,
"cname": "标清;(270P)",
"br": 37,
"profile": 1,
"drm": 0,
"video": 1,
"audio": 1,
"fs": 5705248,
"super": 0,
"hdr10enh": 0,
"sname": "标清",
"resolution": "270P",
"sl": 0
},
{
"id": 10212,
"name": "hd",
"lmt": 0,
"sb": 1,
"cname": "高清;(480P)",
"br": 42,
"profile": 1,
"drm": 0,
"video": 1,
"audio": 1,
"fs": 10092707,
"super": 0,
"hdr10enh": 0,
"sname": "高清",
"resolution": "480P",
"sl": 0
},
{
"id": 10201,
"name": "shd",
"lmt": 0,
"sb": 1,
"cname": "超清;(720P)",
"br": 62,
"profile": 1,
"drm": 0,
"video": 1,
"audio": 1,
"fs": 19246460,
"super": 0,
"hdr10enh": 0,
"sname": "超清",
"resolution": "720P",
"sl": 1
},
{
"id": 10209,
"name": "fhd",
"lmt": 3,
"sb": 1,
"cname": "蓝光;(1080P)",
"br": 67,
"profile": 1,
"drm": 0,
"video": 1,
"audio": 1,
"fs": 40730393,
"super": 0,
"hdr10enh": 0,
"sname": "蓝光",
"resolution": "1080P",
"sl": 0
}
]
},
"fp2p": 2,
"hs": 0,
"ip": "113.110.230.111",
"ls": 0,
"preview": 299,
"s": "o",
"sfl": {
"cnt": 0
},
"tstid": 3,
"tm": 1574871391,
"vl": {
"cnt": 1,
"vi": [
{
"br": 62,
"ch": 0,
"cl": {
"fc": 1,
"ci": [
{
"idx": 1,
"cs": 19246460,
"cd": "299.96",
"cmd5": "f579ecf14d0aa58c0bc846f40ebe0c7c",
"vkey": "",
"urllist": [],
"keyid": "q0020tfo8j7.10201.1"
}
]
},
"ct": 21600,
"drm": 0,
"dsb": 0,
"fclip": 1,
"fmd5": "f579ecf14d0aa58c0bc846f40ebe0c7c",
"fn": "q0020tfo8j7.p201.mp4",
"fs": 19246460,
"fst": 5,
"fvkey": "6D7FDA1832CC88940F6F20E281EE9727639DF6B0D70FFF73083818AB45289A0507A0FD280B370536D0918C1A3564AA34F9698B83C61A88962F765BBF7EA67010F5B4D1D11737658D783A86ED5A5EA22933C2838506DEC3B16C1223E7727442E18A1AA2630567BFD436C4353F31F5A0E5",
"head": 0,
"hevc": 0,
"iflag": 0,
"level": 0,
"lnk": "q0020tfo8j7",
"logo": 1,
"mst": 8,
"pl": [
{
"cnt": 3,
"pd": [
{
"cd": 2,
"h": 45,
"w": 80,
"r": 10,
"c": 10,
"fmt": 40001,
"fn": "q1",
"url": "http: //puui.qpic.cn/video_caps/0/"
},
{
"cd": 2,
"h": 90,
"w": 160,
"r": 5,
"c": 5,
"fmt": 40002,
"fn": "q2",
"url": "http://puui.qpic.cn/video_caps/0/"
},
{
"cd": 2,
"h": 135,
"w": 240,
"r": 5,
"c": 5,
"fmt": 40003,
"fn": "q3",
"url": "http://puui.qpic.cn/video_caps/0/"
}
]
}
],
"share": 1,
"sp": 0,
"st": 2,
"tail": 0,
"td": "299.96",
"ti": "恐龙先生弄丢了",
"tie": 0,
"type": 1036,
"ul": {
"ui": [
{
"url":"http://113.105.141.22/vlive.qqvideo.tc.qq.com/AF094anUFAellJsZIYnUozoSnZLLcgP480IDBq7WleyE/uwMROfz2r5zCIaQXGdGnC2dfKb2-hlZWyQT_tzD-Vsr
eqSpl/",
"vt": 203,
"dtc": 0,
"dt": 2
},
{
"url": "http: //lmsjy.qq.com/uwMROfz2r5zCIaQXGdGnCmdfKb0i2sHXl3M2Wy9RmDZEeplY/",
"vt": 170,
"dtc": 0,
"dt": 2
},
{
"url": "http://lmbsy.qq.com/uwMROfz2r5zCIaQXGdGmm2dfKb0Pk2-yYlV7ZrIrO9TJ-LqW/",
"vt": 130,
"dtc": 0,
"dt": 2
},
{
"url": "http://video.dispatch.tc.qq.com/uwMROfz2r5zCIaQXGdGmlWdfKb2svKK_VNuAbg616jtBjIn0/",
"vt": 0,
"dtc": 0,
"dt": 2
}
]
},
"vh": 720,
"vid": "b0020buglwx",
"videotype": 106,
"vr": 0,
"vst": 2,
"vw": 1280,
"wh": 1.7777778,
"wl": {
"wi": []
},
"uptime": 1465198419,
"fvideo": 0,
"cached": 1,
"fvpint": 0,
"swhdcp": 0
}
]
}
}
看到这其中包含很多的url
地址,可以确定视频地址的信息就在其中,不过还需要分析一下,怎么样根据这些数据把视频地址拼接出来。
其实浏览器也是根据这个json
数据,通过调用js
脚本获取到视频地址,但是如果要通过解析js
脚本获取地址就会很麻烦,尤其是考虑到js
脚本可能经过了压缩和加密。所以还是通过浏览器开发者工具,查看浏览器实际get
请求中的数据格式,以供参考。
经测试其中有些参数可以不传入。最终代码如下,只需传入一个网址,就可以获取到mp4格式视频的一个url(默认就是最高清晰度)。
import requests
import re
import json
headers = {
'accept-language': 'zh-CN,zh;q=0.9,en;q=0.8',
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36'}
def getTencentVideoUrl(url):
vid = url.split('/')[-1][:-5]
url = 'http://h5vv.video.qq.com/getinfo?otype=json&platform=11&defnpayver=1&appver=3.4.40&defn=fhd&vid=' + vid
html = requests.get(url, headers=headers).text
# 获取json数据
p = re.compile(r'({.*})', re.S)
jsonstr = re.findall(p, html)[0]
json_data = json.loads(jsonstr)
print(jsonstr)
# 解析json数据获取url
fvkey = json_data['vl']['vi'][0]['fvkey']
keyid = json_data['vl']['vi'][0]['cl']['ci'][0]['keyid'].split('.')
filename = keyid[0] + '.p' + keyid[1][2:] + '.' + keyid[2] + '.mp4'
baseUrl = json_data['vl']['vi'][0]['ul']['ui'][3]['url'] # 实际数据中有多个cdn可供选择
result = baseUrl + filename + '?vkey=' + fvkey
return result
if __name__ == '__main__':
url = input('请输入腾讯视频网址:')
print('视频下载地址:\n', getTencentVideoUrl(url))
三、问题:
- 实际上有些视频是分成了多个小部分,这时就会需要获取每个视频的地址,下载下来然后再拼接成一个完整的视频文件,如果想要做的话也是可以的,自动下载多段视频,然后自动合并成1个完整的文件。
这里可能就不能只发送一次get
请求了,而是根据视频分割成了多少个,发送多次请求。