爬虫案例之 xxx 视频网站

爬虫案例之视频爬取与合成

案例网址:https://v6.dious.cc

下载视频的网址:https://www.99meijutt.com/play/97071-0-0.html

  • 用到的知识点
    • asynic,协程异步操作。
    • ffmpeg,合成视频指令
    • aiohttp,在协程里面发送异步请求

【一】分析

(1)分析网站

  • 打开网站首页

  • 打开开发者模式进行抓包

    • 思路:我们已经知道每个视频都是由一个个ts文件片段组成
      • 而这些ts片段的地址就存在m3u8文件中
      • 所以我们现在的主要目标放在m3u8文件中
  • 这里我们找到了第一个m3u8文件,查看其响应内容

  • 我们可以发现,其响应内容中存在类似链接的地址
    • 我们继续往下看到第二个m3u8文件,打开,查看其响应内容

  • 我们可以清楚的看到,其响应内容中存在的就是我们一个个ts文件的链接

(2)准备爬取工作

  • 本次案例使用的是asynic模块
    • 不为别的,就是单纯的下载批量文件图一块!嘎嘎快!

【二】分析完就动手!

(1)导入模块

# 请求头中的headers中的User-Agent模拟
from fake_useragent import UserAgent
# os 系统模块
import os
# 正则表达式模块
import re
# 涉及到加密动作,需要解密模块(本次涉及到的是AES加密)
from Crypto.Cipher import AES
# asynic 异步协程模块
import asyncio
# 协程中的请求模块
import aiohttp

(2)初识化需要用到的全局变量

# 初始化模块
    def __init__(self):
        # 初始化请求头 headers
        self.headers = {
            'User-Agent': UserAgent().random
        }
        # 初始化文件夹名字(存储ts文件)
        self.file_name = 'video'
        # 判断文件夹是否存在(做判断)
        if not os.path.exists(self.file_name):
            os.mkdir(self.file_name)

(3)获取到所有的ts文件的链接

# 这里是请求第一个m3u8文件操作
    async def get_page_url(self):
        # 这是找到的第一个m3u8的网址
        index1_url = 'https://v6.dious.cc/20220428/X2mBsQ9X/index.m3u8'
        # 对上述链接发起请求
        async with aiohttp.ClientSession() as session:
            async with session.get(url=index1_url, headers=self.headers) as response:
                # 拿到第一个m3u8文件里的内容,将下一个m3u8文件地址解析
                page_text = await response.text()  # 这里是文本文件,所以用response.text()
                # 从获取到的文件中解析获得第二个m3u8地址
                index2_url = 'https://v6.dious.cc' + page_text.split('\n')[-2]  # 做字符串拼接成完整的下一个链接
                # 对上述链接发起请求
                async with aiohttp.ClientSession() as session:
                    async with session.get(url=index2_url, headers=self.headers) as response:
                        # 拿到第二个m3u8文件,这里面存的就是所有ts文件的链接地址
                        page_text = await response.text()  # 这里是文本文件,所以用response.text()
                        # 将带有ts地址的m3u8文件写入到本地 - 为下一步的合并做准备
                        # 定义文件路径
                        file_path = f'{self.file_name}' + '\\' + 'index.m3u8'
                        # 打开文件夹
                        with open(file_path, 'w') as f:
                            # 循环获取取到的第二个m3u8文件中的每一个ts文件
                            for line in page_text.split('\n'):
                                # 如果文件中不存在以 .ts 文件为结尾的文件
                                if not line.endswith('.ts'):
                                    # 则直接写入
                                    # 目的是为了合并做准备,ffmpeg合并文件,其中m3u8文件必须带头部,否则会报错
                                    f.write(line + '\n')
                                # 如果文件中存在 .key 结尾的文件,将其头部补充完整
                                elif line.endswith('.key'):
                                    # 拼接成完整的url
                                    f.write('http://v6.dious.cc' + line + '\n')
                                # 如果存在 .ts 为结尾的文件
                                else:
                                    # 将字符串进行分隔,取最后一部分,因为文件名不能存在 / \ 等特殊字符
                                    line = line.split('/')[-1]
                                    f.write(line + '\n')

                        # 将所有的ts文件链接提取出来
                        tss_urls = re.findall(r'(.*.ts)', page_text)
        # 将所有ts文件链接提取出来返回,留待下一部分使用
        return tss_urls

(4)下载所有ts文件

# 这个部分是下载文件的部分
    async def download_file(self, i, sem):
        '''
        
        :param i: 每一个ts链接
        :param sem: 信号量
        :return: 打印每一个文件的下载情况
        '''
        # 设置并发量  --- 防止异步协程太快,造成timeout
        async with sem:
            # 将每一个ts链接拼接完整
            ts_url = 'https://v6.dious.cc' + i
            # 对每一个ts链接发起请求
            async with aiohttp.ClientSession() as session:
                async with session.get(url=ts_url, headers=self.headers) as response:
                    # 这里请求到的数据是被加密的ts文件数据(视频文件)
                    response_data = await response.read()  # 这里返回的是二进制文件,所以用response.read()
                    # 这里是将每个ts文件的后缀名字取出来,当做每一个文件的名字
                    ts_name = os.path.basename(ts_url)
                    # 通过观察m3u8文件,发现其存在AES加密,这是cbc加密模式所需要的key的链接(在m3u8文件的上方,可以看到有一个key.key的文件,其中存的就是key)
                    key_url = 'https://v6.dious.cc/20220428/X2mBsQ9X/1500kb/hls/key.key'
                    # 对链接发起请求
                    async with aiohttp.ClientSession() as session:
                        async with session.get(url=key_url, headers=self.headers) as response:
                            # 拿到key并存储为二进制数据
                            key = await response.read()  # 这里返回的是二进制文件,所以用response.read()
                            # 这是cbc模式所需要的iv偏移量(文件中没有声明,默认设置为16位的 0 )
                            iv = b'0000000000000000'  # 需要将格式转为二进制模式,才能传到aes参数里面
                            # 创建aes对象
                            aes = AES.new(key=key, mode=AES.MODE_CBC, iv=iv)
                            # 声明文件路径
                            file_path = f'{self.file_name}' + '\\' f'{ts_name}'
                            # 打开文件
                            with open(file_path, 'wb') as f:
                                # 如果需要解密ts视频文件
                                # data = aes.decrypt(response_data)
                                # f.write(data)
                                # 这里采取不解密ts文件
                                f.write(response_data)
                                print(f'{ts_name}已经下载完成')

(5)ffmpeg视频合并命令

# 这里的部分是用来合并ts片段
    async def merge_video(self, filename='video'):
        '''
        
        :param filename: 合成文件的文件名
        :return: 
        '''
        # ffmpeg -i ts文件夹下的m3u8格式文件 -c copy 合成后的视频名字
        # ffmpeg -i index.m3u8 -c copy apple.mp4
        # 切换到ts文件列表
        os.chdir(f'{self.file_name}')
        # 合成文件
        os.system(f'ffmpeg -protocol_whitelist "file,http,crypto,tcp" -i index.m3u8 -c copy {filename}.mp4')

(6)设置协程的主程序入口

async def main(self):
        # 声明任务列表
        global tasks
        # 创建任务列表
        tasks = []
        # 信号量:控制协程下载的并发量
        sem = asyncio.Semaphore(50)
        # 获取到所有ts链接
        tss_urls = await self.get_page_url()
        # 循环获取到每一个ts链接
        for i in tss_urls:
            # 创建任务,并传入参数,传给下一个函数调用下载
            task = asyncio.create_task(self.download_file(i, sem))
            # 将每一个任务添加到所有的任务中
            tasks.append(task)
        # 收集任务
        await asyncio.wait(tasks)
        # 待所有文件下载完成后进行ts文件合并
        await self.merge_video()

(7)设置文件的主程序入口

if __name__ == '__main__':
    # 实例化类对象
    s = Spider()
    # 调用类对象中的main方法
    # 执行协程启动run函数
    asyncio.run(s.main())
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值