本博客所写爬取规则最近更新日期为:2020/12/11
新增:西瓜视频
皮皮虾的解析规则已经失效,新版规则已更新

提醒:转载请标明作者和原文链接!!!

CSDN个人主页: 高智商白痴
原文地址: https://blog.csdn.net/qq_44700693/article/details/108089085

日常跳转:

  • 导入:
    • 分平台解释:
      • 皮皮虾
      • 皮皮搞笑
        • 抓包分析
        • 源码及结果
      • 抖音 / 抖音极速版
        • 抓包分析
        • 代码及结果
        • 小优化:
      • 腾讯微视
        • 抓包及分析
        • 代码及结果
      • 开眼 Eyepetizer
        • 抓包分析
        • 代码及结果
      • 快手/快手极速版
        • 单个视频下载
          • 抓包分析
          • 代码及结果
        • 用户视频下载
          • 抓包分析
          • 代码及结果
      • 抖音火山版/火山极速版
        • 单个视频下载
          • 代码及结果:
        • 用户视频下载
          • 抓包分析
          • 代码及结果:
      • 最右
        • 单个视频下载
          • 代码及结果
        • 话题视频下载
          • 代码及分析
          • 代码及结果:
          • 小优化:
      • VUE
        • 单个视频下载
          • 抓包与分析
          • 代码及结果
        • 话题视频下载
          • 抓包与分析
          • 代码及结果:
      • 看看视频
        • 抓包与分析
        • 代码及结果
        • 小优化
      • 哔哩哔哩
      • AcFun
      • ZzzFun
      • 西瓜视频
        • 抓包分析
        • 类源码
        • 结果
    • 编写中。。。

导入:

虽然目前有些软件还没适配,但是,我发了 Blink 后有一写人留言或者私信找我要源码,不过我还在增加适配的软件,所以还没有时间写这篇博客,今天呢,就先把我目前适配了的代码拿出来,后续还会继续适配的!

分平台解释:

皮皮虾

皮皮虾的话,我之前就已经写过一个单独的博客了,这里就不再赘述:—> Python爬虫:皮皮虾短视频无水印下载


该解析规则已失效,仅供学习使用,新版地址:Python爬虫:皮皮虾短视频无水印下载(新版)


皮皮搞笑

皮皮搞笑与皮皮虾很类似,也是先获取分享链接,在电脑端进行分析:

抓包分析


我们可以很容易的在抓包资源 HXR 中找到某一固定的链接: https://h5.ippzone.com/ppapi/share/fetch_content ,在该链接中的 video 字段可以看到有两个链接,分别打开尝试一下可以发现: 后缀含有 wm 的链接是含有水印的视频,另一个则是我们的目标链接了,但是我们又发现,在 video 字段下,还有一个全是数字的字符串,我们在当前 json 文件中搜索可以发现:

在上一个标签 img 下,有一个 id 字段,和字符串标签一样。
知道了视频链接的存放位置和获取方式,接下来开始分析请求:


在尝试过几次抓包后发现,请求 URL 始终都没有变化,只是下面的 请求负载 有所变化,第三个参数 post 默认不变就好,至于前两个参数,都在分享链接跳转的链接中:

https://h5.ippzone.com/pp/post/350259149175?zy_to=copy_link&share_count=1&m=0cd13da8548a1bc85813d8c60d331e22&app=&type=post&did=d2bddf23159ae495&mid=1270840711117&pid=350259149175

源码及结果

一切准备工作做好后,开始编写代码:

"""
CSDN :高智商白痴
CSDN个人主页:https://blog.csdn.net/qq_44700693
"""
class PPGX():  # 皮皮搞笑def __init__(self, url):s_url = urlself.headers = {'Host': 'share.ippzone.com','Origin': 'http://share.ippzone.com','Referer': s_url,'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.105 Safari/537.36 Edg/84.0.522.52'}self.JSON = {"pid": int(str(s_url).split('=')[-1]),"mid": int(str(s_url).split('&')[-2].split('=')[-1]),"type": "post"}def ppgx_download(self):"""CSDN :高智商白痴CSDN个人主页:https://blog.csdn.net/qq_44700693"""URL = 'http://share.ippzone.com/ppapi/share/fetch_content'r = requests.post(URL, proxies=proxy, headers=self.headers, json=self.JSON)video_name = r.json()['data']['post']['content'].replace(' ','')if video_name == '':video_name = int(random.random() * 2 * 1000)if len(str(video_name)) > 20:video_name = video_name[:20]video_url = r.json()['data']['post']['videos'][str(r.json()['data']['post']['imgs'][0]['id'])]['url']video = requests.get(video_url, proxies=proxy).contentwith open(path + str(video_name) + '.mp4', 'wb') as f:f.write(video)print("【皮皮搞笑】: {}.mp4 无水印视频下载完成!".format(video_name))

结果:


抖音 / 抖音极速版

接下来以 抖音 为例(抖音极速版的解析方式和抖音相同):
同样的思路,拿到分享链接到电脑浏览器中抓包分析:

拿到如下信息:
摆摊的第二天……#架子鼓演奏 #架子鼓 #乐器 #听心 https://v.douyin.com/JMKHkqt/ 复制此链接,打开【抖音短视频】,直接观看视频!
所以为了方便,使用正则表达式来提取该内容中的链接:
url = re.findall('(https?://[^\s]+)', s_url)[0] # 正则提取字符串中的链接

抓包分析


我们可以在抓包资源 HXR 中找到某一请求,在该 json 文件中 play_addr 字段下可以找到视频链接,用浏览器打开:

该链接跳转到了视频是没错,不过直接跳转到了又水印的链接上,这就有点卡住了。
不过,按照皮皮搞笑的链接区分来看, wm 是含有水印的视频的话。

https://aweme.snssdk.com/aweme/v1/playwm/?video_id=v0200f750000bsegsdpphaglno4mqd8g&ratio=720p&line=0

当我们删掉链接中的 wm 字段后:

没有加载??????? 而且我们可以发现链接根本都还没有跳转。
但是: 当我们把设备切换为手机时: 链接跳转到了无水印的视频链接:

代码及结果

当一切都分析完后,开始编写代码:
注意: 当我们去掉 wm 字段后,下载视频时,需要带上模拟手机端的请求头!

class DY():  # 抖音headers = {  # 模拟手机端'user-agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.3 Mobile/15E148 Safari/604.1 Edg/84.0.4147.105'}def __init__(self, s_url):self.url = re.findall('(https?://[^\s]+)', s_url)[0]  # 正则提取字符串中的链接def dy_download(self):"""CSDN :高智商白痴CSDN个人主页:https://blog.csdn.net/qq_44700693"""rel_url = str(requests.get(self.url, proxies=proxy, headers=self.headers).url)if 'video' == rel_url.split('/')[4]:URL = 'https://www.iesdouyin.com/web/api/v2/aweme/iteminfo/?item_ids=' + rel_url.split('/')[5] + '&dytk='r = requests.get(URL, proxies=proxy, headers=self.headers)video_url = r.json()['item_list'][0]['video']['play_addr']['url_list'][0].replace('/playwm/', '/play/')video_name = r.json()['item_list'][0]['share_info']['share_title'].split('#')[0].split('@')[0].replace(' ','')if video_name == '':video_name = int(random.random() * 2 * 1000)if len(str(video_name)) > 20:video_name = video_name[:20]video = requests.get(video_url, proxies=proxy, headers=self.headers).contentwith open(path + str(video_name) + '.mp4', 'wb') as f:f.write(video)print("【抖音短视频】: {}.mp4 无水印视频下载完成!".format(video_name))".format(video_name))

结果:由于抖音和抖音极速版的分享内容是一样的,无法分辨哪个平台,所以统一输出。

但是: 我发现抖音没事就爱搞幺蛾子,有时分享链接是上文所示:
有时又是一段原链接…所以我,做了一点小小的优化:

小优化:
if 'www.iesdouyin.com' in self.s_url:print("【抖音短视频】: {}.mp4 无水印视频下载完成!".format(video_name))
if 'v.douyin.com' in self.s_url:print("【抖音短视频/抖音极速版】: {}.mp4 无水印视频下载完成!".format(video_name))

腾讯微视

套路依旧,获取分享链接:

拿到链接:

上不上班无所谓,主要是想蹦迪https://h5.weishi.qq.com/weishi/feed/6XSB277Nr1K5nIKb6/wsfeed?wxplay=1&id=6XSB277Nr1K5nIKb6&spid=8813798054214369280&qua=v1_and_weishi_8.0.6_588_312028000_d&chid=100081014&pkg=3670&attach=cp_reserves3_1000370011

抓包及分析

注意: 这里是一个写爬虫的常用思路:将设备切换为手机,因为相对于电脑端,手机端的健壮性没有电脑端好,所以很多东西都可以通过这种方式来抓取,就如这个例子:

未切换:

已切换:


依次检查请求后发现:链接也就摆放在 json 数据中。
那么,接下来直接分析请求:


对于 请求负载 中的参数,我们可以直接在请求链接中截取:

https://h5.weishi.qq.com/weishi/feed/6XSB277Nr1K5nIKb6/wsfeed?wxplay=1&id=6XSB277Nr1K5nIKb6&spid=8813798054214369280&qua=v1_and_weishi_8.0.6_588_312028000_d&chid=100081014&pkg=3670&attach=cp_reserves3_1000370011

其他的参数默认就好,而至于请求链接,同一个视频刷新几次,参数 t 就有多少个值:

https://h5.weishi.qq.com/webapp/json/weishi/WSH5GetPlayPage?t=0.764612279656077&g_tk=
https://h5.weishi.qq.com/webapp/json/weishi/WSH5GetPlayPage?t=0.3168301677339891&g_tk=
https://h5.weishi.qq.com/webapp/json/weishi/WSH5GetPlayPage?t=0.8888910469548954&g_tk=

而且,有时候长度都不一样,这就把我吓到了!什么加密?这么复杂!
既然同一个视频每次刷新都不一样,是不是跟时间戳有关系???但是这明显不是啊!
正在我为这个参数发愁时,我也不知道我当时是怎么想的,无聊,随机修改了参数 t 的几个数字发现,仍然能够获取到视频!!!!!!!
然后我发现:所有的数全是在 0—1 之间变化,我用Python的 random 产生了一组随机数来看:

import random
print(random.random())#  结果:
#  0.5890812460827893

我都惊呆了!!就是这种数据啊!我用这个随机数去请求时,结果居然是可行的,这…
居然误打误撞的给破解了…

代码及结果
class TXWS():  # 腾讯微视headers = {  # 模拟手机端'user-agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.3 Mobile/15E148 Safari/604.1 Edg/84.0.4147.105'}def __init__(self, s_url):self.url = re.findall('(https?://[^\s]+)', s_url)[0]  # 正则提取字符串中的链接self.data = {'datalvl': "all",'feedid': str(self.url).split('/')[5],'recommendtype': '0','_weishi_mapExt': '{}'}def txws_download(self):  # 参数 t 为随机数"""CSDN :高智商白痴CSDN个人主页:https://blog.csdn.net/qq_44700693"""url = 'https://h5.weishi.qq.com/webapp/json/weishi/WSH5GetPlayPage?t={}&g_tk='.format(random.random())r = requests.post(url, proxies=proxy, headers=self.headers, data=self.data)video_name = r.json()['data']['feeds'][0]['feed_desc'].replace(' ','')if video_name == '':video_name = int(random.random() * 2 * 1000)if len(str(video_name)) > 20:video_name = video_name[:20]video_url = r.json()['data']['feeds'][0]['video_url']video = requests.get(video_url, proxies=proxy, headers=self.headers).contentwith open(path + str(video_name) + '.mp4', 'wb') as f:f.write(video)print("【腾讯微视】: {}.mp4 无水印视频下载完成!".format(video_name))

结果:


开眼 Eyepetizer

虽然 开眼 下载的视频,并没有水印,但是下载好的视频只能在软件内观看,但是我还是想让它下载到它该下载的地址:
套路,套路,还是套路:

抓包分析


还是很简单的,没什么反爬机制,很容易就会找到了视频的下载地址,接下来直接分析请求:

请求链接中只有几个数字需要解析,但是我们发现,它就出现在分享链接中:

https://www.eyepetizer.net/detail.html?vid=208234&utm_campaign=routine&utm_medium=share&utm_source=others&uid=0&resourceType=video&udid=c65aab71b05749d584eac4ee7944bb6274e17596&vc=6030061&vn=6.3.6&size=1080X2070&deviceModel=9&first_channel=xiaomi&last_channel=xiaomi&system_version_code=27

代码及结果
class KY_Eyepetizer():  # 开眼def __init__(self, url):self.vid = str(url).split('=')[1].split('&')[0]self.headers = {'origin': 'https://www.eyepetizer.net','referer': url,'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.105 Safari/537.36 Edg/84.0.522.58'}def ky_download(self):"""CSDN :高智商白痴CSDN个人主页:https://blog.csdn.net/qq_44700693"""url = 'https://baobab.kaiyanapp.com/api/v1/video/{}?f=web'.format(self.vid)r = requests.get(url, proxies=proxy, headers=self.headers)video_name = r.json()['title'].replace(' ','')if video_name == '':video_name = int(random.random() * 2 * 1000)if len(str(video_name)) > 20:video_name = video_name[:20]video_url = r.json()['playUrl']video = requests.get(video_url, proxies=proxy, headers=self.headers).contentwith open(path + str(video_name) + '.mp4', 'wb') as f:f.write(video)print("【开眼 Eyepetizer】: {}.mp4 无水印视频下载完成!".format(video_name))

结果:


快手/快手极速版

单个视频下载

我们还是以快手为例(快手极速版的解析规则和快手一样):
依旧按照套路来,不过因为快手的限制,必须登录才能分享:

抓包分析

按照以前的套路,将链接粘贴到浏览器,F12 抓包,结果并没有获取到任何与当前视频链接相关的信息,既然不在 json 文件中,难道在网页源码中???去挨个儿查看后,也不是,这该怎么爬取呢??
别忘了我前面提到的方法:更改设备。
当我把设备换成手机端后,json 数据中也还是没有相关数据,不过!!我在网页源码中找到了我们想要的链接:

到这里我们也已经找到了链接存放的地址,接下来则是,如何在这么多的字符里将链接提取出来呢???
答案是: 使用正则表达式 。

代码及结果
class KS():  # 快手def __init__(self, s_url):self.s_url=s_url.replace('\n','')self.url = re.findall('(https?://[^\s]+)', s_url)[0]  # 正则提取字符串中的链接self.headers = {'User-Agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.3 Mobile/15E148 Safari/604.1 Edg/84.0.4147.105'}def ks_download(self):"""CSDN :高智商白痴CSDN个人主页:https://blog.csdn.net/qq_44700693"""html = requests.get(self.url, headers=self.headers).textvideo_name = re.findall('name":"(.*?)"', html)[0].replace(' ','')if video_name == '':video_name = int(random.random() * 2 * 1000)if len(str(video_name)) > 20:video_name = video_name[:20]video_url = re.findall('srcNoMark":"(.*?)"', html)[0]video = requests.get(video_url, proxies=proxy, headers=self.headers).contentwith open(path + str(video_name) + '.mp4', 'wb') as f:f.write(video)if '【快手App】' in self.s_url:print("【快手】: {}.mp4 无水印视频下载完成!".format(video_name))elif '【快手极速版App】' in self.s_url:print("【快手极速版】: {}.mp4 无水印视频下载完成!".format(video_name))

结果:


用户视频下载

同样的我们拿到用户首页的分享地址:

看了这么多快手,还是「贝贝兔很」最好玩了! https://v.kuaishou.com/69cjtE 复制此链接,打开【快手】直接观看!

抓包分析

还是一贯的套路,更改设备,模拟手机向下刷新发现:

在新加载出的 json 文件中,含有两个重要信息:

  • feeds :包含第19条到37条的视频信息(不是链接,而是一个中亚参数,我后面会提到怎么使用)。
  • pcursor :请求某一部分视频的重要参数

既然包含了第19条到37条的视频,那么第1到第18条的视频信息在哪里呢?
根据前面的教训,我直接查看了切换到移动设备时的源码发现:

这里确实是有18条数据,而且每一个链接直接导向了该视频的首页,那么解析方式就和单个视频的下载方式一样了!
对于这18条数据,我们同样可以用正则表达式直接提取,但是后面的几十条数据就需要我们来进行解析了。

我们从新加载的 json 文件中可以看到,这里面包含了很多信息,但是却没有视频的链接,但是,既然刷新又必须加载它,说明肯定有什么重要的东西:
后来我发现正则表达式提取出的链接都有像是之处,所以我拿出了几个用正则表达式提取出的链接。(默认前缀)

/fw/photo/3x3m9e644ep95qg?cc=share_copylink&fid=574031739&shareId=227013708994&shareToken=X8rcLJByeLfC10c_A&appType=21&kpn=KUAISHOU
/fw/photo/3xggwttf4kquza9?cc=share_copylink&fid=574031739&shareId=227013708994&shareToken=X8rcLJByeLfC10c_A&appType=21&kpn=KUAISHOU

我发现对于同一个用户,所有视频的基本形式除了黄色部分不一样以外,其余的都是固定的!这不就是一个参数的问题嘛, json 文件中那么多的数据,不信找不到:我一一尝试后终于找到了那个参数:

既然我们已经知道如何拼接每个视频的链接,那么,最主要的就是如何获取那个视频的关键参数。
我前面已经说了,在 json 文件中的 pcursor 字段很是重要!


我尝试多次后终于发现发现:前一个 post 请求所返回数据中的 pcursor 字段的值,就是下一次请求所需要的参数!而两次请求中,视频的顺序刚好和用户的一样,所以如何请求后续的视频信息,我们已经知道方法了,不过,第一次的请求的参数是怎么来的呢??

解决: 在编写代码时,我给第一次 post 请求的参数 pcursor 赋了个空值,也是能够请求成功的!
而请求结束的标志就是:pcursor 字段的值等于 no_more

代码及结果
class KS():  # 快手def __init__(self, s_url):self.s_url=s_url.replace('\n','')self.url = re.findall('(https?://[^\s]+)', s_url)[0]  # 正则提取字符串中的链接self.headers = {'User-Agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.3 Mobile/15E148 Safari/604.1 Edg/84.0.4147.105'}self.video_list = []self.rel_url = requests.get(self.url, proxies=proxy, headers=self.headers)  # 真实网址def ks_download(self):"""CSDN :高智商白痴CSDN个人主页:https://blog.csdn.net/qq_44700693"""if 'user' != self.rel_url.url.split('/')[4]:self.ks_download_video()if 'user' == self.rel_url.url.split('/')[4]:self.ks_download_user()def ks_download_video(self):"""CSDN :高智商白痴CSDN个人主页:https://blog.csdn.net/qq_44700693"""video_name = re.findall('name":"(.*?)"', self.rel_url.text)[0].replace(' ', '')if video_name == '':video_name = int(random.random() * 2 * 1000)if len(str(video_name)) > 20:video_name = video_name[:20]video_url = re.findall('srcNoMark":"(.*?)"', self.rel_url.text)[0]video = requests.get(video_url, proxies=proxy, headers=self.headers).contentwith open(path + str(video_name) + '.mp4', 'wb') as f:f.write(video)if '【快手App】' in self.s_url:print("【快手】: {}.mp4 无水印视频下载完成!".format(video_name))elif '【快手极速版App】' in self.s_url:print("【快手极速版】: {}.mp4 无水印视频下载完成!".format(video_name))def ks_download_user(self):"""CSDN :高智商白痴CSDN个人主页:https://blog.csdn.net/qq_44700693"""global user_nameheaders = {'Cookie': '粘贴自己的Cookie信息','Origin': 'https://c.kuaishou.com','User-Agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.3 Mobile/15E148 Safari/604.1 Edg/84.0.4147.125'}rel_url = requests.get(self.url, proxies=proxy, headers=headers)  # 真实网址user_name = re.findall('<div class="name">(.*?)</div>', rel_url.text)[-1]if os.path.exists(path + user_name + '/'):passelse:os.makedirs(path + user_name + '/')videos = re.findall('<a href="(.*?)" role="ev" data-lazy=', rel_url.text)for video1 in videos:self.video_list.append('https://c.kuaishou.com' + video1)pcursor = ''url = 'https://c.kuaishou.com/rest/kd/feed/profile'flag = 1while flag:data = {"eid": str(rel_url.url).split('/')[-1].split('?')[0], "count": 18, "pcursor": pcursor}r = requests.post(url, proxies=proxy, headers=headers, json=data)for video2 in tqdm(r.json()['feeds'], desc='正在准备视频链接: '):photoId = video2['share_info'].split('=')[-1]temp_last = '?' + self.video_list[0].split('?')[-1]self.video_list.append('https://c.kuaishou.com/fw/photo/' + photoId + temp_last)pcursor = r.json()['pcursor']if r.json()['pcursor'] == "no_more":flag = 0print('用户 {} 共 {} 个视频!'.format(user_name,len(self.video_list)))for video_url in self.video_list:html = requests.get(video_url, headers={'User-Agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X)'})  # 真实网址print(html.text)video_name = int(random.random() * 2 * 1000)video_url = re.findall('srcNoMark":"(.*?)"', html.text)[0]video = requests.get(video_url, proxies=proxy, headers=headers).contentwith open(path + user_name + '/' + str(video_name) + '.mp4', 'wb') as f:f.write(video)print("【快手】: {}.mp4 无水印视频下载完成!".format(video_name))

结果:


抖音火山版/火山极速版

单个视频下载

对于这两个软件的解析,其实我是偷懒了,嘿嘿嘿,我翻看以前别人的博客,细细研究后发现了一个快手视频解析的接口:
其实很抱歉我忘记了我在哪个地方看到的那片文章,通过那篇文章,我们可以获取到原火山小视频的视频加载api :

https://api-hl.huoshan.com/hotsoon/item/video/_source/?item_id=6859730122820291840 原火山小视频无水印接口
https://api.huoshan.com/hotsoon/item/video/_reflow/?item_id=6859730122820291840 抖音火山版水印接口
https://api.huoshan.com/hotsoon/item/video/_source/?item_id=6859730122820291840 抖音火山版无水印接口

现在,已经知道视频加载的 api 后,就只需获取视频的 item_id 参数了。这个参数在跳转链接中就可以找到。

代码及结果:

所以我直接写出来解析代码:

class DY_HSB():headers = {   # 模拟手机'user-agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.3 Mobile/15E148 Safari/604.1 Edg/84.0.4147.105'}def __init__(self, s_url):self.s_url=s_urlself.url = re.findall('(https?://[^\s]+)', s_url)[0]  # 正则提取字符串中的链接def dyhsb_download(self):"""CSDN :高智商白痴CSDN个人主页:https://blog.csdn.net/qq_44700693"""rel_url = str(requests.get(self.url, proxies=proxy, headers=self.headers).url)video_name = int(random.random() * 2 * 1000)video_url = 'https://api.huoshan.com/hotsoon/item/video/_source/?item_id=' + \rel_url.split('=')[1].split('&')[0]video = requests.get(video_url, proxies=proxy, headers=self.headers).contentwith open(path + str(video_name) + '.mp4', 'wb') as f:f.write(video)if '【抖音火山版】' in self.s_url:print("【抖音火山版】: {}.mp4 无水印视频下载完成!".format(video_name))elif '【火山极速版】' in self.s_url:print("【火山极速版】: {}.mp4 无水印视频下载完成!".format(video_name))

结果:因为这个 api 无法获取其余信息,所以就以随机数来命名。

用户视频下载

我们还是拿到用户首页的分享链接:

「天使航拍」也在抖音火山版,快来看 TA 的精彩作品吧!「天使航拍」上传了 173 个视频作品,一起来围观>>https://share.huoshan.com/hotsoon/s/FJ0C7M5rWa8/ 复制此链接,打开【抖音火山版】,直接找到TA~

抓包分析


我们可以很快地找到一个用户视频的请求接口,然后拿到视频的 item_id 参数。但是!这个接口好像有参数去限制请求视频的数量,目前我只是试了一下修改请求参数,不过也只能最多爬取该用户40—50 个视频,如果以后有时间,我再去研究一下。
虽然请求参数较多,但是经过我的尝试,只有两个参数是必须的:

  • encrypted_id : 在请求跳转链接中以 to_user_id 的参数存在。
  • count :请求数量。(但是最多只能请求40—50个??这是怎么回事,我目前还没弄清楚!)

https://share.huoshan.com/pages/user/index.html?to_user_id=MS4wLjABAAAA6iUfN2mZ0H4Z7iLtZQ73TYdXoyTUIjk6oDdVWuRtn_g&timestamp=1597806131&share_ht_uid=0&did=67279005018&iid=3113420875114797&utm_medium=huoshan_android&tt_from=copy_link&app=live_stream&utm_source=copy_link&schema_url=sslocal%3A%2F%2Fprofile%3Fid%3D75014355319

代码及结果:
class DY_HSB():headers = {   # 模拟手机'user-agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.3 Mobile/15E148 Safari/604.1 Edg/84.0.4147.105'}def __init__(self, s_url):self.s_url=s_urlself.url = re.findall('(https?://[^\s]+)', s_url)[0]  # 正则提取字符串中的链接def dyhsb_download(self):"""CSDN :高智商白痴CSDN个人主页:https://blog.csdn.net/qq_44700693"""rel_url = str(requests.get(self.url, proxies=proxy, headers=self.headers).url)if 'item' == rel_url.split('/')[4]:  # 单个视频video_name = int(random.random() * 2 * 1000)video_url = 'https://api.huoshan.com/hotsoon/item/video/_source/?item_id=' + \rel_url.split('=')[1].split('&')[0]video = requests.get(video_url, proxies=proxy, headers=self.headers).contentwith open(path + str(video_name) + '.mp4', 'wb') as f:f.write(video)if '【抖音火山版】' in self.s_url:print("【抖音火山版】: {}.mp4 无水印视频下载完成!".format(video_name))elif '【火山极速版】' in self.s_url:print("【火山极速版】: {}.mp4 无水印视频下载完成!".format(video_name))if 'user' == rel_url.split('/')[4]:  # 用户视频########### 缺陷:最多支持下载 40--50 个该用户视频。##########to_user_id = rel_url.split('=')[1].split('&')[0]info_json = requests.get('https://share.huoshan.com/api/user/info?encrypted_id={}'.format(to_user_id))item_count = info_json.json()['data']['item_count']user_name = info_json.json()['data']['nickname']if os.path.exists(path + user_name + '/'):passelse:os.makedirs(path + user_name + '/')videos_url = 'https://share.huoshan.com/api/user/video?encrypted_id={}&count={}'.format(to_user_id,item_count)video_info = requests.get(videos_url, proxies=proxy, headers=self.headers).json()['data']for info in tqdm(video_info, desc='正在下载用户 {} 的视频:'.format(user_name)):video_name = int(random.random() * 2 * 1000)video_url = 'https://api.huoshan.com/hotsoon/item/video/_source/?item_id=' + info['item_id']video = requests.get(video_url, proxies=proxy, headers=self.headers).contentwith open(path + user_name + '/' + str(video_name) + '.mp4', 'wb') as f:f.write(video)if '【抖音火山版】' in self.s_url:print("【抖音火山版】: 用户 {} 的无水印视频下载完成!".format(user_name))elif '【火山极速版】' in self.s_url:print("【火山极速版】: 用户 {} 的无水印视频下载完成!".format(user_name))

结果:


最右

依旧是通过拿到分享链接,然后抓包分析:

拿到分享链接:

#最右#分享一条有趣的内容给你,不好看算我输。请戳链接>> https://share.izuiyou.com/hybrid/share/post?pid=191652885&zy_to=applink&share_count=1&m=ce86942098b72ec745e740e69ab9f6ec&d=fd238824d489ba3c1d65dfb74793074fd42ce27cafa76630b9eecfd7d657f50c&app=zuiyou&recommend=top_ctr&name=use_push_only&title_type=post

单个视频下载

基本思路也还是不变,更改设备后查看源码,将部分源码提取出来:



将这部分源码拿到 JSON在线解析的网站上去:

虽然提示有错误,不过我们可以编辑一下源码,改正这个错误:
先搜索错误的信息:

然后修改:

因为这个参数不影响我们的爬取,所以,无论改成什么字符都可以,切记一定要双引号!

随后,经过我的一番查找,终于找到了视频的无水印链接:

由于这并不是标准的 json 数据,所以,我们还是直接用正则表达式来提取。

代码及结果
class ZY():  # 最右headers = {  # 模拟成手机'Host': 'share.izuiyou.com','User-Agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.3 Mobile/15E148 Safari/604.1 Edg/84.0.4147.105'}def __init__(self, s_url):self.url = re.findall('(https?://[^\s]+)', s_url)[0]  # 正则提取字符串中的链接def zy_download(self):"""CSDN :高智商白痴CSDN个人主页:https://blog.csdn.net/qq_44700693"""url_flag = str(self.url).split('/')[3]html = requests.get(self.url, proxies=proxy, headers=self.headers).textflag = re.findall('"imgs":\[{"id":(.*?),"h":', html)[0]video_name = re.findall('{"id":.*?,"share":.*?,"content":"(.*?)","title":"', html)[0].replace(' ','')if video_name == '':video_name = int(random.random() * 2 * 1000)if len(str(video_name)) > 20:video_name = video_name[:20]video_url = re.findall(',"thumb":' + flag + ',"playcnt":.*?"url":"(.*?)","prior', html)[0] \.replace('u002F', '').replace('\\', '/')video = requests.get(video_url, proxies=proxy).contentwith open(path + str(video_name) + '.mp4', 'wb') as f:f.write(video)print("【最右】: {}.mp4 无水印视频下载完成!".format(video_name))

结果:

话题视频下载

同样的,我们拿到某一话题的链接:

#最右#发现一个超给力的话题,快来围观!请戳链接>> https://share.izuiyou.com/topic/429972?m=ce86942098b72ec745e740e69ab9f6ec&d=fd238824d489ba3c1d65dfb74793074fd42ce27cafa76630b9eecfd7d657f50c&app=zuiyou

代码及分析

对于这一话题,我们在电脑抓包 XHR 选项中可以看到有一个 list 字段,里面就是关于该话题的视频信息:

备注: 因为此次都是用电脑浏览器抓的包,并没有去分析手机端的 APP 请求方式,所以对于话题下载。只能下载前 10 个视频,后续有机会或者有需求的话,我会再抓包分析!
对与请求链接:https://share.izuiyou.com/api/topic/details ,并没有发生什么变化,但是传参就有点复杂了:

不过在我几次筛选之后发现有用的参数并不复杂:

  • app: 默认 “zuiyou” 就好!
  • d、m、tid: 虽然不知道这个参数是干嘛的,不过我们可以在主页链接中找到。注意: 参数 tid 是 int 类型的参数。
  • ua: 这个就很明显了啊,就是请求的 User-Agent

于是我们就可以构建一下请求负载:
这里需要注意的是:请求负载是 JSON 类型的数据,而不是最常见的 DATA

JSON = {'app': "zuiyou",'d': str(self.url).split('=')[2].split('&')[0],'m': str(self.url).split('=')[1].split('&')[0],'tid': int(str(self.url).split('/')[4].split('?')[0]),'ua': "Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.3 Mobile/15E148 Safari/604.1 Edg/84.0.4147.125"}

同样的我们发现,在 video 字段下,还有一个全是数字的字符串:

我们在当前 json 文件中搜索可以发现,在 img 字段的信息里有一串一模一样的字段:

代码及结果:

知道了视频链接保存的地方与请求方式,下面开始编写代码:

class ZY():  # 最右headers = {  # 模拟成手机'Host': 'share.izuiyou.com','User-Agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.3 Mobile/15E148 Safari/604.1 Edg/84.0.4147.105'}def __init__(self, s_url):self.url = re.findall('(https?://[^\s]+)', s_url)[0]  # 正则提取字符串中的链接def zy_download(self):"""CSDN :高智商白痴CSDN个人主页:https://blog.csdn.net/qq_44700693"""url_flag = str(self.url).split('/')[3]if 'hybrid' == url_flag:html = requests.get(self.url, proxies=proxy, headers=self.headers).textflag = re.findall('"imgs":\[{"id":(.*?),"h":', html)[0]video_name = re.findall('{"id":.*?,"share":.*?,"content":"(.*?)","title":"', html)[0].replace(' ','')if video_name == '':video_name = int(random.random() * 2 * 1000)if len(str(video_name)) > 20:video_name = video_name[:20]video_url = re.findall(',"thumb":' + flag + ',"playcnt":.*?"url":"(.*?)","prior', html)[0] \.replace('u002F', '').replace('\\', '/')video = requests.get(video_url, proxies=proxy).contentwith open(path + str(video_name) + '.mp4', 'wb') as f:f.write(video)print("【最右】: {}.mp4 无水印视频下载完成!".format(video_name))if 'topic' == url_flag:############  缺陷:话题区最多下载 10 个视频###########JSON = {'app': "zuiyou",'d': str(self.url).split('=')[2].split('&')[0],'m': str(self.url).split('=')[1].split('&')[0],'tid': int(str(self.url).split('/')[4].split('?')[0]),'ua': "Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.3 Mobile/15E148 Safari/604.1 Edg/84.0.4147.125"}URL = 'https://share.izuiyou.com/api/topic/details'r = requests.post(URL, json=JSON, proxies=proxy, headers=self.headers)video_info = r.json()['data']['list']type_name = r.json()['data']['topic']['topic'].replace(' ','')if os.path.exists(path + type_name + '/'):passelse:os.makedirs(path + type_name + '/')for video_info in tqdm(video_info, desc='正在下载类型 【{}】 的视频: '.format(type_name)):video_name = video_info['content'].replace(' ','')if video_name == '':video_name = int(random.random() * 2 * 1000)if len(str(video_name)) > 20:video_name = video_name[:20]flag = video_info['imgs'][0]['id']video_url = video_info['videos'][str(flag)]['url']video = requests.get(video_url, proxies=proxy).contentwith open(path + type_name + '/' + str(video_name) + '.mp4', 'wb') as f:f.write(video)print("【最右】: 类型 【{}】 无水印视频下载完成!".format(type_name))
小优化:
  • 1、因为后来我发现在话题区可能有图片的动态,不是视频的话,可能会出错,所以我对可能会出错的地方进行了处理
  • 2、当我在测试的过程中发现,有些视频的神评中也含有视频,所以我准备再加一点点代码,把那些也都下载下来:

先找到审评的保存地方:

对于神评中的视频,规则和单个视频很相似:在 video 字段下,还有一个全是数字的字符串:

在当前 json 文件中搜索可以发现,在 img 字段的信息里有一串一模一样的字段:

然而,也有可能在神评中没有视频或者没有神评,所以这一次直接把可能出现问题的地方处理掉:

#
#  上面不做修改
#
"""
CSDN :高智商白痴
CSDN个人主页:https://blog.csdn.net/qq_44700693
"""
if 'topic' == url_flag:############  缺陷:话题区最多下载 10 个视频###########JSON = {'app': "zuiyou",'d': str(self.url).split('=')[2].split('&')[0],'m': str(self.url).split('=')[1].split('&')[0],'tid': int(str(self.url).split('/')[4].split('?')[0]),'ua': "Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.3 Mobile/15E148 Safari/604.1 Edg/84.0.4147.125"}URL = 'https://share.izuiyou.com/api/topic/details'r = requests.post(URL, json=JSON, proxies=proxy, headers=self.headers)video_info = r.json()['data']['list']type_name = r.json()['data']['topic']['topic'].replace(' ','')if os.path.exists(path + type_name + '/'):passelse:os.makedirs(path + type_name + '/')for video_info in tqdm(video_info, desc='正在下载类型 【{}】 的视频: '.format(type_name)):video_name = video_info['content'].replace(' ','')if video_name == '':video_name = int(random.random() * 2 * 1000)if len(str(video_name)) > 20:video_name = video_name[:20]flag = video_info['imgs'][0]['id']try:video_url = video_info['videos'][str(flag)]['url']video = requests.get(video_url, proxies=proxy).contentwith open(path + type_name + '/' + str(video_name) + '.mp4', 'wb') as f:f.write(video)except:passtry:video_info1=video_info['god_reviews'][0]['videos']for a in tqdm(range(len(video_info1)),desc="正在下载该视频下的评论视频:"):flag1=video_info['god_reviews'][0]['imgs'][a]['id']video_url1=video_info['god_reviews'][0]['videos'][str(flag1)]['url']video_name = int(random.random() * 2 * 1000)video = requests.get(video_url1, proxies=proxy).contentwith open(path + type_name + '/' + str(video_name) + '.mp4', 'wb') as f:f.write(video)except:passprint("【最右】: 类型 【{}】 无水印视频下载完成!".format(type_name))

结果:


VUE

单个视频下载

vue平台虽然视频本身没有频台水印,但是却没有给我们提供下载功能,所以我才会编写这段代码:
同样拿到某一个视频的分享链接:

拿到的分享链接:

https://v.vuevideo.net/share/post/-5263687500195767823

抓包与分析


在电脑端打开分享网站后,又检查工具查看,我们所需要的东西都可以很轻松的找到,于是:

代码及结果
class VUE():  # VUEvlogheaders = {'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.125 Safari/537.36 Edg/84.0.522.59'}def __init__(self, url):self.url = urldef vue_download(self):"""CSDN :高智商白痴CSDN个人主页:https://blog.csdn.net/qq_44700693"""rel = requests.get(self.url, proxies=proxy, headers=self.headers)video_name = parsel.Selector(rel.text).xpath('//div[@class="videoTitle"]/text()').extract()[0].replace(' ','')if video_name == '':video_name = int(random.random() * 2 * 1000)if len(str(video_name)) > 20:video_name = video_name[:20]video_url = parsel.Selector(rel.text).xpath('//div[@class="videoContainer"]/video/@src').extract()[0]video = requests.get(video_url, proxies=proxy).contentwith open(path + str(video_name) + '.mp4', 'wb') as f:f.write(video)print("【VUE】: {}.mp4 视频下载完成!".format(video_name))

结果:

话题视频下载

对于话题区,我们也还是拿到分享链接:

https://v.vuevideo.net/share/topics/193

抓包与分析

代码及结果:

按照单个视频下载的方式,同样的能够获得我们所需要的所有信息,每一个 <li>…</li> 标签都包裹着每一个视频的信息,所以:

class VUE():  # VUEvlogheaders = {'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.125 Safari/537.36 Edg/84.0.522.59'}def __init__(self, url):self.url = urldef vue_download(self):"""CSDN :高智商白痴CSDN个人主页:https://blog.csdn.net/qq_44700693"""rel = requests.get(self.url, proxies=proxy, headers=self.headers)if str(rel.url).split('/')[4] == 'post':  # 单个视频video_name = parsel.Selector(rel.text).xpath('//div[@class="videoTitle"]/text()').extract()[0].replace(' ','')if video_name == '':video_name = int(random.random() * 2 * 1000)if len(str(video_name)) > 20:video_name = video_name[:20]video_url = parsel.Selector(rel.text).xpath('//div[@class="videoContainer"]/video/@src').extract()[0]video = requests.get(video_url, proxies=proxy).contentwith open(path + str(video_name) + '.mp4', 'wb') as f:f.write(video)print("【VUE】: {}.mp4 视频下载完成!".format(video_name))if str(rel.url).split('/')[4] == 'topics':  # 主题视频all_li = parsel.Selector(rel.text).xpath('//div[@class="info-layout"]').extract()topics_name = re.findall('><span>(.*?)</span><', rel.text)[0].replace(' ', '')for li_info in tqdm(all_li, desc="正在下载类型为 {} 的视频:".format(topics_name)):video_name = re.findall('="post-title-text">(.*?)</span></div', li_info)[0].replace(' ', '').replace(':', '')if video_name == '':video_name = int(random.random() * 2 * 1000)if len(str(video_name)) > 20:video_name = video_name[:20]video_url = re.findall('src="(.*?)"', li_info)[1].replace(' ', '')if os.path.exists(path + topics_name + '/'):passelse:os.makedirs(path + topics_name + '/')video = requests.get(video_url, proxies=proxy).contentwith open(path + topics_name + '/' + str(video_name) + '.mp4', 'wb') as f:f.write(video)print("【VUE】: 类型 【{}】 无水印视频下载完成!".format(topics_name))

结果:


看看视频

看看视频,是将一部电视剧的主要情节剪辑下来,让你在上厕所的时候追完一部剧。
按照我们以前的套路,我们肯定要先拿到视频的分享链接,不过,看看视频并没有给我们提供这样的功能,但是,我们直接分享到QQ时发现,这就是一个分享链接:

https://micro.kankan.com/index?productId=207063&setId=220133&productName=隐秘的角落&userId=-1&userid=-1&moviesId=207063

抓包与分析

将链接放到浏览器抓包可以很快的找到视频信息的保存地址:

同样的我们能够得到全部和单个视频的请求接口。

代码及结果

我们对结果进行梳理后,开始编写代码:

class KKSP():  # 看看视频def __init__(self, s_url):self.moviesId = str(s_url).split('=')[-1]print(self.moviesId)self.headers = {'Host': 'svideo-api.kankan.com','Origin': 'https://micro.kankan.com','Referer': s_url,'terminal': 'H5','User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.83 Safari/537.36 Edg/85.0.564.44','userid': '-1'}def kksp_download(self):"""CSDN :高智商白痴CSDN个人主页:https://blog.csdn.net/qq_44700693"""global video_url, video_nameurl = 'https://svideo-api.kankan.com/microvision/getSetListByMoviesId?moviesId=' + self.moviesIdprint(url)r = requests.get(url, proxies=proxy, headers=self.headers)video_infos = r.json()['data']['moviesSetList']name = r.json()['data']['moviesName']print(name)

但是当我运行以上代码时发现在 ’Referer’: s_url 的地方出现了错误,时编码的问题:

UnicodeEncodeError: 'latin-1' codec can't encode characters in position 73-77: ordinal not in range(256)
大致翻译为:
UnicodeEncodeError:“ latin-1”编解码器无法对位置73-77中的字符进行编码:序数不在范围内(256)
原因是链接中含有中文。

小优化
class KKSP():  # 看看视频def __init__(self, s_url):self.moviesId = str(s_url).split('=')[-1]self.headers = {'Host': 'svideo-api.kankan.com','Origin': 'https://micro.kankan.com','Referer': quote(s_url),'terminal': 'H5','User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.83 Safari/537.36 Edg/85.0.564.44','userid': '-1'}def kksp_download(self):"""CSDN :高智商白痴CSDN个人主页:https://blog.csdn.net/qq_44700693"""global video_url, video_nameurl = 'https://svideo-api.kankan.com/microvision/getSetListByMoviesId?moviesId=' + self.moviesIdprint(url)r = requests.get(url, proxies=proxy, headers=self.headers)video_infos = r.json()['data']['moviesSetList']name = r.json()['data']['moviesName']if os.path.exists(path + name):passelse:os.makedirs(path + name)video_num = 0for video_info in tqdm(video_infos, desc="正在下载 {}: ".format(name)):video_num += 1video_name = '第{}集 '.format(video_num) + video_info['des']if video_name == '':video_name = int(random.random() * 2 * 1000)if len(str(video_name)) > 20:video_name = video_name[:20]video_url = video_info['moviesSetScreenList'][0]['vodurl']video = requests.get(video_url, proxies=proxy).contentwith open(path + name + '/' + str(video_name) + '.mp4', 'wb') as f:f.write(video)print("【看看视频】: {}.mp4 视频下载完成!".format(name))

结果:


哔哩哔哩

由于B站不同于其他平台的短视频,所以我决定单独写一篇博客:
点击跳转:Python爬虫: 哔哩哔哩视频下载


AcFun

A站我也单独写了篇文章来介绍:点击跳转:Python爬虫:AcFun弹幕视频网


ZzzFun

Python爬虫:ZzzFun动漫视频网


西瓜视频

抓包分析

对于西瓜视频来说,既有安卓端的APP,也有网页版的主站。
但是呢,经过我的观察与验证发现,无论是哪个平台,请求资源的链接都是一样的,举个例子:

在安卓端拿到的分享链接:【https://v.ixigua.com/J4wB5ek/ 】
与在浏览器端拿到的链接:【https://www.ixigua.com/6873787245292159495/】
其实是定向到了同一个链接下:https://www.ixigua.com/6873787245292159495/

其实说白了就是,在安卓端拿到的分享链接是一个短的映射链接,而在浏览器地址栏显示的才是映射到的真正的地址链接,(不知道我说明白没有哈哈~~)

在我明白了以上的对应关系之后,我又发现了一个有用的信息,那就是:

APP端和电脑网页端在线播放时,视频本身就是加载的无水印的链接,而当我们用手机浏览器打开时会发现,即使是在线观看,也是有水印的视频。

知道了这些,那我们就可以确定,我们的请求头中 user-agent 字符,不能是手机的了。

既然APP端和电脑浏览器端都是一样的请求链接,那我们就不用再用APP端来获取链接了,直接再浏览器网页端点击岂不是更香~~

我们先随便点击一个视频打开调试工具:

让视频播放几秒钟后发现,网站一直在反复请求两个相识的链接,这是怎么回事呢?有种似曾相识的感觉…

没错~~就是和B站的加载机制相同:分别加载音频和视频。

我们先来查看网页源码:找到渲染完成的视频标签的位置后发现:

每个视频都是以 blob:https://… 的形式出现的。那么这条路就是行不通的。
接下来再来看看有没有什么可供使用的接口或者JSON数据,答案是:全都没有。这可就难办了,看着那些请求链接,有很多的参数,不过我发现:链接中的 ? 之后所有字段都不影响我们去请求资源,真正有用的就只有 ? 前面的字段。

举个例子:
链接一:https://v3-xg-web.ixigua.com/599d2dfe9869f497674442f1eb94a612/5fd385d0/video/tos/cn/tos-cn-vd-0026/f99dcd1c27af4b5d82fe4cd1be06b3b9/media-video-avc1/?a=1768&br=8283&bt=2761&cd=0%7C0%7C0&cr=0&cs=0&cv=1&dr=0&ds=4&er=0&l=202012112139480100220282230D1D20F6&lr=default&mime_type=video_mp4&qs=0&rc=ajhlc2kzc280djMzZDczM0ApZTg8NTg2Mzw4Nzc1ZTM0ZWdecTZzNC1famdfLS02LS9zcy1hXmMxMGAuY2NfYzQwLjM6Yw%3D%3D&vl=&vr=&range=0-1826
链接二:https://v3-xg-web.ixigua.com/8487c167d9680b36f5b19e039114f30d/5fd385d0/video/tos/cn/tos-cn-vd-0026/f99dcd1c27af4b5d82fe4cd1be06b3b9/media-audio-und-mp4a/?a=1768&br=0&bt=0&cd=0%7C0%7C0&cr=0&cs=0&cv=1&dr=0&ds=&er=0&l=202012112139480100220282230D1D20F6&lr=default&mime_type=video_mp4&qs=0&rc=ajhlc2kzc280djMzZDczM0ApZ15xNnM0LV9qZ18tLTYtL3NzOmM%3D&vl=&vr=&range=0-1743

那么中间的那些字段到底是怎么来的呢?我对每个字段进行搜索,结果连一条消息都没有找到,说明这个链接的参数没有在别的地方先生成或者加载。而是直接请求的该链接…

并且后来我发现,同一个视频,浏览器刷新后,参数是会改变的,并非像之前那样是固定生成的链接,所以,我们还得另辟途径~~

在这里,我就不提我遇到的那些坑了,(怕你们笑话,哈哈哈~)
我直接开始解释链接的加载位置。

我们打开某个视频的网页源码:

咦~~~~ 只有 23 行数据。不过我们都被骗了~~因为我发现在我浏览器的右下角有个这个东西:

原来有很多数据都被写在了一行… 把滚动条往右一拉,才发现有好多的JSON数据啊,话不多说,我们将之前的视频播放时加载的链接拿来一搜索,结果:

我去,这是怎么回事,我又将全部的网页源码拷贝出来,放在了我的前端编译器上进行格式化:

呵,数据藏得还不少!!!
我从上到下的继续翻看源码,发现,视频的一些信息确实是在这里:
就比如视频的名字:

紧接着我又发现了与视频清晰度相关的一些信息:

那么在这里是不是隐藏这一些视频的请求信息呢?
于是我又继续查找,知道我发现了一些重要的信息:

这…名字取得应该够明白了吧,确实是验证了我们之前所说的,该网站是将视频和音频分开请求的。然后我开始分析每一个参数到底是什么,不过我一眼 就看上了一个参数:

main_url :看到这个 main 我就觉得它不简单,于是我就先拿他下手:

"main_url":“aHR0cD…ZyPQ==” (太长了,我就用…代替了)

先拿出一个来看一看,后面的两个等号很熟悉吧,一串字符后面跟着等号,这让我以下就想到了 BASE64 加密,不管怎么说,我们先拿出来验证一下:

将链接拿到浏览器打开发现,结果还真是我们所要寻找的链接,那么按照这个规矩,音频的链接也是这么提取的,所以我们开始编写代码:

class XGSP:main_headers = {'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9','accept-language': 'zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6','cache-control': 'max-age=0','sec-fetch-dest': 'document','sec-fetch-mode': 'navigate','sec-fetch-site': 'none','sec-fetch-user': '?1','upgrade-insecure-requests': '1','user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.67 Safari/537.36 Edg/87.0.664.55'}def __init__(self, s_url):self.url = s_urldef XGSP_download(self):"""CSDN :高智商白痴CSDN个人主页:https://blog.csdn.net/qq_44700693"""r = requests.get(self.url, headers=self.main_headers)r.encoding = 'utf-8'video_info = (re.findall('"packerData":{"video":(.*?)}}}},"', r.text)[0] + "}}}}").replace("undefined",'"undefined"')video_json = json.loads(video_info)video_name = video_json["title"].replace("|", "-").replace(" ", "")print("视频名:" + video_name)video_url = base64.b64decode(video_json['videoResource']['dash']['dynamic_video']['dynamic_video_list'][-1]['main_url']).decode("utf-8")print("视频链接:" + video_url)audio_url = base64.b64decode(video_json['videoResource']['dash']['dynamic_video']['dynamic_audio_list'][-1]['main_url']).decode("utf-8")print("音频链接:" + audio_url)

注解:

  • 细心的朋友应该会发现,按照这个流程写下来的代码,获取到的网页源码全是乱码。那是在浏览器端复制过来的请求头中会有这样的字段:accept-encoding: gzip, deflate, br,但是我并没有写上去,反而在请求分享链接时进行了如下的设置:r.encoding = 'utf-8’,这样才能使网页源码正常爬取。
  • video_info = (re.findall(’“packerData”:{“video”: (.*?)}}}},"’, r.text)[0] + “}}}}”).replace(“undefined”, ‘“undefined”’) :为了减少需要再次清晰数据,我们直接用正则表达式提取出视频和音频的链接部分。因为视频不同, video 字段后的一个字段会有所不同,所以我直接拿比较明显的 4 个 **” } “**来截取,然后拼接上。
  • base64.b64decode(video_json[‘videoResource’][‘dash’][‘dynamic_video’][‘dynamic_video_list’][-1][‘main_url’]).decode(“utf-8”) :则是base64解码

现在我们就可以拿到每一次的视频和音频的链接了(我这里以最高的清晰度来作为演示)。

class XGSP:main_headers = {##  不变#}video_headers = {'accept': '*/*','accept-encoding': 'gzip, deflate, br','accept-language': 'zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6','origin': 'https://www.ixigua.com','referer': 'https://www.ixigua.com/','sec-fetch-dest': 'empty','sec-fetch-mode': 'cors','sec-fetch-site': 'same-site','user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36 Edg/87.0.664.57'}def __init__(self, s_url):self.url = s_urldef XGSP_download(self):"""CSDN :高智商白痴CSDN个人主页:https://blog.csdn.net/qq_44700693"""##  不变#with open(path + video_name + ".flv", "wb") as f:f.write(requests.get(video_url, headers=self.video_headers).content)print("视频文件下载完成...")with open(path + video_name + "-1.flv", "wb") as f:f.write(requests.get(audio_url, headers=self.video_headers).content)print("音视频均下载完成,即将开始拼接...")

现在我们已经下载好了视频和音频,接下来就需要将它们合并为一个视频,既然加载方式和B站很相似,那么下载方式也应该差不多:
Python爬虫:哔哩哔哩(bilibili)视频下载

类源码

或者直接参考我的代码:

class XGSP:main_headers = {'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9','accept-language': 'zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6','cache-control': 'max-age=0','sec-fetch-dest': 'document','sec-fetch-mode': 'navigate','sec-fetch-site': 'none','sec-fetch-user': '?1','upgrade-insecure-requests': '1','user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.67 Safari/537.36 Edg/87.0.664.55'}video_headers = {'accept': '*/*','accept-encoding': 'gzip, deflate, br','accept-language': 'zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6','origin': 'https://www.ixigua.com','referer': 'https://www.ixigua.com/','sec-fetch-dest': 'empty','sec-fetch-mode': 'cors','sec-fetch-site': 'same-site','user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36 Edg/87.0.664.57'}def __init__(self, s_url):"""CSDN :高智商白痴CSDN个人主页:https://blog.csdn.net/qq_44700693:param s_url: 视频分享链接"""self.url = s_urldef XGSP_download(self):r = requests.get(self.url, headers=self.main_headers)r.encoding = 'utf-8'video_info = (re.findall('"packerData":{"video":(.*?)}}}},"', r.text)[0] + "}}}}").replace("undefined",'"undefined"')video_json = json.loads(video_info)video_name = video_json["title"].replace("|", "-").replace(" ", "")print("视频名:" + video_name)video_url = base64.b64decode(video_json['videoResource']['dash']['dynamic_video']['dynamic_video_list'][-1]['main_url']).decode("utf-8")print("视频链接:" + video_url)audio_url = base64.b64decode(video_json['videoResource']['dash']['dynamic_video']['dynamic_audio_list'][-1]['main_url']).decode("utf-8")print("音频链接:" + audio_url)with open(path + video_name + ".flv", "wb") as f:f.write(requests.get(video_url, headers=self.video_headers).content)print("视频文件下载完成...")with open(path + video_name + "-1.flv", "wb") as f:f.write(requests.get(audio_url, headers=self.video_headers).content)print("音视频均下载完成,即将开始拼接...")video_add_mp3("D:/ffmpeg-2020-09-30-essentials_build/bin/", path, path + video_name + ".flv",path + video_name + "-1.flv")def video_add_mp3(ffmpeg_path, save_path, file1_path, file2_path):"""CSDN :高智商白痴CSDN个人主页:https://blog.csdn.net/qq_44700693ffmpeg -i video.mp4 -i audio.m4a -c:v copy -c:a copy output.mp4视频添加音频:param ffmpeg_path: ffmpeg的安装 bin 路径:param save_path: 文件保存路径:param file1_path: 传入视频频文件的路径:param file2_path: 传入音频文件的路径:return:""" mp4_name = file1_path.split('/')[-1].split('.')[0] + '-temp.mp4'mp3_name = file1_path.split('/')[-1].split('.')[0] + '-temp.mp3'outfile_name = file1_path.split('.')[0] + '.mp4'os.system(r'%sffmpeg -i %s %s' % (ffmpeg_path, file1_path, save_path + mp4_name))os.system(r'%sffmpeg -i %s %s' % (ffmpeg_path, file2_path, save_path + mp3_name))os.system(r'%sffmpeg -i %s -i %s -c:v copy -c:a copy %s' % (ffmpeg_path, save_path + mp4_name, save_path + mp3_name, outfile_name))os.remove(save_path + mp4_name)os.remove(save_path + mp3_name)os.remove(file1_path)os.remove(file2_path)

因为不知道原视频的真实格式,所以将视频和音频的格式都手动改为 flv 格式,在合并前进行格式转换。

结果


编写中。。。

Python爬虫:短视频平台无水印下载(上)相关推荐

  1. Python爬虫:短视频平台无水印下载!强不强?

    导入: 虽然目前有些软件还没适配,但是,我发了 Blink 后有一写人留言或者私信找我要源码,不过我还在增加适配的软件,所以还没有时间写这篇博客,今天呢,就先把我目前适配了的代码拿出来,后续还会继续适 ...

  2. python爬app无水印视频_Python爬虫:短视频平台无水印下载 (上)

    导入: 虽然目前有些软件还没适配,但是,我发了 Blink 后有一写人留言或者私信找我要源码,不过我还在增加适配的软件,所以还没有时间写这篇博客,今天呢,就先把我目前适配了的代码拿出来,后续还会继续适 ...

  3. Python爬虫:短视频平台无水印下载!这个操作有点东西!

    导入: 虽然目前有些软件还没适配,但是,我发了 Blink 后有一写人留言或者私信找我要源码,不过我还在增加适配的软件,所以还没有时间写这篇博客,今天呢,就先把我目前适配了的代码拿出来,后续还会继续适 ...

  4. 另一大短视频网址无水印下载,爱了吗?

    前言 一.爬虫思路 二.代码实现 1.引入库,设置UA 2.获得首次请求,提取源链接 3.通过真链接获取响应保存数据 总结 前言 呐,GoGoGO!!!再次走上爬虫道路了,这次的目标是一个叫" ...

  5. PHP短视频聚合无水印在线解析源码

    PHP短视频聚合无水印在线解析源码,一键解析,方便快捷 接口是作者那边的API,不保证时效性! 有技术的朋友可以更换成自己的,将复制的分享链接粘贴到输入框即可进行解析. 链接: https://pan ...

  6. Python实现将快手个人主页的视频进行无水印下载

    前言 本文是该专栏的第26篇,后面会持续分享python的各种干货知识,值得关注. 笔者之前有详细介绍快手滑块验证码的识别方法(Python如何解决"快手滑块验证码"(4)),感兴 ...

  7. 全网首创Python某短视频平台百叶窗短视频卡点自动混剪第1版

    最近朋友发给我一个某短视频平台的视频问我程序能不能做,于是乎打开电脑先瞅瞅. 这模板居然要6块钱,而且音乐.片段都限制太死了.不好不好,然后这视频看了几遍花了2个晚上搞出来一套Python全程素材内容 ...

  8. 产业分析:短视频平台研究报告

    导读: 首先,报告通过分析数字经济发展的现状,提出促进公平.创造社会价值等正在推动互联网企业进入发展的新阶段.短视频平台在价值层面上对公平和普惠的重视.在战略层面上向社会利益的倾斜和运营层面上对治理责 ...

  9. 短视频平台程序化广告设计方案

    移动互联网的发展,视频行业借势雄起,尤其短视频行业,快速获得了大批受众.数据显示,截至目前,抖音.快手月活用户规模都已达到6亿+的规模,在移动用户进入存量时代的当下,短视频平台仍然保持正增长.中国互联 ...

最新文章

  1. mysql oracle sqlit_【Go语言】连接数据库SQLite、MySQL、Oracle
  2. SAP 零售商品listing不成功,补充listing的方法
  3. Fast_Human_Pose_Estimation_Pytorch
  4. Visual Studio Code启动项目
  5. 需要自我总结的知识点
  6. XMLHttpResponse 在项目里面的运用
  7. SpringMVC学习笔记(二)常用注解
  8. TensorFlow1-张量
  9. Docker快速搭建TeamSpeak多人语音聊天服务器
  10. 【干货】神策数据朱德康:用户中台建设实践解析(附PPT下载链接)
  11. 每个数据科学家都应该知道的 20 个 NumPy 操作
  12. 大数据元数据管理系统功能有哪些
  13. Illustrator 教程,如何在 Illustrator 中创建无缝平铺图案?
  14. 医学图像分割--U-Net: Convolutional Networks for Biomedical Image Segmentation
  15. 移植oprofile到海思
  16. 整点小浪漫~Python27行代码绘制一幅满天星
  17. RTL8305NB傻瓜式交换机无法工作
  18. 利用BP神经网络求解非线性方程组
  19. Bat脚本学习之 常用测试脚本
  20. Win7系统运行慢如何解决?——系统天地

热门文章

  1. uni-app小程序 阿里巴巴字体图标兼容ios
  2. 微信公众号计算机编程,微信公众号群发文章怎么添加小程序?-电脑教程
  3. 一文看懂PPP协议——PPP协议简介
  4. [已解决]显示器分屏只能复制无法扩展
  5. FlightGear编译小结
  6. 第三方插件的引用(4):JAVA网站接入QQ登录
  7. debian技术--搭建debian软件仓库
  8. HTC Vive VR操作规范书
  9. 博客系统[Java]
  10. 关键词拍卖中最优保留价的研究