爬虫日记01:爬取m3u8格式视频和解密

Posted 新手_six

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了爬虫日记01:爬取m3u8格式视频和解密相关的知识,希望对你有一定的参考价值。

开发环境

·python3.10

·pycharm

相关模块的应用

import requests
from bs4 import BeautifulSoup
import asyncio
import aiohttp
import aiofiles

确定目标需求

对某盗版网站进行浴血黑帮视频的爬取

嘿嘿

进行网页数据分析,找寻我们所需的数据来源

 由于该视频存在2个m3u8

 所以我们得先得到第一层的m3u8文件下载地址,再从第一层m3u8文件中得到第二层m3u8文件的下载地址。当然,你也可以直接在第二个m3u8里获取它的url进行视频下载。

整体思路

1.拿到主页面的页面源代码,找到iframe

2.从iframe的页面源代码中拿到m3u8文件

3.下载第一层m3u8文件,----》下载第二层m3u8文件(得到视频的全部ts路径)

4.下载视频

5.下载秘钥进行解密操作

6.合并所有ts文件为一个mp4文件(利用各种办法:工具或者代码都行)

代码的实现

1.对浴血黑帮视频的下载

import requests
from bs4 import BeautifulSoup
import asyncio
import aiohttp
import aiofiles

# 获取iframe_src
def get_iframe_src(url):
    res = requests.get(url) # 获取页面源代码
    main_page = BeautifulSoup(res.text,"html.parser") # 解析页面源码
    src = main_page.find("iframe").get("src") # 得到我们所需的iframe的src
    return src

# 得到第一层的m3u8下载地址
def get_first_m3u8(url):
    # 取出第一层m3u8的真正下载地址
    # 由于所得到的的url存在其他的字符,将它们进行删除
    src_url = url.strip("/js/player/?url=") 
    src_url_2 = src_url.strip("&id=16721&num=1&count=6&vt=1")
    # print(src_url_2)
    return src_url_2

# 下载第m3u8文件
def download_m3u8_file(url, name):
    res = requests.get(url) # 请求m3u8的url
    with open(name,mode="wb") as f:
        f.write(res.content)


async def download_ts(ts_url,name,session):
    async with session.get(ts_url) as res_ts:
        async with aiofiles.open(f"./m3u8/浴血黑帮第五季/name",mode="wb") as f:
            await f.write(await res_ts.content.readany()) # 把下载到的内容写入文件中
    print(f"name下载完毕!")

async def aio_download():
    tasks = []
    async with aiohttp.ClientSession() as session:  # 因为有很多个视频片段所以提前准备好session
        async with aiofiles.open("浴血黑帮第五季第一集_second_m3u8.txt",mode="r",encoding="utf-8") as f:
            async for line in f:
                if line.startswith("#"): #从第二层m3u8文件里读取文件内容,遇到#开始的内容就不要
                    continue
                line = line.strip() # 去除空白换行符
                name = line.rsplit("/",1)[1] # 取ts文件最后一个/后面的内容作为视频名字
                task = asyncio.create_task(download_ts(line,name,session)) # 创建协程任务
                tasks.append(task)


            await asyncio.wait(tasks) #等待任务结束



def main(url):
    # 1.拿到主页面的页面源代码,找到iframe对应的url
    iframe_src = get_iframe_src(url)
    # 2.拿到第一层的m3u8文件的下载地址
    first_m3u8_url = get_first_m3u8(iframe_src)
    # print(first_m3u8_url)

    # 3.1下载第一层的m3u8文件
    download_m3u8_file(first_m3u8_url,"浴血黑帮第五季第一集_first_m3u8.txt")

    # 3.2下载第二层的m3u8文件
    with open("浴血黑帮第五季第一集_first_m3u8.txt",mode="r",encoding="utf-8") as f2:
        for line in f2:
            if line.startswith("#"): # 读取本地文件,遇到#开始的内容不要
                continue
            else:
                line = line.strip() # 去掉空白或换行符
                # 拼接第二层m3u8的下载路径
                second_m3u8_url = first_m3u8_url.split("/20201008")[0] +line
                download_m3u8_file(second_m3u8_url,"浴血黑帮第五季第一集_second_m3u8.txt")
                # print(second_m3u8_url)
    # 4.下载视频
    # 异步协程(利用异步协程下载速度会很快)
    # 这俩个运行方法都会报警告,如果有大佬知道怎么解决希望能告知
    # asyncio.run(aio_download())
    loop = asyncio.get_event_loop()
    loop.run_until_complete(aio_download())

if __name__ == '__main__':
    url = "http://www.wbdy.tv/play/16721_1_1.html"
    main(url)
    print("下载完成!")

 但这些视频不可播放,在第二层m3u8的文件里得知这些视频是被AES加密进行加密了

 2.对视频进行解密

import asyncio
import aiofiles
import requests
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad

# 得到秘钥
def get_key(url):
    res  = requests.get(url)
    return res.text


async def dec_ts(name, key):
    # 对key进行编码,不然会报错:Object type <class 'str'> cannot be passed to C code
    aes = AES.new(key=key.encode("utf-8"),IV=b"0102030405060708",mode=AES.MODE_CBC)

    async with aiofiles.open(f"m3u8/浴血黑帮第五季/name",mode="rb") as f1,\\
        aiofiles.open(f"m3u8/浴血黑帮第五季/解密/name",mode="wb") as f2:
        bs = await f1.read() # 从源文件获取
        bs = pad(bs,16) # 对加密数据padding到16byte的整数倍,不然会报错:Data must be padded to 16 byte boundary in CBC mode
        await f2.write(aes.decrypt(bs)) # 把解密好的内容写入文件
    print(f"name处理完毕!")


async def aio_dec(key):
    tasks = []
    # 解密
    async with aiofiles.open("浴血黑帮第五季第一集_second_m3u8.txt",mode="r",encoding="utf-8") as f:
        async for line in f:
            if line.startswith("#"):
                continue
            line = line.strip()

            name = line.rsplit("/",1)[1]
            # 创建异步任务
            task = asyncio.create_task(dec_ts(name,key))
            tasks.append(task)
        await asyncio.wait(tasks) # 等待任务结束



if __name__ == '__main__':
    # 5.1 拿到秘钥
    # 从第二层m3u8文件里得到AES加密的url地址
    key_url = "https://ts5.hhmm0.com:9999/20201008/hLgxAmB8/1000kb/hls/key.key"
    key = get_key(key_url)
    # 5.2 解密
    loop = asyncio.get_event_loop()
    loop.run_until_complete(aio_dec(key))

因为视频是被AES进行加密,所以需要导入相关的库

 如果不对加密数据填充到16byte的整数倍,会报错:

Data must be padded to 16 byte boundary in CBC mode

最后就是把视频合成了 ,你可以利用各种工具,也可以视频python的FFmpeg库,反正有手就行了。

 新手一枚,还有很多不知,可以多交流交流!

Node 爬虫,批量爬取头条视频并保存

项目地址:GitHub

目标网站:西瓜视频
项目功能:下载头条号【维辰财经】下的最新20个视频
姊妹项目:批量下载美女图集

简介

一般批量爬取视频或者图片的套路是,使用爬虫获得文件链接集合,然后通过 writeFile 等方法逐个保存文件。然而,头条的视频,在需要爬取的 html 文件(服务端渲染输出)中,无法捕捉视频链接。视频链接是页面在客户端渲染时,通过某些 js 文件内的算法或者解密方法,根据视频的已知 key 或者 hash 值,动态计算出来并添加到 video 标签的。这也是网站的一种反爬措施。

我们在浏览这些页面时,通过审核元素,可以看到计算后的文件地址。然而在批量下载时,逐个手动的获取视频链接显然不可取。开心的是,puppeteer 提供了模拟访问 Chrome 的功能,使我们可以爬取经过浏览器渲染出来的最终页面。

今日头条里有很多有意思的头条号玩家,他们发布了很多视频在里面。如果大家有批量下载某个头条号视频的需求,这个爬虫就派上用场了。当然,其他视频站也都大同小异,更改下部分代码设置就可以使用啦。

项目启动

命令

npm i
npm start
// 安装 puppeteer 的过程稍慢,耐心等待。

单个文件下载命令

npm run single
// 在文件 single.js 中设置视频名称和 src 即可。

配置文件

// 配置相关
module.exports =  {
  originPath: ‘https://www.ixigua.com‘, // 页面请求地址
  savePath: ‘D:/videoZZ‘ // 存放路径
}
// 单个视频下载设置
const folderName = ‘D:/videoLOL‘
const fileName = ‘S8预选赛TOP5:Haro李青无解操作支配战局「LOL七周年」‘
const videoSrc = ‘http://v11-tt.ixigua.com/e2b7cbd320031f6c19890001503a6ca0/5b9fd7bb/video/m/2203ce04dd18e0e426381abfe64ea44f19b115bbe0a000027c1f6e94a77/‘

// 初始化方法
const start = async () => {
  method.mkdirSaveFolder(folderName)
  let video = {
    src: videoSrc,
    title: fileName
  }
  downloadVideo(video)
}

技术点

puppeteer

官方API

puppeteer 提供一个高级 API 来控制 Chrome 或者 Chromium。

puppeteer 主要作用:

  • 利用网页生成 PDF、图片
  • 爬取SPA应用,并生成预渲染内容(即“SSR” 服务端渲染)
  • 可以从网站抓取内容
  • 自动化表单提交、UI测试、键盘输入等

使用到的 API:

  • puppeteer.launch() 启动浏览器实例
  • browser.newPage() 创建一个新页面
  • page.goto() 进入指定网页
  • page.screenshot() 截图
  • page.waitFor() 页面等待,可以是时间、某个元素、某个函数
  • page.$eval() 获取一个指定元素,相当于 document.querySelector
  • page.$$eval() 获取某类元素,相当于 document.querySelectorAll
  • page.$(‘#id .className‘) 获取文档中的某个元素,操作类似jQuery

代码示例

const puppeteer = require(‘puppeteer‘);
 
(async () => {
  const browser = await puppeteer.launch();
  const page = await browser.newPage();
  await page.goto(‘https://example.com‘);
  await page.screenshot({path: ‘example.png‘});
 
  await browser.close();
})();

视频文件下载方法

  • 下载视频主方法
const downloadVideo = async video => {
  // 判断视频文件是否已经下载
  if (!fs.existsSync(`${config.savePath}/${video.title}.mp4`)) {
    await getVideoData(video.src, ‘binary‘).then(fileData => {
      console.log(‘下载视频中:‘, video.title)
      savefileToPath(video.title, fileData).then(res =>
        console.log(`${res}: ${video.title}`)
      )
    })
  } else {
    console.log(`视频文件已存在:${video.title}`)
  }
}
  • 获取视频数据
getVideoData (url, encoding) {
  return new Promise((resolve, reject) => {
    let req = http.get(url, function (res) {
      let result = ‘‘
      encoding && res.setEncoding(encoding)
      res.on(‘data‘, function (d) {
        result += d
      })
      res.on(‘end‘, function () {
        resolve(result)
      })
      res.on(‘error‘, function (e) {
        reject(e)
      })
    })
    req.end()
  })
}
  • 将视频数据保存到本地
savefileToPath (fileName, fileData) {
  let fileFullName = `${config.savePath}/${fileName}.mp4`
  return new Promise((resolve, reject) => {
    fs.writeFile(fileFullName, fileData, ‘binary‘, function (err) {
      if (err) {
        console.log(‘savefileToPath error:‘, err)
      }
      resolve(‘已下载‘)
    })
  })
}

爬取结果截图

技术分享图片

说明

此爬虫仅用于个人学习,如果侵权,即刻删除!

 

项目地址:GitHub

 



以上是关于爬虫日记01:爬取m3u8格式视频和解密的主要内容,如果未能解决你的问题,请参考以下文章

python解决m3u8直播视频的爬取

怎么下载m3u8格式视频?Python爬取A站m3u8格式视频案例讲解

python-将爬取到的m3u8合并为mp4

python爬取视频网站m3u8视频,下载.ts后缀文件,合并成整视频

什么是m3u8格式? Python合并ts文件至mp4格式及解密教程

python爬虫日记01