异步爬虫实战(1)——简单版

1. 主要过程

    1. 找到m3u8文件(各种手段)
    1. 通过m3u8文件下载ts文件
    1. 将ts文件合并为一个MP4文件
      首先整理一下思路
      视频网站原理

1.低效率模式 <video src=‘123.mp4’>name</video>
2. 用户上传 ——> 转码(把视频做处理,2k,1080,标清) ——> 切片处理(把单个文件进行拆分)
用户在点播或者拉动进度条的时候

问题:
顺序, 需要一个文件记录至少两个东西(1文件播放顺序,2视频存放路径.)
M3U8 txt json => 文本

存在的问题解决方案
得到了JPG文件

hexdumop -C -n 1000 test.jpg
dd if=test.jpg of=outputs.ts bs=1 skip=126

配合 终端命令合并文件

ffmpeg -safe 0 -f concat -i file_list.txt -c copy output.mp4

2. 具体代码解析

2.1 需要引入的库文件

这里主要导入了需要用到的库,异步网络请求、文件读写等

import os
import aiohttp
import asyncio
import aiofiles
import requests

2.2 选取的下载地址

这里省略了如何获得这个下载地址,很简单,因为这里要学的东西很多,直接一步到位了

url = 'https://api.wangchuanxin.top/m3/国外剧/叶卡捷琳娜大帝第一季/叶卡捷琳娜大帝第一季01.m3u8'

2.3 主函数

第一步:
async def main():
	mkdir('ts')

建立一个存放所有ts文件的文件夹。

第二步:
	resp = requests.get(url)
	a = resp.content.decode('utf-8')
	# with open('叶卡捷琳娜大帝.m3u8', mode='wb') as f:
	# 	f.write(resp.content)
	resp.close()

使用 requests.get() 方法访问URL得到 m3u8数据,注意要解码!,当然也可以通过 ** open()** 直接写入文件,这里使用前者;直接打印会得到以下结果:

#EXTM3U
#EXT-X-VERSION:3
#EXT-X-MEDIA-SEQUENCE:0
#EXT-X-ALLOW-CACHE:YES
#EXT-X-TARGETDURATION:8
#EXTINF:8.000000,
https://ali6.a.yximgs.com/udata/music/music_1cf8274d305d42b8a9737bbd1af249410.jpg
#EXTINF:4.000000,
https://ali6.a.yximgs.com/udata/music/music_2e7dc59d24fe4892b92fcf7a9cd811450.jpg
#EXTINF:4.000000,
.........
.........

我们需要的数据仅为下载链接和序列信息,所以要对原始的数据进行处理,使用以下方法

ss = a.replace('\n', '').split('#')
第三步:创建异步任务

首先建立一个空表,和一个计数器 i (方便后续文件命名),从链接表中提取到单个链接,使用单个链接创建异步任务,最后将任务挂起

	tasks = []
	i = 0
	for s in ss[6:-1]:
		# print(s)
		i = i + 1
		[_, tap] = s.split(',')
		# sec = sec.replace('EXTINF:', '')
		# tsp = tap.replace('jpg', 'ts')
		# print(i, tap)
		tasks.append(asyncio.create_task(downLink(tap,i)))
	await asyncio.wait(tasks)
	print('over')
第四步:输出文件信息
mklist('ts')

2.4 ts文件下载函数

传入 link 和 计数器 i 后 创建异步会话,这里的 aiohttp.ClientSession() 等价于 requests模块 ,进行一系列操作后写入ts文件。

async def downLink(links,i):
	async with aiohttp.ClientSession() as session:
		async with session.get(links) as resp:
			async with aiofiles.open('ts/{:03d}.ts'.format(i), mode='wb') as fp:
				ts = await resp.content.read()
				await fp.write(ts[126:])

在这里存在一个坑不知道大家发现没有,应该写入的是 .ts 文件,但是从2.3第二步可以知道,url后缀是以 .jpg 结尾的!

将下载好的jpg文件使用 hexdump 打开如下(部分)

luoyang@xdwll:~/Desktop/数据挖掘/04$ hexdump -C -n 1000 test.jpg
00000000  89 50 4e 47 0d 0a 1a 0a  00 00 00 0d 49 48 44 52  |.PNG........IHDR|
00000010  00 00 03 20 00 00 03 20  08 03 00 00 00 ec ae f6  |... ... ........|
00000020  5a 00 00 00 19 74 45 58  74 53 6f 66 74 77 61 72  |Z....tEXtSoftwar|
00000030  65 00 41 64 6f 62 65 20  49 6d 61 67 65 52 65 61  |e.Adobe ImageRea|
00000040  64 79 71 c9 65 3c 00 00  00 06 50 4c 54 45 00 00  |dyq.e<....PLTE..|
00000050  00 00 00 00 a5 67 b9 cf  00 00 00 01 74 52 4e 53  |.....g......tRNS|
00000060  00 40 e6 d8 66 00 00 02  86 49 44 41 54 78 da ec  |.@..f....IDATx..|
00000070  c1 01 0d 00 00 00 c2 a0  f7 4f 6d 0e 37 a0 47 40  |.........Om.7.G@|
00000080  11 10 00 42 f0 25 00 01  c1 00 00 ff 01 ff 00 01  |...B.%..........|
00000090  fc 80 14 48 12 01 06 46  46 6d 70 65 67 09 53 65  |...H...FFmpeg.Se|
000000a0  72 76 69 63 65 30 31 77  7c 43 ca ff ff ff ff ff  |rvice01w|C......|
000000b0  ff ff ff ff ff ff ff ff  ff ff ff ff ff ff ff ff  |................|
*

可以看到头文件确实是图像存储格式,但是从 000000707D 位置开始为 FFmpeg 文件的格式 , 即在 第 127字节 开始的位置。
使用如下代码验证一下:

dd if=test.jpg of=outputs.ts bs=1 skip=126

得到的 outputs.ts 可以正常进行播放!
在这里插入图片描述
而在python里就简单的很多

await fp.write(ts[126:])

将得到的字节按照从126位置开始进行切片操作就行了,配合传入的计数器i对ts文件进行命名,亦可以非常容易的完成播放时序排序的操作,至此,我们算是完成了一部视频的所有ts文件的下载操作。

2.5 其他函数

这里还是用到了两个函数,一个是创建文件夹的函数非常简单,
而另外一个是创建所有ts文件信息的函数,这是为了便于后续的ts文件合并成MP4文件。

def mkdir(path):
	folder = os.path.exists(path)

	if not folder:  # 判断是否存在文件夹如果不存在则创建为文件夹
		os.makedirs(path)  # makedirs 创建文件时如果路径不存在会创建这个路径
		print("---  new folder...  ---")
		print("---  OK  ---")
	else:
		print("---  There is this folder!  ---")
def mklist(path):
	file_list = sorted(os.listdir(path))
	with open('file_list.txt', 'w+') as f:
		for file in file_list:
			f.write('file ./ts/{}\n'.format(file))

后续合并所需要使用的脚本如下

ffmpeg -safe 0 -f concat -i file_list.txt -c copy output.mp4

最后可以得到完整的MP4文件
在这里插入图片描述

3. 完整代码

可能与上方代码不同,要注意啦!

import os
import aiohttp
import asyncio
import aiofiles
import requests

url = 'https://api.wangchuanxin.top/m3/国外剧/叶卡捷琳娜大帝第一季/叶卡捷琳娜大帝第一季01.m3u8'


def mkdir(path):
	folder = os.path.exists(path)

	if not folder:  # 判断是否存在文件夹如果不存在则创建为文件夹
		os.makedirs(path)  # makedirs 创建文件时如果路径不存在会创建这个路径
		print("---  new folder...  ---")
		print("---  OK  ---")
	else:
		print("---  There is this folder!  ---")


def mklist(path):
	file_list = sorted(os.listdir(path))
	with open('file_list.txt', 'w+') as f:
		for file in file_list:
			f.write('file ./ts/{}\n'.format(file))


async def viewLink(links, i):
	async with aiohttp.ClientSession() as session:
		async with session.get(links) as resp:
			async with aiofiles.open('ts/{:03d}.ts'.format(i), mode='wb') as fp:
				ts = await resp.content.read()
				await fp.write(ts[126:])


async def main():
	mkdir('ts')
	resp = requests.get(url)
	a = resp.content.decode('utf-8')
	# with open('叶卡捷琳娜大帝.m3u8', mode='wb') as f:
	# 	f.write(resp.content)
	resp.close()
	ss = a.replace('\n', '').split('#')
	print(ss)
	tasks = []
	i = 0
	for s in ss[6:-1]:
		# print(s)
		i = i + 1
		[_, jap] = s.split(',')
		# sec = sec.replace('EXTINF:', '')
		# jap = jap.replace('jpg', 'ts')
		print(i, jap)
		tasks.append(asyncio.create_task(viewLink(jap, i)))

	await asyncio.wait(tasks)
	mklist('ts')
	print('over')


if __name__ == '__main__':
	asyncio.run(main())

4. 总结

总的来说,爬取没有加密的视频还是比较简单的,就怕对方在 m3u8ts 文件进行加密,这样还要自己看 JS 代码,这是相当的痛苦。。。,但是大部分视频网站确实是这样的,我能找到这样的,仅仅只需要修改返回的文件头就行了,属实是我比较幸运。

异步下载是视频是真的快,我下午看用平板看直播呢,忽然电脑风扇狂转,而且直播也卡起来了,这一集的视频大小有1.3G大,下了不到 30s 吧。

学习之路,道阻且长

不要在最能吃苦的阶段选择安逸,哪怕躺着学呢(滑稽)。

参考:

1. 修改JPG文件头操作
2. 合并ts文件操作
3. 我自学爬虫的教程

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值