Python获取腾讯视频真实地址

一、需求

当我们浏览视频网页时,有可能想要下载下来某个视频,或者想要跳过臭长的广告(慎),但是现在的网站往往不提供直接下载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,就是视频地址本身提供的:

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. 实际上有些视频是分成了多个小部分,这时就会需要获取每个视频的地址,下载下来然后再拼接成一个完整的视频文件,如果想要做的话也是可以的,自动下载多段视频,然后自动合并成1个完整的文件。
    这里可能就不能只发送一次get请求了,而是根据视频分割成了多少个,发送多次请求。
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,711评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,079评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,194评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,089评论 1 286
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,197评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,306评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,338评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,119评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,541评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,846评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,014评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,694评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,322评论 3 318
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,026评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,257评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,863评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,895评论 2 351

推荐阅读更多精彩内容