多线程视频批量下载

2020-01-17 11:30:25 浏览数 (1)

前一阵子我的一个好哥们小海知道我转行学了计算机,想要我帮忙下载某个网站的视频(因其只能在线观看,而且播放不够流畅~),本着男人不能说不行的原则,于是便有了本文。

声明:本文仅供技术交流,主要使用requests,threading,os,re等库,并且使用FFmpeg进行视频的合并。

01.获取视频播放链接

首先需要获取视频播放链接,打开视频页面,查看网页源代码,利用XPath便可轻易的获取视频标题以及播放链接。

02.获取m3u8文件

HTTP Live Streaming(HLS)是一个由苹果公司提出的基于HTTP的流媒体网络传输协议。而HLS的m3u8,是一个ts的列表,其中ts是一种视频的封装格式。简单来说,整个视频是通过一个一个ts连续播放展示出来的。因此我们需要获取这个m3u8文件,从而得到所有的ts文件链接。

那么我们如何得到这个m3u8文件呢?经过观察,这个链接可以在视频播放页面的源代码中看到一丝端倪。

这个share链接打开之后可以看到m3u8文件的url链接,可以通过简单的正则表达式将其匹配。得到连接之后,我们就可以通过requests把这个m3u8文件下载到本地。

03.批量下载ts文件

根据下载的m3u8文件,加上url的前缀,便可获得ts文件的完整url连接,因为往往一个视频包含几百乃至上千个ts文件,这时多线程就派上了用场。

threading的使用可以大大加快下载的速度,由于电脑配置的限制,多线程的数目需要控制,不然就会频繁报错,甚至程序崩溃。经过测试,按100个线程同时开启,下载会非常稳健~ 所以我将ts文件按100个一组分类,最后不到100个分为一组。

代码语言:javascript复制
def main(self):
               
        self.get_shrae_url()
        self.check_folder('m3u8')
        for i in range(len(self.m3u8_urls)):
            os.chdir(self.retval)
            files,files_url=self.get_files(i)
            print("*****************开始下载'{}',该视频共有{}个ts文件*****************".format(self.titles[i],len(files)))
            segs=len(files)//100 
            for seg in range(segs 1):
                tasks=[]           # 线程池
                left=100*seg
                right=left 100 if seg!=segs else len(files)
                for j in range(left,right):
                    task=threading.Thread(target=self.run, args=(i,files[j],files_url[j],))
                    tasks.append(task)
                    task.start()
                time.sleep(1)
        
                # 等待所有线程完成
                for _ in tasks:
                    _.join() 
                print("*****************已经完成{}个ts文件下载!*****************".format(right))
            print("*****************{}的所有ts文件已经完成下载!*****************".format(self.titles[i]))
            
            self.merge(i)
            self.delete(files)
        
    def run(self,i,file,file_url):
        
        ts_file_folder=self.titles[i]
        try:      
            if os.path.exists('{}/{}'.format(ts_file_folder,file)):
                pass
            else:
                r=requests.get(file_url,headers=self.headers)
                with open('{}/{}'.format(ts_file_folder,file),'wb') as f:
                    f.write(r.content)        
            print("{}已下载".format(file))
    
        except Exception as e:
            print(str(e))
            print("出现异常,正在自动重新下载!")
            self.run(i,file,file_url)

04.ts合并成mp4

FFmpeg的视音频编解码功能非常太强大,几乎囊括了现存所有的视音频编码标准。因此我们通过其来将我们之前获取的ts文件合并成完整的mp4视频文件。

合并方法主要有两种,一是直接将需要合并的文件名通过“|”分隔

代码语言:javascript复制
ffmpeg -i "concat:1.ts|2.ts" -c copy output.mp4

我采取了另外一种方法,编辑一个txt文本文件,罗列需要合并的子文件路径和名称,每一行的最前面需要加上“file”。

代码语言:javascript复制
ffmpeg -f concat -i temp.txt -c copy output.mp4

利用os.system执行合并命令,其中要注意路径的问题,ffpmeg命令执行后在当前路径生成mp4文件。

代码语言:javascript复制
    def merge(self,i):
        
        time.sleep(3)
        # 合并ts文件
        os.chdir("{}/".format(self.titles[i]))
        shell_str='ffmpeg -f concat -i temp.txt -c copy {}.mp4'.format(self.titles[i])    
        # print(shell_str)
        os.system(shell_str)
        print("*****************视频'{}'合并成功*****************".format(self.titles[i]))

05.删除所有ts文件

当视频完成合并之后,ts文件比较占据空间,我们需要将它们批量删除。这里files是所有的ts文件名的列表。

代码语言:javascript复制
    def delete(self,files):
        
        for file in files:
            if os.path.exists(file):
                os.remove(file)
        print("*****************已删除所有ts文件!*****************")

知道了一个视频是如何下载的,批量下载多个视频便可信手拈来了。小海在体验高速视频下载之后,投来了羡慕的目光~

什么?你们只想要代码?后台回复“视频”即可

0 人点赞