前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >python3爬虫(4)各种网站视频下载

python3爬虫(4)各种网站视频下载

作者头像
py3study
修改2023-09-20 20:02:13
2.3K0
修改2023-09-20 20:02:13
举报
文章被收录于专栏:python3python3

理论上来讲只要是网上(浏览器)能看到图片,音频,视频,都能够下载下来,然而实际操作的时候也是有一定难度和技术的,这篇文章主要讲述各个网站视频资源如何下载。

  1. B站视频

页面链接:

https://www.bilibili.com/bangumi/play/ep118490?from=search&seid=7943855106424547918

首先我们用万能下载器“you-get”测试一下,下载成功,60多兆的视频文件,打开可以观看。我们在浏览器输入该网址,F12打开网络监测,回车进入该网页,点击播放视频,观看一分钟左右,为什么要观看一分钟,主要是看视频是一个链接传输,还是不停的更换视频链接,还有就是1分钟会有挺大的视频缓冲数据,明显比其他网络资源大,方便咱们分析。暂停视频,停止抓包,看到抓包栏信息如下:

我们重点注意,Size,Time,Watefall栏,因为视频链接要返回数据,大小和花费时间明显比其他的资源大的多,滚动下看看所有信息,找到一个怀疑目标

看到返回数据29.4兆,这应该就是视频资源,先别急着分析这个链接,我们再看看有无其他怀疑目标,滚动一边发现仅此一个。选中该链接,看看详细信息

我们看到该链接是个get请求和一个关键字.flv,这个应该就是视频连接地址。

全连接如下:

https://upos-hz-mirrorkodo.acgvideo.com/upgcxcode/49/50/29645049/29645049-1-32.flv?e=ig8euxZM2rNcNbKHhwdVhoMMnWdVhwdEto8g5X10ugNcXBlqNxHxNEVE5XREto8KqJZHUa6m5J0SqE85tZvEuENvNC8xNEVE9EKE9IMvXBvE2ENvNCImNEVEK9GVqJIwqa80WXIekXRE9IMvXBvEuENvNCImNEVEua6m2jIxux0CkF6s2JZv5x0DQJZY2F8SkXKE9IB5QK==&deadline=1547005191&dynamic=1&gen=playurl&oi=22475807&os=kodo&platform=pc&rate=490000&trid=52c1879aeb584205af339c5624957e09&uipk=5&uipv=5&um_deadline=1547005191&um_sign=6049af7768edf6ebf7819f897bbda605&upsig=0b7a5fa168a1d70655e645783d7184d3

这个链接结构式视频链接+参数的形式,’?’号后面都是参数,

视频链接如下:

https://upos-hz-mirrorkodo.acgvideo.com/upgcxcode/49/50/29645049/29645049-1-32.flv

把这两个地址分别输入浏览器地址栏试试,发现都没什么反应,再用浏览器自带下载工具试试(当然也可以用其他下载工具试,如迅雷),

发现全连接那个下载失败,视频连接那个下载成功,下载到了60多兆的视频文件,可以播放,这样这个下载地址就算找到了,我们再试试上次用python写的简单7行代码试试,

代码链接:

python3爬虫(2)下载有固定链接的视频

发现瞬间结束,调试一下发现下载失败了,错误码:459。如下图:

这个不应该啊,估计是http请求头出了问题,我们抓下浏览器是什么头,发现浏览器自带抓包工具无法抓下载的包头,只能够抓浏览网页的头,用抓包工具Fiddler抓吧。

我们把这个包头写到请求里面,发现下载成功了。完整代码如下:

代码语言:javascript
复制
import requests

hd = {
    'Connection':'keep-alive',
    'Host':'upos-hz-mirrorkodo.acgvideo.com:443',
    'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36 SE 2.X MetaSr 1.0'
}

print("开始下载")
url = 'https://upos-hz-mirrorkodo.acgvideo.com/upgcxcode/49/50/29645049/29645049-1-32.flv'
r = requests.get(url, headers=hd, stream=True)

with open('test.mp4', "wb") as mp4:
    for chunk in r.iter_content(chunk_size=1024 * 1024):
        if chunk:
            mp4.write(chunk)

print("下载结束")

2.优酷

页面链接:

http://v.youku.com/v_show/id_XMTQ2NzQyMjY1Ng.html

还是老规矩,

首先我们用万能下载器“you-get”测试一下,下载成功,13多兆的视频文件,打开可以观看。我们在浏览器输入该网址,F12打开网络监测,回车进入该网页,点击播放视频,观看一分钟左右。暂停视频,停止抓包。观察抓包栏目,这次和上面的例子不一样,这次找了半天也没找到1个超过1兆的数据包,推测是分开传输的,经过进一步的寻找发现m3u8链接,如下图:

经过前面的学习我们知道这是m3u8+ts传输视频流的,具体技术请看:

python3爬虫(3)下载流媒体m3u8

我们看下这个具体细节,手工下载一下m3u8文件,可以下载,和网页端比较一下是一样的,手工下载ts列表里面的ts文件,也是可以下载的,播放一下看看,都没啥问题。这些步骤可以用下面的代码实现,前提是知道m3u8下载地址(包括地址里面的参数)

代码语言:javascript
复制
import os
import requests

"""
下载M3U8文件里的所有片段
"""

def download(url):
    download_path = os.getcwd() + "\download"
    if not os.path.exists(download_path):
        os.mkdir(download_path)
    all_content = requests.get(url).text # 获取M3U8的文件内容
    file_line = all_content.split("\r\n") # 读取文件里的每一行
    # 通过判断文件头来确定是否是M3U8文件
    if file_line[0] != "#EXTM3U":
        raise BaseException(u"非M3U8的链接")
    else:
        unknow = True   # 用来判断是否找到了下载的地址
        for index, line in enumerate(file_line):
            if "EXTINF" in line:
                unknow = False
                    # 拼出ts片段的URL
                pd_url = file_line[index + 1]
                res = requests.get(pd_url)
                # c_fule_name = str(index)+ '.ts'
                c_fule_name = "%(index)02d" % {'index': index} + '.ts'
                with open(download_path + "\\" + c_fule_name, 'ab') as f:
                    f.write(res.content)
                    f.flush()
        if unknow:
            raise BaseException("未找到对应的下载链接")
        else:
            print("下载完成")

#合并的时候名字要有规律,从前往后排
def merge_file(path):
    os.chdir(path)
    os.system("copy /b * new.mp4")

if __name__ == '__main__':
    download("http://pl-ali.youku.com/playlist/m3u8?vid=XMTQ2NzQyMjY1Ng&type=hd2&ups_client_netip=0156f41f&utid=cKsgFHBPZVECAXUjhXp%2Bu8Ip&ccode=0502&psid=244b3690aa7b9cd1c11c2f6c8ae6582b&duration=90&expire=18000&drm_type=1&drm_device=7&ups_ts=1547012701&onOff=0&encr=0&ups_key=9a7e324bb33543281964c43caa15dc80")
    merge_file(os.getcwd() + "\download")

我们这个时候就考虑能否仅根据网页地址全自动下载呢,毕竟you-get可以做到全自动,我们来看看这个m3u8地址:

http://pl-ali.youku.com/playlist/m3u8?vid=XMTQ2NzQyMjY1Ng&type=mp4&ups_client_netip=0156f41f&utid=cKsgFHBPZVECAXUjhXp%2Bu8Ip&ccode=0502&psid=db59c8bd03f9e26f6b21b17bccf8f1c9&duration=90&expire=18000&drm_type=1&drm_device=7&ups_ts=1547003676&onOff=0&encr=0&ups_key=49fa2661f64619e0e57d22611df8e5b7

地址是固定的,参数才是关键,参数如下:

vid:XMTQ2NzQyMjY1Ng

type:mp4

ups_client_netip:0156f41f

utid:cKsgFHBPZVECAXUjhXp+u8Ip

ccode:0502

psid:db59c8bd03f9e26f6b21b17bccf8f1c9

duration:90

expire:18000

drm_type:1

drm_device:7

ups_ts:1547003676

onOff:0

encr:0

ups_key:49fa2661f64619e0e57d22611df8e5b7

这些参数我们猜测,应该是获取这个m3u8之前向服务器获取的,应该是以json方式返回来的,在抓包栏目里面我们找找,紧挨着这个m3u8向上找,找到了1个,

几个ups属性找到了,没有ups_key,这个估计可能是js算出来的码,不是从服务器获取到的。Vid属性可以从网址里面提取出来,type是清晰度,mp4:标清,hd:高清,hd2:超清,(我们切换清晰度,观察m3u8地址变化总结出来的),utid是个固定值,我们换个视频还是这个码,ccode, drm_type, drm_device, onOff, encr,也一样固定值;duration进过总结就是视频时长(秒),按道理来讲应该是服务器放回来的,简单找了一下没找到。先记录到这里吧。

================以下为2019/1/10 13:20 更新=======================

接着上次聊。

这个m3u8的参数:type:mp4,ups_key:49fa2661f64619e0e57d22611df8e5b7,都是不容易找到。

上次我们找到ups那个json里面当时也看了看其他节点,收获还真不小,如下图:

注意这个stream分支,打开他的分支0,

我们看到了m3u8地址,这就太好了,再往下看看,

有个cdn_url,从这个地址我们可以看出这就是一个视频链接。试着下载一下看看,就是我们在网页里面看到的视频。刚不是看到stream有4个子项吗,分别打开看看,每个子项里面都有m3u8_url和cdn_url,试了一下每个都可以下载视频,一种height属性:288,288,378,622。看下界面有,标清,高清,超清,这些应该是互相对应的。亦即只要获得了这个json其实就获取视频源头。我们看下这个请求的地址:

http://acs.youku.com/h5/mtop.youku.play.ups.appinfo.get/1.1/?jsv=2.5.0&appKey=24679788&t=1547091011531&sign=be34df75c65e2e871e720039252ee056&api=mtop.youku.play.ups.appinfo.get&v=1.1&timeout=20000&YKPid=20160317PLF000211&YKLoginRequest=true&AntiFlood=true&AntiCreep=true&type=jsonp&dataType=jsonp&callback=mtopjsonp1&data=%7B%22steal_params%22%3A%22%7B%5C%22ccode%5C%22%3A%5C%220502%5C%22%2C%5C%22client_ip%5C%22%3A%5C%22192.168.1.1%5C%22%2C%5C%22utid%5C%22%3A%5C%22cKsgFHBPZVECAXUjhXp%2Bu8Ip%5C%22%2C%5C%22client_ts%5C%22%3A1547091011%2C%5C%22version%5C%22%3A%5C%220.6.8%5C%22%2C%5C%22ckey%5C%22%3A%5C%22DIl58SLFxFNndSV1GFNnMQVYkx1PP5tKe1siZu%2F86PR1u%2FWh1Ptd%2BWOZsHHWxysSfAOhNJpdVWsdVJNsfJ8Sxd8WKVvNfAS8aS8fAOzYARzPyPc3JvtnPHjTdKfESTdnuTW6ZPvk2pNDh4uFzotgdMEFkzQ5wZVXl2Pf1%2FY6hLK0OnCNxBj3%2Bnb0v72gZ6b0td%2BWOZsHHWxysSo%2F0y9D2K42SaB8Y%2F%2BaD2K42SaB8Y%2F%2BahU%2BWOZsHcrxysooUeND%5C%22%7D%22%2C%22biz_params%22%3A%22%7B%5C%22vid%5C%22%3A%5C%22XMTQ2NzQyMjY1Ng%3D%3D%5C%22%7D%22%2C%22ad_params%22%3A%22%7B%5C%22vs%5C%22%3A%5C%221.0%5C%22%2C%5C%22pver%5C%22%3A%5C%220.6.8%5C%22%2C%5C%22sver%5C%22%3A%5C%221.0%5C%22%2C%5C%22site%5C%22%3A1%2C%5C%22aw%5C%22%3A%5C%22w%5C%22%2C%5C%22fu%5C%22%3A0%2C%5C%22d%5C%22%3A%5C%220%5C%22%2C%5C%22bt%5C%22%3A%5C%22pc%5C%22%2C%5C%22os%5C%22%3A%5C%22win%5C%22%2C%5C%22osv%5C%22%3A%5C%227%5C%22%2C%5C%22dq%5C%22%3A%5C%22auto%5C%22%2C%5C%22atm%5C%22%3A%5C%22%5C%22%2C%5C%22partnerid%5C%22%3A%5C%22null%5C%22%2C%5C%22wintype%5C%22%3A%5C%22interior%5C%22%2C%5C%22isvert%5C%22%3A0%2C%5C%22vip%5C%22%3A0%2C%5C%22emb%5C%22%3A%5C%22AjM2Njg1NTY2NAJ2LnlvdWt1LmNvbQIvdl9zaG93L2lkX1hNVFEyTnpReU1qWTFOZy5odG1s%5C%22%2C%5C%22p%5C%22%3A1%2C%5C%22rst%5C%22%3A%5C%22mp4%5C%22%2C%5C%22needbf%5C%22%3A2%7D%22%7D

看看他的参数列表:

jsv:2.5.0

appKey:24679788

t:1547091011531

sign:be34df75c65e2e871e720039252ee056

api:mtop.youku.play.ups.appinfo.get

v:1.1

timeout:20000

YKPid:20160317PLF000211

YKLoginRequest:true

AntiFlood:true

AntiCreep:true

type:jsonp

dataType:jsonp

callback:mtopjsonp1

data:{"steal_params":"{\"ccode\":\"0502\",\"client_ip\":\"192.168.1.1\",\"utid\":\"cKsgFHBPZVECAXUjhXp+u8Ip\",\"client_ts\":1547091011,\"version\":\"0.6.8\",\"ckey\":\"DIl58SLFxFNndSV1GFNnMQVYkx1PP5tKe1siZu/86PR1u/Wh1Ptd+WOZsHHWxysSfAOhNJpdVWsdVJNsfJ8Sxd8WKVvNfAS8aS8fAOzYARzPyPc3JvtnPHjTdKfESTdnuTW6ZPvk2pNDh4uFzotgdMEFkzQ5wZVXl2Pf1/Y6hLK0OnCNxBj3+nb0v72gZ6b0td+WOZsHHWxysSo/0y9D2K42SaB8Y/+aD2K42SaB8Y/+ahU+WOZsHcrxysooUeND\"}","biz_params":"{\"vid\":\"XMTQ2NzQyMjY1Ng==\"}","ad_params":"{\"vs\":\"1.0\",\"pver\":\"0.6.8\",\"sver\":\"1.0\",\"site\":1,\"aw\":\"w\",\"fu\":0,\"d\":\"0\",\"bt\":\"pc\",\"os\":\"win\",\"osv\":\"7\",\"dq\":\"auto\",\"atm\":\"\",\"partnerid\":\"null\",\"wintype\":\"interior\",\"isvert\":0,\"vip\":0,\"emb\":\"AjM2Njg1NTY2NAJ2LnlvdWt1LmNvbQIvdl9zaG93L2lkX1hNVFEyTnpReU1qWTFOZy5odG1s\",\"p\":1,\"rst\":\"mp4\",\"needbf\":2}"}

这条路走下去主要有3个难点:

难点一:

sign:cda7f7031b84db2b741d31ac4a8bec89

难点二:

ckey:115#133lN51O1TaT1YgQMCfR1Csou61hIeAacuvuZj .............

难点三:

emb:AjM2Njg1NTY2NAJ2LnlvdWt1LmNvbQIvdl9zaG93L2lkX1hNVFEyTnpReU1qWTFOZy5odG1s

这3个值不知道哪里去弄,当然按道理来讲肯定有点方去弄,只是难度有点大。

刚刚看到希望,瞬间蔫了,因为我们看了一下其他链接,返回json的不是很多,json里面有这些字段值是没有,这就有点棘手了。

回头想想,不是you-get能够获取到这个视频吗,我们看看他是怎么获得到的。打开抓包工具Fiddler,这个是名气最大的,简单看下,7个请求,其中有6个是python进程的请求,1个是浏览器sogouexplore.exe请求。

看看这个6个python请求。第一个是log.mmstat.com,第二个是https请求,后边4次好像是重复的动作,一模一样,其实是两次请求。点开https请求,发现没有什么有价值的信息,好像是Fiddler解析https需要配置什么,上次配置之后又导致浏览器不能正常访问网页。换个抓包工具吧,我下载了一个HttpAnalyzer。效果还不错,先看下抓包结果,

选中那个https请求,看下是能够完全解析的。返回数据支持json,data,hex,preview四种展示方式

我们看下,这个返回结果其实和上面咱们那个比较长的请求结果是一样的。You-get获取到这个json之后解析了里面的cdn_url选择最清晰的下载了下来。看下他传输的时候有哪些参数,看了下这个参数少了很多,鉴于这个请求之前没有和优酷服务器通讯,我们猜测这些值都是定值,有仔细看了下client_ts字段值很想时间戳,确定了一下就是时间戳,

我们简短的写代码,模拟一下这个请求,果然成功了,返回了想要的json。这个请求这么好,浏览器里面是不是也有这个请求而我们没注意到呢?我们搜索一下ups.youku.com。看看浏览器有没发出类似的请求,

发现0个请求,也就是没有发出向这个网站的请求,那you-get又是怎么知道的呢,估计是以前版本,请求是向ups.youku.com发出的,现在更新了。既然更新了为什么老的还能用?因为整个优酷太大,前台页面有很多,更新前台页面工作量太大,所以现在服务器是新老本兼容的,只不过以后的页面都会以更新之后的出现。

第一个链接http://log.mmstat.com/eg.js是干什么的,一开始没注意,因为根据现有内容已经可以获得我们想要的内容,后来测试的路中出现问题:客户端无权播放,

You-get也会出现类似问题,只不过概率很低,我们分一下原因,发现you-get发送请求中utid的值是变动,这个值是哪里来的?本地有个列表循环着来,不太合适吧,我们看下第一个链接http://log.mmstat.com/eg.js 正好发现了这个值。我们改下自己代码,每次请求之先获取一下这个utid,发现现在就很流畅了。

注意:

通过这次实战我们也了解到了,不会存在一种给个页面就能下载页面里面的视频的通杀方案,you-get之所以能下载大部分主流网站是因为他为每个网站都做了适配,亦即每个网站下载视频原理他都已经研究了。

源码(想要下载你的视频,将param里面的vid值改成你的地址栏的值就行了)

代码语言:javascript
复制
import requests
import time
import json

def downfile(filename, url):
    r = requests.get( url, stream=True)
    with open(filename, "wb") as mp4:
        for chunk in r.iter_content(chunk_size=1024 * 1024):
            if chunk:
                mp4.write(chunk)

# 获取证书
r = requests.get('http://log.mmstat.com/eg.js')
start = len('window.goldlog=(window.goldlog||{});goldlog.Etag=\"')
end = len('window.goldlog=(window.goldlog||{});goldlog.Etag=\"66C9FJlZrDYCAQFQU4AhkzO0')
sert = r.text[start:end]

param = {
    'vid':'XMTQ2NzQyMjY1Ng',
    'ccode':'0590',
    'client_ip':'192.168.1.1',
    'utid':'2py9FNCXjUcCAQFQU4APrwPf',
    'client_ts':'1547028409',
    'ckey':'DIl58SLFxFNndSV1GFNnMQVYkx1PP5tKe1siZu/86PR1u/Wh1Ptd+WOZsHHWxysSfAOhNJpdVWsdVJNsfJ8Sxd8WKVvNfAS8aS8fAOzYARzPyPc3JvtnPHjTdKfESTdnuTW6ZPvk2pNDh4uFzotgdMEFkzQ5wZVXl2Pf1/Y6hLK0OnCNxBj3+nb0v72gZ6b0td+WOZsHHWxysSo/0y9D2K42SaB8Y/+aD2K42SaB8Y/+ahU+WOZsHcrxysooUeND'
}

headers = {
    'Accept-Encoding': 'identity',
    'Host': 'ups.youku.com',
    'Referer': 'http://v.youku.com',
    'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.101 Safari/537.36',
    'Connection': 'close'
}

now = time.time()
nowint = int(now)
nowstr = str(nowint)
param['client_ts'] = nowstr
param['utid'] = sert

url = 'http://v.youku.com/ups/get.json?'
r = requests.get(url, headers=headers, params=param)

r_j = json.loads(r.text)
streams = r_j.get('data').get('stream')


for stream in streams:
    m3u8_url = stream.get('m3u8_url')
    cdn_url = stream.get('segs')[0].get('cdn_url')

    print('m3u8_url:' + m3u8_url)
    print('cdn_url:' + cdn_url)
    filename = cdn_url[len('http://ykugc.cp31.ott.cibntv.net/65720C705E33C7182E34B311F/'):len('http://ykugc.cp31.ott.cibntv.net/65720C705E33C7182E34B311F/030020010056B748030687093F3C3CF7C1644A-3E5C-1E70-1F07-0C1AD8DC39BB.mp4')]
    downfile(filename,cdn_url)

print('end')
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2019-08-07 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档