本文爲兩類人準備:技術控和工具控。html
抖音短視頻解析下載平臺python
Python3批量下載抖音無水印視頻android
本文的代碼已經不是最新的,可是抓取思路就是如此,能夠參考,代碼能夠直接運行使用,持續維護中。git
抖音愈來愈火,感受它有毒,越刷越上癮,總感受下一個視頻必定會更精彩,根本停不下來。想將抖音裏喜歡的小哥哥/小姐姐的視頻所有存到電腦硬盤裏該如何操做?不想有抖音的視頻水印該如何處理?github
當初寫完代碼的截屏:web
首先,但願你已經具有手機APP抓包分析的能力,若是不會請去自行學習:點擊跳轉chrome
先說說帶水印的視頻如何抓去吧。在定好爬取目標的時候,咱們應該知道本身須要那些步驟完成這項任務。好比本文中提到的任務:抖音APP固定用戶的視頻批量下載。json
思考過程:sublime-text
瞧,這樣思考下來,問題是否是梳理的很清楚?api
搜索接口:
那麼接下來就是抓包分析了,抓包過程請自行嘗試。步驟是這樣的:
經過分析你會發現,咱們經過搜索接口返回的JSON數據能夠找到用戶主頁信息,接下里用一樣的方法抓取主頁用戶信息再分析一波,這時候就遇到問題了,你會發現用戶主頁連接使用了as和cp參數進行了加密,這該如何是好?好比連接以下:
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自帶視頻分享功能,分享連接格式以下:
1
|
https://www.douyin.com/share/video/6511132370416962829/?region=CN...share_iid=28037626243
|
中間參數都不重要,在此省略。www.douyin.com域名下存放的是分享的視頻,那麼這個用戶主頁信息是否能夠經過這個域名進行訪問呢?小小測試一下你會發現,徹底沒有問題!
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不調出瀏覽器界面,直接在後臺模擬各類請求,非常方便。
這部分的代碼很簡單,無非就是填充元素,肯定解析按鈕位置,點擊按鈕,獲取視頻下載連接便可。這點小問題,就自行分析吧。
總體代碼:
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()
|
方法二:
這個方法是經過網友@羽葵的反饋得知的,對下載連接直接修改便可獲得無水印下載連接。
1
|
download_url = video_html['video']['play_addr']['url_list'][0].replace('playwm','play')
|
方法簡單粗暴,很好用。好處就是處理速度飛快,缺點是這種方法通用性不強,不一樣視頻發佈平臺的打碼方法可能有不一樣,須要自行分析。
玩爬蟲的日子仍是頗有意思的,很久沒有那種舒爽感了。還有,找工做也是蠻心累的事。