手机网页抓取视频(用Python爬取网页视频(HD)去爬取视频资源)
优采云 发布时间: 2021-09-11 16:04手机网页抓取视频(用Python爬取网页视频(HD)去爬取视频资源)
记录:使用Python抓取网络视频
相关:python、请求爬虫、m3u8文件、合成ts
前几天刚自学了python爬虫,萌生了一个想法:爬取网络上的视频资源。所以就去做吧!
但是因为我只学python的基本语法,对视频格式不是很了解,所以弄了两个晚上(week四、五后班后),今天正好是星期六,所以记录下两晚的结果吧!
一、获取网页的HTML代码
当然很简单
requests.get(url).text
将他保存为txt文件,方便操作
我们打开文件,用ctl+f搜索,发现没有所谓的mp4或者其他可以直接下载播放的文件链接(根据网站当然,如果有mp4链接,情况简单10000倍,下面你就不用看了)
我们可以找到m3u8的字符串
这里有五个 .m3u8 链接。我猜是因为网站有5条播放线,所以我们自然选择第一条线(高清)来抓取视频资源!
二、处理m3u8文件
关于 m3u8
m3u8是苹果推出的视频播放标准,是m3u的一种,但是编码方式是utf-8,是一种文件检索格式。将视频剪成小段ts格式的视频文件,然后存储在服务器中(现在为了减少I/O访问次数,一般存储在服务器的内存中),路径通过m3u8解析,然后发出请求。
1.
HD%u9ad8%u6e05%24https%3A%2F%2Fyouku.com-l-youku.com%2F20190207%2F20335_d1f19bfb%2Findex.m3u8
这个链接根本打不开。我研究了一下,发现链接前面有mac_url=unescape(,意思是下面的链接需要解码才能使用。我查了这个解码方法:
s2 = unescape (s1)
规则:
所有以 %xx 十六进制形式编码的字符都替换为 ASCII 字符集中的等效字符。
%uxxxx 格式编码的字符(Unicode 字符)替换为十六进制编码 xxxx 的 Unicode 字符。
由此,我们可以解码这个字符串:
前面的“HD%u9ad8%u6e05%24”代表“HD HD$”,不是链接的一部分,可以忽略。
# 字符(十六进制)转ASCII码
def hexToAscii(h):
d = int(h,16) # 转成十进制
return chr(d) # 转成ASCII码
# 从得到的html代码中获取m3u8链接(不同网站有区别)
def getM3u8(http_s):
ret1 = http_s.find("unescape")
ret2 = http_s.find(".m3u8")
ret3 = http_s.find("http", ret1, ret2) # "unescape"和".m3u8"之间找"http"
m3u8_url_1 = http_s[ret3: ret2 + 5] # 未解码的m3u8链接
# 下面对链接进行解码
while True:
idx = m3u8_url_1.find('%')
if idx != -1:
m3u8_url_1 = m3u8_url_1.replace(m3u8_url_1[idx:idx+3], \
hexToAscii(m3u8_url_1[idx+1:idx+3]))
else:
break
return m3u8_url_1
不错
所以我们得到了m3u8的链接
网上也有在线解码网站,我们可以测试一下
是不是一模一样! ! !
另外,我们可以使用在线m3u8在线播放测试网站进行测试,可以直接播放! !这将表明链接是正确的
2.
好的,那么让我们看看这个链接里有什么?直接进入浏览器,它会让你下载一个.m3u8文件,下载后用notepad++打开。
#EXTM3U
#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=800000,RESOLUTION=1080x608
1000k/hls/index.m3u8
所以,这是什么鬼? ? ?
事实证明,
m3u8 链接是分层的(我是这么理解的),所以需要解析。最后一行的1000k/hls/index.m3u8其实是为了获取二级KEY。即用它来代替之前解析过的链接的最后一个“index.m3u8”,形成一个新的链接。
# 寻找字符串s中最后出现字符c的index
def findLastchr(s, c):
ls = []
sum = 0
while True:
i = s.find(c)
if i != -1:
s = s[i+1:]
ls.append(i)
else:
break
for i in range(len(ls)):
sum += (ls[i] + 1)
return sum - 1
def getM3u8_2(m3u8_url_1):
r1 = requests.get(m3u8_url_1)
r1.raise_for_status()
text = r1.text
idx = findLastchr(text, '\n')
key = text[idx + 1:] # 得到第一层m3u8中的key
idx = findLastchr(m3u8_url_1, '/')
m3u8_url_2 = m3u8_url_1[:idx + 1] + key # 组成第二层的m3u8链接
return m3u8_url_2
好的,接下来我们在浏览器中打开这个链接,我们会下载另一个.m3u8文件,打开记事本就可以看到
我们看到了什么
284e2012ca2000000.ts 等...
当然要替换二级m3u8链接后面的index.m3u8,也就是ts链接。
例如
ts 文件将按顺序编号
例如,这里我从 284e2012ca2000000.ts 到 284e2012ca2001806.ts
以上是直接用浏览器下载,这里我们使用python程序自动完成(会调用之前定义的函数)
# 从最原始的url-->生成一个ts列表的文件
def getTsFile(url, filename):
try:
r = requests.get(url)
r.encoding = "utf-8"
r.raise_for_status()
http_s = r.text
m3u8_url_1 = getM3u8(http_s)
print("第一层m3u8链接" + m3u8_url_1)
m3u8_url_2 = getM3u8_2(m3u8_url_1)
print("第二层m3u8链接" + m3u8_url_2)
# 通过新的m3u8链接,获取真正的ts播放列表
# 由于列表比较长,为他创建一个txt文件
r2 = requests.get(m3u8_url_2)
f = open(filename, "w", encoding="utf-8") # 这里要改成utf-8编码,不然默认gbk
f.write(r2.text)
f.close()
print("创建ts列表文件成功")
return "success"
except:
print("爬取失败")
return "failed"
ts 文件处理1.
毕竟这些链接太多了,用链表管理更方便,以后批量下载可以用索引循环
两步:
1. 提取ts列表文件内容,将ts的url一一拼接成列表
2.批量下载ts文件
# 提取ts列表文件的内容,逐个拼接ts的url,形成list
def getPlayList(filename, m3u8_url_2):
ls = []
f = open(filename, "r")
line = " " # line不能为空,不然进不去下面的循环
idx = findLastchr(m3u8_url_2, '/')
while line:
line = f.readline()
if line != '' and line[0] != '#':
line = m3u8_url_2[:idx+1] + line
ls.append(line[:-1]) # 去掉'\n'
return ls
# 批量下载ts文件
def loadTs(ls):
root = "D://mp4//"
length = len(ls)
try:
if not os.path.exists(root):
os.mkdir(root)
for i in range(length):
tsname = ls[i][-7:]
path = root + tsname
r = requests.get(ls[i])
with open(path, 'wb') as f:
f.write(r.content)
f.close()
print('\r' + tsname + " -->OK ({}/{}){:.2f}%".format(i, length, i*100/length), end='')
print("全部ts下载完毕")
except:
print("批量下载失败")
我这里用的是D:\mp4路径,当然也可以改
注意:下载生成ts文件时,命名一定要规范,
如:0000.ts 0001.ts 0002.ts…… 1806.ts
没有1.ts 2.ts 3.ts…… 1806.ts
因为下面的合并是根据字符匹配的
2.
接下来我们将ts合并成mp4
其实就是调用windows的命令
复制/b *.ts new.mp4
直接放代码
# 整合所有ts文件,保存为mp4格式
def tsToMp4():
print("开始合并...")
root = "D://mp4//"
outdir = "output"
os.chdir(root)
if not os.path.exists(outdir):
os.mkdir(outdir)
os.system("copy /b *.ts new.mp4")
os.system("move new.mp4 {}".format(outdir))
print("结束合并...")
好的
就是这样! !
这里不给出main函数。我所有源代码的链接如下: