【异步爬虫:利用异步协程抓取一部电影】

利用异步协程抓取一部电影

我们把目光转向wbdy. 目前该案例还是可以用的.

我们想要抓取网上的视频资源就必须要了解我们的视频网站是如何工作的. 这里我用91看剧来做举例. 其他网站的原理是一样的.

1.视频网站是如何工作的

假设, 你现在想要做一个视频网站. 也有很多的UP猪帮你上传视频. OK, 作为服务器作者的你. 只需要把视频保存起来. 然后给出一个视频的链接即可. 然后在你的HTML代码中通过video标签引入即可.

在这里插入图片描述

<video src="1_1_爬虫概述.mp4"></video>

就可以了. 但是, 如果你这么做. 你的用户和老板一定会把你骂的狗血临头. 为什么呢?

假设你的视频是10个G的高清无码大资源. 那么此时, 你的用户和你老板将面临如下困境

  1. 用户: 这个视频怎么加载的这么慢. 点击快进也快进不了. 太慢了. 塔喵的烦死了.
  2. 老板: 怎么这个月的流量费又这么高啊. 要死的拉好不~

为什么会这样? 聪明的我告诉你答案. 你的视频那么大. 每次用户打开的时候. 可能只是差了最后几分钟没看呢. 那此时它必须把整个视频都传输完毕. 才能看到他想看的那里. 等待时间肯定超长的好不. 而每次都要把10G的文件进行网络传输. 流量费~你懂的. 三大运营商最喜欢的就是你这种朴实无华的送钱行为.

OK~ 不扯了. 但凡有点儿经验的程序员肯定会想办法把用户上传好的视频进行转码(不同清晰度)做切片(ts)处理. 这样既方便用户进行大跨度的调整进度条(最小延迟). 也能为公司节省大量的流量费.

既然要把视频切成非常多个小碎片. 那就需要有个文件来记录这些小碎片的路径. 该文件一般为M3U文件. M3U文件中的内容经过UTF-8的编码后, 就是M3U8文件. 今天, 我们看到的各大视频网站平台使用的几乎都是M3U8文件.

如何解读M3U8文件.

在这里插入图片描述

基本知道这些就够了.

2. 网吧电影网站分析

​ 接下来, 我们以网吧电影这个网站中<寄生虫>http://www.wbdy.tv/play/30288_1_1.html作为目标, 先分析一下该网站的视频是如何播放的.

2.1 找到M3U8文件

在这里插入图片描述

从图上我们能发现播放视频的video标签是被嵌套在一个叫iframe标签内部的. 而iframe是当前页面中被嵌入的一个小窗口. 嵌套规则如下:

在这里插入图片描述

iframe实际上是HTML语言中的一个标签, 它可以将另一个网页的内容嵌入进来. 也就是说. iframe里面其实是另一个html页面.

OK, 接下来, 我们去页面源代码中看一看(一定要去页面源代码里看. 不要再F12的elements里看!!!)

在这里插入图片描述

我们发现, 当前网页的页面源代码中是有iframe标签的. 里面的src对应的是另一个url网址. 我们可以点击一下这个网址.

在这里插入图片描述

从图上可以看到, 我们之前推到的结构是没问题的. 我们想要看到的视频播放确实放在了这个iframe对应的url里面.

然后我们再去看看这个iframe里的页面源代码. 注意, 这个页面被视频播放器给填满了. 所以必须去F12的source里看.

在这里插入图片描述

至此, 我们终于找到了该电影所对应的M3U8文件地址了.

先做一个小总结, 针对网吧电影这个网站而言, 我们想要得到M3U8文件, 过程是:

  1. 访问视频主页的页面源代码. 提取到iframe中的src属性. 暂时记做iframe_url
  2. 提取iframe_url的页面源代码. 提取到M3U8文件的地址

注意, 有些网站不是这样的结构, 所以该逻辑未必适用于所有视频网站. 不过一般的网站都大同小异, 只要盯着M3U8即可

2.2 M3U8文件解析

我们把刚才的M3U8文件的url地址https://video.buycar5.cn/20200824/1EcQQ5Ww/index.m3u8拿出来. 丢到浏览器里. 会自动开始下载m3u8文件

用记事本打开它,

在这里插入图片描述

里面前两行内容不用管. 第三行里存放的是另一个M3U8文件的地址. 依然是一个url. 我们把这个url和第一次拿到的那个url整合在一起. 继续丢浏览器下载.

在这里插入图片描述

简单解读一下. 前面的内容, 只有EXT-X-KEY有用. 这里面的METHOD=AES-128表示该视频是经过加密的. 需要进行解密. 解密的秘钥存放在URI对应的网址内.

后面就简单多了. 每一个不以#开头的, 都是视频切片ts文件.

后期解析起来的整体思路也就出来了.

  1. 下载每一个ts文件

  2. 获取到秘钥key

  3. 使用AES对ts文件进行解密.

  4. 将众多ts文件合并为MP4文件.

3. 代码

# <video src="http://www.baidu.com/苍井空.mp4"></video>
# 不论是用户体验. 还是公司流量费的压力. 上面方案是不行的
# 一般的视频网站都会做以下操作:
# 1. 把视频进行备份.
# 2. 对视频进行转码
# 3. 对视频进行切片. 3-5s 之间, 视频的顺序又很难保障.
# 4. 把切片顺序保存在一个文件中(M3U)文件.一般M3U文件编码成utf-8进行存储. m3u8文件, M3U8里面有每个切片的名字和正确的播放顺序
# 5. 用户进行播放是一个视频的时候. 通常先加载这个M3U8文件.
# 6. 从M3U8中读取到ts文件的名称和地址. 然后按照M3U8文件的顺序进行播放# 如果有了M3U8  这个视频就可以进行下载了(盗版视频)"""
过程:上半场:1. 访问主页面http://www.wbdy.tv/play/30288_1_1.html得到iframe的地址2. 从iframe的地址中. 获取到第一层M3U8的地址3. 下载第一层M3U8并解析出第二层M3U8的地址, 下载第二层M3U8文件4. 读取第二层M3U8的文件. 下载每一个TS文件下半场:1. 拿到秘钥2. 用这个秘钥去解密所有ts文件3. 根据M3U8的顺序把ts文件合并起来.
"""
import requests
from lxml import etree
from urllib.parse import urljoin
import re
import asyncio
import aiohttp
import aiofiles
from Crypto.Cipher import AES  # 需要安装, PCrypto. pip install pycryptodome
import osdef get_iframe_src(url):resp = requests.get(url)resp.encoding = 'utf-8'page_source = resp.textresp.close()tree = etree.HTML(page_source)iframe_url = tree.xpath("//iframe/@src")if iframe_url:iframe_url = iframe_url[0]else:iframe_url = ""return iframe_urldef get_first_m3u8_url(iframe_url):resp = requests.get(iframe_url)# 尝试打印有没有乱码  如果没有. 就这么招resp.encoding='utf-8'page_source = resp.textresp.close()# 如果你想要从一段js里面拿到东西. 最好的方案就是REobj = re.compile(r'url: "(?P<m3u8_url>.*?)",', re.S)result = obj.search(page_source)m3u8_url = result.group("m3u8_url")return m3u8_urldef download_m3u8_file(m3u8_url):resp1 = requests.get(m3u8_url)with open("第一层M3U8.txt", mode="wb") as f:f.write(resp1.content)resp1.close()# 读取第一层M3U8的文件. 下载第二层M3U8的文件with open("第一层M3U8.txt", mode="r", encoding="utf-8") as f:for line in f:if line.startswith("#"):continueelse:# 一定要处理line = line.strip()  # 去掉无关的\n和空白# /20200824/1EcQQ5Ww/1000kb/hls/index.m3u8second_m3u8_url = urljoin(m3u8_url, line)# 下载第二层M3U8文件resp2 = requests.get(second_m3u8_url)with open("第二层m3u8.txt", mode="wb") as f:f.write(resp2.content)print("第二次M3U8地址下载成功!!")async def download_one(ts_url, session, sem):async with sem:  # 这里也是异步for i in range(5):  # 单个文件重新下载try:  # 自省.# 发送请求出去async with session.get(ts_url) as resp:content = await resp.content.read()# 存储到文件中file_name = ts_url.split("/")[-1]async with aiofiles.open(f"video_1/{file_name}", mode="wb") as f:await f.write(content)print(ts_url, "下载成功!")breakexcept Exception as e:print(e)print(ts_url, "下载失败")async def download_all_ts():# windows的同学. 注意. 如果, 你的电脑运行的时候. 会产生一些奇怪的bug(*访问量过高.....)# 需要添加一个叫信号量的东西. 来控制并发量.sem = asyncio.Semaphore(200)  # 我的测试中. 网吧电影中有些电影必须要控制在3或5以下, 限制访问频率不是这个错误(Server disconnected)tasks = []# 1.读取M3U8文件. 拿到每一个ts的下载地址async with aiohttp.ClientSession() as session:  # 用1个session搞定所有请求with open("第二层m3u8.txt", mode="r", encoding='utf-8') as f:for line in f:if line.startswith("#"):continueline = line.strip()  # 非常重要# https://ts1.yuyuangewh.com:9999/20200824/1EcQQ5Ww/1000kb/hls/o996I0bz.tst = asyncio.create_task(download_one(line, session, sem))tasks.append(t)await asyncio.wait(tasks)def get_key():obj = re.compile(r'#EXT-X-KEY:METHOD=AES-128,URI="(?P<key_url>.*?)"', re.S)with open("第二层m3u8.txt", mode='r', encoding="utf-8") as f:for line in f:result = obj.search(line)if result:   # 如果能search到东西才可以提取.key_url = result.group('key_url')resp = requests.get(key_url)key_content = resp.content  # 这个地方直接拿出字节就可以了. 为了和后面好对接resp.close()return key_contentasync def desc_one_ts(file_name, key):# 解密的过程# 加密或者解密器aes = AES.new(key=key, mode=AES.MODE_CBC, IV=b"0000000000000000")async with aiofiles.open(f"./video_2/{file_name}", mode='rb') as f1,\aiofiles.open(f"./video_3/{file_name}", mode='wb') as f2:# 读取ts文件的内容content = await f1.read()desc_content = aes.decrypt(content)  # 解密await f2.write(desc_content)print(file_name, "解密成功!")async def desc_all_ts(key):tasks = []# 读取每一个ts文件的名字. 然后. 单独的解密该文件即可with open("第二层m3u8.txt", mode="r", encoding='utf-8') as f:for line in f:if line.startswith("#"):continueline = line.strip()file_name = line.split("/")[-1]t = asyncio.create_task(desc_one_ts(file_name, key))  # 去解密tasks.append(t)await asyncio.wait(tasks)def merge():print("记载m3u8......")file_list = []with open("第二层m3u8.txt", mode='r', encoding='utf-8') as f:for line in f:if line.startswith("#"):continueline = line.strip()file_name = line.split("/")[-1]  # 获取到文件的名称file_list.append(file_name)print("记载m3u8, 成功......")print(file_list)# 文件的合并;# mac, linux电脑   cat  1.ts 2.ts 3.ts > xxx.ts# windows  copy /b 1.ts+2.ts+3.ts xxx.ts# python中想要执行命令行的命令. 需要用到os模块中的system(命令)或者popen(命令)# os.system("dir")  # windows那头99.99% 出乱码# r = os.popen("dir")  # windows的兄弟用这个.# print(r.read()) # 这个不乱码# 如果一次性全部合并. 那么命令行会提示; 命令太长# 分段进行合并# 如果我在当前文件夹下进行合并的话.# 先切换目录到video_3里面去. 然后进行合并. 合并后. 再切换出来# 切换工作目录os.chdir("./video_3")temp = []n = 1  # 合并的次数for i in range(len(file_list)):file_name = file_list[i]temp.append(file_name)  # [1.ts, 2.ts, 3.ts]# if i % 50 ==0 and i != 0:if len(temp) == 50:# 合并一批ts文件cmd = f"cat {' '.join(temp)} > {n}.ts"r = os.popen(cmd)print(r.read())# 归零temp = []n += 1# 如果最后还剩下xxx个, 把剩余的再次合并一次# 合并一批ts文件cmd = f"cat {' '.join(temp)} > {n}.ts"r = os.popen(cmd)print(r.read())n += 1  # 这里为什么n+=1# 第二次大合并second_temp = []for i in range(1, n):second_temp.append(f"{i}.ts")cmd = f"cat {' '.join(second_temp)} > mm.mp4"r = os.popen(cmd)print(r.read())os.chdir("../")  # 结束后记着切换回来def main():# url = "http://www.wbdy.tv/play/30288_1_1.html"# iframe_url = get_iframe_src(url)# # print(iframe_url)# # 完善iframe的src的地址# iframe_url = urljoin(url, iframe_url)# first_m3u8_url = get_first_m3u8_url(iframe_url)# download_m3u8_file(first_m3u8_url)# 下载所有的ts文件(协程)# asyncio.run(download_all_ts())# # 获取秘钥# key = get_key()## # 解密# asyncio.run(desc_all_ts(key))# 合并ts文件(纯逻辑)merge()if __name__ == '__main__':main()

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/47316.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【BUG】已解决:java.lang.IllegalStateException: Duplicate key

已解决&#xff1a;java.lang.IllegalStateException: Duplicate key 欢迎来到英杰社区https://bbs.csdn.net/topics/617804998 欢迎来到我的主页&#xff0c;我是博主英杰&#xff0c;211科班出身&#xff0c;就职于医疗科技公司&#xff0c;热衷分享知识&#xff0c;武汉城市…

【数学建模】——多领域资源优化中的创新应用-六大经典问题解答

目录 题目1&#xff1a;截取条材 题目 1.1问题描述 1.2 数学模型 1.3 求解 1.4 解答 题目2&#xff1a;商店进货销售计划 题目 2.1 问题描述 2.2 数学模型 2.3 求解 2.4 解答 题目3&#xff1a;货船装载问题 题目 3.1问题重述 3.2 数学模型 3.3 求解 3.4 解…

超详细信息收集篇

1 域名信息收集 1.1 域名是什么 域名&#xff08;英语&#xff1a;Domain Name&#xff09;&#xff0c;又称网域&#xff0c;是由一串用点分隔的名字组成的 Internet 上某一台 计算机 或计算机组的名称&#xff0c;用于在数据传输时对计算机的定位标识&#xff08;有时也指地…

数据结构——栈和队列(C语言实现)

写在前面&#xff1a; 栈和队列是两种重要的线性结构。其也属于线性表&#xff0c;只是操作受限&#xff0c;本节主要讨论的是栈和队列的定义、表示方法以及C语言实现。 一、栈和队列的定义与特点 栈&#xff1a;是限定仅在表尾进行插入和删除的线性表。对栈来说&#xff0c;表…

【经验分享】关于静态分析工具排查 Bug 的方法

文章目录 编译器的静态分析cppcheck安装 cppcheck运行 cppcheck 程序员的日常工作&#xff0c;不是摸鱼扯皮&#xff0c;就是在写 Bug。虽然这是一个梗&#xff0c;但也可以看出&#xff0c;程序员的日常一定绕不开 Bug。而花更少的时间修复软件中的 Bug&#xff0c;且不引入新…

lightgbm

lightGBM 1.sklearn 使用代码 【机器学习基础】XGBoost、LightGBM与CatBoost算法对比与调参 首先&#xff0c;XGBoost、LightGBM和CatBoost都是目前经典的SOTA&#xff08;state of the art&#xff09;Boosting算法&#xff0c;都可以归类到梯度提升决策树算法系列。三个模…

5. JavaSE ——【适合小白的数组练习题】

&#x1f4d6;开场白 亲爱的读者&#xff0c;大家好&#xff01;我是一名正在学习编程的高校生。在这个博客里&#xff0c;我将和大家一起探讨编程技巧、分享实用工具&#xff0c;并交流学习心得。希望通过我的博客&#xff0c;你能学到有用的知识&#xff0c;提高自己的技能&a…

【区块链 + 智慧政务】澳门:智慧城市建设之证书电子化项目 | FISCO BCOS应用案例

2019 年 2 月 27 日&#xff0c;澳门政府设立的澳门科学技术发展基金与微众银行达成合作&#xff0c;通过区块链、人工智能、大数据、 云计算等创新技术&#xff0c;共同推进澳门特区的智慧城市建设与未来型城市发展&#xff0c;提升粤港澳大湾区的科创能力。在澳 门智慧城市建…

【数学建模】高温作业专用服装设计(2018A)隐式差分推导

为方便计算&#xff0c;对区域进行离散化处理&#xff0c;采用隐式差分格式进行离散计算。隐式差分格式如图&#xff1a; 每层材料内部 对第 j j j层材料: 其中&#xff0c; λ j \lambda_j λj​表示第 j j j层的热扩散率&#xff0c; c j c_j cj​表示第 j j j层的比热容…

linux需要熟悉的命令理解记忆

(1)光标插入 (1)一般模式下: i 插入到光标前方 记忆方法:在一般模式下, 光标选中字符, 我们按下 i, 就会插入光标的前方, insert, 表示插队 (2)一般模式下: a 插入到光标后方 记忆方法: 在一般模式下, 光标选中字符,a表示append, 添加或者附加的意思 (3) 如果要在行首或者行…

css实现每个小盒子占32%,超出就换行

代码 <div class"visitors"><visitor class"item" v-for"(user,index) in userArr" :key"user.id" :user"user" :index"index"></visitor></div><style lang"scss" scoped&…

java乱码问题

文章目录 1.eclipse所有修改编码的地方2.io读取文件乱码问题1.读写统一2.转换字符编码&#xff1a; 3.http请求返回乱码 1.eclipse所有修改编码的地方 2.io读取文件乱码问题 1.读写统一 如果文件是以UTF-8编码保存的&#xff0c;那么在读取文件时也应使用UTF-8编码。 2.转换…

Apple Vision Pro 和其商业未来

机器人、人工智能相关领域 news/events &#xff08;专栏目录&#xff09; 本文目录 一、Vision Pro 生态系统二、Apple Vision Pro 的营销用例 随着苹果公司备受期待的进军可穿戴计算领域&#xff0c;新款 Apple Vision Pro 承载着巨大的期望。 苹果公司推出的 Vision Pro 售…

百分点科技签约潍坊市数据产业发展战略合作

近日&#xff0c;潍坊市数据产业发展战略合作签约仪式举行&#xff0c;潍坊市人民政府副市长张震生&#xff0c;潍坊市财政局党组书记、局长王金祥&#xff0c;潍坊市大数据局党组书记陈强出席大会并致辞。百分点科技受邀进行战略合作签约&#xff0c;共同见证潍坊市数据要素市…

生成式人工智能(AI)的未来

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

深度学习模型快速开发平台推荐

前言 本文面向深度学习初学者或者工程师&#xff0c;推荐几个常用的深度学习模型快速开发平台。可以帮助初学者快速跑通模型&#xff0c;帮助工程师快速对模型进行部署和应用。 huggingface 简介 不多介绍&#xff0c;全球最大的模型托管平台&#xff0c;该平台最大的特点是…

全网超详细Redis主从部署(附出现bug原因)

主从部署 整体架构图 需要再建两个CentOs7,过程重复单机部署 http://t.csdnimg.cn/zkpBE http://t.csdnimg.cn/lUU5gLinux环境下配置redis 查看自己ip地址命令 ifconfig 192.168.187.137 进入redis所在目录 cd /opt/software/redis cd redis-stable 进入配置文件 vim redi…

JavaWeb入门程序解析(Spring官方骨架、配置起步依赖、SpringBoot父工程、内嵌Tomcat)

3.3 入门程序解析 关于web开发的基础知识&#xff0c;我们可以告一段落了。下面呢&#xff0c;我们在基于今天的核心技术点SpringBoot快速入门案例进行分析。 3.3.1 Spring官方骨架 之前我们创建的SpringBoot入门案例&#xff0c;是基于Spring官方提供的骨架实现的。 Sprin…

勘测院如何实现可控便捷的图纸安全外发?

勘测院&#xff0c;也称为勘测设计研究院或勘测设计院&#xff0c;是进行与地质、地形和地貌有关的勘察测量的单位&#xff0c;为各类工程项目提供准确的地质数据和设计依据。 勘测院会产生各类包括图纸在内的文件&#xff0c;如&#xff1a; 1、项目相关文件&#xff1a;项目…

c++模板初阶----函数模板与类模板

目录 泛型编程 函数模板 函数模板的概念 函数模板的格式 函数模板的原理 函数模板的实例化 函数模板的匹配原则 类模板 类模板的定义格式 类模板的实例化 c的模板大致可以分为&#xff1a; 函数模板类模板 首先在我们引入模板之前&#xff0c;先进行介绍泛型编程 泛…