requests是简洁的Python http库, 相较Python标准库urllib, requests更加人性化。
用requests下载视频或大文件时,默认是把请求到的相应数据完整下载到内存中。这样假如下载一个2G的视频,肯定会非常吃内存,也可能在下载过程中中断而前功尽弃。
可以使用requests流式请求来下载, 在requests加入 stream=Ture参数来开启流式请求,之后就可以一点点下载到磁盘上。
import requests
r = requests.get(file_url, stream=True)
之后就可以使用response的iter_content数据来分块下载视频,并且还支持断点续传
with open('a.mp4', 'wb') as f:
for data in r.iter_content(chunk_size=1024):
f.write(data)
但有些网站的视频是分段的, 要合并视频这就要使用FFmpeg了, FFmpeg支持Windows, Linux和Mac三大平台,在终端或者命令行中操作。
之后我会写一篇FFmpeg的安装方法
快速合并视频的方法
ffmpeg -y -f concat -safe 0 -i list.txt -c copy out.mp4
其中list.txt是待合并视频文件的列表,结构如下
file 'video1.mp4'
file 'video2.mp4'
在python内可以简单的用os模块的system方法调用终端命令,不过可以用更好的subprocess模块, subprocess提供了多个方法, 在Python3.5后提供了run, 不过python2还没有可以使用call或check_call。
多视频合并也会产生很多中间文件,这些文件的清除也是一件很麻烦的事情,因此可以使用python的临时文件模块 tempfile ,tempfile模块提供了一个NamedTemporaryFile方法 可以创建一个临时文件,默认路径在/tmp下,关闭后自动删除。
最后你肯定不希望在下载第二个视频时把第一个视频覆盖掉,那么在视频下载前可以切换到一个指定目录中保存文件,下载完成后在切换回来,直接写在函数里显然不太好看,可以把切换目录的工作写在装饰器内。
#coding=utf-8
import os
import requests
import subprocess
from tempfile import NamedTemporaryFile
def switch_dir(fun):
def wapper(*args, **kwargs):
old_path = os.getcwd()
path = kwargs.get('path') or '/tmp'
os.chdir(path)
try:
return fun(*args, **kwargs)
except:
raise e
finally:
os.chdir(old_path)
return wapper
@switch_dir
def down_video(urls, output='out.mp4', chuck_size=1024, *args, **kwargs):
#urls: 待下载的视频url列表 list, 应至少包含一个url
#可选参数
#ouptut 最终保存的视频文件名
#chuck_size 下载视频分块大小
#return: 视频文件名
if len(urls) == 1:
r = requests.get(url, stream=True)
with open(ouptut, 'wb') as f:
for data in r.iter_content(chuck_size=chuck_size):
f.write(data)
return ouptut
video_list = NamedTemporaryFile(dir='.') # 创建一个临时文件记录分段视频的文件名, dir指定在当前工作目录创建
file_list = [] # 创建一个临时列表 保存视频文件对象
for url in urls:
video_file = NamedTemporaryFile(dir='.', suffix='.mp4') # suffix指定文件后缀
r = requests.get(url, stream=True)
for data in r.iter_content(chunk_size=chuck_size):
video_file.write(data)
video_file.file.flush() # 记得写入磁盘
video_list.write("file '{}'\n".format(video_file.name)) # video_file.name 获取视频文件的名字
video_list.file.flush()
file_list.append(video_file) # 临时文件对象存如列表方便合并完成后关闭
# 合并视频 check_call方法会暂时阻塞主进程直到合并完成
sub = subprocess.check_call(
['ffmpeg', '-y', '-f', 'concat', '-safe', '0', '-i', video_list.name, '-c', 'copy', output],
stdout=-1,
stderr=-1)
(i.close() for i in video_list)
video_list.close()
return output
这样就算完成了
调用使用
urls = [
"http://..."
]
path = '/home/myvideo'
down_video(urls, path=path)
网友评论