百般无聊中想和朋友看个电影,不知道看什么,那就看评分高的吧,上次爬取豆瓣电影top250看到评分第一的是《申肖克的救赎》,倒想看看什么样的神作能得到9.6分,因为有腾讯会员于是去了腾讯视频,并没有找到。
清晰度不错,就它了,但是看一下卡很久,应该是没有cdn加速,原始速度没法看。于是想着下载下来再看吧,于是右键审查元素,查看网页源代码,并没有如愿找到资源直链。于是查看网页的请求信息,企图的到点线索,果然发现了一些.m3u8文件,下载下来能看到所有的ts视频片段的信息。网页的网络请求,也请求了这些ts文件,观察这些请求都是有规律可循的,于是可以用requests库下载下来,然后使用windows的copy /b命令拼接起来,有了这个想法于是迫不及待的开始了。
xxx.m3u8文件内容:
#EXTM3U
#EXT-X-VERSION:3
#EXT-X-TARGETDURATION:8
#EXT-X-MEDIA-SEQUENCE:0
#EXTINF:4.24,
/20180705/hPpePutf/800kb/hls/qKHCDKwg5034000.ts
#EXTINF:6.32,
/20180705/hPpePutf/800kb/hls/qKHCDKwg5034001.ts
#EXTINF:4,
/20180705/hPpePutf/800kb/hls/qKHCDKwg5034002.ts
#EXTINF:2.08,
/20180705/hPpePutf/800kb/hls/qKHCDKwg5034003.ts
#EXTINF:4.44,
/20180705/hPpePutf/800kb/hls/qKHCDKwg5034004.ts
qKHCDKwg5034001.ts文件的请求信息:
请求头有个Referer属性这个咱们在请求的时候也需要加上,以下是第一版的代码:
import requests
from bs4 import BeautifulSoup
import os
from pathlib import Path
# 定义请求头的浏览器代理,伪装成浏览器
headers = {'UserAgent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.149 Safari/537.36',
'Referer': 'https://www.xigua555.com/js/player/m3u8.html'}
def download(link):
r = requests.get(link, headers = headers,timeout = 200)
if r.status_code == 200:
print(link + "下载成功!" + str(r.status_code))
else:
print(link + "下载失败!" + str(r.status_code))
return r.content
def save(name,contents):
with open(name, "wb") as file:
file.write(contents)
print("文件{0}已保存。".format(name))
if __name__ == '__main__':
print("程序开始...")
print("创建目录...")
tmpDir = ".tmp/"
if not Path(tmpDir).exists():
os.mkdir(tmpDir)
prefix = "https://cn2.zuidadianying.com/20180705/hPpePutf/800kb/hls/"
tsPre = "qKHCDKwg5034"
# 根据m3u8文件下载0-2137
for tsNum in range(0, 2138):
tsFileName = tsPre + str(tsNum).zfill(3) + ".ts"
url = prefix + tsFileName
print("正在下载文件:" + tsFileName)
while True:
try:
contents = download(url)
save(tmpDir + tsFileName, contents)
except Exception:
print(tsFileName + "正在重试...")
continue
else:
break
print("程序结束...")
果然程序跑起来了,心满意足....不对!等等!怎么下载这么慢10分钟过去才下载5个文件?总共2000多个文件,这下载到什么时候去,顿时就不开心了。问题不大,这不还有多线程吗,python也不是很熟没用过多线程,于是现学现卖喽。
大概思路是,创建128个线程(看有些人机器上开3w进程都没问题,我一开始想开2138个进程,结果发现开一半炸了),所有进程下载完再进行下一轮128个并行的下载,但是其实是有问题的,短板效应会一直等最慢的哪个,甚至有可能一直等不到第二轮。于是想到了队列,生产者消费者模式,但是小菜鸡一枚,写这个还是要花费一些时间的,还是为了省事直接使用sleep先将就着用,于是多线程的版本产生了:
import requests
from bs4 import BeautifulSoup
import os
import threading
import time
# 定义请求头的浏览器代理,伪装成浏览器
headers = {'UserAgent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.149 Safari/537.36',
'Referer': 'https://www.xigua555.com/js/player/m3u8.html'}
# 保存目录
tmpDir = ".tmp/"
prefix = "https://cn2.zuidadianying.com/20180705/hPpePutf/800kb/hls/"
tsPre = "qKHCDKwg5034"
def download(link):
r = requests.get(link, headers = headers,timeout = 200)
if r.status_code == 200:
print(link + "下载成功!" + str(r.status_code))
else:
print(link + "下载失败!" + str(r.status_code))
return r.content
def save(name,contents):
with open(name, "wb") as file:
file.write(contents)
print("文件{0}已保存。".format(name))
# 创建文件夹
def mkdir(file_path):
if os.path.exists(file_path) and os.path.isdir(file_path):
pass
else:
os.mkdir(file_path)
def init():
print("初始化...")
mkdir(tmpDir)
def excute(tsNum):
retry = 5
tsFileName = tsPre + str(tsNum).zfill(3) + ".ts"
url = prefix + tsFileName
print("正在下载文件:" + tsFileName)
while True:
try:
contents = download(url)
save(tmpDir + tsFileName, contents)
except Exception:
if retry > 0:
print(tsFileName + "正在重试...")
continue
else:
print(tsFileName + "重试上限!")
break
else:
break
if __name__ == '__main__':
print("程序开始...")
init()
# 多线程计数器
thread_max = 128
thread_counter = thread_max;
threads = []
# 根据m3u8文件下载0-2137
for tsNum in range(0, 2138):
if thread_counter > 0:
thread_counter -= 1;
t = threading.Thread(target = excute, args = (tsNum,))
threads.append(t)
t.start()
else:
"""
# 等待所有线程任务结束
for t in threads:
t.join()
print("所有线程任务完成")
"""
time.sleep(60)
thread_counter = thread_max
time.sleep(60*60*24)
print("程序结束...")
程序爬起来还是很激动的,毕竟第一次用多线程,按照自己的想法也算达到了一定目的,下载速度峰值达到过5M/s(本来50k/s)总之就是提速不少,大概花费了20多分钟,发现还是有15个文件怎么都下载不下来,但是程序没有记录哪些文件没有下载,于是又写了一段代码,找出哪些没下载的文件。
import os
import re
def getFileList(dir_name):
list = []
for filename in os.listdir(dir_name):
ret = re.search("\d+",filename,flags = 0)
list.append(int(ret.group()))
return list
if __name__ == "__main__":
nlist = getFileList(".tmp/")
nlist.sort()
glist = [int("5034" + str(i).zfill(3)) for i in range(0, 2138)]
diflist = list(set(glist) - set(nlist))
print (diflist)
代码写的有些废话了,不多说了,目的是达到了,找出了没有下载的文件名,写了一段程序继续下载,发现还是半天没动静。15个文件嘛不算太多,最笨的办法了手动下载,链接复制到浏览器,本以为稳了,剩最后3个的时候又出了问题,链接不让访问了,应该是封了IP,于是开了代理下载完剩下的文件。
所有的ts文件终于到手了,现在想怎么合并起来,这里使用windows命令行自带的命令copy /b,支持通配符,但是拼接的顺序是和dir命令查看的顺序一样于是,先把ts文件按名字分类。
分类好之后反复使用下面的命令:
> copy /b *.ts new.ts
最后得到一个774M的ts文件,重命名双击观看,顺序没有问题,心满意足啦!
呼~