本文为两类人准备:技术控和工具控。
抖音短视频解析下载平台
Python3批量下载抖音无水印视频
本文的代码已经不是最新的,但是抓取思路就是如此,可以参考,代码可以直接运行使用,持续维护中。
抖音越来越火,感觉它有毒,越刷越上瘾,总感觉下一个视频一定会更精彩,根本停不下来。想将抖音里喜欢的小哥哥/小姐姐的视频全部存到电脑硬盘里该如何操作?不想有抖音的视频水印该如何处理?
当初写完代码的截屏:
注意,光理论是不够的。这里顺便送大家一套2020最有趣的Pyhon项目实战视频教程,点击此处 进来获取 跟着练习下,希望大家一起进步哦!
目录
一、前言
更新日志
二、实战背景
三、实战
1、带水印视频下载
2、无水印视频下载
四、总结
进来获取 跟着练习下,希望大家一起进步哦!
首先,希望你已经具备手机APP抓包分析的能力,如果不会请去自行学习:点击跳转
先说说带水印的视频如何抓去吧。在定好爬取目标的时候,我们应该知道自己需要那些步骤完成这项任务。比如本文中提到的任务:抖音APP固定用户的视频批量下载。
思考过程:
瞧,这样思考下来,问题是不是梳理的很清楚?
搜索接口:
那么接下来就是抓包分析了,抓包过程请自行尝试。步骤是这样的:
通过分析你会发现,我们通过搜索接口返回的JSON数据可以找到用户主页信息,接下里用同样的方法抓取主页用户信息再分析一波,这时候就遇到问题了,你会发现用户主页链接使用了as和cp参数进行了加密,这该如何是好?比如
Python
1 |
https://aweme.snssdk.com/aweme/v1/aweme/post/?user_id=63386731255&max_cursor=0&count=20...&as=a18575a0311bfa0c2d&cp=55bba65311d10ccde1 |
上述链接省略号部分是一些手机信息,这部分不是必须参数,可以省略。user_id是用户id可以通过上个搜索接口获取,count是用户视频数量,同样可以通过上个搜索接口获取。那最后的as和cp参数怎么办?
我没有逆向抖音APP,就是小小测试了一下,看看能不能绕过这个加密接口?抖音APP自带视频分享功能,分享链接Python
1 |
https://www.douyin.com/share/video/6511132370416962829/?region=CN...share_iid=28037626243 |
中间参数都不重要,在此省略。www.douyin.com域名下存放的是分享的视频,那么这个用户主页信息是否可以通过这个域名进行访问呢?小小测试一下你会发现,完全没有问题!
Python
1 |
https://www.douyin.com/aweme/v1/aweme/post/?user_id=63386731255&max_cursor=0&count=20 |
这就是没有加密的接口,惊不惊喜,意不意外?根据这个用户主页接口,我们就可以轻松获取用户主页所有的视频链接了。
方法一:
无水印视频下载很简单,有一个通用的方法,就是使用去水印平台即可。
我使用的去水印平台是:http://douyin.iiilab.com/
在输入框中输入视频链接点击视频解析,就可以获得无水印视频链接。
这个网站当初我写代码的时候是好使的,当初用这个网站下了一些无水印视频,不过写这篇文章的时候发现这个取水印平台无法正常解析了,等它修复好了再用这个功能吧。
这个平台不仅包括抖音视频去水印,还支持火山、快手、陌陌、美拍等无水印视频。所以做一个这个网站的接口还是很合适的。
简单测试了一下,这个网站的API是需要付费解析的,如果通过模拟请求的方式有些困难,因此决定上浏览器模拟器Splinter。
Splinter是个好东西,跟Selenium使用类似,它的配置可以参考我的早期Selenium文章:http://blog.csdn.net/c406495762/article/details/72331737
Splinter有个很详细的英文文档:http://splinter.readthedocs.io/en/latest/
这里使用方法就不累述,不过有一点可以说的是,我们可以配置headless参数,来将Splinter配置为无头浏览器,啥事无头浏览器呢?就是运行Splinter不调出浏览器界面,直接在后台模拟各种请求,很是方便。
这部分的代码很简单,无非就是填充元素,确定解析按钮位置,点击按钮,获取视频下载链接即可。这点小问题,就自行分析吧。
整体代码
Python
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 |
# -*- coding:utf-8 -*- from splinter.driver.webdriver.chrome import Options, Chrome from splinter.browser import Browser from contextlib import closing import requests, json, time, re, os, sys, time from bs4 import BeautifulSoup
class DouYin(object): def __init__(self, width = 500, height = 300): """ 抖音App视频下载 """ # 无头浏览器 chrome_options = Options() chrome_options.add_argument('user-agent="Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36"') self.driver = Browser(driver_name='chrome', executable_path='D:/chromedriver', options=chrome_options, headless=True)
def get_video_urls(self, user_id): """ 获得视频播放地址 Parameters: user_id:查询的用户ID Returns: video_names: 视频名字列表 video_urls: 视频链接列表 nickname: 用户昵称 """ video_names = [] video_urls = [] unique_id = '' while unique_id != user_id: search_url = 'https://api.amemv.com/aweme/v1/discover/search/?cursor=0&keyword=%s&count=10&type=1&retry_type=no_retry&iid=17900846586&device_id=34692364855&ac=wifi&channel=xiaomi&aid=1128&app_name=aweme&version_code=162&version_name=1.6.2&device_platform=android&ssmix=a&device_type=MI+5&device_brand=Xiaomi&os_api=24&os_version=7.0&uuid=861945034132187&openudid=dc451556fc0eeadb&manifest_version_code=162&resolution=1080*1920&dpi=480&update_version_code=1622' % user_id req = requests.get(url = search_url, verify = False) html = json.loads(req.text) aweme_count = html['user_list'][0]['user_info']['aweme_count'] uid = html['user_list'][0]['user_info']['uid'] nickname = html['user_list'][0]['user_info']['nickname'] unique_id = html['user_list'][0]['user_info']['unique_id'] user_url = 'https://www.douyin.com/aweme/v1/aweme/post/?user_id=%s&max_cursor=0&count=%s' % (uid, aweme_count) req = requests.get(url = user_url, verify = False) html = json.loads(req.text) i = 1 for each in html['aweme_list']: share_desc = each['share_info']['share_desc'] if '抖音-原创音乐短视频社区' == share_desc: video_names.append(str(i) + '.mp4') i += 1 else: video_names.append(share_desc + '.mp4') video_urls.append(each['share_info']['share_url'])
return video_names, video_urls, nickname
def get_download_url(self, video_url): """ 获得带水印的视频播放地址 Parameters: video_url:带水印的视频播放地址 Returns: download_url: 带水印的视频下载地址 """ req = requests.get(url = video_url, verify = False) bf = BeautifulSoup(req.text, 'lxml') script = bf.find_all('script')[-1] video_url_js = re.findall('var data = \[(.+)\];', str(script))[0] video_html = json.loads(video_url_js) download_url = video_html['video']['play_addr']['url_list'][0] return download_url
def video_downloader(self, video_url, video_name, watermark_flag=False): """ 视频下载 Parameters: video_url: 带水印的视频地址 video_name: 视频名 watermark_flag: 是否下载不带水印的视频 Returns: 无 """ size = 0 if watermark_flag == True: video_url = self.remove_watermark(video_url) else: video_url = self.get_download_url(video_url) with closing(requests.get(video_url, stream=True, verify = False)) as response: chunk_size = 1024 content_size = int(response.headers['content-length']) if response.status_code == 200: sys.stdout.write(' [文件大小]:%0.2f MB\n' % (content_size / chunk_size / 1024))
with open(video_name, "wb") as file: for data in response.iter_content(chunk_size = chunk_size): file.write(data) size += len(data) file.flush()
sys.stdout.write(' [下载进度]:%.2f%%' % float(size / content_size * 100) + '\r') sys.stdout.flush()
def remove_watermark(self, video_url): """ 获得无水印的视频播放地址 Parameters: video_url: 带水印的视频地址 Returns: 无水印的视频下载地址 """ self.driver.visit('http://douyin.iiilab.com/') self.driver.find_by_tag('input').fill(video_url) self.driver.find_by_xpath('//button[@class="btn btn-default"]').click() html = self.driver.find_by_xpath('//div[@class="thumbnail"]/div/p')[0].html bf = BeautifulSoup(html, 'lxml') return bf.find('a').get('href')
def run(self): """ 运行函数 Parameters: None Returns: None """ self.hello() user_id = input('请输入ID(例如40103580):') video_names, video_urls, nickname = self.get_video_urls(user_id) if nickname not in os.listdir(): os.mkdir(nickname) print('视频下载中:共有%d个作品!\n' % len(video_urls)) for num in range(len(video_urls)): print(' 解析第%d个视频链接 [%s] 中,请稍后!\n' % (num+1, video_urls[num])) if '\\' in video_names[num]: video_name = video_names[num].replace('\\', '') elif '/' in video_names[num]: video_name = video_names[num].replace('/', '') else: video_name = video_names[num] self.video_downloader(video_urls[num], os.path.join(nickname, video_name)) print('\n')
print('下载完成!')
def hello(self): """ 打印欢迎界面 Parameters: None Returns: None """ print('*' * 100) print('\t\t\t\t抖音App视频下载小助手') print('\t\t作者:Jack Cui') print('*' * 100)
if __name__ == '__main__': douyin = DouYin() douyin.run() |
方法二:
这个方法是通过网友@羽葵的反馈得知的,对下载链接直接修改即可得到无水印下载链接。
Python
1 |
download_url = video_html['video']['play_addr']['url_list'][0].replace('playwm','play') |
方法简单粗暴,很好用。好处就是处理速度飞快,缺点是这种方法通用性不强,不同视频发布平台的打码方法可能有不同,需要自行分析。
玩爬虫的日子还是很有意思的,好久没有那种舒爽感了。还有,找工作也是蛮心累的事。最后送大家一套2020最有趣的Pyhon项目实战视频教程,点击此处 进来获取 跟着练习下,希望大家一起进步哦!
本文的文字及图片来源于网络加上自己的想法,仅供学习、交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理。