参考资料:

# 配置环境

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. 关于前端使用JavaScript获取base64图片大小的方法

    base64原理 Base64编码要求把3个8位字节(38=24)转化为4个6位的字节(46=24),之后在6位的前面补两个0,形成8位一个字节的形式. 如果剩下的字符不足3个字节,则用0填充,输出字 ...

  2. 如何快速找出Linux中的重复文件

    md5sum | sort | uniq -w32 --all-repeated=separate [1]find -not -empty -type f -printf “%s\n” :find是查 ...

  3. javascript学习内容

    http协议 犀牛书 MDN js单线程 let只在代码块内有效 es5只有全局作用域 const变量指向的内存地址不得改动,值不能保证不变 全局变量不加var node.js 更改连接到服务器的方式 ...

  4. Docker Container开机自动启动

    重启策略: 使用在Docker run的时候使用--restart参数来设置. no - Container不重启 on-failure - container推出状态非0时重启 always - 始 ...

  5. toString和valueOf

    toString 今天在看以前写的代码时,遇到某个老师的点评: var obj = {};var root = [1, 2, 3];obj[root.join(',')] = value; 一看到这个 ...

  6. linux下大文件处理

    linux下采用先分割后合并的策略处理大文件 第一步:分割文件 split split 参数:-a, --suffix-length=N     指定输出文件名的后缀,默认为2个-b, --bytes ...

  7. SYC极客大挑战部分题目writeup

    Welcome 复制黏贴flag即可 我相信你正在与我相遇的路上马不停蹄 关注微信工作号回复"我要flag"即可获得flag 代号为geek的行动第一幕:毒雾初现 发现flag为摩 ...

  8. H5新增特性之语义化标签

    H5新增特性之语义化标签 语义化标签顾名思义标签有自己的含义,浏览器或者程序员一看就知道是什么.在HTML 5出来之前,我们用div来表示页面章节,但是这些div都没有实际意义.(即使我们用css样式 ...

  9. CSS——NO.7(布局模型)

    */ * Copyright (c) 2016,烟台大学计算机与控制工程学院 * All rights reserved. * 文件名:text.cpp * 作者:常轩 * 微信公众号:Worldhe ...

  10. EOS2.0环境搭建-centos7

    需要安装启动的有三个组件 nodes,keosd,cleos,看看三者的关系 nodeos:核心程序,用于启动eos节点服务,在后台运行,可以配置不同 插件.该进程负责账户管理.区块生成.共识建立,并 ...