注:本人小白一枚,爬虫也是刚接触,写的不好请多指点

        这里给出后半部分,转换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文件时:

python爬取m3u8文件 python爬取m3u8地址_python

就会发现每个url后面的都是.png后缀,和平常的.ts不太一样 

直接在网页中打开的图像是这样的

python爬取m3u8文件 python爬取m3u8地址_html_02

         我们将这个png下载,在Hexeditor中打开会发现前面头部信息是png(有的是有的不是),所以说有的并不能通过直接修改后缀名来将这个文件转化为ts类型,为了保险起见,我们将这些数据的头部都进行修改,首先我们要将这个网站上的这个文件以字节流的形式下载下来

下载ts文件

将这个网站上的文件以字节流的形式下载下来

python爬取m3u8文件 python爬取m3u8地址_html_03

其实也就是为了去获取这个网站的这个文件,然后再将它转换成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)

下载之后如图:

python爬取m3u8文件 python爬取m3u8地址_python爬取m3u8文件_04

处理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文件内容如下:

python爬取m3u8文件 python爬取m3u8地址_python_05

合并工作

这里用到了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()

运行结果

python爬取m3u8文件 python爬取m3u8地址_爬虫_06

注意

这里出现的有关绝对路径都需要自行进行修改

还有一点,本人能力有限(批量处理还不会多线程操作)

尚不知道以下情况该怎么解决,这里爬取后缀不是index.m3u8文件是没有影响的

python爬取m3u8文件 python爬取m3u8地址_python_07