利用协程爬取m3u8视频
利用协程爬取m3u8视频
在进行了爬虫的相关学习之后,自己尝试爬取了一些视频,但动辄ts文件就几百个,单线程伤不起那时间,一个一个等实在太慢了,想利用多线程,查看相关资料,又说python是假的多线程,而且爬取视频这操作也属于IO阻塞操作多的那种,感觉时间提升也不大,多线程和多进程还有协程,协程挺适合这种的,就毅然决然的使用协程了。注:各位大佬轻喷
一.查看网站并分析
1.找到各集数对应网站
首先理清爬取思路,对我这种刚入门的菜鸡来说,爬视频就是看它是不是mp4,或者是不是m3u8格式的,但如标题所言,我今天找的这个是m3u8格式的。直接查看网站源码,如图所示,我们想爬取所有集数首先需要找到这些集数的href
可以看到,我们如果想进这些集数的网站里面得到m3u8文件还是需要进行拼接操作的,这样才能得到进一步m3u8文件。
我们首先进入第一集里面看一看,瞅一瞅,熟悉的打开网站,熟悉的搜索video,打开一看,哇,
是不是很简单
我的上帝啊,瞧瞧那document上面一排是什么,哇,是iframe标签,看来还需要打开一个网站,这标签代表着视频是在内嵌的框架播放的,因为网页中嵌入的<Iframe></Iframe>
所包含的内容与整个页面是一个整体(上面这句专业的话那肯定不是我说的),大概就是相当于另一个网站吧。直接requests肯定得不到m3u8文件了。接下来找到那个内嵌网站
2.找到内嵌网站
接下里我们就直接进入网站源码里面进行查找吧,腚眼一看
这不就找到了内嵌网站了吗,只是斜杠在处理的时候需要换一下,这里注意,处理的时候需要转义一下,单斜杠替换的时候不会处理掉的。
3.进入内嵌网站拿到m3u8
进入视频网站,可以知道,整个屏幕都是视频,我们怎么查看源码呢,同样是按F12,在sources里面打开网址路径对应的文件夹下打开,可以找到m3u8文件
什么,你说你看到上面也有一个m3u8文件
我才不会跟你说我写这一篇博客的时候才看到,没事,大不了多一个步骤。刷新页面,可以看到这个m3u8的预览
很明显,我们需要通过这个m3u8文件拿到完整的m3u8文件,
当拿到完整的m3u8文件之后,我们也可以通过这个网站先看一下完整的m3u8文件预览
对味了,啊,那居然还有加密,可以看到是AES-128加密,没事,我们到时候把每一集的m3u8文件这个对应URI拿到并且得到key就行,从uri里面得到密码直接解密,AES的原理详情我也不是很清楚,但好像解密步骤就那回事,需要看看是AES的什么类型。这些可以拿到每一集的m3u8文件了,并且边解密边写入文件
4.将ts文件写入对应的文件夹
这里就利用到我们需要的协程了,直接贴完整代码吧,首先是总体思路
总体思路就是这样,其他的都是一些细枝末节了,接下来也就是看协程怎么运用了。至于ts的合并,就放在下边,也不多赘述了,主要就是利用顺序来合并,自动合并的话顺序有些问题。
import os
import globfor t in range(0,14):if t < 10:x = glob.glob0(f"./第0{t}集/*.ts")print(len(x))with open(f"第0{t}集总合并.ts", "ab") as g:for i in range(0, len(x) - 1):with open(f"./第0{t}集/{i}.ts", 'rb') as xx:g.write(xx.read())else:x = glob.glob(f"./第{t}集/*.ts")print(len(x))with open(f"第{t}集总合并.ts", "ab") as g:for i in range(0, len(x) - 1):with open(f"./第{t}集/{i}.ts", 'rb') as xx:g.write(xx.read())
二.完整代码
import os
import random
import aiofiles
from lxml import html
import requests
import asyncio
import aiohttp
import re
import datetime
from Crypto.Cipher import AES
import timeetree = html.etree
episode_list = []
html_list = []
headers = {'User-Agent': 'Mozilla / 5.0(WindowsNT10.0;Win64;x64) AppleWebKit / 537.36(KHTML, likeGecko) Chrome / ''91.0.4472.124Safari / 537.36 '}def get_key(name):with open(f"{name}的m3u8.txt", "r") as f:for i in f:# 使用正则来得到密码连接x = re.compile(r'URI="(.*?)"')t = x.findall(i)if len(t):# print(t[0])# 直接把密码原文得到,byte类型resp = requests.get(t[0], headers=headers).content# print(resp)return resp# 正式写入文件夹里面的ts文件
# 必须得用n来排序,要不然顺序乱了
async def download_ts_descrpt(name, line, n, key,sem):
#这里就是调用同时并发协程的数量async with sem:# aiohttp.ClientSession() as session:可以放在上一个函数打开文件上面,因为我的操作相当于每一次运用协程# 都创建一个连接池,不建议我这样的做法async with aiohttp.ClientSession() as session:async with session.get(url=line, headers=headers) as f:#需要利用aiofiles来打开文件,也是异步操作async with aiofiles.open(f"{name}/{n}.ts", 'wb') as x:t = await f.content.read() #使用协会时候需要使用read()# 利用密钥进行解密操作,CBC为猜的,偏移量数量设置跟解出来的密码个数相同aes = AES.new(key=key, IV=b'0000000000000000', mode=AES.MODE_CBC)await x.write(aes.decrypt(t))# 创建保存ts的文件夹,从m真实的m3u8文件里面读取ts
async def create_ts_encrpt(name, key):count = 0#使用count来计数,这样不会使协程写入的时候,ts文件顺序变乱tasks = []#判断是否有文件夹 ,如果有就跳过,没有就创建if not os.path.exists(f'{name}'):os.makedirs(f"{name}")#这里注意,协程里面再次创建协程,这就是进行异步操作,把每一步ts文件添加到协程任务列表#sem = asyncio.Semaphore(5)代表允许的并发协程数量为5,这里不建议设多,也不建议删掉,太多# 的话容易信号灯超时,也就是aiohttp.ClientSession创建的连接池里面请求,网站会响应不过来sem = asyncio.Semaphore(5)async with aiofiles.open(f"./{name}的m3u8.txt", mode='r') as f:async for line1 in f:if line1.startswith("#"):continueelse:line1 = line1.strip()# print(line1)# 添加另外一个协程事件进行操作,这就是写入加密得tstasks.append(download_ts_descrpt(name, line1, count, key,sem))count += 1await asyncio.wait(tasks)# 把第二层m3u8文件写下来
async def download_m3u82(url, name):async with aiohttp.ClientSession() as session:async with session.get(url, headers=headers) as f:f1 = await f.content.read()# 因为只能写入content所以不需要decodewith open(f"{name}的m3u8.txt", 'wb') as f2:f2.write(f1)# 这里得到每一个密钥然后传入下一个协程函数。这里调用了get_key函数得到每一个密钥key_encrpt = get_key(name)await create_ts_encrpt(name, key_encrpt)# 把第一层m3u8文件写下来,并把第二层m3u8的文件读出来
async def download_m3u8(url, name):async with aiohttp.ClientSession() as session:async with session.get(url, headers=headers) as f:f1 = await f.content.read()# 因为只能写入content所以不需要decodewith open(f"{name}.txt", 'wb') as f2:f2.write(f1)#这里就是读出来with open(f"{name}.txt", 'r', encoding='utf-8') as y:for line in y:if line.startswith("#"):continueelse:# 去掉空白和换行符line = line.strip()line = "https://vod2.buycar5.cn" + line# print(line)await download_m3u82(line, name) #又传入下一个函数# 在视频里面把每一个m3u8文件拿出来
async def get_html_m3u8(url, name):async with aiohttp.ClientSession() as session:async with session.get(url, headers=headers) as f:html2 = await f.content.read()html2 = html2.decode('utf-8')# print(html2)html2_m3u8 = re.findall(r'var main = "(.*?)";', html2)[0]html2_m3u8 = 'https://vod2.buycar5.cn' + html2_m3u8await download_m3u8(html2_m3u8, name)# 从每一集的链接里面把视频的链接提取出来
async def get_html_m3u8_pre(url, name):async with aiohttp.ClientSession() as session: #实例化session通过session来getasync with session.get(url, headers=headers) as f:html1 = await f.content.read()html1 = html1.decode('utf-8')# print(html1),第一集吗,格式跟其他集数有所不同可以理解if name == "第01集":html1_m3u8 = re.findall(r'"link_pre":"","url":"(.*?)","url_next"', html1)[0].replace("\\", "") #这里就是双斜杠,文中提到的需要转义的地方else:html1_m3u8 = re.findall(r'.html","url":"(.*?)","url_next"', html1)[0].replace("\\", "")# print(html1_m3u8)await get_html_m3u8(html1_m3u8, name) #使用await调用协程函数#得到每一集的初始链接
async def get_html(url):tasks = [] #因为有12集,从主页面的li下面的得到的每一集的链接需要进行拼接,并且将12集用协程来完成,创建协程列表resp = requests.get(url, headers=headers).content.decode('utf-8')resp = etree.HTML(resp)resp_list = resp.xpath('//ul[@class="stui-content__playlist clearfix"]')for res in resp_list:episode = res.xpath('./li/a/@href') #得到名字和网址,此时返回的都是列表,注意episode_name = res.xpath('./li/a/text()')# print(episode_name)for i in range(0, len(episode)):episode[i] = "https://www.autonicdq.com" + episode[i]tasks.append(get_html_m3u8_pre(episode[i], episode_name[i])) #逐项添加协程任务,此时协程任务的函数写在这里面await asyncio.wait(tasks) #执行协程函数# 得到主页面的的每一集的链接if __name__ == '__main__':t1 = datetime.datetime.now() #记录开始时间url_main = "https://www.autonicdq.com/voddetail/4002.html"loop = asyncio.get_event_loop() #协程的开始实例化一个loop对象loop.run_until_complete(get_html(url_main)) #在loop对象添加要完成的任务t2 = datetime.datetime.now() #记录结束时间的print(t2 - t1)
速度比正常快了很多,虽然把协程并发数量进行了限制,但不限制,网站遭不住
注:该文章仅供学习交流
利用协程爬取m3u8视频相关推荐
- Python爬虫——aiohttp异步协程爬取同程旅行酒店评论
大家好!我是霖hero Python并发编程有三种方式:多线程(Threading).多进程(Process).协程(Coroutine),使用并发编程会大大提高程序的效率,今天我们将学习如何选择多线 ...
- 送书 | aiohttp异步协程爬取同程旅行酒店评论并作词云图
大家好!我是啃书君! Python并发编程有三种方式:多线程(Threading).多进程(Process).协程(Coroutine),使用并发编程会大大提高程序的效率,今天我们将学习如何选择多线程 ...
- python多线程爬取m3u8视频(包含AES解密)
python爬取m3u8视频(包含AES解密) 前情提要 部分代码摘录于某位大哥(写代码的时候收藏书签了的打算写博客的时候带上链接的,无奈手贱删除了chrome用户,所有的书签也没了,找到再补上),在 ...
- Python分别用单线程,多线程,异步协程爬取一部小说,最快仅需要5s
文章目录 单线程爬取 多线程爬取 异步协程爬取 本文运用了三种方式爬取一整部小说,分别运用了单线程爬取,多线程爬取和异步协程爬取. 小说网址:` http://www.doupo321.com/dou ...
- python从网址爬图片协程_Python爬虫多任务协程爬取虎牙MM图片
查看: 4420|回复: 241 [作品展示] Python爬虫多任务协程爬取虎牙MM图片 电梯直达 发表于 2019-4-17 21:35:47 | 只看该作者 |倒序浏览 |阅读模式 马上注册,结 ...
- 链家网开源java_异步协程爬取链家租房信息
异步协程抓取链家数据+pandas写入csv import asyncio import aiohttp import pandas from bs4 import BeautifulSoup fro ...
- Python初级爬虫(利用多任务协程爬取虎牙MM图片)
Python多任务协程下载虎牙直播MM图片 # coding = utf-8 import re import gevent from gevent import monkey, pool impor ...
- 协程爬取整站豆瓣网络
爬取豆瓣网络思路: 从标签页进入,提取所有标签URL 进入每个标签页,提取所有列表URL 进入每个列表页,提取每一页的详情URL和下一页列表URL 进入每个详情页,拿到书名 如此往复循环,直到数据抓取 ...
- 多协程爬取中大微博内容(以及转发数,点赞数,评论数)
这个是在之前的微博爬取(Python)–中大微博前100条微博内容以及评论转发点赞数目爬取 的并发版本 代码 import requests from gevent import monkey imp ...
最新文章
- 吴裕雄 Bootstrap 前端框架开发——Bootstrap 按钮:禁用按钮
- 12.PHP_PDO数据库抽象层
- hibernate 继承映射
- C C++ 面试知识总结,包含STL,数据结构等
- linux分区知识,Linux硬盘分区知识
- 自定义Dictionary支持线程安全
- Ubuntu switch window switch terminal tab
- matlab计算截断误差,Matlab相位截断误差仿真综述.doc
- www.how2j.com_HOW-TO:快速开始使用Spring 4.0,以构建简单的REST-Like API(演练)
- matlab出错及解决办法,Linux下使用Matlab符号函数出错的解决办法
- 网友的VOIP总结 1
- ValidateRequest=false 不在.net2.0 中该怎么办?
- 如何实现扫码登陆 扫码登陆原理
- 多御安全浏览器升级1.7.6版,新增扫描二维码功能
- dede后台登陆提示 验证码不正确 解决办法
- 杀毒软件 McAfee 创始人自杀,75 年传奇人生画下句号
- html文本如何逐渐淡入,CSS如何实现文字淡入效果
- 以P2P网贷为例互联网金融产品如何利用大数据做风控?
- 常见的拖垮中小公司技术团队的10宗罪
- 市场调研报告-全球与中国4K手术显示器市场现状及未来发展趋势