参考资料:

# 配置环境

import requests,re
import sys,time
import os
import numpy as np
import glob work_dir = os.getcwd()
print(work_dir) # 用来保存ts文件
file_dir = os.path.join(work_dir,'file_tmp') if not os.path.exists(file_dir):
os.mkdir(file_dir)

先定义保存文件的函数

def savefile(file_url,file_name):
# 配置headers防止被墙,一般问题不大
headers = {
'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.116 Safari/537.36'
} r = requests.get(file_url,headers=headers) if r.status_code == 200:
with open(file_name, 'wb') as f:
f.write(r.content)

从 m3u8 文件中解析出 ts 信息

怎么查找m3u8文件?

假设在chrome上打开视频页

右键检查,Network -> All ,过滤.m3u8

一般可以看到两个m3u8地址,其中一个是带hls的,这个文件可以解析出ts信息

拿个网址来举例,比如这个视频

# 如果url中没有hls的,那就是源m3u8文件
# 源m3u8文件会跳转到另一个m3u8文件,这个地址中就带有hls # 这个是源m3u8文件,不带hls
url_m3u8 = 'https://wuji.zhulong-zuida.com/20190706/762_c260ca6c/index.m3u8' r = requests.get(url_m3u8)
r.encoding='utf-8'
# 查看内容

print(r.text)

输出:

#EXTM3U

#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=800000,RESOLUTION=1080x608

800k/hls/index.m3u8

可以看到最后一行就是跳转后的m3u8地址

# 合成带有hls的m3u8地址
if r.text.split('\n')[-1] == '':
hls_mark = r.text.split('\n')[-2] # 以防\n结尾
else:
hls_mark = r.text.split('\n')[-1]
url_m3u8_hls = url_m3u8.replace('index.m3u8',hls_mark)
url_m3u8_hls

输出:

'https://wuji.zhulong-zuida.com/20190706/762_c260ca6c/800k/hls/index.m3u8'

# 不过有时候可能没法查到跳转后的带hls的连接
# 但是视频加载文件的网址格式为 主url+文件名.ts
# 这个主url是带hls的
# m3u8的index目录 格式为 主url/index.m3u8
url_m3u8_hls = 'https://wuji.zhulong-zuida.com/20190706/762_c260ca6c/800k/hls/index.m3u8'
# 带有hls的m3u8文件中获得的是ts信息
# 包括ts文件名称,以及该文件的持续时间 # 这个文件有用,先保存一下
file_m3u8 = url_m3u8_hls.split('/')[-1]
with open(file_m3u8,'wb') as f:
f.write(r.content)
# iter_lines得到的是bytesstring
text_bytes = list(r.iter_lines()) # 转化成正常string
text_string = [i.decode('utf-8') for i in text_bytes]
# 筛选以.ts结尾的行
# 有些情况下可能是以其他格式的文件,比如png,下载后修改后缀即可
# ts_name = [i for i in text_string if i.endswith('.ts')]
ts_name = [i for i in text_string if not i.startswith('#')] ts_name[:3]

输出:

['36962c1a1b0000000.ts', '36962c1a1b0000001.ts', '36962c1a1b0000002.ts']

有时候ts文件信息中可能还包含一部分路径信息。

因为路径都是统一的,所以我们只需要文件名就可以了

if '/' in ts_name[1]:
# 部分ts文件名中带有路径信息,只保留文件名即可
ts_name = [i.split('/')[-1] for i in ts_name] ts_name[:3]

接下来处理时间戳。

# 筛选带有时间的行
ts_time = [float(re.findall('[.\d]+',i)[0]) for i in text_string if i.startswith('#EXTINF')] ts_time[:3]

输出:

[4.1283, 4.3785, 4.17]

# 检验解析出来的时间戳和文件名数量是否匹配
len(ts_name) == len(ts_time)

输出:

True

按时间截取视频

# 建立时间基准
# 得到累计时间序列
time_cum = np.cumsum(ts_time)
# 那如果我要看51分05秒~55分46秒,应该下载哪些文件呢?

time_start = 1*3600+26*60+5
time_end = 1*3600+46*60+20 # 如果有多段时间截取,可以写个函数将时间序列进行转化
# 输入:[(0.0.0,0.9.30),(0.10.0,0.20.0)]
# 输出 累计时间戳的index [(0,38),(40,80)]
# 对于起始时间
# 筛选累计时间戳<开始时间的最大值,再找对应的index index_start = sum(time_cum<time_start)+1-1
# +1是为了截取
# -1 是为了矫正index序号

这个就是最终的index,我们这里的原则是最后取到的时间区间是完全包含目标区间的

这里的index实际上做了一个位移的,本来index是0开始,所以说是351

e.g. 假设前3个ts的时间为2,3,3

现在我要的是第4秒后的信息,得到的累计时间序列是2,5,8

按照我们刚才的那个判断,<4的只有1个时间位,但从自然顺序上我们是要从第二个ts文件开始截取

这就相当于index不用再变动了,如果其他软件序列语法是1开始的话,那么这个index

# 对于截止时间
index_end = len(time_cum) - sum(time_cum>time_end) +1 - 1

同样假设 整个累计时间序列为 2,5,8,10

现在截取到6秒,所以要取到第3个ts文件(Python index为2)

时间序列长度4-大于6的个数2+偏移1位-index矫正1位

这就是最终的index

print(index_start,index_end)

输出:

1290 1594

抓取 ts 文件

单文件测试

ts_name[0] # 这个是片头

输出:

'36962c1a1b0000000.ts'

# 这个网址去除掉最后的文件名就是**主url**了。
file_url = 'https://wuji.zhulong-zuida.com/20190706/762_c260ca6c/800k/hls/36962c1a1b0000000.ts' # 提取文件名
file_name = os.path.join(file_dir,file_url.split('/')[-1]) # 下载ts文件到本地
savefile(file_url,file_name)

批量下载

# 先看下我们要抓取的ts文件的index的起始位置
print(index_start,index_end)

输出:

1290 1594

# ts文件的主url以/hls/结束
url_m3u8_hls

输出:

'https://wuji.zhulong-zuida.com/20190706/762_c260ca6c/800k/hls/index.m3u8'

# 提取主url
url_ts_main = url_m3u8_hls.replace('index.m3u8','')
# 绝大部分hls文件的名称都是index.m3u8,个别的也可能是其他名字 # range 函数取头不取尾,所以+1
for idx in range(index_start,index_end+1):
# 拼接url
file_name = ts_name[idx]
file_url = url_ts_main+file_name # 对于后缀可能是其他格式的情况下,保存为以.ts结尾的文件即可
# 有的服务器可能会改变后缀假装自己不是ts文件
if not file_name.endswith('ts'):
tmp_name = file_name.split('.')[:-1]
tmp_name.append('ts')
file_name = '.'.join(tmp_name) file_path = os.path.join(file_dir,file_name) # 保存文件
savefile(file_url,file_path) # 提示进度
sys.stdout.write('\r当前进度 第%d页 剩余%d页'%(idx,index_end-idx))
sys.stdout.flush()
time.sleep(0.1)

输出:

当前进度 第1594页 剩余0页

合并 ts 文件

第一种方式可以考虑,使用命令行操作

如果是在windows上操作

  • copy /b 路径\*.ts 路径\合并文件.ts
  • copy /b “1.ts”+“2.ts”+…+”n.ts” /y “combine.ts”

如果是在mac上操作

  • cat 1.ts 2.ts > combine.ts

注意:文件的顺序要正确才行

# 如果有ts文件的index
# 那么可以用index直接来生成有顺序的list即可
file_list = [os.path.join(file_dir,ts_name[i]) \
for i in range(index_start,index_end+1)] # 直接扫描路径下的ts文件也是可以的
# 也可以删掉部分ts文件
# file_list = glob.glob(os.path.join(file_dir,'*.ts'))
# file_list.sort() # 这里是在mac上操作,所以名称以空格相连
filepath_cat = ' '.join(file_list) cmd_str = 'cat ' + filepath_cat + '> merge.ts'
# cat 1.ts 2.ts > combine.ts os.system(cmd_str)

执行成功的话,会返回0

第二种合并ts文件的方式,可以将所有的ts文件按顺序写到一个新的文件中

file_out = 'merge_02.ts'

with open(file_out,'wb') as f_out:
for f_in in file_list:
f_out.write(open(f_in,'rb').read())

将合并的ts文件转化为视频文件

最后,我们将合成的ts文件转化成视频文件(比如MP4格式)

这里我们调用 ffmpeg 将 ts 文件转化为视频文件

转化命令为

  • ffmpeg -i 文件名称.ts -c copy [视频名称]
  • e.g. ffmpeg -i merge.ts -c copy '视频截取片段.mp4'
file_in = os.path.join(work_dir,'merge.ts')

#如果路径中有空格,所以路径需要用上双引号,否则会找不到该文件

file_out = "merge.mp4"

# 这里是去ffmpeg官网下载编译好的软件包,免安装的
cmd = './ffmpeg -i '+file_in +' -c copy ' + file_out os.system(cmd) # 运行正常返回0

爬虫 | Python下载m3u8视频的更多相关文章

  1. python爬虫脚本下载YouTube视频

    python爬虫脚本下载YouTube视频 爬虫 python YouTube视频 工作环境: python 2.7.13 pip lxml, 安装 pip install lxml,主要用xpath ...

  2. 下载m3u8视频

    分两种情况 同时支持m3u8和mp4文件 某些视频同时支持m3u8和mp4视频文件,将m3u8改成mp4后直接: wget -c http://www.xxx.com/xxxx.mp4 只有m3u8视 ...

  3. python 下载bilibili视频

    说明: 1.清晰度的选择要登录,暂时还没做,目前下载的视频清晰度都是默认的480P 2.进度条仿linux的,参考了一些博客修改了下,侵删 3.其他评论,弹幕之类的相关爬虫代码放在了https://g ...

  4. 爬虫爬取m3u8视频文件

    一.m3u8视频格式 一般m3u8文件和 视频流ts文件放在同一目录 而m3u8文件格式存放的一般都是ts 文件的一个列表 二.根据m3u8视频存放以及写法的规律 思路 我们一般网站上能找到的m3u8 ...

  5. python代码下载m3u8视频

    代码如下: # -*- coding: utf-8 -*- import requests import re import os import base64 from Crypto.Cipher i ...

  6. python下载youtube视频

    谷歌开源了一个新的数据集,BoundingBox,(网址在这里)这个数据集是经过人工标注的视频数据集,自然想将它尽快地运用在实际之中,那么首先需要将其下载下来:可以看到网址上给出的是csv文件,该文件 ...

  7. python下载网页视频

    因网站不同需要修改. 下载 mp4 连接 from bs4 import BeautifulSoup import requests import urllib import re import js ...

  8. (Python基础教程之二十二)爬虫下载网页视频(video blob)

    Python基础教程 在SublimeEditor中配置Python环境 Python代码中添加注释 Python中的变量的使用 Python中的数据类型 Python中的关键字 Python字符串操 ...

  9. Python 爬虫实例(13) 下载 m3u8 格式视频

    Python  requests  下载  m3u8 格式    视频 最近爬取一个视频网站,遇到  m3u8 格式的视频需要下载. 抓包分析,视频文件是多个  ts 文件,什么是 ts文件,请去百度 ...

随机推荐

  1. Kafka与RabbitMQ、ActiveMQ协议区别

    对于Kafka与RabbitMQ.ActiveMQ协议,它们具体的区别如下: activemq:        activemq支持主从复制.集群.但是集群功能看起来很弱,只有failover功能,即 ...

  2. springmvc拦截器入门及其执行顺序源码分析

    springmvc拦截器是偶尔会用到的一个功能,本案例来演示一个较简单的springmvc拦截器的使用,并通过源码来分析拦截器的执行顺序的控制.具体操作步骤为:1.maven项目引入spring依赖2 ...

  3. 吴裕雄--天生自然 PYTHON数据分析:基于Keras的CNN分析太空深处寻找系外行星数据

    #We import libraries for linear algebra, graphs, and evaluation of results import numpy as np import ...

  4. iPhone X价格下跌!用户依旧冷眼相看为哪般?

    X价格下跌!用户依旧冷眼相看为哪般?" title="iPhone X价格下跌!用户依旧冷眼相看为哪般?"> 其实对于刚刚过去的2017年手机市场来说,根本没有一款 ...

  5. 爬虫入门-使用python写简单爬虫

    从第一章到上一章为止,基本把python所有的基础点都已经包括了,我们有控制逻辑的关键字,有内置数据结构,有用于工程需要的函数和模块,又有了标准库和第三方库,可以写正规的程序了. python可以做非 ...

  6. 遗弃.Forsaken.2015.BluRay.720p.x264.DTS-beAst

    ◎译 名 遗弃/落日孤影(台)/赎罪◎片 名 Forsaken◎年 代 2015◎产 地 加拿大/法国/美国◎类 别 剧情/西部◎语 言 英语◎上映日期 2015-09-16(多伦多电影节)/2016 ...

  7. Andrew's Blog / 《Network Programming with Go》学习笔记

    第一章: Architecture(体系结构) Protocol Layers(协议层) ISO OSI Protocol 每层的功能: 网络层提供交换及路由技术 传输层提供了终端系统之间的数据透明传 ...

  8. 某某项目SDV软件测试报告范例

    说明:本范例为符合CMMI 5级要求的范例 Prepared by 拟制 小张 Date 日期 2008-04-09 Reviewed by 评审人 小丽.小王.小李.小莉.小三.小四.小猪.小猫.小 ...

  9. Bugku的一道注入

    继续补sqli的题 这道题与之前的题的区别是在第二部分中加了一道waf,所以需要特殊的手段来进行注入. 题目来源:http://123.206.87.240:9004/1ndex.php?id=1 第 ...

  10. NumPy的随机函数子库——numpy.random

    NumPy的随机函数子库numpy.random 导入模块:import numpy as np 1.numpy.random.rand(d0,d1,...,dn) 生成一个shape为(d0,d1, ...