Skip to content

Commit 3fe42a4

Browse files
authored
Create douyin.py
Create douyin.py
1 parent 8965d0b commit 3fe42a4

File tree

1 file changed

+174
-0
lines changed

1 file changed

+174
-0
lines changed

douyin/douyin.py

Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
# -*- coding:utf-8 -*-
2+
from contextlib import closing
3+
import requests, json, re, os, sys, random
4+
from ipaddress import ip_address
5+
from subprocess import Popen, PIPE
6+
7+
class DouYin(object):
8+
def __init__(self, width = 500, height = 300):
9+
"""
10+
抖音App视频下载
11+
"""
12+
rip = ip_address('.'.join(map(str, (random.randint(0, 255) for _ in range(4)))))
13+
while rip.is_private:
14+
rip = ip_address('.'.join(map(str, (random.randint(0, 255) for _ in range(4)))))
15+
self.headers = {
16+
'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8',
17+
'accept-encoding': 'gzip, deflate, br',
18+
'accept-language': 'zh-CN,zh;q=0.9',
19+
'cache-control': 'max-age=0',
20+
'upgrade-insecure-requests': '1',
21+
'user-agent': 'Mozilla/5.0 (Linux; U; Android 5.1.1; zh-cn; MI 4S Build/LMY47V) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/53.0.2785.146 Mobile Safari/537.36 XiaoMi/MiuiBrowser/9.1.3',
22+
'X-Real-IP': str(rip),
23+
'X-Forwarded-For': str(rip),
24+
}
25+
26+
def get_video_urls(self, user_id):
27+
"""
28+
获得视频播放地址
29+
Parameters:
30+
user_id:查询的用户ID
31+
Returns:
32+
video_names: 视频名字列表
33+
video_urls: 视频链接列表
34+
nickname: 用户昵称
35+
"""
36+
video_names = []
37+
video_urls = []
38+
share_urls = []
39+
unique_id = ''
40+
device_id = str(random.randint(3, 5)) + ''.join(map(str, (random.randint(0, 9) for _ in range(10))))
41+
search_url = 'https://api.amemv.com/aweme/v1/discover/search/?cursor=0&keyword={0}&count=10&type=1&retry_type=no_retry&device_id={1}&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&manifest_version_code=162&resolution=1080*1920&dpi=480&update_version_code=1622'.format(user_id, device_id)
42+
req = requests.get(search_url, headers=self.headers)
43+
html = json.loads(req.text)
44+
aweme_count = 32767 # html['user_list'][0]['user_info']['aweme_count']
45+
uid = html['user_list'][0]['user_info']['uid']
46+
nickname = html['user_list'][0]['user_info']['nickname']
47+
unique_id = html['user_list'][0]['user_info']['unique_id']
48+
if unique_id != user_id:
49+
unique_id = html['user_list'][0]['user_info']['short_id']
50+
if unique_id != user_id:
51+
print('用户ID可能输入错误或无法搜索到此用户ID')
52+
sys.exit()
53+
share_user_url = 'https://www.amemv.com/share/user/%s' % uid
54+
share_user = requests.get(share_user_url, headers=self.headers)
55+
_dytk_re = re.compile(r"dytk: '(.+)'")
56+
dytk = _dytk_re.search(share_user.text).group(1)
57+
print('签名JS下载中')
58+
self.video_downloader('https://github.com/Jack-Cherish/python-spider/blob/master/douyin/fuck-byted-acrawler.js', 'fuck-byted-acrawler.js')
59+
try:
60+
process = Popen(['node', 'fuck-byted-acrawler.js', str(uid)], stdout=PIPE, stderr=PIPE)
61+
except (OSError, IOError) as err:
62+
print('请先安装 node.js: https://nodejs.org/')
63+
sys.exit()
64+
sign = process.communicate()[0].decode()
65+
user_url = 'https://www.amemv.com/aweme/v1/aweme/post/?user_id=%s&max_cursor=0&count=%s&aid=1128&_signature=%s&dytk=%s' % (uid, aweme_count, sign, dytk)
66+
req = requests.get(user_url, headers=self.headers)
67+
html = json.loads(req.text)
68+
for each in html['aweme_list']:
69+
share_desc = each['share_info']['share_desc']
70+
if os.name == 'nt':
71+
for c in r'\/:*?"<>|':
72+
nickname = nickname.replace(c, '').strip()
73+
share_desc = share_desc.replace(c, '').strip()
74+
share_id = each['aweme_id']
75+
if share_desc in ['抖音-原创音乐短视频社区', 'TikTok']:
76+
video_names.append(share_id + '.mp4')
77+
else:
78+
video_names.append(share_id + '-' + share_desc + '.mp4')
79+
share_urls.append(each['share_info']['share_url'])
80+
video_urls.append(each['video']['play_addr']['url_list'][0])
81+
82+
return video_names, video_urls, share_urls, nickname
83+
84+
def get_download_url(self, video_url, watermark_flag):
85+
"""
86+
获得带水印的视频播放地址
87+
Parameters:
88+
video_url:带水印的视频播放地址
89+
Returns:
90+
download_url: 带水印的视频下载地址
91+
"""
92+
# 带水印视频
93+
if watermark_flag == True:
94+
download_url = video_url
95+
# 无水印视频
96+
else:
97+
download_url = video_url.replace('playwm', 'play')
98+
99+
return download_url
100+
101+
def video_downloader(self, video_url, video_name, watermark_flag=False):
102+
"""
103+
视频下载
104+
Parameters:
105+
video_url: 带水印的视频地址
106+
video_name: 视频名
107+
watermark_flag: 是否下载带水印的视频
108+
Returns:
109+
110+
"""
111+
size = 0
112+
video_url = self.get_download_url(video_url, watermark_flag=watermark_flag)
113+
with closing(requests.get(video_url, headers=self.headers, stream=True)) as response:
114+
chunk_size = 1024
115+
content_size = int(response.headers['content-length'])
116+
if response.status_code == 200:
117+
sys.stdout.write(' [文件大小]:%0.2f MB\n' % (content_size / chunk_size / 1024))
118+
119+
with open(video_name, 'wb') as file:
120+
for data in response.iter_content(chunk_size = chunk_size):
121+
file.write(data)
122+
size += len(data)
123+
file.flush()
124+
125+
sys.stdout.write(' [下载进度]:%.2f%%' % float(size / content_size * 100) + '\r')
126+
sys.stdout.flush()
127+
128+
def run(self):
129+
"""
130+
运行函数
131+
Parameters:
132+
None
133+
Returns:
134+
None
135+
"""
136+
self.hello()
137+
user_id = input('请输入ID(例如145651081):')
138+
watermark_flag = int(input('是否下载带水印的视频(0-否,1-是):'))
139+
video_names, video_urls, share_urls, nickname = self.get_video_urls(user_id)
140+
if nickname not in os.listdir():
141+
os.mkdir(nickname)
142+
print('视频下载中:共有%d个作品!\n' % len(video_urls))
143+
for num in range(len(video_urls)):
144+
print(' 解析第%d个视频链接 [%s] 中,请稍后!\n' % (num + 1, share_urls[num]))
145+
if '\\' in video_names[num]:
146+
video_name = video_names[num].replace('\\', '')
147+
elif '/' in video_names[num]:
148+
video_name = video_names[num].replace('/', '')
149+
else:
150+
video_name = video_names[num]
151+
if os.path.isfile(os.path.join(nickname, video_name)):
152+
print('视频已存在')
153+
else:
154+
self.video_downloader(video_urls[num], os.path.join(nickname, video_name), watermark_flag)
155+
print('\n')
156+
print('下载完成!')
157+
158+
def hello(self):
159+
"""
160+
打印欢迎界面
161+
Parameters:
162+
None
163+
Returns:
164+
None
165+
"""
166+
print('*' * 100)
167+
print('\t\t\t\t抖音App视频下载小助手')
168+
print('\t\t作者:Jack Cui')
169+
print('*' * 100)
170+
171+
172+
if __name__ == '__main__':
173+
douyin = DouYin()
174+
douyin.run()

0 commit comments

Comments
 (0)