• python抓取、解析、下载小电影


    前言

    一到周末就想搞点有意思的事,比如之前分享的arduino开发,比如上周分享的博客爬虫,今天我又想搞点有意思的事,所以就有了今天的内容——python爬取m3u8视频资源。不过需要在这里需要着重说明的是,技术无罪,切勿用技术搞违法犯罪的事,不然日子真的就越来越有判头了。

    知识扩展

    在开始抓取m3u8视频之前,我们先了解下m3u8的相关知识,了解的更多,可以让我们少走弯路。

    m3u8是什么?

    在此之前,我仅仅知道m3u8是一种网络串流,在平时娱乐时候会找一些m3u8的资源,看看直播啥的,直到今天要分享m3u8的相关内容,才真正开始搜集m3u8的相关知识点。关于m3u8连百度百科都没有说明,搜到知乎一篇内容(【全网最全】m3u8到底是什么格式?一篇文章搞定m3u8下载),下面的原理图也是参照的这篇内容:

    从上面的原理图中我们可以得到以下知识点:

    • 首先m3u8并非是视频格式,而是视频文件的索引。
    • ts文件才是我们真正播放的视频资源。ts是日本高清摄像机拍摄下进行的封装格式,全称为MPEG2-TSts即"Transport Stream"的缩写。MPEG2-TS格式的特点就是要求从视频流的任一片段开始都是可以独立解码的。

    m3u8通常分两种格式,一种是单码率(固定分辨率),一种是多码率(包含多种分别率)。下面就是一个单码率的m3u8文件的内容:

    #EXTM3U
    #EXT-X-VERSION:3
    #EXT-X-TARGETDURATION:10
    #EXT-X-MEDIA-SEQUENCE:1619459525
    #EXT-X-PROGRAM-DATE-TIME:2021-10-23T05:39:42Z
    #EXTINF:10.0,
    1634967582-1-1619459525.hls.ts
    #EXT-X-PROGRAM-DATE-TIME:2021-10-23T05:39:52Z
    #EXTINF:10.0,
    1634967592-1-1619459526.hls.ts
    #EXT-X-PROGRAM-DATE-TIME:2021-10-23T05:40:02Z
    #EXTINF:10.0,
    1634967602-1-1619459527.hls.ts
    

    这个就是一个多码率的m3u8文件,从多码率的文件格式可以看出来,多码率中包括了多个单码率是m3u8文链接:

    #EXTM3U
    #EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=500000,RESOLUTION=480x270
    500kb/hls/index.m3u8
    #EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=300000,RESOLUTION=360x202
    300kb/hls/index.m3u8
    
    m3u8文件指令

    另外我在另外一篇博客发现m3u8其实是m3u文件的扩展(参考文档2),这可能就是为什么没有找到m3u8相关词条的原因吧,同时在m3u的词条中发现了M#U文件的指令描述(每个字段的含义):

    #EXTM3U //必需,表示一个扩展的m3u文件
    
    #EXT-X-VERSION:3 //hls的协议版本号,暗示媒体流的兼容性
    
    #EXT-X-MEDIA-SEQUENCE:xx //首个分段的sequence number
    
    #EXT-X-ALLOW-CACHE:NO //是否缓存
    
    #EXT-X-TARGETDURATION:5 //每个视频分段最大的时长(单位秒)
    
    #EXT-X-DISCONTINUITY //表示换编码
    
    #EXTINF: //每个切片的时长
    

    另外关于m3u的文件指令是有国际标准的,感兴趣的小伙伴可以去看下:

    http://tools.ietf.org/html/draft-pantos-http-live-streaming-06
    

    好了,关于m3u8的相关内容,我们就说这么多,感兴趣的小伙伴可以自己继续探索,下面我们看下如何用python抓取、解析和下载m3u8文件索引中的视频文。

    抓取、解析、下载

    首先我们先要拿到目标视频资源的索引文件,也就是m3u8文件。一般稍微懂点web开发的小伙伴,应该都知道浏览器抓包吧,F12打开浏览器控制台,然后选择Network,刷新下页面,在左侧资源区找到index.m3u8文件。

    通常会有两个m3u8,第一个是获取视频码率列表的,也就是多码率m3u8,这个文件我们是没办法直接解析的,我们要找的是包含ts视频资源的m3u8文件。这里我随便在网上搜了一个葫芦娃的视频,然后通过控制台拿到m3u8文件地址:

    https://vod1.bdzybf1.com/20200819/wMgIH6RN/1000kb/hls/index.m3u8
    

    下面我们就用python解析下载这个视频文件。

    解析m3u8文件

    解析m3u8文件最核心的地方是分析m3u8的文件结构,然后根据其文件内容写出对应的解析逻辑。这里我推荐直接用requests库模拟调用,然后分析响应结果,因为用文本工具之类的查看m3u8文件的话,换行符\n、制表符\t这些看起来不够直观,但是requests就不会有这个问题,因为我们解析的就是requests的响应结果。

    这里的以我们上面m3u8文件为例响应结果如下:

    可以清晰地看出,这个m3u8文件是通过换行符\n分割的,有部分m3u8文件中会出现制表符和换行符组合的情况,所以具体情况具体分析。

    另外,我从响应内容中发现,这个视频资源是进行了AES-128加密的,所以后面在下载视频资源的时候要解密。

    解析ts视频地址

    因为python代码都很简单,代码量也不多,所以就不展开讲了,看代码注释应该可以看懂。这个方法主要是为了获取ts视频文件的地址。

    import requests
    
    def getTsFileUrlList(m3u8Url):
        # 组装请求头
        headers = {
                'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36'
                }
        # 请求 m3u8文件,并拿到文件内容
        ts_rs = requests.get(url = m3u8Url, headers=headers).text
        print(ts_rs)
        # 换行符分割文件内容
        list_content = ts_rs.split('\n');
        print('list_content:{}'.format(list_content))
        player_list = []
        # 循环分割结果
        for line in list_content:
          # 以下拼接方式可能会根据自己的需求进行改动
          if '#EXTINF' in line:
            continue;
          # 仅记录 ts文件地址  
          elif line.endswith('.ts'):
            player_list.append(line)
        print('数据列表组装完成-size: {}'.format(len(player_list)));
        return player_list;
    

    运行上面这个方法的话,最终会获取到m3u8索引文件中所有的ts视频地址,但是由于我们刚才已经从m3u8文件中发现了,视频资源是加密的,所以在下载的时候我们需要解密。

    下载文件

    这里的方法是下载并解密视频资源,这里我加了一个解密操作,因为注释够清晰,所以我也不过多赘述了

    def fileDownloadWithdecrypt(fileSavePath, player_list):
        # 创建文件夹
        if not os.path.exists(fileSavePath):
            os.mkdir(fileSavePath);
        # 循环 ts 视频资源列表
        for index, url in enumerate(player_list):
            # 发送下载请求
            ts_video = requests.get(url = url, headers=headers)
            # 保存文件
            with open('{}/{}.ts'.format(fileSavePath, str(index + 1)), 'wb') as file:
                # 视频资源解密
                context = decrypt(ts_video.content);
                # 文件写入
                file.write(context)
                print('正在写入第{}个文件'.format(index + 1))
        print('下载完成');
    

    下面是解密代码

    from Crypto.Cipher import AES   # 用于AES解码
    
    def decrypt(context):
        # 加密的key
        key =  b'2cd1da2aedacaec8';
        # 解密
        cryptor = AES.new(key, AES.MODE_CBC, key);
        decrypt_content = cryptor.decrypt(context);
        return decrypt_content;
    

    这里用的是pycryptodome库,安装方式如下:

     pip3 install pycryptodome
    

    关于密文,在m3u8文件里面已经有了,直接下载就可以拿到,这里我就不通过代码拿了:

    这里下载视频会比较费时间,为了提高下效率可以用多线程,但是由于时间的关系就不演示了。

    合并视频

    合并视频就更简单了,就是讲前面我们保存的视频合并成一个完整的视频,合并完之后的格式是mp4

    def fileMerge(filePath):
        # 查询出文件中的ts文件
        c = os.listdir(filePath)
        # 打开视频保存文件
        with open('%s.mp4' % filePath, 'wb+') as f:
          # 循环
          for i in range(len(c)):
            # 打开 ts 视频文件
            x = open('{}/{}.ts'.format(filePath, str(i + 1)), 'rb').read()
            f.write(x)
        print('合并完成')
    

    我看了下,短短的一集葫芦娃,总共被分割成272ts文件(好像比这个多,我没下载完就把网断了):

    272ts文件合并成一个视频文件:

    好了,到这里我们python爬取m3u8视频资源的实例就结束了,今天的示例还算比较完美,目标也比较完美的达成了。

    完整代码如下:

    import requests
    import os
    from Crypto.Cipher import AES   # 用于AES解码
    
    headers = {
                'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36'
                }
    
    def getTsFileUrlList(m3u8Url):
        ts_rs = requests.get(url = m3u8Url, headers=headers).text
        print(ts_rs)
        list_content = ts_rs.split('\n');
        print('list_content:{}'.format(list_content))
        player_list = []
        index = 1;
        for line in list_content:
          # 以下拼接方式可能会根据自己的需求进行改动
          if '#EXTINF' in line:
            continue;
          elif line.endswith('.ts'):
            player_list.append(line)
        print('数据列表组装完成-size: {}'.format(len(player_list)));
        return player_list;    
        
    def fileDownloadWithdecrypt(fileSavePath, player_list):
        if not os.path.exists(fileSavePath):
            os.mkdir(fileSavePath);
        for index, url in enumerate(player_list):
            ts_video = requests.get(url = url, headers=headers)
            with open('{}/{}.ts'.format(fileSavePath, str(index + 1)), 'wb') as file:
                context = decrypt(ts_video.content);
                file.write(context)
                print('正在写入第{}个文件'.format(index + 1))
        print('下载完成');
        
        
    def fileMerge(filePath):
        c = os.listdir(filePath)
        with open('%s.mp4' % filePath, 'wb+') as f:
          for i in range(len(c)):
            x = open('{}/{}.ts'.format(filePath, str(i + 1)), 'rb').read()
            f.write(x)
        print('合并完成')
        
        
    def decrypt(context):
        key =  b'2cd1da2aedacaec8';
        cryptor = AES.new(key, AES.MODE_CBC, key);
        decrypt_content = cryptor.decrypt(context);
        return decrypt_content;
        
        
    if __name__ == '__main__':
        m3u8Url = 'https://vod1.bdzybf1.com/20200819/wMgIH6RN/1000kb/hls/index.m3u8';
        videoList = getTsFileUrlList(m3u8Url);
        print(videoList)
        savePath = "./test"
        fileDownloadWithdecrypt(savePath, videoList);
        fileMerge(savePath);
    

    运行上面的代码,你就可以得到一集完整的葫芦娃
    当然,如果你能找到资源,用上面这段代码爬取小电影也是可以的

    总结

    今天的视频爬虫很简单,可以说非常简单,核心技术点也不多,主要涉及如下几点:

    • request请求
    • 字符串解析(响应结果解析)
    • 文件操作
    • ASE解密

    只需要稍微有一点python基础,就可以做出来,所以这里我也没什么好总结的了。

    最后,免费为python做一个无偿广告。我一直觉得python是一门不错的语言,特别是作为脚本使用的时候,真的是太方便了,今天它也依然没有让我失望。

    其实严格来说,我学python,但是之前一直诟病于它的缩进语法,所以也就一直没入门,直到做了一段时间java web开发之后,回头再看下python,觉得好简单,于是就又愉快地使用它了,不过用它写脚本真的太爽了,短短几行代码,搞定java一个繁琐的项目,而且用起来也很轻便。

    最近一年多的时间,我用它处理过数据、用它跑数据库统计过数据,然后日程工作中我可以用它生成文件目录、爬取资料,也感觉我对它越来越有好感,所以在这里强烈安利各位小伙伴都来学下python,特别是那些非开发岗位的小伙伴,python简直是统计数据的利器,虽然不像广告吹的那么秀,但是技多不压身呀,而且它真的是可以极大提供我们的效率。

    最后的最后,再强调下,技术无罪,切勿用技术搞违法犯罪的事,不然日子真的就越来越有判头了!

    技术无罪,切勿用技术搞违法犯罪的事,不然日子真的就越来越有判头了!

    技术无罪,切勿用技术搞违法犯罪的事,不然日子真的就越来越有判头了!

    重要的事情说三遍!!!

    另外,今天忙着搞爬虫了,设计模式的类图还没来得及补,明天要加下油了。好了,铁子们,晚安吧

    参考内容
  • 相关阅读:
    canvas实现音乐中的歌词播放效果
    canvas调节视频颜色
    clip API实现遮罩
    总有那么几款发型 是经典不过时的
    很多人喜欢露脚踝你觉得时尚吗?
    王者荣耀花木兰攻略解析
    十位王者给出的单排心得
    IntelliJ IDEA2017 + tomcat 即改即生效 实现热部署
    IntelliJ IDEA2017 + tomcat 即改即生效 实现热部署
    jqGrid分页查询出错
  • 原文地址:https://www.cnblogs.com/caoleiCoding/p/15449821.html
Copyright © 2020-2023  润新知