注:本人小白一枚,爬虫也是刚接触,写的不好请多指点
这里给出后半部分,转换m3u8视频的方法
上一节讲到我们能获取到m3u8文件所在的url地址,这一节的思路是将m3u8文件进行下载,并将其转化为MP4格式
目录
m3u8文件的下载
下载ts文件
处理ts文件
合并工作
删除
完整代码
运行结果
注意
m3u8文件的下载
这里用到了urllib库中request方法中的urlretrieve函数
代码如下:
from urllib import request
def download_m3u8(url,name):
"""
下载要解析的m3u8文件
:param url: 我们获得到的m3u8文件的url
:param name: 名字就是m3u8文件的文件名
:return: 下载m3u8文件
"""
request.urlretrieve(url, f'{name}.m3u8')
这样我们就能将文件从网页中下载下来,运行完后会在当前目录下多出一个m3u8文件
当我们打开这个m3u8文件时:
就会发现每个url后面的都是.png后缀,和平常的.ts不太一样
直接在网页中打开的图像是这样的
我们将这个png下载,在Hexeditor中打开会发现前面头部信息是png(有的是有的不是),所以说有的并不能通过直接修改后缀名来将这个文件转化为ts类型,为了保险起见,我们将这些数据的头部都进行修改,首先我们要将这个网站上的这个文件以字节流的形式下载下来
下载ts文件
将这个网站上的文件以字节流的形式下载下来
其实也就是为了去获取这个网站的这个文件,然后再将它转换成ts文件
def download_ts(name):
"""
以字节流形式下载ts文件
:param name: 我们下载过来的m3u8文件
:return: 将尚未处理的ts文件,存入inlib文件夹下,在运行中返回的是一共多少个ts文件
"""
web_list=[]
with open(f"{name}.m3u8","r") as files:
lines_list=files.readlines()
for https in lines_list:
web=re.search("https://.+",https)
if web:
web_list.append(web.group())
files.close()
headers={
"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.5060.134 Safari/537.36 Edg/103.0.1264.71"
}
#remove_or_set(r"D:\pythonProject\pythonlearn\anime\outlib")
#remove_or_set(r"D:\pythonProject\pythonlearn\anime\inlib")
for url,num in zip(web_list,range(1,len(web_list)+1)):
resp=requests.get(url,headers=headers)
with open(f"inlib/{num}.ts","wb") as codes:
codes.write(resp.content)
codes.close()
return len(web_list)
下载之后如图:
处理ts文件
修改ts文件的头部信息,然后在这个函数中向filelist.txt文件中写入之后要合并的ts文件的信息
代码如下:
def change(num):
"""
由于这个m3u8文件比较特殊,里面头部信息为png,我们要将它修改成ts
:param num: 这个就是总共的ts数
:return: 返回修改过后的ts文件
"""
print("正在加载....")
with open("filelist.txt","a+") as file:
file.write("\n")
for i in range(1,num+1):#num+1
file.write(f"file 'D:\\pythonProject\\pythonlearn\\anime\\outlib\\{i}.ts'\n")
with open(f"inlib/{str(i)}.ts","rb") as infile:
out_ts = f"outlib/{str(i)}.ts"
outfile = open(out_ts, "wb")
data = infile.read()
outfile.write(data)
outfile.seek(0x00)
outfile.write(b'\xff\xff\xff\xff')
outfile.flush()
outfile.close()
infile.close()
file.close()
filelist.txt文件内容如下:
合并工作
这里用到了ffmpy这个库中的FFmpeg函数,代码如下:
from ffmpy import FFmpeg
def together(name):
"""
合并outlib列表中的所有ts文件
:param name: 爬取视频的名字
:return: 返回一个MP4文件
"""
ff=FFmpeg(
inputs={
"D:\\pythonProject\\pythonlearn\\anime\\filelist.txt":"-f concat -safe 0"
},
outputs={
f"{name}.mp4":"-c copy"
}
)
# print(ff.cmd)
ff.run()
删除
清空文件夹中的所有文件,如果没有文件就进行创建
import re
import shutil
def remove_or_set(filepath):
"""
删除多余的文件防止下一次爬取的时候合并出问题
"""
if not os.path.exists(filepath):
os.mkdir(filepath)
else:
shutil.rmtree(filepath)
os.mkdir(filepath)
完整代码
这里把上一次的代码一并附上
import os
import re
import shutil
import requests
import time
import sys
from ffmpy3 import FFmpeg
from urllib import request
def parse_search_url(url):
"""
用于提取搜索网页的内容也就是http://159.75.0.62:11234/ssszz.php?top=10&q={}网页中的内容,{}中为搜索值
:param url: http://159.75.0.62:11234/ssszz.php?top=10&q={}
:return: detail_url返回解析后的url后缀,格式是/acq/74378类似的形式,movies_name返回影片名字
"""
headers = {
"User-Agent":
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.5060.134 Safari/537.36 Edg/103.0.1264.71",
"Cookie":
"PHPSESSID=8jnc6i33n2lablu2arfqev17l7"
}
resp = requests.get(url, headers=headers)
html = resp.content.decode("utf-8")
# print(html)
detail_url = re.findall(".+(/.+/\d+/)", html)
movies_name = re.findall(r"""title.+"(.+?)",""", html)
return detail_url, movies_name
def episodes(url):
"""
用于显示视频更新到哪一集
:param url: http://yhdm81.com/+后面的后缀,如:/tv/74378/
:return: 没有返回值,只是告知使用者该动画更新到第几集了
"""
headers = {
"User-Agent":
"Mozilla / 5.0(Windows NT 10.0;Win64;x64) AppleWebKit / 537.36(KHTML, likeGecko) Chrome / 103.0.5060.134Safari / 537.36Edg / 103.0.1264.71"
}
resp = requests.get(url, headers=headers)
html = resp.content.decode("utf-8")
episodes_num = re.findall(r"""<span style="font-size:12px;margin-left:10px;">(\D.+)</span><span.+""", html)
return f"目前{episodes_num[0]}"
def true_video_web(video_num, episode_num):
"""
获取视频的网络位置
:param video_num:视频的编号,如:/tv/74378/中的74378
:param episode_num: 选择的集数
:return: 返回获取到的m3u8地址
"""
headers = {
"User-Agent":
"Mozilla / 5.0(Windows NT 10.0;Win64;x64) AppleWebKit / 537.36(KHTML, likeGecko) Chrome / 103.0.5060.134Safari / 537.36Edg / 103.0.1264.71"
}
playarr_url = f"http://d.gqyy8.com:8077/ne2/s{video_num}.js"
resp = requests.get(playarr_url, headers=headers)
html = resp.content.decode("utf-8")
video_m3u8 = re.findall(r"""playarr_.{1,3}\[\d\]="https://.+/.+/[a-zA-Z]+?\d{2}\.m3u8.+""", html)
# video_url=f"https://yun.66dm.net/SBDM/{video_m3u8[0][:-1]}{episode_num}.m3u8"
# 以上两行代码是基于爬取的视频是漫画的前提下写的
#print(video_m3u8)
m3u8_url_list = video_m3u8[0].split(";")
#print(m3u8_url_list)
for i in range(len(m3u8_url_list)):
m3u8_url_episode = m3u8_url_list[i]
# print(m3u8_url_episode)
m3u8_name = re.findall("playarr_.{1,3}\[\d\]=.https://.+/.+/\D{1,20}\d{1,3}\.m3u8.+,\d", m3u8_url_episode)
# print(m3u8_name)
if m3u8_name:
comp_num=m3u8_name[0][-1]
#print(comp_num)
if str(episode_num)==comp_num:
m3u8 = re.findall(f"https://.+\.m3u8", m3u8_url_episode)
m3u8_name=re.findall(".+/(.+)\.m3u8",m3u8[0])
#print(m3u8,m3u8_name)
return m3u8[0], m3u8_name[0]
def get_m3u8_url():
"""
根据自己的需要筛选的程序
:return: 返回利用true_video_web函数返回m3u8文件所在的url以及m3u8文件名称
"""
search_url = "http://159.75.0.62:11234/ssszz.php?top=10&q={}".format(input("请输入你要搜索的内容:"))
urls, names = parse_search_url(search_url)
print(f"一共找到以下{len(names)}项内容:")
for name, num in zip(names, range(1, len(names) + 1)):
print(f"第{num}项:", "\t", f"{name}")
try:
choice = int(input("请选择你要找的影视片(输入数字即可):")) - 1
except:
print("输入有误!")
return None
movies_url = "http://yhdm81.com/" + urls[choice]
print("注意:出现正片,备用或空白,直接选择1即可")
print(episodes(movies_url))
episode_num = input("请选择你要的集数(输入数字即可):")
try:
episode_list=episode_num.split("-")
episode_list=[i for i in range(1,int(episode_list[1])+1)]
#print(episode_list)
except:
try:
episode_list=episode_num.split(",")
except:
try:
episode_list=list(episode_num)
except:
return None
video_num = re.findall("/(acg|zongyi|tv|mov)/(\d.+)/", urls[choice])
videoname=names[choice]
return video_num[0][1],videoname,episode_list
def download_m3u8(url, name):
"""
下载要解析的m3u8文件
:param url: 我们获得到的m3u8文件的url
:param name: 名字就是m3u8文件的文件名
:return: 下载m3u8文件
"""
request.urlretrieve(url, fr'D:\PythonProject\anime\{name}.m3u8')
def download_ts(name):
"""
以字节流形式下载ts文件
:param name: 我们下载过来的m3u8文件
:return: 返回尚未处理的ts文件
"""
web_list = []
with open(fr"D:\PythonProject\anime\{name}.m3u8", "r") as files:
lines_list = files.readlines()
for https in lines_list:
web = re.search("https://.+", https)
if web:
web_list.append(web.group())
files.close()
headers = {
"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.5060.134 Safari/537.36 Edg/103.0.1264.71"
}
remove_or_set(r"D:\PythonProject\anime\outlib")
remove_or_set(r"D:\PythonProject\anime\inlib")
print("正在下载ts文件......")
print("ts视频加载进度:")
length=len(web_list)
start = time.perf_counter()
for url, num in zip(web_list, range(1, length + 1)):
a = "▋" * int(num/length*35)
c = (num / length) * 100
dur = time.perf_counter() - start
print("\r{:^3.0f}% | 用时: {:.2f}s {}".format(c,dur,a), end="")
resp = requests.get(url, headers=headers)
with open(fr"D:\PythonProject\anime\inlib\{num}.ts", "wb") as codes:
codes.write(resp.content)
codes.close()
return len(web_list)
def change(num):
"""
由于这个m3u8文件比较特殊,里面头部信息为png,我们要将它修改成ts
:param num: 这个就是总共的ts数
:return: 返回修改过后的ts文件
"""
with open(r"D:\PythonProject\anime\filelist.txt", "a+") as file:
file.write("\n")
time.sleep(1)
print("\n\n正在转化ts文件......")
print("ts视频转化进度:")
start = time.perf_counter()
for i in range(1, num + 1): # num+1
a = "▋" * int(i/num*30)
c = (i / num) * 100
dur = time.perf_counter() - start
print("\r{:^3.0f}% | 用时: {:.2f}s {}".format(c, dur, a), end="")
file.write(f"file 'D:\\PythonProject\\anime\\outlib\\{i}.ts'\n")
with open(f"D:\\PythonProject\\anime\\inlib\\{str(i)}.ts", "rb") as infile:
out_ts = f"D:\\PythonProject\\anime\\outlib\\{str(i)}.ts"
outfile = open(out_ts, "wb")
data = infile.read()
outfile.write(data)
outfile.seek(0x00)
outfile.write(b'\xff\xff\xff\xff')
outfile.flush()
outfile.close()
infile.close()
file.close()
def together(directory,name):
"""
合并outlib列表中的所有ts文件
:param name: 爬取视频的名字
:return: 返回一个MP4文件
"""
ff = FFmpeg(
inputs={
r"D:\PythonProject\anime\filelist.txt": "-f concat -safe 0"
},
outputs={
fr"C:\Users\14040\Desktop\{directory}\{name}.mp4": "-c copy"
}
)
# print(ff.cmd)
ff.run()
def remove_or_set(filepath):
"""
删除多余的文件防止下一次爬取的时候合并出问题
"""
if not os.path.exists(filepath):
os.mkdir(filepath)
else:
shutil.rmtree(filepath)
os.mkdir(filepath)
def main():
"""
这里的文件位置根据自己不同需要进行修改
:return:
"""
video_num,videoname,episode_list = get_m3u8_url()
remove_or_set(fr"C:\Users\14040\Desktop\{videoname}")
for num in episode_list:
try:
episode_num=int(num)
except:
episode_num=num
url,name=true_video_web(video_num,episode_num)
print(f"\n\n\n\n▋▋▋▋▋▋▋▋▋开始下载《{videoname}-第{num}集》▋▋▋▋▋▋▋▋▋")
#print(name)
download_m3u8(url, name)
change(download_ts(name))
together(f"{videoname}",f"{videoname}-第{num}集")
remove_or_set(r"D:\PythonProject\anime\outlib")
remove_or_set(r"D:\PythonProject\anime\inlib")
os.remove(fr"D:\PythonProject\anime\{name}.m3u8")
os.remove(fr"D:\PythonProject\anime\filelist.txt")
print(f"▋▋▋▋▋▋▋▋▋{videoname}第{num}集下载完成▋▋▋▋▋▋▋▋▋")
if __name__ == '__main__':
main()
运行结果
注意
这里出现的有关绝对路径都需要自行进行修改
还有一点,本人能力有限(批量处理还不会多线程操作)
尚不知道以下情况该怎么解决,这里爬取后缀不是index.m3u8文件是没有影响的