前言
上周看到一个有趣的项目是使用Python+ADB做一个Python 抖音机器人 Douyin-Bot,自动翻页+颜值识别,自动点赞加关注,效果如下图,可以说是非常秀了。
源码地址: https://github.com/wangshub/Douyin-Bot
而我们今天实现的就是批量下载抖音视频,结合上面的机器人可以说是机器人届的蒂花之秀。
今天我们实现的抖音爬虫主要有下面四点功能:
废话说多了,先上一波爬取的结果:
下载视频截图
输出日志截图
引入类库
import requests
import json
import datetime
import re
import sys
import os
from urllib.parse import urlencode
from contextlib import closing
from requests.packages import urllib3
import random
本次代码的主要功能模块如下:
这次的项目主要是根据用户分享的链接自动下载,首先我们通过分享得到下面的链接:
# 这是用户主页的分享链接
https://www.douyin.com/share/user/61806758871/?share_type=link&from=singlemessage
# 这是音乐界面的分享链接
https://www.iesdouyin.com/share/music/6562721743650491139?timestamp=1528546868&utm_source=weixin&utm_campaign=client_share&utm_medium=android&app=aweme&iid=33943329942
# 这是主题界面的分享链接
https://www.iesdouyin.com/share/challenge/1602334725005380?timestamp=1528546923&utm_source=weixin&utm_campaign=client_share&utm_medium=android&app=aweme&iid=33943329942
根据上面的链接我们可以得到以下代码,并且获得唯一的ID标识
# 解析文件里面读取出来的链接
def parse_url(urls):
musics_id = []
challenges_id = []
users_id = []
for i in range(len(urls)):
url = urls[i]
if url:
# 分析链接是音乐链接
if re.search('share/music',url):
music_id = re.findall('share/music/(.*)\?', url)
# if len(musics_id):
musics_id.append(music_id[0])
for music in musics_id:
print(music)
if music not in os.listdir():
os.mkdir(music)
download_music_media(music)
# 分析链接是主题链接
if re.search('share/challenge', url):
challenge_id = re.findall('share/challenge/(.*)\?',url)
challenges_id.append(challenge_id[0])
for challenge in challenges_id:
if challenge not in os.listdir():
os.mkdir(challenge)
# print(challenge)
download_challenge_media(challenge)
# 分析链接是用户主页,请求下载的是用户喜欢的视频
if re.search('share/user', url):
user_id = re.findall('share/user/(.*)/\?',url)
users_id.append(user_id[0])
for u_id in users_id:
if u_id not in os.listdir():
os.mkdir(u_id)
# print(challenge)
download_ulike_media(u_id)
我们通过浏览器打开上面的链接可以获得以下的Headers信息:
headers = {
'user-agent':random.choice(hds),
'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8',
'accept-encoding': 'gzip, deflate, br',
'accept-language': 'zh-CN,zh;q=0.9',
'cache-control': 'max-age=0'
}
我们以第二个获取点赞视频为例,打开我们分享的链接,将开发者模式调整成手机版,点击「喜欢」可以看到请求的链接,如下图:
查看请求链接
获得请求参数 可以看到请求的参数中有一个很奇怪的参数_signature,且每次请求这个参数都不同,通过查阅gayhub上相关项目资料,发现这个参数是链接中的ID号加密获得,所以接下来可以通过调用加密JS对ID加密,就可以构建一个完整的请求。代码如下:
# 构建请求参数
def download_ulike_media(u_id):
p = os.popen('node fuck-byted-acrawler.js %s' % u_id)
signature = p.readlines()[0]
params = {
'user_id': str(u_id),
'count': '21',
'max_cursor': '0',
'aid': '1128',
'_signature': signature
}
可以看到上面调用了node.js来执行加密js,所以我们需要安装NODE.JS,安装文件在公众号后台回复「node」获取。 通过构建请求,我们顺利得到请求的结果,如下图,这个时候我们需要解析请求的数据得到视频的链接。
我们通过查看返回的数据,可以发现正确视频的链接形式如下:
https://www.amemv.com/share/video/xxxxxxxxxxx
在这里我们可以获得视频的id就可以构建完整的视频链接。代码如下:
# 拼接视频信息
def get_ulike_url(max_cursor=None, video_count=0):
video_names = []
video_urls = []
url = 'https://www.amemv.com/share/video/'
if max_cursor:
params['max_cursor'] = str(max_cursor)
ulike_url = 'https://www.douyin.com/aweme/v1/aweme/favorite/?' + urlencode(params)
# print(ulike_url)
res = requests.get(ulike_url, headers=headers, verify=False)
ulike_ms = json.loads(res.content.decode('utf-8'))
favorite_list = str(ulike_ms['aweme_list'])
v_id = re.findall('https://www.amemv.com/share/video/(.*?)\'',favorite_list)
for l in v_id:
share_desc = l + '.mp4'
s_url = url + l
video_names.append(share_desc)
video_urls.append(s_url)
parse_media_url(video_names, video_urls, u_id)
if ulike_ms.get('has_more') == 1:
return get_ulike_url(ulike_ms.get('max_cursor'), video_count)
video_count = get_ulike_url()
if video_count == 0:
print('这个用户没有喜欢的视频')
我们点击我们上面构建的视频链接,看下页面的具体情况是什么样的。
查看网页返回的信息 我们打开上图红框中的链接,可以看到是视频的资源地址。
打开源视频地址 按照上述的思路,我们可以构建以下的代码:
# 下载模块
def _download_video(video_url, path):
video_content = get_video_url(video_url)
# print(video_content)
rec = re.compile(r'class="video-player" src="(.*?)"')
pattern = re.compile(r'playwm')
downloadwm_url = rec.search(video_content).group(1)
# 构建无水印下载链接
download_url = re.sub(pattern, 'play', downloadwm_url)
print('正在下载:',download_url, path)
with closing(requests.get(download_url, headers=headers, stream=True, verify=False)) as response:
chunk_size = 1024
if response.status_code == 200:
with open(path, 'wb') as f:
for data in response.iter_content(chunk_size=chunk_size):
f.write(data)
# flush() 方法是用来刷新缓冲区的,即将缓冲区中的数据立刻写入文件,同时清空缓冲区,不需要是被动的等待输出缓冲区写入。
f.flush()
以上就是下载用户点赞视频的代码,相比于其他功能稍微复杂了点,其他功能通过手机抓包就可以获得请求接口,且没有加密参数。本次的项目代码基本类似,这里就以下载音乐视频的代码为例,讲下抓包部分: 这次使用的抓包工具是Charles,基础的配置可以看下面的文章: 10行代码实现自动参与抽奖助手抽奖(上) 配置好Charles后,打开抖音,通过刷新手机页面,可以看到左边栏的请求链接中有两处链接高亮,Charles截图如下:
Charles截图 点击开响应的数据可以看到每一个链接,我们只要解析每个链接中share_url中包含的videoid,再带入到API中就可以得到真实的视频地址了。
解析图中红框中链接包含的videoid 在测试API时强烈建议可以使用Postman来测试链接的可用性,以减少我们请求的参数数量和测试的复杂度。
测试接口时的部分截图