python下载《申肖克的救赎》 —— 记一次心路历程

百般无聊中想和朋友看个电影,不知道看什么,那就看评分高的吧,上次爬取豆瓣电影top250看到评分第一的是《申肖克的救赎》,倒想看看什么样的神作能得到9.6分,因为有腾讯会员于是去了腾讯视频,并没有找到。这时候想到了西瓜视频,首先找到了电影资源:https://www.xigua555.com/dianying/juqingpian/xiaoshenkedejiushu/player-2-0.html

清晰度不错,就它了,但是看一下卡很久,应该是没有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文件,重命名双击观看,顺序没有问题,心满意足啦!

呼~

发布了19 篇原创文章 · 获赞 9 · 访问量 2993

猜你喜欢

转载自blog.csdn.net/Necrolic/article/details/105569482