目录
- 实现原理
- # urls.py
- #web/urls.py
- # common/DouYin.py
- #common/utils.py
- # templates/home.html
- # templates/download.html
- 完整源码地址:
项目地址是:https://www.chenshiyang.com/dytk
接下来我们分析下源码简要看下实现原理。
实现原理
该项目不需要使用模型(models), 最核心的只有两个页面:一个主页面(home)展示包含下载url地址的表单,一个下载页面(download)处理表单请求,并展示去水印后的视频文件地址及文件大小,以及用于手机预览的二维码。
对应两个核心页面的路由如下所示,每个url对应一个视图函数。
# urls.py
from django.urls import path
from web.views import home, download
urlpatterns = [
path('home', home),
path('downloader', download),
]
#web/urls.py
from django.http import HttpResponse
from django.shortcuts import render, redirect
# Create your views here.
from common.utils import format_duration, load_media
from common.DouYin import DY
def home(request):
"""首页"""
return render(request, 'home.html')
def download(request):
"""下载"""
url = request.POST.get('url', None)
assert url != None
dy = DY()
data = dy.parse(url)
mp4_path, mp4_content_length = load_media(data['mp4'], 'mp4')
mp3_path, mp3_content_length = load_media(data['mp3'], 'mp3')
realpath = ''.join(['https://www.chenshiyang.com', mp4_path])
print('realpath---------------------', realpath)
if len(data['desc'].split('#')) > 2:
topic = data['desc'].split('#')[2].rstrip('#')
return render(request, 'download.html', locals())
可以看出通过home页面表单提交过来的下载url会交由download函数处理。common模块的DouYin.py中定义的DY类负责对url继续解析,爬取相关视频地址,通过自定义utils.py中的load_media方法下载文件,并返回文件路径以及文件大小。
由于解析下载url,从抖音爬取数据的代码都封装到DY类里了,所以我们有必要贴下这个类的代码。另外,我们还需要贴下load_media这个方法的代码。
# common/DouYin.py
# -*- coding: utf-8 -*-
# @Time : 2020-07-03 13:10
# @Author : chenshiyang
# @Email : chenshiyang@blued.com
# @File : DouYin.py
# @Software: PyCharm
import re
from urllib.parse import urlparse
import requests
from common.utils import format_duration
class DY(object):
def __init__(self, app=None):
self.app = app
if app is not None:
self.init_app(app)
self.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-encoding': 'gzip, deflate, br',
'accept-language': 'zh-CN,zh;q=0.9',
'cache-control': 'no-cache',
'cookie': 'sid_guard=2e624045d2da7f502b37ecf72974d311%7C1591170698%7C5184000%7CSun%2C+02-Aug-2020+07%3A51%3A38+GMT; uid_tt=0033579d9229eec4a4d09871dfc11271; sid_tt=2e624045d2da7f502b37ecf72974d311; sessionid=2e624045d2da7f502b37ecf72974d311',
'pragma': 'no-cache',
'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 (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'
}
self.domain = ['www.douyin.com',
'v.douyin.com',
'www.snssdk.com',
'www.amemv.com',
'www.iesdouyin.com',
'aweme.snssdk.com']
def init_app(self, app):
self.app = app
def parse(self, url):
share_url = self.get_share_url(url)
share_url_parse = urlparse(share_url)
if share_url_parse.netloc not in self.domain:
raise Exception("无效的链接")
dytk = None
vid = re.findall(r'\/share\/video\/(\d*)', share_url_parse.path)[0]
match = re.search(r'\/share\/video\/(\d*)', share_url_parse.path)
if match:
vid = match.group(1)
response = requests.get(
share_url,
headers=self.headers,
allow_redirects=False)
match = re.search('dytk: "(.*?)"', response.text)
if match:
dytk = match.group(1)
if vid:
return self.get_data(vid, dytk)
else:
raise Exception("解析失败")
def get_share_url(self, url):
response = requests.get(url,
headers=self.headers,
allow_redirects=False)
if 'location' in response.headers.keys():
return response.headers['location']
elif '/share/video/' in url:
return url
else:
raise Exception("解析失败")
def get_data(self, vid, dytk):
url = f"https://www.iesdouyin.com/web/api/v2/aweme/iteminfo/?item_ids={vid}dytk={dytk}"
response = requests.get(url, headers=self.headers, )
result = response.json()
if not response.status_code == 200:
raise Exception("解析失败")
item = result.get("item_list")[0]
author = item.get("author").get("nickname")
mp4 = item.get("video").get("play_addr").get("url_list")[0]
cover = item.get("video").get("cover").get("url_list")[0]
mp4 = mp4.replace("playwm", "play")
res = requests.get(mp4, headers=self.headers, allow_redirects=True)
mp4 = res.url
desc = item.get("desc")
mp3 = item.get("music").get("play_url").get("url_list")[0]
data = dict()
data['mp3'] = mp3
data['mp4'] = mp4
data['cover'] = cover
data['nickname'] = author
data['desc'] = desc
data['duration'] = format_duration(item.get("duration"))
return data
从代码你可以看到返回的data字典里包括了mp3和mp4源文件地址,以及视频的封面,作者昵称及描述等等。
接下来你可以看到load_media方法爬取了视频到本地,并提供了新的path和大小。
#common/utils.py
# -*- coding: utf-8 -*-
# @Time : 2020-06-29 17:26
# @Author : chenshiyang
# @Email : chenshiyang@blued.com
# @File : utils.py
# @Software: PyCharm
import os
import time
import requests
def format_duration(duration):
"""
格式化时长
:param duration 毫秒
"""
total_seconds = int(duration / 1000)
minute = total_seconds // 60
seconds = total_seconds % 60
return f'{minute:02}:{seconds:02}'
SUFFIXES = {1000: ['KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'],
1024: ['KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB']}
def approximate_size(size, a_kilobyte_is_1024_bytes=True):
'''Convert a file size to human-readable form.
Keyword arguments:
size -- file size in bytes
a_kilobyte_is_1024_bytes -- if True (default), use multiples of 1024
if False, use multiples of 1000
Returns: string
'''
if size 0:
raise ValueError('number must be non-negative')
multiple = 1024 if a_kilobyte_is_1024_bytes else 1000
for suffix in SUFFIXES[multiple]:
size /= multiple
if size multiple:
return '{0:.1f} {1}'.format(size, suffix)
raise ValueError('number too large')
def do_load_media(url, path):
"""
对媒体下载
:param url: 多媒体地址
:param path: 文件保存路径
:return: None
"""
try:
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"}
pre_content_length = 0
# 循环接收视频数据
while True:
# 若文件已经存在,则断点续传,设置接收来需接收数据的位置
if os.path.exists(path):
headers['Range'] = 'bytes=%d-' % os.path.getsize(path)
res = requests.get(url, stream=True, headers=headers)
content_length = int(res.headers['content-length'])
# 若当前报文长度小于前次报文长度,或者已接收文件等于当前报文长度,则可以认为视频接收完成
if content_length pre_content_length or (
os.path.exists(path) and os.path.getsize(path) == content_length):
break
pre_content_length = content_length
# 写入收到的视频数据
with open(path, 'ab') as file:
file.write(res.content)
file.flush()
print('receive data,file size : %d total size:%d' % (os.path.getsize(path), content_length))
return approximate_size(content_length, a_kilobyte_is_1024_bytes=False)
except Exception as e:
print('视频下载异常:{}'.format(e))
def load_media(url, path):
basepath = os.path.abspath(os.path.dirname(os.path.dirname(__file__)))
# 生成13位时间戳
suffixes = str(int(round(time.time() * 1000)))
path = ''.join(['/media/', path, '/', '.'.join([suffixes, path])])
targetpath = ''.join([basepath, path])
content_length = do_load_media(url, targetpath)
return path, content_length
def main(url, suffixes, path):
load_media(url, suffixes, path)
if __name__ == "__main__":
# url = 'https://aweme.snssdk.com/aweme/v1/play/?video_id=v0200fe70000br155v26tgq06h08e0lgratio=720pline=0'
# suffixes = 'test'
# main(url, suffixes, 'mp4',)
print(approximate_size(3726257, a_kilobyte_is_1024_bytes=False))
接下来我们看下模板, 这个没什么好说的。
# templates/home.html
{% extends "base.html" %}
{% block content %}
div class="jumbotron custom-jum no-mrg">
div class="container">
div class="row">
div class="col-md-12">
div class="center">
div class="home-search">
h1>抖音无水印视频下载器/h1>
h2>将抖音无水印视频下载到Mp4和Mp3/h2>
/div>
div class="form-home-search">
form id="form_download" action='https://www.chenshiyang.com/dytk/downloader' method='POST'>
div class="input-group col-lg-10 col-md-10 col-sm-10">
input name="url" class="form-control input-md ht58" placeholder="输入抖音视频 URL ..." type="text"
required="" value="">
span class="input-group-btn">button class="btn btn-primary input-md btn-download ht58" type="submit"
id="btn_submit">下载/button>/span>
/div>
/form>
/div>
/div>
/div>
/div>
/div>
/div>
/div>
{% endblock %}
# templates/download.html
{% extends "base.html" %}
{% block content %}
div class="page-content">
div class="container">
div class="row">
div class="col-lg-12 col-centered">
div class="ads mrg-bt20 text-center">
ins class="adsbygoogle" style="display:inline-block;width:728px;height:90px"
data-ad-client="ca-pub-2984659695526033" data-ad-slot="5734284394">/ins>
/div>
div class="card">
div class="row">
div class="col-md-4 col-sm-4">
a href="{{mp4_path}}" rel="external nofollow" rel="external nofollow" data-toggle="modal" class="card-aside-column img-video"
style="height: 252px; background: url(quot;{{data.cover}}quot;) 0% 0% / cover;" title="">
span class="btn-play-video">i class="glyphicon glyphicon-play">/i>/span>
p class="time-video" id="time">{{data.duration}}/p>
/a>
h5>作者: {{data.nickname}}/h5>
h5>a href="#" rel="external nofollow" >{{topic}} i class="open-new-window">/i>/a>/h5>
p class="card-text">{{data.desc}}/p>
/div>
div class="col-md-8 col-sm-8 col-table">
table class="table">
thead>
tr>
th>format/th>
th>size/th>
th>Downloads/th>
/tr>
/thead>
tbody>
tr>
td>mp4/td>
td>{{mp4_content_length}}/td>
td>
a href="{{mp4_path}}" rel="external nofollow" rel="external nofollow" class="btn btn-download" download="">下载/a>
/td>
/tr>
tr>
td>mp3/td>
td>{{mp3_content_length}}/td>
td>
a href="{{mp3_path}}" rel="external nofollow" class="btn btn-download" download="">下载/a>
/td>
/tr>
/tbody>
/table>
/div>
/div>
/div>
div class="card card-qrcode">
div class="row">
div class="col-md-12 qrcode">
div class="text-center">
p class="qrcode-p">扫描下面的二维码直接下载到您的智能手机或平板电脑!/p>
/div>
/div>
div class="col-md-4 col-centered qrcode">
div id="qrcode" title="{{realpath}}">
script src="/static/js/qrcode.min.js">/script>
script type="text/javascript">
new QRCode(document.getElementById("qrcode"), {
text: "{{realpath}}",
width: 120,
height: 120,
correctLevel: QRCode.CorrectLevel.L
});
/script>
/div>
/div>
/div>
/div>
/div>
/div>
/div>
/div>
{% endblock %}
完整源码地址:
https://github.com/tinysheepyang/python_api。
以上就是Django实现在线无水印抖音视频下载(附源码及地址)的详细内容,更多关于Django 无水印抖音视频下载的资料请关注脚本之家其它相关文章!
您可能感兴趣的文章:- python tkinter实现下载进度条及抖音视频去水印原理
- python gui开发——制作抖音无水印视频下载工具(附源码)
- 使用Python下载抖音各大V视频的思路详解
- 基于Python实现全自动下载抖音视频
- Python爬虫 批量爬取下载抖音视频代码实例
- python批量爬取下载抖音视频
- python批量下载抖音视频
- python3下载抖音视频的完整代码
- python实现抖音视频批量下载