mirror of
https://github.com/Evil0ctal/Douyin_TikTok_Download_API.git
synced 2025-04-20 17:55:05 +08:00
🐛 Fix Douyin TikTok API
This commit is contained in:
parent
ac6c4fad5f
commit
1db94fc8ad
@ -30,7 +30,7 @@ cd Douyin_TikTok_Download_API/ || exit
|
|||||||
|
|
||||||
pip install -r requirements.txt
|
pip install -r requirements.txt
|
||||||
|
|
||||||
echo 'Please edit config.ini, all input must be numbers!'
|
echo 'Please edit config.yml, all input must be numbers!'
|
||||||
|
|
||||||
python3 config.py
|
python3 config.py
|
||||||
|
|
||||||
|
107
config.ini
107
config.ini
@ -1,107 +0,0 @@
|
|||||||
# -*- encoding: utf-8 -*-
|
|
||||||
# @Author: https://github.com/Evil0ctal/
|
|
||||||
# @Time: 2021/11/06
|
|
||||||
# @Update: 2022/11/09
|
|
||||||
# @Function:
|
|
||||||
# 项目的配置文件/Config file of the project
|
|
||||||
|
|
||||||
[Scraper] # scraper.py
|
|
||||||
# 是否使用代理(如果部署在IP受限国家需要开启默认为False关闭,请自行收集代理,下面代理仅作为示例不保证可用性)
|
|
||||||
# Whether to use proxy (if deployed in a country with IP restrictions, it needs to be turned on by default, False is closed. Please collect proxies yourself. The following proxies are only for reference and do not guarantee availability)
|
|
||||||
Proxy_switch = False
|
|
||||||
|
|
||||||
# 是否根据不同协议(http/https)使用不同代理,设置为True时修改Http_proxy/Https_proxy这两个变量的值
|
|
||||||
# Whether to use different proxies for different protocols (http/https). When set to True, modify the values of the two variables Http_proxy/Https_proxy
|
|
||||||
Use_different_protocols = False
|
|
||||||
|
|
||||||
# http/https协议都使用以下代理(Use_different_protocols为False时生效)
|
|
||||||
# Both http/https protocols use the following proxy (effective when Use_different_protocols is False)
|
|
||||||
All = 45.167.124.5:9992
|
|
||||||
|
|
||||||
# http协议使用以下代理(Use_different_protocols为True时生效)
|
|
||||||
# The http protocol uses the following proxy (effective when Use_different_protocols is True)
|
|
||||||
Http_proxy = http://45.167.124.5:9992
|
|
||||||
|
|
||||||
# https协议使用以下代理(Use_different_protocols为True时生效)
|
|
||||||
# The https protocol uses the following proxy (effective when Use_different_protocols is True)
|
|
||||||
Https_proxy = https://45.167.124.5:9992
|
|
||||||
|
|
||||||
# 抖音cookies配置项
|
|
||||||
# odin_tt=xxx;sessionid_ss=xxx;ttwid=xxx;passport_csrf_token=xxx;msToken=xxx;
|
|
||||||
DouYinCookies =
|
|
||||||
|
|
||||||
[Web_API] # web_api.py
|
|
||||||
# API链接 如:http://127.0.0.1:2333 或 http://api.douyin.wtf (末尾不要留斜杠)
|
|
||||||
# API link, such as: http://127.0.0.1:2333 or http://api.douyin.wtf (no slash at the end)
|
|
||||||
Domain = http://api.douyin.wtf
|
|
||||||
|
|
||||||
# 限制API的请求次数/Limited API requests
|
|
||||||
Rate_Limit = 10/minute
|
|
||||||
|
|
||||||
# API默认运行端口/Default port of API
|
|
||||||
Port = 8000
|
|
||||||
|
|
||||||
# 默认下载目录/Default download directory
|
|
||||||
Download_Path = ./download
|
|
||||||
|
|
||||||
# 是否开启下载[tag = Download]功能(默认开启,关闭后无法下载)/Whether to enable the download [tag = Download] function (default open, closed after download)
|
|
||||||
Download_Switch = True
|
|
||||||
|
|
||||||
# 是否自动清理下载目录/Whether to automatically clean up the download directory
|
|
||||||
Download_Path_Clean_Switch = True
|
|
||||||
|
|
||||||
# 下载文件夹自动删除时间(单位:秒)/Download folder automatic deletion time (unit: seconds)
|
|
||||||
Download_Path_Clean_Timer = 3600
|
|
||||||
|
|
||||||
# 默认下载文件名前缀/Default download file name prefix
|
|
||||||
File_Name_Prefix = api.douyin.wtf_
|
|
||||||
|
|
||||||
# 是否记录API调用日志/Whether to record API call logs
|
|
||||||
Allow_Logs = True
|
|
||||||
|
|
||||||
# 快捷指令版本/Shortcut version
|
|
||||||
iOS_Shortcut_Version = 6.0
|
|
||||||
|
|
||||||
# 快捷指令Link(Chinese_Language)
|
|
||||||
iOS_Shortcut_Link = https://www.icloud.com/shortcuts/4465d514869e4ca585074d40328f3e0e
|
|
||||||
|
|
||||||
# Shortcut Link(English_Language)
|
|
||||||
iOS_Shortcut_Link_EN = https://www.icloud.com/shortcuts/58e3a2cbac784a6782f1031c6b1dd9f8
|
|
||||||
|
|
||||||
# 快捷指令更新时间/Shortcut update time
|
|
||||||
iOS_Shortcut_Update_Time = 2022/11/06
|
|
||||||
|
|
||||||
# 快捷指令更新记录/Shortcut update log
|
|
||||||
iOS_Shortcut_Update_Note = 重新适配https://api.douyin.wtf(API-V1 3.0.0版本)
|
|
||||||
|
|
||||||
# iOS shortcut update note
|
|
||||||
iOS_Shortcut_Update_Note_EN = Re-adapt https://api.douyin.wtf (API-V1 3.0.0 version)
|
|
||||||
|
|
||||||
[Web_APP] # web_app.py
|
|
||||||
# 网页默认运行端口/Web default running port
|
|
||||||
Port = 80
|
|
||||||
|
|
||||||
# PyWebIO是否使用CDN来获取前端的静态资源(防止CDN被墙导致无法正常显示)
|
|
||||||
# Whether PyWebIO uses CDN to obtain static resources of the front end (to prevent CDN from being blocked and displayed normally)
|
|
||||||
PyWebIO_CDN = True
|
|
||||||
|
|
||||||
# 最大接受提交URL的数量/Maximum number of URLs accepted for submission
|
|
||||||
Max_Take_URLs = 200
|
|
||||||
|
|
||||||
# 是否记录错误日志/Whether to record error logs
|
|
||||||
Allow_Logs = True
|
|
||||||
|
|
||||||
# 网页标题
|
|
||||||
Web_Title = TikTok/抖音无水印在线解析下载
|
|
||||||
|
|
||||||
# Web Title English
|
|
||||||
Web_Title_English = Douyin/TikTok online parsing and download without watermark
|
|
||||||
|
|
||||||
# 网页描述
|
|
||||||
Web_Description = 在线批量解析TikTok/抖音视频和图片,支持无水印下载,官方数据接口,稳定,开源,免费,无广告。
|
|
||||||
|
|
||||||
# Web Description English
|
|
||||||
Web_Description_English = Online batch parsing of TikTok/Douyin videos and pictures, support for no watermark download, official data interface, stable, open source, free, no ads.
|
|
||||||
|
|
||||||
# 网页关键词/Keywords of the web page
|
|
||||||
Keywords = 抖音,tiktok,水印,无水印,no-watermark,抖音去水印,tiktok no watermark,在线,online,api,快捷指令,shortcut,下载,解析,parsing,tiktok api,抖音api,抖音去水印在线,tiktok去水印在线,downloader,下载器,free api,免费api
|
|
43
config.py
43
config.py
@ -1,46 +1,56 @@
|
|||||||
import configparser
|
import yaml
|
||||||
|
|
||||||
config = configparser.ConfigParser()
|
config_path = 'config.yml'
|
||||||
config_path = 'config.ini'
|
|
||||||
config.read(config_path, encoding='utf-8')
|
with open(config_path, 'r', encoding='utf-8') as file:
|
||||||
|
config = yaml.safe_load(file)
|
||||||
|
|
||||||
|
|
||||||
def api_config():
|
def api_config():
|
||||||
api_default_port = config.get('Web_API', 'Port')
|
api_default_port = config['Web_API']['Port']
|
||||||
api_new_port = input(f'Default API port: {api_default_port}\nIf you want use different port input new API port here: ')
|
api_new_port = input(
|
||||||
|
f'Default API port: {api_default_port}\nIf you want use different port input new API port here: ')
|
||||||
|
|
||||||
if api_new_port.isdigit():
|
if api_new_port.isdigit():
|
||||||
if int(api_new_port) == int(api_default_port):
|
if int(api_new_port) == int(api_default_port):
|
||||||
print(f'Use default port for web_app.py: {api_default_port}')
|
print(f'Use default port for web_app.py: {api_default_port}')
|
||||||
else:
|
else:
|
||||||
print(f'Use new port for web_api.py: {api_new_port}')
|
print(f'Use new port for web_api.py: {api_new_port}')
|
||||||
config.set('Web_API', 'Port', api_new_port)
|
config['Web_API']['Port'] = int(api_new_port)
|
||||||
config.write(open(config_path, "w", encoding="utf-8"))
|
with open(config_path, "w", encoding="utf-8") as file:
|
||||||
|
yaml.dump(config, file, allow_unicode=True)
|
||||||
else:
|
else:
|
||||||
print(f'Use default port for web_app.py: {api_default_port}')
|
print(f'Use default port for web_app.py: {api_default_port}')
|
||||||
|
|
||||||
req_limit = config.get('Web_API', 'Rate_Limit')
|
req_limit = config['Web_API']['Rate_Limit']
|
||||||
new_req_limit = input(f'Default API rate limit: {req_limit}\nIf you want use different rate limit input new rate limit here: ')
|
new_req_limit = input(
|
||||||
|
f'Default API rate limit: {req_limit}\nIf you want use different rate limit input new rate limit here: ')
|
||||||
|
|
||||||
if new_req_limit.isdigit():
|
if new_req_limit.isdigit():
|
||||||
if int(new_req_limit) == int(req_limit.split('/')[0]):
|
if int(new_req_limit) == int(req_limit.split('/')[0]):
|
||||||
print(f'Use default rate limit for web_api.py : {req_limit}')
|
print(f'Use default rate limit for web_api.py : {req_limit}')
|
||||||
else:
|
else:
|
||||||
print(f'Use new rate limit: {new_req_limit}/minute')
|
print(f'Use new rate limit: {new_req_limit}/minute')
|
||||||
config.set('Web_API', 'Rate_Limit', f'{new_req_limit}/minute')
|
config['Web_API']['Rate_Limit'] = f'{new_req_limit}/minute'
|
||||||
config.write(open(config_path, "w", encoding="utf-8"))
|
with open(config_path, "w", encoding="utf-8") as file:
|
||||||
|
yaml.dump(config, file, allow_unicode=True)
|
||||||
else:
|
else:
|
||||||
print(f'Use default rate limit for web_api.py: {req_limit}')
|
print(f'Use default rate limit for web_api.py: {req_limit}')
|
||||||
|
|
||||||
|
|
||||||
def app_config():
|
def app_config():
|
||||||
app_default_port = config.get('Web_APP', 'Port')
|
app_default_port = config['Web_APP']['Port']
|
||||||
app_new_port = input(f'Default App port: {app_default_port}\nIf you want use different port input new App port here: ')
|
app_new_port = input(
|
||||||
|
f'Default App port: {app_default_port}\nIf you want use different port input new App port here: ')
|
||||||
|
|
||||||
if app_new_port.isdigit():
|
if app_new_port.isdigit():
|
||||||
if int(app_new_port) == int(app_default_port):
|
if int(app_new_port) == int(app_default_port):
|
||||||
print(f'Use default port for web_app.py: {app_default_port}')
|
print(f'Use default port for web_app.py: {app_default_port}')
|
||||||
else:
|
else:
|
||||||
print(f'Use new port: {app_new_port}')
|
print(f'Use new port: {app_new_port}')
|
||||||
config.set('Web_APP', 'Port', app_new_port)
|
config['Web_APP']['Port'] = int(app_new_port)
|
||||||
config.write(open(config_path, "w", encoding="utf-8"))
|
with open(config_path, "w", encoding="utf-8") as file:
|
||||||
|
yaml.dump(config, file, allow_unicode=True)
|
||||||
else:
|
else:
|
||||||
print(f'Use default port for web_app.py : {app_default_port}')
|
print(f'Use default port for web_app.py : {app_default_port}')
|
||||||
|
|
||||||
@ -48,4 +58,3 @@ def app_config():
|
|||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
api_config()
|
api_config()
|
||||||
app_config()
|
app_config()
|
||||||
|
|
||||||
|
109
config.yml
Normal file
109
config.yml
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
# -*- encoding: utf-8 -*-
|
||||||
|
# @Author: https://github.com/Evil0ctal/
|
||||||
|
# @Time: 2021/11/06
|
||||||
|
# @Update: 2024/03/25
|
||||||
|
# @Function:
|
||||||
|
# 项目的配置文件/Config file of the project
|
||||||
|
# 去看看我们的另外一个项目吧,一个YAML文件的多态键值路径映射解析器:https://github.com/PKVPM/PKVPM
|
||||||
|
# Check out our other project, a polymorphic key-value path mapping parser for YAML files:
|
||||||
|
|
||||||
|
Scraper: # scraper.py
|
||||||
|
# 是否使用代理(如果部署在IP受限国家需要开启默认为False关闭,请自行收集代理,下面代理仅作为示例不保证可用性)
|
||||||
|
# Whether to use proxy (if deployed in a country with IP restrictions, it needs to be turned on by default, False is closed. Please collect proxies yourself. The following proxies are only for reference and do not guarantee availability)
|
||||||
|
Proxy_switch: false
|
||||||
|
|
||||||
|
# 是否根据不同协议(http/https)使用不同代理,设置为True时修改Http_proxy/Https_proxy这两个变量的值
|
||||||
|
# Whether to use different proxies for different protocols (http/https). When set to True, modify the values of the two variables Http_proxy/Https_proxy
|
||||||
|
Use_different_protocols: false
|
||||||
|
|
||||||
|
# http/https协议都使用以下代理(Use_different_protocols为False时生效)
|
||||||
|
# Both http/https protocols use the following proxy (effective when Use_different_protocols is False)
|
||||||
|
All: "45.167.124.5:9992"
|
||||||
|
|
||||||
|
# http协议使用以下代理(Use_different_protocols为True时生效)
|
||||||
|
# The http protocol uses the following proxy (effective when Use_different_protocols is True)
|
||||||
|
Http_proxy: "http://45.167.124.5:9992"
|
||||||
|
|
||||||
|
# https协议使用以下代理(Use_different_protocols为True时生效)
|
||||||
|
# The https protocol uses the following proxy (effective when Use_different_protocols is True)
|
||||||
|
Https_proxy: "https://45.167.124.5:9992"
|
||||||
|
|
||||||
|
# 抖音cookies配置项
|
||||||
|
# odin_tt=xxx;sessionid_ss=xxx;ttwid=xxx;passport_csrf_token=xxx;msToken=xxx;
|
||||||
|
DouYinCookies: __ac_nonce=06601efc9000687edabfb; __ac_signature=_02B4Z6wo00f01DY5d.gAAIDCmbcjj5Gs2Wg2CXNAAGuWde; ttwid=1%7CE0Fz_BrMgjzSm3__CbFk_z_YxJTZvuoIynWz56Jr2gw%7C1711402953%7C0bae07e4ed08cc7062e5025b43028917c0f101ce950429d713bdfc98ce8900e4; douyin.com; device_web_cpu_core=16; device_web_memory_size=-1; architecture=amd64; IsDouyinActive=true; home_can_add_dy_2_desktop=%220%22; dy_swidth=1344; dy_sheight=756; stream_recommend_feed_params=%22%7B%5C%22cookie_enabled%5C%22%3Atrue%2C%5C%22screen_width%5C%22%3A1344%2C%5C%22screen_height%5C%22%3A756%2C%5C%22browser_online%5C%22%3Atrue%2C%5C%22cpu_core_num%5C%22%3A16%2C%5C%22device_memory%5C%22%3A0%2C%5C%22downlink%5C%22%3A%5C%22%5C%22%2C%5C%22effective_type%5C%22%3A%5C%22%5C%22%2C%5C%22round_trip_time%5C%22%3A0%7D%22; strategyABtestKey=%221711402955.353%22; volume_info=%7B%22isUserMute%22%3Afalse%2C%22isMute%22%3Atrue%2C%22volume%22%3A0.5%7D; stream_player_status_params=%22%7B%5C%22is_auto_play%5C%22%3A0%2C%5C%22is_full_screen%5C%22%3A0%2C%5C%22is_full_webscreen%5C%22%3A0%2C%5C%22is_mute%5C%22%3A1%2C%5C%22is_speed%5C%22%3A1%2C%5C%22is_visible%5C%22%3A1%7D%22; passport_csrf_token=6cbad4aeddb5063d2da49dba0f0001a4; passport_csrf_token_default=6cbad4aeddb5063d2da49dba0f0001a4; msToken=Vnmw2WIIMakoPHjKeN_pdZQUaL3Pqe2Bkc5voIdZJlfpQQTmmYIi2Oy8V8VhGn9VoYyx9G2EoNLUubHK_OSaTL3-W4LTM7DTTwkNhZVAMJ7BVBWW85jPHqXCAjFn; FORCE_LOGIN=%7B%22videoConsumedRemainSeconds%22%3A180%7D; odin_tt=fff2328d744fdd887dfe8236d1343072c7d667d877a05e9fbf0c4dea2cf6cd0fb24cc994eb844d91502053a9fb48b5cf1f37be4285cfeab319cac618493b1cc78a21c68b7e2b6af04f08d1f3417b5617; csrf_session_id=120d8aacffb06addd01cb40859003c8e; bd_ticket_guard_client_data=eyJiZC10aWNrZXQtZ3VhcmQtdmVyc2lvbiI6MiwiYmQtdGlja2V0LWd1YXJkLWl0ZXJhdGlvbi12ZXJzaW9uIjoxLCJiZC10aWNrZXQtZ3VhcmQtcmVlLXB1YmxpYy1rZXkiOiJCUE8vYUdEdlNsRE1ZYTBpRXVrQTk0aVppQ2t0Y2NwcWs3NGh2aitjN2NFZ1I5alZTQXdRTFNvL2tza3U3R1JJMUtLc0xJakROam91WDBZcm9XdytVTDQ9IiwiYmQtdGlja2V0LWd1YXJkLXdlYi12ZXJzaW9uIjoxfQ%3D%3D; bd_ticket_guard_client_web_domain=2; msToken=O6EKBamnejH7dsEfHq4JRyU3rYJv__HW7Bz7OPeOD-AjZMtNe31tPGkhM0Ez06aM_J0uy0OjAGvflkpOukOAYUerarzl5OSuwKFddmm6JL40dUcahW_TBmSgNOOJ
|
||||||
|
|
||||||
|
Web_API: # web_api.py
|
||||||
|
# API链接 如:http://127.0.0.1:2333 或 http://api.douyin.wtf (末尾不要留斜杠)
|
||||||
|
# API link, such as: http://127.0.0.1:2333 or http://api.douyin.wtf (no slash at the end)
|
||||||
|
Domain: "http://api.douyin.wtf"
|
||||||
|
|
||||||
|
# 限制API的请求次数/Limited API requests
|
||||||
|
Rate_Limit: "10/minute"
|
||||||
|
|
||||||
|
# API默认运行端口/Default port of API
|
||||||
|
Port: 8000
|
||||||
|
|
||||||
|
# 默认下载目录/Default download directory
|
||||||
|
Download_Path: "./download"
|
||||||
|
|
||||||
|
# 是否开启下载[tag = Download]功能(默认开启,关闭后无法下载)/Whether to enable the download [tag = Download] function (default open, closed after download)
|
||||||
|
Download_Switch: true
|
||||||
|
|
||||||
|
# 是否自动清理下载目录/Whether to automatically clean up the download directory
|
||||||
|
Download_Path_Clean_Switch: true
|
||||||
|
|
||||||
|
# 下载文件夹自动删除时间(单位:秒)/Download folder automatic deletion time (unit: seconds)
|
||||||
|
Download_Path_Clean_Timer: 3600
|
||||||
|
|
||||||
|
# 默认下载文件名前缀/Default download file name prefix
|
||||||
|
File_Name_Prefix: "api.douyin.wtf_"
|
||||||
|
|
||||||
|
# 是否记录API调用日志/Whether to record API call logs
|
||||||
|
Allow_Logs: true
|
||||||
|
|
||||||
|
# 快捷指令版本/Shortcut version
|
||||||
|
iOS_Shortcut_Version: "6.0"
|
||||||
|
|
||||||
|
# 快捷指令Link(Chinese_Language)
|
||||||
|
iOS_Shortcut_Link: "https://www.icloud.com/shortcuts/4465d514869e4ca585074d40328f3e0e"
|
||||||
|
|
||||||
|
# Shortcut Link(English_Language)
|
||||||
|
iOS_Shortcut_Link_EN: "https://www.icloud.com/shortcuts/58e3a2cbac784a6782f1031c6b1dd9f8"
|
||||||
|
|
||||||
|
# 快捷指令更新时间/Shortcut update time
|
||||||
|
iOS_Shortcut_Update_Time: "2022/11/06"
|
||||||
|
|
||||||
|
# 快捷指令更新记录/Shortcut update log
|
||||||
|
iOS_Shortcut_Update_Note: "重新适配https://api.douyin.wtf(API-V1 3.0.0版本)"
|
||||||
|
|
||||||
|
# iOS shortcut update note
|
||||||
|
iOS_Shortcut_Update_Note_EN: "Re-adapt https://api.douyin.wtf (API-V1 3.0.0 version)"
|
||||||
|
|
||||||
|
Web_APP: # web_app.py
|
||||||
|
# 网页默认运行端口/Web default running port
|
||||||
|
Port: 80
|
||||||
|
|
||||||
|
# PyWebIO是否使用CDN来获取前端的静态资源(防止CDN被墙导致无法正常显示)
|
||||||
|
# Whether PyWebIO uses CDN to obtain static resources of the front end (to prevent CDN from being blocked and displayed normally)
|
||||||
|
PyWebIO_CDN: true
|
||||||
|
|
||||||
|
# 最大接受提交URL的数量/Maximum number of URLs accepted for submission
|
||||||
|
Max_Take_URLs: 200
|
||||||
|
|
||||||
|
# 是否记录错误日志/Whether to record error logs
|
||||||
|
Allow_Logs: true
|
||||||
|
|
||||||
|
# 网页标题
|
||||||
|
Web_Title: "TikTok/抖音无水印在线解析下载"
|
||||||
|
|
||||||
|
# Web Title English
|
||||||
|
Web_Title_English: "Douyin/TikTok online parsing and download without watermark"
|
||||||
|
|
||||||
|
# 网页描述
|
||||||
|
Web_Description: "在线批量解析TikTok/抖音视频和图片,支持无水印下载,官方数据接口,稳定,开源,免费,无广告。"
|
||||||
|
|
||||||
|
# Web Description English
|
||||||
|
Web_Description_English: "Online batch parsing of TikTok/Douyin videos and pictures, support for no watermark download, official data interface, stable, open source, free, no ads."
|
||||||
|
|
||||||
|
# 网页关键词/Keywords of the web page
|
||||||
|
Keywords: "抖音,tiktok,水印,无水印,no-watermark,抖音去水印,tiktok no watermark,在线,online,api,快捷指令,shortcut,下载,解析,parsing,tiktok api,抖音api,抖音去水印在线,tiktok去水印在线,downloader,下载器,free api,免费api"
|
@ -9,7 +9,7 @@ services:
|
|||||||
container_name: douyin_tiktok_download_api
|
container_name: douyin_tiktok_download_api
|
||||||
restart: always
|
restart: always
|
||||||
volumes:
|
volumes:
|
||||||
- ./config.ini:/app/config.ini
|
- ./config.yml:/app/config.yml
|
||||||
environment:
|
environment:
|
||||||
TZ: Asia/Shanghai
|
TZ: Asia/Shanghai
|
||||||
deploy:
|
deploy:
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
aiohttp==3.8.4
|
aiohttp~=3.8.4
|
||||||
aiosignal==1.3.1
|
aiosignal==1.3.1
|
||||||
anyio==3.6.2
|
anyio==3.6.2
|
||||||
async-timeout==4.0.2
|
async-timeout==4.0.2
|
||||||
@ -8,24 +8,24 @@ charset-normalizer==3.0.1
|
|||||||
click==8.1.3
|
click==8.1.3
|
||||||
colorama==0.4.6
|
colorama==0.4.6
|
||||||
Deprecated==1.2.13
|
Deprecated==1.2.13
|
||||||
fastapi==0.92.0
|
fastapi~=0.92.0
|
||||||
frozenlist==1.3.3
|
frozenlist==1.3.3
|
||||||
h11==0.14.0
|
h11==0.14.0
|
||||||
idna==3.4
|
idna==3.4
|
||||||
limits==2.8.0
|
limits==2.8.0
|
||||||
multidict==6.0.4
|
multidict==6.0.4
|
||||||
orjson==3.8.6
|
orjson==3.9.15
|
||||||
packaging==22.0
|
packaging==22.0
|
||||||
pydantic==1.10.5
|
pydantic~=1.10.12
|
||||||
PyExecJS==1.5.1
|
PyExecJS==1.5.1
|
||||||
pywebio==1.7.1
|
pywebio==1.7.1
|
||||||
six==1.16.0
|
six==1.16.0
|
||||||
slowapi==0.1.7
|
slowapi==0.1.7
|
||||||
sniffio==1.3.0
|
sniffio==1.3.0
|
||||||
starlette==0.25.0
|
starlette~=0.25.0
|
||||||
tenacity==8.2.1
|
tenacity==8.2.1
|
||||||
tornado==6.2
|
tornado==6.3.3
|
||||||
typing_extensions==4.5.0
|
typing_extensions==4.8.0
|
||||||
ua-parser==0.16.1
|
ua-parser==0.16.1
|
||||||
user-agents==2.2.0
|
user-agents==2.2.0
|
||||||
uvicorn==0.20.0
|
uvicorn==0.20.0
|
||||||
@ -33,3 +33,4 @@ wrapt==1.15.0
|
|||||||
yarl==1.8.2
|
yarl==1.8.2
|
||||||
httpx~=0.25.0
|
httpx~=0.25.0
|
||||||
requests~=2.28.2
|
requests~=2.28.2
|
||||||
|
PyYAML~=6.0.1
|
130
scraper.py
130
scraper.py
@ -2,7 +2,7 @@
|
|||||||
# -*- encoding: utf-8 -*-
|
# -*- encoding: utf-8 -*-
|
||||||
# @Author: https://github.com/Evil0ctal/
|
# @Author: https://github.com/Evil0ctal/
|
||||||
# @Time: 2021/11/06
|
# @Time: 2021/11/06
|
||||||
# @Update: 2024/03/14
|
# @Update: 2024/03/25
|
||||||
# @Version: 3.1.8
|
# @Version: 3.1.8
|
||||||
# @Note:
|
# @Note:
|
||||||
# Core code, valued at 1 bucks (๑•̀ㅂ•́)و✧
|
# Core code, valued at 1 bucks (๑•̀ㅂ•́)و✧
|
||||||
@ -12,7 +12,6 @@
|
|||||||
# 核心代码,估值1块(๑•̀ㅂ•́)و✧
|
# 核心代码,估值1块(๑•̀ㅂ•́)و✧
|
||||||
# 用于爬取Douyin/TikTok/Bilibili/xigua的数据并以字典形式返回。
|
# 用于爬取Douyin/TikTok/Bilibili/xigua的数据并以字典形式返回。
|
||||||
# 如果本项目对您有帮助,请给我一个star,谢谢!
|
# 如果本项目对您有帮助,请给我一个star,谢谢!
|
||||||
import random
|
|
||||||
import re
|
import re
|
||||||
import os
|
import os
|
||||||
import time
|
import time
|
||||||
@ -22,15 +21,18 @@ import httpx
|
|||||||
import platform
|
import platform
|
||||||
import asyncio
|
import asyncio
|
||||||
import traceback
|
import traceback
|
||||||
import configparser
|
import yaml
|
||||||
import urllib.parse
|
import urllib.parse
|
||||||
import random
|
import random
|
||||||
import json
|
|
||||||
|
|
||||||
from zlib import crc32
|
from zlib import crc32
|
||||||
from typing import Union
|
from typing import Union
|
||||||
from tenacity import *
|
from tenacity import *
|
||||||
|
|
||||||
|
# 读取配置文件
|
||||||
|
with open('config.yml', 'r', encoding='utf-8') as yaml_file:
|
||||||
|
config = yaml.safe_load(yaml_file)
|
||||||
|
|
||||||
|
|
||||||
class Scraper:
|
class Scraper:
|
||||||
"""__________________________________________⬇️initialization(初始化)⬇️______________________________________"""
|
"""__________________________________________⬇️initialization(初始化)⬇️______________________________________"""
|
||||||
@ -44,10 +46,11 @@ class Scraper:
|
|||||||
'accept-encoding': 'gzip, deflate, br',
|
'accept-encoding': 'gzip, deflate, br',
|
||||||
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36',
|
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36',
|
||||||
'Referer': 'https://www.douyin.com/',
|
'Referer': 'https://www.douyin.com/',
|
||||||
'cookie': ''
|
# 如果抖音接口不返回数据,可能是因为cookie过期,需要更新cookie/If the Douyin interface does not return data, it may be because the cookie has expired and needs to be updated
|
||||||
|
'cookie': config['Scraper']['DouYinCookies']
|
||||||
}
|
}
|
||||||
self.tiktok_api_headers = {
|
self.tiktok_api_headers = {
|
||||||
'User-Agent': 'com.ss.android.ugc.trill/494+Mozilla/5.0+(Linux;+Android+12;+2112123G+Build/SKQ1.211006.001;+wv)+AppleWebKit/537.36+(KHTML,+like+Gecko)+Version/4.0+Chrome/107.0.5304.105+Mobile+Safari/537.36'
|
'User-Agent': "Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Mobile Safari/537.36"
|
||||||
}
|
}
|
||||||
self.bilibili_api_headers = {
|
self.bilibili_api_headers = {
|
||||||
'User-Agent': 'com.ss.android.ugc.trill/494+Mozilla/5.0+(Linux;+Android+12;+2112123G+Build/SKQ1.211006.001;+wv)+AppleWebKit/537.36+(KHTML,+like+Gecko)+Version/4.0+Chrome/107.0.5304.105+Mobile+Safari/537.36'
|
'User-Agent': 'com.ss.android.ugc.trill/494+Mozilla/5.0+(Linux;+Android+12;+2112123G+Build/SKQ1.211006.001;+wv)+AppleWebKit/537.36+(KHTML,+like+Gecko)+Version/4.0+Chrome/107.0.5304.105+Mobile+Safari/537.36'
|
||||||
@ -86,9 +89,8 @@ class Scraper:
|
|||||||
'sec-ch-ua-platform': '"macOS"',
|
'sec-ch-ua-platform': '"macOS"',
|
||||||
}
|
}
|
||||||
# 判断配置文件是否存在/Check if the configuration file exists
|
# 判断配置文件是否存在/Check if the configuration file exists
|
||||||
if os.path.exists('config.ini'):
|
if os.path.exists('config.yml'):
|
||||||
self.config = configparser.RawConfigParser()
|
self.config = config
|
||||||
self.config.read('config.ini', encoding='utf-8')
|
|
||||||
if not self.config['Scraper']['DouYinCookies'] is None:
|
if not self.config['Scraper']['DouYinCookies'] is None:
|
||||||
self.douyin_api_headers['Cookie'] = str(self.config['Scraper']['DouYinCookies'])
|
self.douyin_api_headers['Cookie'] = str(self.config['Scraper']['DouYinCookies'])
|
||||||
# 判断是否使用代理
|
# 判断是否使用代理
|
||||||
@ -319,22 +321,6 @@ class Scraper:
|
|||||||
|
|
||||||
"""__________________________________________⬇️Douyin methods(抖音方法)⬇️______________________________________"""
|
"""__________________________________________⬇️Douyin methods(抖音方法)⬇️______________________________________"""
|
||||||
|
|
||||||
# 生成抖音X-Bogus签名/Generate Douyin X-Bogus signature
|
|
||||||
# 下面的代码不能保证稳定性,随时可能失效/ The code below cannot guarantee stability and may fail at any time
|
|
||||||
def generate_x_bogus_url(self, url: str) -> str:
|
|
||||||
"""
|
|
||||||
生成抖音X-Bogus签名
|
|
||||||
:param url: 视频链接
|
|
||||||
:return: 包含X-Bogus签名的URL
|
|
||||||
"""
|
|
||||||
# 调用JavaScript函数
|
|
||||||
query = urllib.parse.urlparse(url).query
|
|
||||||
xbogus = execjs.compile(open(self.relpath('./X-Bogus.js')).read()).call('sign', query,
|
|
||||||
self.headers['User-Agent'])
|
|
||||||
# print('生成的X-Bogus签名为: {}'.format(xbogus))
|
|
||||||
new_url = url + "&X-Bogus=" + xbogus
|
|
||||||
return new_url
|
|
||||||
|
|
||||||
# 获取抖音视频ID/Get Douyin video ID
|
# 获取抖音视频ID/Get Douyin video ID
|
||||||
async def get_douyin_video_id(self, original_url: str) -> Union[str, None]:
|
async def get_douyin_video_id(self, original_url: str) -> Union[str, None]:
|
||||||
"""
|
"""
|
||||||
@ -382,19 +368,50 @@ class Scraper:
|
|||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
# 构造访问链接/Construct the access link
|
# 构造访问链接/Construct the access link
|
||||||
api_url = f"https://www.douyin.com/aweme/v1/web/aweme/detail/?device_platform=webapp&aid=6383&channel=channel_pc_web&aweme_id={video_id}&pc_client_type=1&version_code=190500&version_name=19.5.0&cookie_enabled=true&screen_width=1344&screen_height=756&browser_language=zh-CN&browser_platform=Win32&browser_name=Firefox&browser_version=118.0&browser_online=true&engine_name=Gecko&engine_version=109.0&os_name=Windows&os_version=10&cpu_core_num=16&device_memory=&platform=PC&webid=7284189800734082615&msToken=B1N9FM825TkvFbayDsDvZxM8r5suLrsfQbC93TciS0O9Iii8iJpAPd__FM2rpLUJi5xtMencSXLeNn8xmOS9q7bP0CUsrt9oVTL08YXLPRzZm0dHKLc9PGRlyEk="
|
# parameters provided by https://github.com/Johnserf-Seed
|
||||||
api_url = self.generate_x_bogus_url(api_url)
|
domain = "https://www.douyin.com"
|
||||||
|
endpoint = "/aweme/v1/web/aweme/detail/"
|
||||||
|
query_body = {
|
||||||
|
"aweme_id": video_id,
|
||||||
|
"device_platform": "webapp",
|
||||||
|
"aid": "6383",
|
||||||
|
"channel": "channel_pc_web",
|
||||||
|
"pc_client_type": 1,
|
||||||
|
"version_code": "170400",
|
||||||
|
"version_name": "17.4.0",
|
||||||
|
"cookie_enabled": "true",
|
||||||
|
"screen_width": 1920,
|
||||||
|
"screen_height": 1080,
|
||||||
|
"browser_language": "zh-CN",
|
||||||
|
"browser_platform": "Win32",
|
||||||
|
"browser_name": "Edge",
|
||||||
|
"browser_version": "117.0.2045.47",
|
||||||
|
"browser_online": "true",
|
||||||
|
"engine_name": "Blink",
|
||||||
|
"engine_version": "117.0.0.0",
|
||||||
|
"os_name": "Windows",
|
||||||
|
"os_version": "10",
|
||||||
|
"cpu_core_num": 12,
|
||||||
|
"device_memory": 8,
|
||||||
|
"platform": "PC",
|
||||||
|
"downlink": 10,
|
||||||
|
"effective_type": "4g",
|
||||||
|
"round_trip_time": 100,
|
||||||
|
"msToken": "Hello From Evil0ctal!"
|
||||||
|
}
|
||||||
|
# 将参数编码为 URL 查询字符串
|
||||||
|
query_string = urllib.parse.urlencode(query_body, quote_via=urllib.parse.quote)
|
||||||
|
api_url = urllib.parse.urljoin(domain, endpoint) + "?" + query_string
|
||||||
|
api_url = self.generate_x_bogus_url(api_url, self.douyin_api_headers)
|
||||||
# 访问API/Access API
|
# 访问API/Access API
|
||||||
print("正在请求抖音视频API: {}".format(api_url))
|
print("正在请求抖音视频API: {}".format(api_url))
|
||||||
async with aiohttp.ClientSession() as session:
|
# 使用httpx请求
|
||||||
self.douyin_api_headers['Referer'] = f'https://www.douyin.com/video/{video_id}'
|
async with httpx.AsyncClient() as client:
|
||||||
async with session.get(api_url, headers=self.douyin_api_headers, proxy=self.proxies,
|
response = await client.get(api_url, headers=self.douyin_api_headers, timeout=10)
|
||||||
timeout=10) as response:
|
response = response.json()
|
||||||
response = await response.json()
|
print(response)
|
||||||
# 获取视频数据/Get video data
|
# 获取视频数据/Get video data
|
||||||
video_data = response['aweme_detail']
|
video_data = response["aweme_detail"]
|
||||||
# print('获取视频数据成功!')
|
|
||||||
print("抖音API返回数据: {}".format(video_data))
|
|
||||||
return video_data
|
return video_data
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise ValueError(f"获取抖音视频数据出错了: {e}")
|
raise ValueError(f"获取抖音视频数据出错了: {e}")
|
||||||
@ -456,19 +473,28 @@ class Scraper:
|
|||||||
# print('正在获取TikTok视频数据...')
|
# print('正在获取TikTok视频数据...')
|
||||||
try:
|
try:
|
||||||
# 构造访问链接/Construct the access link
|
# 构造访问链接/Construct the access link
|
||||||
api_url = f'https://api22-normal-c-useast2a.tiktokv.com/aweme/v1/feed/?aweme_id={video_id}'
|
# params provided by https://github.com/sheldygg
|
||||||
print("正在获取视频数据API: {}".format(api_url))
|
params = {
|
||||||
async with aiohttp.ClientSession() as session:
|
"iid": "7318518857994389254",
|
||||||
async with session.get(api_url, headers=self.tiktok_api_headers, proxy=self.proxies,
|
"device_id": "7318517321748022790",
|
||||||
timeout=10) as response:
|
"channel": "googleplay",
|
||||||
response = await response.json()
|
"app_name": "musical_ly",
|
||||||
video_data = response['aweme_list'][0]
|
"version_code": "300904",
|
||||||
# print('获取视频信息成功!')
|
"device_platform": "android",
|
||||||
|
"device_type": "ASUS_Z01QD",
|
||||||
|
"os_version": "9",
|
||||||
|
"aweme_id": video_id
|
||||||
|
}
|
||||||
|
api_url = f"https://api22-normal-c-alisg.tiktokv.com/aweme/v1/feed/"
|
||||||
|
# 使用httpx请求
|
||||||
|
async with httpx.AsyncClient() as client:
|
||||||
|
response = await client.get(api_url, params=params, headers=self.tiktok_api_headers, timeout=10)
|
||||||
|
response = response.json()
|
||||||
|
# 获取视频数据/Get video data
|
||||||
|
video_data = response["aweme_list"][0]
|
||||||
return video_data
|
return video_data
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print('获取视频信息失败!原因:{}'.format(e))
|
raise ValueError(f"获取TikTok视频数据出错了: {e}")
|
||||||
# return None
|
|
||||||
raise e
|
|
||||||
|
|
||||||
"""__________________________________________⬇️bilibili methods(Bilibili方法)⬇️______________________________________"""
|
"""__________________________________________⬇️bilibili methods(Bilibili方法)⬇️______________________________________"""
|
||||||
|
|
||||||
@ -978,8 +1004,14 @@ if __name__ == '__main__':
|
|||||||
# async_test(_douyin_url=douyin_url, _tiktok_url=tiktok_url, _bilibili_url=bilibili_url, _ixigua_url=ixigua_url,
|
# async_test(_douyin_url=douyin_url, _tiktok_url=tiktok_url, _bilibili_url=bilibili_url, _ixigua_url=ixigua_url,
|
||||||
# _kuaishou_url=kuaishou_url))
|
# _kuaishou_url=kuaishou_url))
|
||||||
|
|
||||||
tiktok_id = asyncio.run(api.get_tiktok_video_id(tiktok_url))
|
# tiktok_id = asyncio.run(api.get_tiktok_video_id(tiktok_url))
|
||||||
|
#
|
||||||
|
# tiktok_data = asyncio.run(api.get_tiktok_video_data(tiktok_id))
|
||||||
|
#
|
||||||
|
# print(tiktok_data)
|
||||||
|
|
||||||
tiktok_data = asyncio.run(api.get_tiktok_video_data(tiktok_id))
|
douyin_id = asyncio.run(api.get_douyin_video_id(douyin_url))
|
||||||
|
|
||||||
print(tiktok_data)
|
douyin_data = asyncio.run(api.get_douyin_video_data(douyin_id))
|
||||||
|
|
||||||
|
print(douyin_data)
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
# -*- encoding: utf-8 -*-
|
# -*- encoding: utf-8 -*-
|
||||||
# @Author: https://github.com/Evil0ctal/
|
# @Author: https://github.com/Evil0ctal/
|
||||||
# @Time: 2021/11/06
|
# @Time: 2021/11/06
|
||||||
# @Update: 2023/09/25
|
# @Update: 2024/03/25
|
||||||
# @Version: 3.1.8
|
# @Version: 3.1.8
|
||||||
# @Function:
|
# @Function:
|
||||||
# 创建一个接受提交参数的FastAPi应用程序。
|
# 创建一个接受提交参数的FastAPi应用程序。
|
||||||
@ -16,7 +16,7 @@ import aiohttp
|
|||||||
import uvicorn
|
import uvicorn
|
||||||
import zipfile
|
import zipfile
|
||||||
import threading
|
import threading
|
||||||
import configparser
|
import yaml
|
||||||
|
|
||||||
from fastapi import FastAPI, Request
|
from fastapi import FastAPI, Request
|
||||||
from fastapi.responses import ORJSONResponse, FileResponse
|
from fastapi.responses import ORJSONResponse, FileResponse
|
||||||
@ -29,8 +29,8 @@ from starlette.responses import RedirectResponse
|
|||||||
from scraper import Scraper
|
from scraper import Scraper
|
||||||
|
|
||||||
# 读取配置文件
|
# 读取配置文件
|
||||||
config = configparser.ConfigParser()
|
with open('config.yml', 'r', encoding='utf-8') as yaml_file:
|
||||||
config.read('config.ini', encoding='utf-8')
|
config = yaml.safe_load(yaml_file)
|
||||||
# 运行端口
|
# 运行端口
|
||||||
port = int(config["Web_API"]["Port"])
|
port = int(config["Web_API"]["Port"])
|
||||||
# 域名
|
# 域名
|
||||||
|
@ -2,13 +2,13 @@
|
|||||||
# -*- encoding: utf-8 -*-
|
# -*- encoding: utf-8 -*-
|
||||||
# @Author: https://github.com/Evil0ctal/
|
# @Author: https://github.com/Evil0ctal/
|
||||||
# @Time: 2021/11/06
|
# @Time: 2021/11/06
|
||||||
# @Update: 2023/09/25
|
# @Update: 2024/03/25
|
||||||
# @Version: 3.1.8
|
# @Version: 3.1.8
|
||||||
# @Function:
|
# @Function:
|
||||||
# 用于在线批量解析Douyin/TikTok的无水印视频/图集。
|
# 用于在线批量解析Douyin/TikTok的无水印视频/图集。
|
||||||
# 基于 PyWebIO,将scraper.py返回的内容显示在网页上。
|
# 基于 PyWebIO,将scraper.py返回的内容显示在网页上。
|
||||||
|
|
||||||
import configparser
|
import yaml
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
import time
|
import time
|
||||||
@ -20,8 +20,9 @@ from pywebio.input import *
|
|||||||
from pywebio.output import *
|
from pywebio.output import *
|
||||||
from pywebio.session import info as session_info, run_asyncio_coroutine
|
from pywebio.session import info as session_info, run_asyncio_coroutine
|
||||||
|
|
||||||
config = configparser.ConfigParser()
|
# 读取配置文件
|
||||||
config.read('config.ini', encoding='utf-8')
|
with open('config.yml', 'r', encoding='utf-8') as yaml_file:
|
||||||
|
config = yaml.safe_load(yaml_file)
|
||||||
|
|
||||||
# 创建一个Scraper类的实例/Create an instance of the Scraper class
|
# 创建一个Scraper类的实例/Create an instance of the Scraper class
|
||||||
api = Scraper()
|
api = Scraper()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user