• 企业400电话
  • 微网小程序
  • AI电话机器人
  • 电商代运营
  • 全 部 栏 目

    企业400电话 网络优化推广 AI电话机器人 呼叫中心 网站建设 商标✡知产 微网小程序 电商运营 彩铃•短信 增值拓展业务
    Django实现在线无水印抖音视频下载(附源码及地址)

    项目地址是: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实现抖音视频批量下载
    上一篇:Django给表单添加honeypot验证增加安全性
    下一篇:Python 如何安装Selenium(推荐)
  • 相关文章
  • 

    © 2016-2020 巨人网络通讯 版权所有

    《增值电信业务经营许可证》 苏ICP备15040257号-8

    Django实现在线无水印抖音视频下载(附源码及地址) Django,实现,在线,无,水印,