视频文件下载
一、单集视频下载
分析思路
-
1、分析目标网址:有无反爬、是否需要逆向、存储视频文件的包的位置
-
2、一般的视频文件。都是由一个个的ts文件组成,我们看到的整集视频,是由一个个小片段组合而成
-
3、分析得到我们想要的存放ts文件的包即可下载我们想要的片段
-
4、整合所有片段
第一步:确认网址
- 首先打开网址,并进入,可以看到正常访问
第二步:打开开发者工具抓包
- 定位到当前页面的响应数据返回的数据包
- 重点关注“ .m3u8 ” 结尾的文件
1、第一个“ index.m3u8 ”文件
-
我们可以发现其响应数据被重新定向
2、第二个“ index.m3u8 ”文件
-
我们发现其相应数据中存储着内容,留意其中的内容,方便下一步分析
3、第三个“ FA0EEF118EB32F0BAFDDEA715476C39C.m3u8 ”文件
-
我们可以发现其响应数据中存储的正是我们想要的一个个ts文件,但是我们需要想办法将其下载下来。
第三步、分析抓到的这几个“ .m3u8 ” 结尾的文件包
-
对比三个文件的请求头
1、第一个文件
-
请求数据: 请求网址: https://c2.monidai.com/20230302/YXXKaX4f/index.m3u8 状态代码: 302 location: https://h0.rzisytn.cn/20230302/YXXKaX4f/index.m3u8 响应数据: 无法加载响应数据:没有可显示的内容,因为此请求被重定向了
2、第二个文件
-
请求数据: 请求网址: https://h0.rzisytn.cn/20230302/YXXKaX4f/index.m3u8 状态代码: 200 OK 响应数据: #EXTM3U #EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=1000000,RESOLUTION=1280x532 /ppvod/FA0EEF118EB32F0BAFDDEA715476C39C.m3u8
3、第三个文件
-
请求数据: 请求网址: https://h0.rzisytn.cn/ppvod/FA0EEF118EB32F0BAFDDEA715476C39C.m3u8 状态代码: 200 OK 响应数据: #EXTM3U #EXT-X-VERSION:3 #EXT-X-TARGETDURATION:10 #EXT-X-MEDIA-SEQUENCE:0 #EXTINF:6.8, /20230302/YXXKaX4f/1000kb/hls/CvBp8uD4551000.ts #EXTINF:2.04, /20230302/YXXKaX4f/1000kb/hls/CvBp8uD4551001.ts #EXTINF:9.32, .........
4、随机抓一个ts文件
-
请求数据: 请求网址: https://h0.rzisytn.cn/20230302/YXXKaX4f/1000kb/hls/CvBp8uD4551000.ts 状态代码: 200 OK 响应数据: 一堆乱码,我们看不懂(因为视频是二进制数据)
第四步、比较分析
1、请求头及响应数据信息
第一个文件
请求网址: https://c2.monidai.com/20230302/YXXKaX4f/index.m3u8
状态代码: 302
location: https://h0.rzisytn.cn/20230302/YXXKaX4f/index.m3u8
第二个文件
请求数据:
请求网址: https://h0.rzisytn.cn/20230302/YXXKaX4f/index.m3u8
状态代码: 200 OK
响应数据:
#EXTM3U
#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=1000000,RESOLUTION=1280x532
/ppvod/FA0EEF118EB32F0BAFDDEA715476C39C.m3u8
第三个文件
请求数据:
请求网址: https://h0.rzisytn.cn/ppvod/FA0EEF118EB32F0BAFDDEA715476C39C.m3u8
状态代码: 200 OK
响应数据:
#EXTM3U
#EXT-X-VERSION:3
#EXT-X-TARGETDURATION:10
#EXT-X-MEDIA-SEQUENCE:0
#EXTINF:6.8,
/20230302/YXXKaX4f/1000kb/hls/CvBp8uD4551000.ts
随机ts文件
请求数据:
请求网址: https://h0.rzisytn.cn/20230302/YXXKaX4f/1000kb/hls/CvBp8uD4551000.ts
状态代码: 200 OK
响应数据:
一堆乱码,我们看不懂(因为视频是二进制数据)
2、分析1
-
第一个文件的状态码:302,以及其响应数据1展示的内容,即可了解到,其请求被重新定向,因此我们需要重新请求定向网址。
-
定向的目标网址就是我们第二个文件的网址
-
因此我们对第二个网址发起请求,获取响应数据2
-
3 、分析2
- 我们从响应数据1和我们的第三个网址对比发现,其响应数据的最后一部分正是我们下一个文件的请求网址的一部分,我们将其提取出来,并发起请求,得到响应数据3
4、分析3
- 我们从响应数据3,分析可看到我们的响应数据中包含的ts文件后缀名,正是我们每一个小文件的组成部分。因此我们对其发起请求,得到一个个小部分
5、得到目标数据,解析并存储
五、补充:
-
1、第一个 m3u8 文件一定不是凭空出现,一定是基于该网页请求得到的响应数据。事实也是如此
-
2、我们将第一个文件的网址复制,并在网页得到的响应数据页面进行搜索,就可以发现我们的请求网址,正是网页响应数据的一部分。
-
3、从这里我们也可以看到,下一页的请求url我们也能得到,通过对第一页数据的解析过程,我们可以得到第二页的数据。方法是相通的。
-
4、对于视频的合并,可以自行网上查找相关的工具或教程,也可通过代码实现。
六、单集视频文件下载代码:
import requests
import os
import time
from lxml import etree
import re
from fake_useragent import UserAgent
import asyncio
import aiohttp
# 去除ssl验证报错语句
requests.packages.urllib3.disable_warnings()
# UA 伪装
fake_ua = UserAgent()
headers = {
'User-Agent': fake_ua.random
}
#创建session对象
session = requests.Session()
#该部分用来获得每一个ts文件的请求地址
async def get_every_ts_urls():
#os方法创建文件夹
filename = "movie"
if not os.path.exists(filename):
os.mkdir(filename)
#对第二个文件发起请求获得响应书(已经被第一个文件重定向的url)
first_url = 'https://h0.rzisytn.cn/20230302/YXXKaX4f/index.m3u8'
#协程发起请求的固定方式
async with aiohttp.ClientSession() as session:
async with session.get(first_url, headers=headers, ssl=False) as response:
page_text = await response.text()
# /ppvod/FA0EEF118EB32F0BAFDDEA715476C39C.m3u8
#解析并获得第三个文件所在的url
index1_url = 'https://h0.rzisytn.cn' + page_text.split('\n')[2]
#发起请求并做解析
async with aiohttp.ClientSession() as session:
async with session.get(index1_url, headers=headers, ssl=False) as response:
page_text = await response.text()
#得到每一个ts文件的网址
every_ts_urls = re.findall(r'\n(.*?\.ts)\n', page_text)
#存放所有url的列表
ts_list = []
#对每一个ts文件进行补充,使其成为完整的URL
for t in every_ts_urls:
url = 'https://h0.rzisytn.cn' + t
#添加到列表中
ts_list.append(url)
#返回所有ts文件所在的列表
return ts_list,url
#该部分用来解析并存储所有的ts文件
async def download_ts(url):
#对每一个ts文件发起请求
async with aiohttp.ClientSession() as session:
async with session.get(url, headers=headers, ssl=False) as response:
#获得响应数据:注意这里是二进制文件,所以不能text
data = await response.read()
title = os.path.basename(url)
#定义文件路径
filepath = os.path.join("movie", title)
#利用try方法防止报错
try:
with open(filepath, 'wb') as f:
f.write(data)
f.close()
print(f'{title}已下载完成')
#若发生错误则报错
except Exception as e:
print(e)
async def main():
start_time = time.time()
# 获取到url列表
ts_list,url = await get_every_ts_urls()
#得到每一个ts文件的名字
name = url.os.path.basename(url)
#创建任务列表存放任务
tasks = []
#循环获得每一个url
for url in ts_list:
#创建并提交协程任务
t = asyncio.create_task(download_ts(url))
tasks.append(t)
#收集 任务
await asyncio.wait(tasks)
print(f'总耗时为{time.time() - start_time}s')
if __name__ == '__main__':
#运行任务
asyncio.run(main())
七、纠正
对于单文件下方法中关于提取到所有ts文件url步骤的问题,上面的过于繁琐,且不好找
友情提醒:对数据不满意就多试试!有时候真的出人意料!
1·、请求视频页数据
2、分析响应数据
-
在单文件中提到获取到 m3u8 文件地址的方法
-
在这里我们获得视频页的响应数据,对其进行xpath方法,取到第一个文件的网址
-
import requests import os from lxml import etree import re from fake_useragent import UserAgent # 去除ssl验证报错语句 requests.packages.urllib3.disable_warnings() # UA 伪装 fake_ua = UserAgent() headers = { 'User-Agent': fake_ua.random } session = requests.Session() def get_third_index(): first_url = 'https://www.9tata.cc/play/96891-0-0.html' response = session.get(first_url, headers=headers, verify=False) response.encoding = 'utf8' page_text = response.text #利用正则找到我们下一个地址的url(第一个文件对应的URL) first_link = re.findall(r'now="(.*?m3u8)"', page_text)[0] # 这是我们想要的URL:https://c2.monidai.com/20230302/YXXKaX4f/index.m3u8
-
-
接着我们对提取到的该网址进行二次请求,得到响应数据
-
import requests import os from lxml import etree import re from fake_useragent import UserAgent # 去除ssl验证报错语句 requests.packages.urllib3.disable_warnings() # UA 伪装 fake_ua = UserAgent() headers = { 'User-Agent': fake_ua.random } session = requests.Session() def get_third_index(): first_url = 'https://www.9tata.cc/play/96891-0-0.html' response = session.get(first_url, headers=headers, verify=False) response.encoding = 'utf8' page_text = response.text first_link = re.findall(r'now="(.*?m3u8)"', page_text)[0] # https://c2.monidai.com/20230302/YXXKaX4f/index.m3u8 response = session.get(first_link, headers=headers, verify=False) response.encoding = 'utf8' page_text = response.text # 这里打印的是获得到的响应数据 print(page_text) # #EXTM3U # #EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=1000000,RESOLUTION=1280x532 # /ppvod/FA0EEF118EB32F0BAFDDEA715476C39C.m3u8 second_url = page_text.split('\n')[2] # 这正是我们想要的第三个文件对应的URL:https://h0.rzisytn.cn/ppvod/FA0EEF118EB32F0BAFDDEA715476C39C.m3u8 if __name__ == '__main__': get_third_index()
-
-
获得到的响应数据如下:
-
# #EXTM3U # #EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=1000000,RESOLUTION=1280x532 # /ppvod/FA0EEF118EB32F0BAFDDEA715476C39C.m3u8
-
-
通过这里我们可以发现这正是我们第三个文件所对应的URL地址
https://h0.rzisytn.cn/ppvod/FA0EEF118EB32F0BAFDDEA715476C39C.m3u8
八、改良单集视频文件下载代码:
import requests
import os
import time
from lxml import etree
import re
from fake_useragent import UserAgent
import asyncio
import aiohttp
# 去除ssl验证报错语句
requests.packages.urllib3.disable_warnings()
# UA 伪装
fake_ua = UserAgent()
headers = {
'User-Agent': fake_ua.random
}
session = requests.Session()
async def get_every_ts_urls():
filename = "movie"
if not os.path.exists(filename):
os.mkdir(filename)
# 视频页的url
first_page_url = 'https://www.9tata.cc/play/96891-0-0.html'
# 对视频页的url发起请求
async with aiohttp.ClientSession() as session:
async with session.get(first_page_url, headers=headers, ssl=False) as response:
# 获取响应数据
page_text = await response.text()
# 每一集的名字
name = re.findall(r'<td class="col2 hidden-xs">(?P<name>\w+)</td>', page_text)[0]
print(f'当前视频是{name}')
# 第一个m3u8的地址
first_link = re.findall(r'now="(.*?m3u8)"', page_text)[0]
async with aiohttp.ClientSession() as session:
async with session.get(first_link, headers=headers, ssl=False) as response:
page_text = await response.text()
# 存放ts文件地址的url
# https://h0.rzisytn.cn/ppvod/FA0EEF118EB32F0BAFDDEA715476C39C.m3u8
second_url = 'https://h0.rzisytn.cn' + page_text.split('\n')[2]
async with aiohttp.ClientSession() as session:
async with session.get(second_url, headers=headers, ssl=False) as response:
page_text = await response.text()
# 提取出文件中每一个ts文件地址的后缀
every_ts_urls = re.findall(r'\n(.*?\.ts)\n', page_text)
ts_list = []
# 拼接url
for t in every_ts_urls:
url = 'https://h0.rzisytn.cn' + t
ts_list.append(url)
return ts_list, name
async def download_ts(url):
async with aiohttp.ClientSession() as session:
async with session.get(url, headers=headers, ssl=False) as response:
data = await response.read()
title = os.path.basename(url)
filepath = os.path.join("movie", title)
try:
with open(filepath, 'wb') as f:
f.write(data)
f.close()
print(f'{title}已下载完成')
except Exception as e:
print(e)
def merge(filename='output'):
'''
视频合并
:param filetime:
:return:
'''
# 进入到下载目录
os.chdir("movie")
# 视频合并的命令
os.system(f'ffmpeg -i index.m3u8 -c copy {filename}.mp4')
print(f'{filename} -----合并成功!-----')
async def main():
start_time = time.time()
# 获取到url列表
ts_list, name = await get_every_ts_urls()
tasks = []
for url in ts_list:
t = asyncio.create_task(download_ts(url))
tasks.append(t)
await asyncio.wait(tasks)
# merge(name) #合并视频文件方法,需要配置相关程序,若无则注释掉
print(f'总耗时为{time.time() - start_time}s')
if __name__ == '__main__':
asyncio.run(main())
标签:协程,19.2,m3u8,19,text,ts,url,https,import
From: https://www.cnblogs.com/dream-ze/p/17252989.html