运行展示

程序运行后的结果如下所示:

系统可以对mp4和m3u8的视频进行下载,具体的下载方法见下文描述。

流程分析

对于下载网站的在线视频,首先需要找到视频对应的url,我们知道浏览器在线播放的视频肯定是从服务器端进行的资源获取,那么就存在视频的url来让浏览器端进行视频的获取,在线视频的url一般就是两种格式:.mp4 和 .ts,而.mp4的链接就可以直接对链接进行请求获取视频,但是以.ts结尾的视频,实际上是将一个大的视频进行分片,分为若干个小的视频片段,所以对于.ts结尾的视频,需要获取所有的完整的.ts片段,也就是需要获取.m3u8文件内的所有链接,对这些链接逐一请求下载,然后再对每一个片段合并即是完整的视频。

现在的部分网站的反爬措施做的比较好,而且视频进行加密处理,获取难度较高。所以我们仅针对没有进行视频加密的网站进行在线视频获取。主要分为MP4和m3u8两种视频的获取。

1、MP4

对于部分在线视频,直接可以利用浏览器的抓包工具获取视频的MP4链接, 例如,以浙江卫视为例,打开某个视频片段,然后右键检查,就可以看到网页的源代码,如下所示。

在源码中可以直接找到以.mp4结尾的视频的链接,如下所示。

或者,点击network,过滤MP4或者m3u8查看链接,如下图所示。

其中的request url就是MP4视频的链接。

2、m3u8

对于某些网站,其在线视频采用.ts的片段进行播放,所以需要获取到所有的.ts片段的链接,以cctv为例,进行抓包,可以看见大量的.ts结尾的包,如下所示。经过观察可以发现,每个ts的url只有最后一部分不一样。

.ts片段的链接(或者每个ts不一样的部分)一般会存放在.m3u8文件中,过滤出m3u8,如下所示。

利用m3u8里面的数据就可以获取到每一个ts片段了。

代码编写

主要实现的功能如下:

实现对在线视频(mp4、m3u8)的下载,具体包括:
(1)多线程下载
(2)ts视频文件合并
(3)进度条显示
(4)日志记录
(5)重复下载提示

1、mp4文件下载方法

"""以下是对直接有mp4文件地址的视频进行下载的操作"""# 直接针对mp4链接视频的获取def get_video_mp4(self):mp4_path = 'videoMP4\\' + 'video' + ''.join(str(time.time()).split('.', 2)) + '.mp4'# stream=True 意味着,当函数返回时,仅响应标头被下载,响应主体不会try:response_head = requests.get(url=self.url_m3u8, stream=True, headers=self.headers)except Exception as e:print(e)exit()# 判断是否有正确的响应,如没有正确响应则说明url出错,不能正确下载,退出程序if response_head.status_code != 200:print('下载出错!')exit()# 获取响应头中的文件大小length = float(response_head.headers['content-length'])# 下载文件,对文件进行分块下载,每次512kb,方便对文件目录实时读取获得已下载的大小def write_video():count = 0with open(mp4_path, 'wb') as fp:for chunk in response_head.iter_content(chunk_size=512):if chunk:fp.write(chunk)count += len(chunk)# 计算下载进度,用已下载的文件大小/文件总的大小def progress_bar():start = time.perf_counter()while True:down_size = os.path.getsize(mp4_path)p = math.ceil((down_size / length) * 100)  # 向上进行取整,确保下载完成是的进度为100%dur = time.perf_counter() - startprint("\r", end="")print("下载进度: {}%: ".format(p), "▋" * (p // 2), "{:.2f}s".format(dur), end="")sys.stdout.flush()time.sleep(0.05)if p == 100:breakprint()t1 = threading.Thread(target=write_video)t1.start()progress_bar()t1.join()self.path_log = os.path.join(os.getcwd(), mp4_path)print('视频合并完成!视频路径:{0}'.format(self.path_log))

2、m3u8视频下载方法

对于m3u8视频,首先需要输入m3u8文件对于的url,根据url获取m3u8文件,由于分片较多,所以这里可以采用多线程对m3u8内的ts多线程下载,也就是可以让每个线程负责一部分ts分片视频的下载,例如,本程序先获得m3u8文件的行数,然后将其平均分配成8个子文件,每个子文件的链接就作为一个线程进行下载,程序如下。

# 获取视频流切片m3u8的对应网址,下载视频切片就按照文件内的网址进行下载def get_m3u8(self):global responsetry:response = requests.get(url=self.url_m3u8, headers=self.headers)except Exception as e:print(e)exit()response_data = response.textif response.status_code == 200:with open('data.txt', 'w', encoding='utf-8') as fp:fp.write(response_data)print('链接获取完成!')else:print('网址解析出错!')exit()# 对文件的视频流地址进行提取并划分为8个文件,方便启用八线程下载def spl_file(self):# 对m3u8文件内容的行数和下载链接数进行统计,方便后续的文件分块和计算下载进度with open('data.txt', 'r', encoding='utf-8') as fp:while True:self.count_line += 1link = fp.readline()if not link:breakif link[:4] != "#EXT":self.count += 1with open('data.txt', 'r', encoding='utf-8') as fp:flag, num = 0, 1while True:link = fp.readline()if not link:breakfilename = 'filedir/' + 'thread' + str(num)with open(filename, 'a') as fp1:fp1.write(link)flag += 1n = math.ceil(self.count_line / 8)  # 分为8个文件,方便8线程下载if flag == n:flag = 0num += 1

接下来,就是开启多线程对m3u8文件的ts链接进行下载,由于在对ts文件下载后需要对ts文件进行合并,所以ts的最终顺序需要保持和m3u8里的一致,那么对于多线程而言下载顺序不会从前到后保持一致,但是每一个线程内的下载的顺序是按照子文件的顺序进行的,所以这里利用线程号+下载时间进行标识就可以实现ts文件的顺序排序,程序如下。

# 根据获取的视频切片链接下载视频切片def get_video_m3u8(self, *args):filename, num = args[0], args[1]try:  # 处理m3u8内容少,不够8个线程时的异常with open(filename, 'r', encoding='utf-8') as fp:while True:link = fp.readline()if not link:breakif link[:4] != "#EXT":  # 提取出下载链接的行if link[:4] != 'hhtp':re_link = self.pre_url + '/' + link[:len(link) - 1]  # 去掉换行符else:re_link = link[:len(link) - 1]tmp = str(time.time()).split('.', 2)  # 以下载时间对视频片段进行排序# 为了保证视频切片有序,需要依照每个线程的进行变换,如线程1,编号1+下载时间.ts,线程2,编号2+下载时间.tstsName = 'videoTS/' + str(num) + '-' + ''.join(tmp) + '.ts'fp_video = open(tsName, 'wb')response = requests.get(url=re_link, headers=self.headers)response_video = response.contentif response.status_code != 200:print('下载出错!')exit()fp_video.write(response_video)fp_video.close()except Exception as e:print(e)"""下载进度条,用来显示下载进度预先在spl_file()计算下载的总文件数:self.count不停的监控已下载到文件夹的文件数量:progress下载进度:p=progress / self.count"""def progress_bar(self):start = time.perf_counter()while True:list_ts_path = os.listdir('videoTS')progress = len(list_ts_path)p = math.ceil((progress / self.count) * 100)dur = time.perf_counter() - startprint("\r", end="")print("下载进度: {}%: ".format(p), "▋" * (p // 2), "{:.2f}s".format(dur), end="")sys.stdout.flush()time.sleep(0.05)if p == 100:breakprint()

3、日志记录和重复下载提示

此外,程序还有日志记录方法和重复下载提示方法。日志记录文件记录了下载的时间,文件路径,和下载的url,每一行作为一次下载记录。重复下载提示,主要是在用户输入下载url的时候查看日志中是否已经存在下载记录,若存在下载记录并且文件目录的文件存在则提醒用户重复下载,若不存在则不提醒。程序如下所示。

# 下载日志,为了对每行进行编号,每次日志记录为一行,为了得到本行的行号,需要知道上一行的行号,+1操作后即是本行行号# 同时,为了节省读取文件时间,每次获取上一行的行号,需要对日志文件从后向前读取最后一行,这里利用seek()方法"""seek()讲解:1、fileObject.seek(offset[, whence])2、offset -- 开始的偏移量,也就是代表需要移动偏移的字节数3、whence:可选,默认值为 0。给offset参数一个定义,表示要从哪个位置开始偏移;0代表从文件开头开始算起,1代表从当前位置开始算起,2代表从文件末尾算起。"""def download_log(self):if not os.path.exists('log.txt'):with open('log.txt', 'a', encoding='utf-8') as fp1:fp1.write('##### download log ##### \n')fp1.write('0: 时间\t\t路径\t\t网址\n')try:with open('log.txt', 'rb') as fp2:  # 打开文件off = -10  # 设置偏移量while True:if os.path.getsize('log.txt') == 0:breakfp2.seek(off, 2)lines = fp2.readlines()  # 读取文件指针范围内所有行if len(lines) >= 2:  # 判断是否最后至少有两行,这样保证了最后一行是完整的last_line = lines[-1]  # 取最后一行break# 如果off为5时得到的readlines只有一行内容,那么不能保证最后一行是完整的# 所以off翻倍重新运行,直到readlines不止一行off *= 2try:self.num = str(last_line.decode())[:1]if int(self.num) >= 100:with open('log.txt', 'w', encoding='utf-8') as f:f.write('##### download log ##### \n')f.write('0: 时间\t\t路径\t\t网址\n')self.num = 0except Exception as e:passexcept Exception as e:passwith open('log.txt', 'a', encoding='utf-8') as fp3:timestamp = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())download_url = self.url_m3u8line_num = str(int(self.num) + 1) + ': 'down_log = timestamp + '  ' + self.path_log + '  ' + download_urlfp3.write(line_num + down_log + '\n')# 验证视频是否重复下载def break_point(self):# 首先对链接视频是否已经存在做一下验证,若已经存在提醒用户,不存在则下载try:  # 处理首次使用没有日志文件的异常with open('log.txt', 'r', encoding='utf-8') as fp:fp.readline()fp.readline()while True:log_info = fp.readline()if not log_info:breaklist_log_info = re.split(r'[ ]', log_info[:len(log_info) - 1])if self.url_m3u8 == list_log_info[-1]:if os.path.exists(list_log_info[-3]):print('视频存在:', list_log_info[-3])choose = input('是否重新下载!(1确认/其它取消):')if choose == '1':passelse:exit()breakelse:passexcept Exception as e:pass

以上就是功能实现的主要程序介绍,完整程序请访问链接下载:https://github.com/Yang-Jianlin/Download_Video/blob/master/getVideo.py

基于python爬虫下载网站在线视频相关推荐

  1. python爬虫下载网站磁力链接

    设计分三步走: 1.获取明星列表地址 2.获取明星作品序列号 3.根据作品序列号查找磁力链接 一.获取网站中明星列表的作品集地址 #coding=utf8 import requests import ...

  2. python爬虫 下载视频网站视频

    python爬虫 下载视频网站视频 xpath解析页面源码 requests.Session() 解决 status_code 302 网页重定向 selenium 获取网页遇到 iframe 标签解 ...

  3. python爬虫实战-爬取视频网站下载视频至本地(selenium)

    #python爬虫实战-爬取视频网站下载视频至本地(selenium) import requests from lxml import etree import json from selenium ...

  4. python爬虫下载影视网站的电影

    python爬虫下载影视网站的电影 我这边选取了vip网站 F12打开调试抓包模式 搜索影片的名称, 观察看看给那个url地址发送了请求, 我看到了 这个请求需要携带发送数据 而这个数据就是我们要的影 ...

  5. 【基于Python的招聘网站爬虫及可视化的设计与实现】

    完整下载:基于Python的招聘网站爬虫及可视化的设计与实现.docx 目录 目录 2 摘要 3 关键词 3 第一章 绪论 3 1.1 研究背景 3 1.2 研究意义 4 1.3 研究目的 6 1.4 ...

  6. 基于Python的招聘网站爬虫及可视化的设计与实现

    摘 要 I Abstract II 引 言 1 第1章 课题概述 2 1.1 课题内容 2 1.2 课题背景 2 1.3 课题意义 3 1.4 运行环境 3 1.5 相关技术 3 1.5.1 Pyth ...

  7. 基于Python+django的 古诗词在线学习网站-计算机毕业设计

    项目介绍 基于Python+django的 古诗词在线学习网站则旨在通过标签分类管理等方式,实现管理员:首页.个人中心.用户管理.诗词管理.主题管理.情感色彩管理.风格管理.我的收藏管理.诗词论坛.系 ...

  8. 利用Python爬虫下载王者荣耀教学视频

    前言: 很多人学习python,不知道从何学起. 很多人学习python,掌握了基本语法过后,不知道在哪里寻找案例上手. 很多已经做案例的人,却不知道如何去学习更加高深的知识. 那么针对这三类人,我给 ...

  9. 基于python爬虫技术的应用_基于Python爬虫技术的应用

    办公自动化杂志 一.引言 本文主要是对 Python 爬虫技术进行阐述,基于 python 的爬虫与其他语言相比的有很多优势.通过爬去某个网站的所有新闻这个案例,来进一步阐释 Python 爬虫技术的 ...

  10. 基于python爬虫————静态页面和动态页面爬取

    基于python爬虫----静态页面和动态页面爬取 文章目录 基于python爬虫----静态页面和动态页面爬取 1. 爬虫的原理 2. 用正则表达式匹配获取数据 3. 页面源码解析 4. 通过req ...

最新文章

  1. Kali linux 2016.2(Rolling)里Metasploit的OpenVAS
  2. (原创)按照一定的格式生成一定数量的随机数的例子
  3. 【Linux笔记】CentOS下找不到eth0设备的解决方法
  4. 新元素之hgroup,header,footer,address,nav
  5. Acwing145. 超市[C++题解]:贪心
  6. 成功解决./nvidia-installer: invalid option: ‐‐no‐opengl‐files ERROR: Invalid commandline, please run `
  7. iOS - 获取安装所有App的Bundle ID
  8. AndroidStudio安卓原生开发_UI高级_自定义主题和样式---Android原生开发工作笔记129
  9. 空间复杂度,实现从excel导出到txt文件中的java代码自动构建,逻辑条件不同实现则不同...
  10. go interface类型转换_Go语言的九大核心特性主要有哪些?
  11. 一个demo学会c++编程
  12. 蚂蚁森林用户须知_蚂蚁森林刷能量漏洞(轻松读懂规则)
  13. 分享不可不知的CAD经典技巧
  14. 机器学习基础:概率论基础
  15. hp打印机一直显示正在打印中_HP打印机提示文档正在打印但就是打印不了
  16. Eclipse 插件大全
  17. An unknown server-side error occurred while processing the command. Original error: Error executing
  18. openGL-读取off、stl、obj文件并旋转平移缩放操作
  19. 找出最接近的数据-MATLAB
  20. 真香还是假香,Python处理分析128张Excel表格竟然不到3秒?

热门文章

  1. markdown 语法
  2. 学习视频处理(一),了解HLS,流媒体,视频编码
  3. ROS创建工作空间和source的解释
  4. 江诗丹顿 VACHERON CONSTANTIN
  5. 动画云渲染【渲染101】渲染小提示
  6. JavaScript键盘鼠标事件处理
  7. Snort的TILE64移植
  8. 实验六 卷积神经网络(4)ResNet18实现MNIST
  9. 开发那点事(六)php抓取北京实时公交数据
  10. 懂的都懂,那些好用的“WEB安全”网站