这篇想写很久了,今天专门搞了搞,现在把用python下载知乎视频的整个流程码下来。

(1)目标站点分析

比如这篇知乎文章https://www.zhihu.com/question/279247693/answer/442578073里有一个小视频,我们想把它下载下来,首先对该网页进行分析。先来看一下原始请求:

原始请求有没有返回视频的链接呢?我们点开“Preview”和“Response”看一看:

这不就是我们想要的东西吗!接下来就是常规操作了,requests请求到网页代码之后可以用正则表达式或者BeautifulSoup和PyQuery之类的解析库提取到视频的URL。

现在让我们看一看上面的视频URL打开之后是什么东西:

显然,这是一个可以播放的视频,问题是我们怎样才能把它下载下来呢?一步一步来分析:

首先,我们发现原始请求返回的状态码是301,而且细心一些就可以看到网页的URL变了,从原来的https://www.zhihu.com/video/1001598833150816256变成了一个新的URLhttps://v.vzuu.com/video/1001598833150816256,如上图。状态码301代表什么呢,同样百度一下:

301表示永久重定向(301 moved permanently),表示请求的资源分配了新url,以后应使用新url。

这样就可以理解了,也就是请求视频的URL之后,转到了一个新的URL,我们可以用一个函数获取到这个新的URL:

def get_real_url(url, try_count=1):if try_count > 3:return Nonetry:headers = {'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.75 Safari/537.36'}response = requests.get(url, headers=headers, timeout=10)if response.status_code >= 400:return get_real_url(url, try_count+1)return response.urlexcept RequestException:return get_real_url(url, try_count+1)

然后,我们看一下请求这个新的URL会发生什么:

请求这个网址之后,点击播放视频,会跳出来很多新的请求,其中有一个含有“m3u8”的请求,打开该请求的URL,发现浏览器下载了一个m3u8文件:

这么小不可能是视频吧,果然打开之后并不能播放:

那这个m3u8文件究竟是什么?

M3U8文件是指UTF-8编码格式的M3U文件。M3U文件是记录了一个索引纯文本文件,打开它时播放软件并不是播放它,而是根据它的索引找到对应的音视频文件的网络地址进行在线播放。

我们用文本编辑器打开它如下:

果然,里面有很多像链接一样的东西,但并不是真正的链接。这时我们注意到原来的请求还返回了很多带有“ts”的请求,而且这些请求的URL的后半部分和m3u8文件里一毛一样!

怀着激动的心情打开“ts”请求的URL,浏览器下载了一个.ts文件,ts文件是什么?

ts是日本高清摄像机拍摄下进行的封装格式,全称为MPEG2-TS。ts即"Transport Stream"的缩写。MPEG2-TS格式的特点就是要求从视频流的任一片段开始都是可以独立解码的。

用播放器打开它,虽然只有2秒,但它正是原视频的开头部分。

也就是说,只要把这一个个ts文件下载下来,就能组成一个完整的视频。

对比以下内容:

# m3u8请求的url
https://vdn.vzuu.com/Act-ss-m3u8-sd/4fcea71e5b2743368ad3b023a224b4ae/1d3ebaf8-8775-11e8-8301-0242ac112a0c.m3u8?auth_key=1534097100-0-0-3f1616ac0b8a4c236d7a5a03756092fa&expiration=1534097100&disable_local_cache=0# m3u8文件中的url
1d3ebaf8-8775-11e8-8301-0242ac112a0c-00001.ts?auth_key=1534097100-0-0-76229a4cf68e95b2590d4be99053665c# ts请求的url
https://vdn.vzuu.com/Act-ss-m3u8-sd/4fcea71e5b2743368ad3b023a224b4ae/1d3ebaf8-8775-11e8-8301-0242ac112a0c-00001.ts?auth_key=1534097100-0-0-76229a4cf68e95b2590d4be99053665c

显然,ts请求的url是由m3u8请求url的前面部分和m3u8文件中的url组合而成的,所以我们只要获得了m3u8请求的url就能构造出ts请求的url,从而把视频流下载下来。那么怎样得到m3u8请求的url呢?

原始请求的下面是一个获取js的GET请求,再下面也是一个GET请求,我们来看一下这个请求,并记住这个重要的url,突破口就在这里:

点开“Preview”,我们发现m3u8请求的url正是在这里:

追本溯源,这个请求的url又是从哪里来的呢?

首先,第二个js请求是由原始请求获得的,因为原始请求的Response中包含了js请求的url:

所以这个关键的url:https://lens.zhihu.com/api/videos/1001598833150816256就必然是包含在js中的,看一下Preview果不其然:

但是这个js太难解析了,写程序时就直接跳过这一步,也就是直接用正则表达式解析https://v.vzuu.com/video/1001598833150816256,得到后面那一串用于区分不同视频的数字,然后和https://lens.zhihu.com/api/videos/拼凑在一起,得到https://lens.zhihu.com/api/videos/1001598833150816256。当然,也可以直接解析https://www.zhihu.com/video/1001598833150816256得到那一串数字。

(2)流程框架

1.抓取视频的URL

利用requests请求目标站点,得到HTML代码,然后用PyQuery解析出视频的URL(可能有多个),类似于“https://www.zhihu.com/video/xxxxxxx”。

2.获取视频的真实URL

访问上面得到的视频URL,会发生重定向,返回其重定向后的真实URL,类似于“https://v.vzuu.com/video/xxxxxxx”。(这一步可以省略)

3.获取m3u8请求的URL

用正则表达式解析“https://v.vzuu.com/video/xxxxxxx”,得到后面那一串数字,然后和“https://lens.zhihu.com/api/videos/”拼凑在一起,得到URL“https://lens.zhihu.com/api/videos/xxxxxxx”,访问该网址,从其Response中解析出m3u8请求的URL。

4.下载视频

根据m3u8请求的URL和m3u8请求的Response构造出ts请求的URL,下载ts视频流并合并为一个mp4文件。简单一些可以使用FFmpeg解析m3u8并下载合并视频,但为了了解其原理,这部分我自己写了一个解析方法。

(3)爬虫代码

import os
import re
import json
import requests
from requests import RequestException
from pyquery import PyQuery as pqdef get_page(url):try:headers = {'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.75 Safari/537.36'}response = requests.get(url, headers=headers, timeout=10)if response.status_code == 200:return response.textreturn Noneexcept RequestException:return Nonedef parse_page(html):doc = pq(html)items = doc('.url').items()for item in items:yield item.text()def get_real_url(url, try_count=1):if try_count > 3:return Nonetry:headers = {'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.75 Safari/537.36'}response = requests.get(url, headers=headers, timeout=10)if response.status_code >= 400:return get_real_url(url, try_count+1)return response.urlexcept RequestException:return get_real_url(url, try_count+1)def get_m3u8_url(url):try:path_pattern = re.compile('(\d+)', re.S).search(url).group(1)get_play_url = 'https://lens.zhihu.com/api/videos/' + path_patternheaders = {'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.75 Safari/537.36'}content = requests.get(get_play_url, headers=headers).textdata = json.loads(content)  # 将json格式的字符串转化为字典if data and 'playlist' in data.keys():m3u8_url = data.get('playlist').get('sd').get('play_url')return m3u8_urlexcept Exception:return Nonedef get_m3u8_content(url, try_count=1):if try_count > 3:print('Get M3U8 Content Failed', url)return Noneheaders = {'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.75 Safari/537.36'}try:response = requests.get(url, headers=headers, timeout=10)if response.status_code == 200:return response.textreturn get_m3u8_content(url, try_count+1)except RequestException:return get_m3u8_content(url, try_count+1)def get_ts(url, try_count=1):if try_count > 3:print('Get TS Failed', url)return Noneheaders = {'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.75 Safari/537.36'}try:response = requests.get(url, headers=headers, timeout=10)if response.status_code == 200:return responsereturn get_ts(url, try_count+1)except RequestException:return get_ts(url, try_count+1)def download_ts(m3u8_url, video_url, video_count):print('准备下载', video_url)download_path = 'E:/PycharmProjects/zhihu_vedio/'try:all_content = get_m3u8_content(m3u8_url)file_line = all_content.split('\n')  # 读取文件里的每一行# 通过判断文件头来确定是否是M3U8文件if file_line[0] != '#EXTM3U':raise BaseException('非M3U8链接')else:unknow = True  # 用来判断是否找到了下载的地址for index, line in enumerate(file_line):if "EXTINF" in line:unknow = False# 拼出ts片段的URLpd_url = m3u8_url.rsplit('/', 1)[0] + '/' + file_line[index + 1]  # rsplit从字符串最后面开始分割response = get_ts(pd_url)c_fule_name = str(file_line[index + 1]).split('?', 1)[0]source_path = c_fule_name.split('-', 1)[0]  # 区分不同源的视频流print('正在下载', c_fule_name)with open(download_path + c_fule_name, 'wb') as f:f.write(response.content)f.close()if unknow:raise BaseException('未找到对应的下载链接')else:print('下载完成,准备合并视频流...')merge_file(download_path, source_path, video_count)except Exception:return Nonedef merge_file(download_path, source_path, video_count):os.chdir(download_path)  # 修改当前工作目录merge_cmd = 'copy /b ' + source_path + '*.ts video' + str(video_count) + '_' + source_path + '.mp4'split_cmd = 'del /Q ' + source_path + '*.ts'os.system(merge_cmd)os.system(split_cmd)def main():url = 'https://www.zhihu.com/question/279405182/answer/410204397'  # 含有知乎小视频的链接html = get_page(url)video_count = 0if html:video_urls = parse_page(html)for video_url in video_urls:if video_url:real_url = get_real_url(video_url)if real_url:m3u8_url = get_m3u8_url(real_url)if m3u8_url:video_count += 1download_ts(m3u8_url, video_url, video_count)if __name__ == '__main__':main()

下载好的文件类似于这样:

python实战笔记之(8):下载知乎视频相关推荐

  1. python实战笔记之(9):TKinter制作知乎视频下载器

    前两天写了一个下载知乎视频的小程序,现在给他加了个图形界面,并生成exe文件.同时对原来的程序进行了优化,使用多线程进行任务分配和下载ts文件,解决了GUI卡顿的问题并大大加快了下载速度. 界面如下: ...

  2. python教程下载地址-最新python实战教程网盘下载地址

    原标题:最新python实战教程网盘下载地址 Python在程序员中始终流行:40%的受访者都会学习,44%的受访者每周都会学习.目前技术领域最热点的技术排名,排在前一位的是:Python.Pytho ...

  3. python需要花钱下载吗_用Python下载知乎视频,非常实用

    原标题:用Python下载知乎视频,非常实用 Python下载知乎视频. # -*- coding: utf-8 -*- """ 下载知乎视频: 依赖: pip inst ...

  4. python实战1.0——爬取知乎某问题下的回复

    python实战1.0--爬取知乎某问题下的回复 确定问题 爬取 进行简单筛选 保存数据 # 获取问题下的回复总数 def get_number():url = 'https://www.zhihu. ...

  5. 怎么下载知乎视频,怎么下载M3U8,利用FFMpeg下载M3U8并转成mp4格式

    昨天一个朋友问我怎么下载知乎视频,这有何难F12,找到视频链接打开就下载了啊.但是,可但是,但可是,我并没有成功. 查了很多资料,知乎使用的是M3U8视频.我还不太懂什么是M3U8,大概就是把视频切成 ...

  6. python 学习笔记第一篇---下载网页内所有图片

    第一步:打开网址,进入开发者模式,选中 Network,选择 Img 然后刷新页面,静静等待页面加载 等待页面加载完成,你可以看到这边有很多图片的资源 随便点开一个图片选择 Headers 可以看到 ...

  7. Python实战笔记-常用知识点

    一.自学Python的最终的目标是 a,实现自动化办公 b,实现数据的爬取 c,实现基本的数据分析(SEMMA) S:Sample(收集数据) 常用手段:问卷调查,数据库查询,实验室实验,仪器设备的记 ...

  8. 趣闻-如何下载知乎视频

    打开带视频的知乎链接.比如: 右键查看网页源代码,可以看到一大堆眼花缭乱的代码. Ctrl f 查找 video-box,可以看到后边跟了个链接.class="video-box" ...

  9. Python实战-让在职教育类网站的视频全自动播放

    时间是公平的,因为每个人的一分钟都是 60 秒:时间又是最不公平的,有人一年的收获顶得上别人十年,这取决于如何利用时间. 成年人的世界,一个字忙,忙得没有时间学习,没有时间健身,没有时间陪家人.其实解 ...

最新文章

  1. 2018全球智能+新商业峰会倒计时2个月,5000名AI从业者将出席
  2. Error: Discrete value supplied to continuous scale
  3. iis php 图片无法显示,iis php 图片无法显示怎么办
  4. C++虚继承(八) --- 虚继承与继承的差异
  5. MongoDB非关系型数据库开发手册
  6. vue-scroller的使用 开发自己的 scroll 插件
  7. Easystructure教程_C语言源代码自动生成流程图
  8. 循序渐进!java开发手册阿里巴巴泰山版
  9. 使用Node.js制作爬虫教程
  10. mysql sbr_腾讯云数据库团队:MySQL语句复制(SBR)的缺陷列举
  11. 如何增加人机数量及平衡_如何识别自己身体类型,针对三种不同身体类型的改变策略...
  12. 基于遗传算法的高校排课系统研究
  13. iOS之app审核遭遇5.2.1的条例被拒
  14. 软考 信息安全工程师(第二版)笔记-第1章 网络信息安全概述
  15. 在Mac上使用中国银行和工商银行网银
  16. 计算机电源接口在哪,电脑电源接口定义图解
  17. shell正则表达式(cut、sort、uniq、tr、paste)小工具
  18. maven配置阿里云镜像 settings.xml文件配置
  19. 谈谈计算数学(转自校内
  20. C++求数组中和为定值的组合

热门文章

  1. hud抬头显示器哪个好_什么样的HUD抬头显示器好?圆盾用是实力说话
  2. 有关chromium浏览器 ios 读证书 验证证书 (注没有找到方法),在这里做一下笔记
  3. Linux命令 磁盘管理——了解磁盘、磁盘分区
  4. 不敢相信,技术栈,居然被P站秒了
  5. 小程序 订阅消息 原来就是如此
  6. #书籍《量子力学》读后感
  7. mysql 1033_mysql报错1033 Incorrect information in file: ‘xxx.frm’问题的解决方法
  8. 默默学Sharding-Sphere(一)
  9. NDK Caused by: java.lang.UnsatisfiedLinkError:
  10. Vim中文本全部选中