欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Django實(shí)現(xiàn)在線無(wú)水印抖音視頻下載(附源碼及地址)

 更新時(shí)間:2021年05月06日 15:17:06   作者:大江狗  
該項(xiàng)目功能簡(jiǎn)單,完全復(fù)制SaveTweetVedio的項(xiàng)目。用戶觀看抖音視頻時(shí)選擇復(fù)制視頻鏈接,輸入到下載輸入欄,即可下載無(wú)水印視頻,還可掃描二維碼手機(jī)上預(yù)覽。親測(cè)成功。

項(xiàng)目地址是:https://www.chenshiyang.com/dytk

接下來(lái)我們分析下源碼簡(jiǎn)要看下實(shí)現(xiàn)原理。

實(shí)現(xiàn)原理

該項(xiàng)目不需要使用模型(models), 最核心的只有兩個(gè)頁(yè)面:一個(gè)主頁(yè)面(home)展示包含下載url地址的表單,一個(gè)下載頁(yè)面(download)處理表單請(qǐng)求,并展示去水印后的視頻文件地址及文件大小,以及用于手機(jī)預(yù)覽的二維碼。

對(duì)應(yīng)兩個(gè)核心頁(yè)面的路由如下所示,每個(gè)url對(duì)應(yīng)一個(gè)視圖函數(shù)。

# 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):
    """首頁(yè)"""
    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())

可以看出通過(guò)home頁(yè)面表單提交過(guò)來(lái)的下載url會(huì)交由download函數(shù)處理。common模塊的DouYin.py中定義的DY類負(fù)責(zé)對(duì)url繼續(xù)解析,爬取相關(guān)視頻地址,通過(guò)自定義utils.py中的load_media方法下載文件,并返回文件路徑以及文件大小。

由于解析下載url,從抖音爬取數(shù)據(jù)的代碼都封裝到DY類里了,所以我們有必要貼下這個(gè)類的代碼。另外,我們還需要貼下load_media這個(gè)方法的代碼。

# 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("無(wú)效的鏈接")
        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源文件地址,以及視頻的封面,作者昵稱及描述等等。

接下來(lái)你可以看到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):
    """
    格式化時(shí)長(zhǎng)
    :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):
    """
    對(duì)媒體下載
    :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

        # 循環(huán)接收視頻數(shù)據(jù)
        while True:
            # 若文件已經(jīng)存在,則斷點(diǎn)續(xù)傳,設(shè)置接收來(lái)需接收數(shù)據(jù)的位置
            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'])
            # 若當(dāng)前報(bào)文長(zhǎng)度小于前次報(bào)文長(zhǎng)度,或者已接收文件等于當(dāng)前報(bào)文長(zhǎng)度,則可以認(rèn)為視頻接收完成
            if content_length < pre_content_length or (
                    os.path.exists(path) and os.path.getsize(path) == content_length):
                break
            pre_content_length = content_length

            # 寫(xiě)入收到的視頻數(shù)據(jù)
            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位時(shí)間戳
    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=v0200fe70000br155v26tgq06h08e0lg&ratio=720p&line=0'
    # suffixes = 'test'
    # main(url, suffixes, 'mp4',)

    print(approximate_size(3726257, a_kilobyte_is_1024_bytes=False))

接下來(lái)我們看下模板, 這個(gè)沒(méi)什么好說(shuō)的。

# 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>抖音無(wú)水印視頻下載器</h1>
              <h2>將抖音無(wú)水印視頻下載到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">掃描下面的二維碼直接下載到您的智能手機(jī)或平板電腦!</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實(shí)現(xiàn)在線無(wú)水印抖音視頻下載(附源碼及地址)的詳細(xì)內(nèi)容,更多關(guān)于Django 無(wú)水印抖音視頻下載的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • python 阿里云oss實(shí)現(xiàn)直傳簽名與回調(diào)驗(yàn)證的示例方法

    python 阿里云oss實(shí)現(xiàn)直傳簽名與回調(diào)驗(yàn)證的示例方法

    這篇文章主要介紹了python 阿里云oss實(shí)現(xiàn)直傳簽名與回調(diào)驗(yàn)證,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2021-03-03
  • 使用Python實(shí)現(xiàn)將數(shù)據(jù)寫(xiě)入Excel工作表

    使用Python實(shí)現(xiàn)將數(shù)據(jù)寫(xiě)入Excel工作表

    在數(shù)據(jù)處理和報(bào)告生成等工作中,Excel?表格是一種常見(jiàn)且廣泛使用的工具,本文中將介紹如何使用?Python?寫(xiě)入數(shù)據(jù)到?Excel?表格,并提供更高效和準(zhǔn)確的?Excel?表格數(shù)據(jù)寫(xiě)入方案,需要的可以參考下
    2024-01-01
  • Python Socket編程詳細(xì)介紹

    Python Socket編程詳細(xì)介紹

    這篇文章主要介紹了Python Socket編程詳細(xì)介紹,socket可以建立連接,傳遞數(shù)據(jù),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下。
    2017-03-03
  • Python搭建代理IP池實(shí)現(xiàn)檢測(cè)IP的方法

    Python搭建代理IP池實(shí)現(xiàn)檢測(cè)IP的方法

    這篇文章主要介紹了Python搭建代理IP池實(shí)現(xiàn)檢測(cè)IP的方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-10-10
  • Django讀取Mysql數(shù)據(jù)并顯示在前端的實(shí)例

    Django讀取Mysql數(shù)據(jù)并顯示在前端的實(shí)例

    今天小編就為大家分享一篇Django讀取Mysql數(shù)據(jù)并顯示在前端的實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2018-05-05
  • 替換python字典中的key值方法

    替換python字典中的key值方法

    今天小編就為大家分享一篇替換python字典中的key值方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2018-07-07
  • python使用cookielib庫(kù)示例分享

    python使用cookielib庫(kù)示例分享

    Python中cookielib庫(kù)(python3中為http.cookiejar)為存儲(chǔ)和管理cookie提供客戶端支持,下面是使用示例
    2014-03-03
  • Python常用工具類之a(chǎn)dbtool示例代碼

    Python常用工具類之a(chǎn)dbtool示例代碼

    本文主要介紹了Python中常用工具類之a(chǎn)db命令的使用,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-08-08
  • python使用Pycharm創(chuàng)建一個(gè)Django項(xiàng)目

    python使用Pycharm創(chuàng)建一個(gè)Django項(xiàng)目

    這篇文章主要介紹了python使用Pycharm創(chuàng)建一個(gè)Django項(xiàng)目,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2018-03-03
  • Python爬取破解無(wú)線網(wǎng)絡(luò)wifi密碼過(guò)程解析

    Python爬取破解無(wú)線網(wǎng)絡(luò)wifi密碼過(guò)程解析

    這篇文章主要介紹了Python爬取破解無(wú)線網(wǎng)絡(luò)密碼過(guò)程解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-09-09

最新評(píng)論