🎨: Update Douyin Web Endpoints with a_bogus parameter

This commit is contained in:
Evil0ctal 2024-06-14 15:09:06 -07:00
parent 0f0f725772
commit 46c06d21cc
6 changed files with 145 additions and 61 deletions

View File

@ -52,13 +52,13 @@
## 🔖TikHub.io API
[TikHub.io](https://beta-web.tikhub.io/en-us/users/signin)是一个API平台提供包括Douyin、TikTok在内的各种公开数据接口如果您想支持 [Douyin_TikTok_Download_API](https://github.com/Evil0ctal/Douyin_TikTok_Download_API) 项目的开发,我们强烈建议您选择[TikHub.io](https://beta-web.tikhub.io/en-us/users/signin)。
[TikHub.io](https://api.tikhub.io/)是一个API平台提供包括Douyin、TikTok在内的各种公开数据接口如果您想支持 [Douyin_TikTok_Download_API](https://github.com/Evil0ctal/Douyin_TikTok_Download_API) 项目的开发,我们强烈建议您选择[TikHub.io](https://api.tikhub.io/)。
#### 特点:
> 📦 开箱即用
省去繁琐的使用流程使用封装好的SDK快速进行开发让调用变得更简单所有API接口都按照OpenAPI规范进行编写并且附带示例参数
简化使用流程利用封装好的SDK迅速开展开发工作。所有API接口均依据RESTful架构设计并使用OpenAPI规范进行描述和文档化附带示例参数确保调用更加简便
> 💰 成本优势
@ -74,6 +74,7 @@ TikHub的部分源代码会开源在Github上并且会赞助一些开源项
#### 链接:
- Github: [TikHub Github](https://github.com/TikHubIO)
- Discord: [TikHub Discord](https://discord.com/invite/aMEAS8Xsvz)
- Register: [TikHub signup](https://beta-web.tikhub.io/en-us/users/signup)
- API Docs: [TikHub API Docs](https://api.tikhub.io/)

View File

@ -13,7 +13,7 @@ DouyinWebCrawler = DouyinWebCrawler()
# 获取单个作品数据
@router.get("/fetch_one_video", response_model=ResponseModel, summary="获取单个作品数据/Get single video data")
async def fetch_one_video(request: Request,
aweme_id: str = Query(example="7345492945006595379", description="作品id/Video id")):
aweme_id: str = Query(example="7372484719365098803", description="作品id/Video id")):
"""
# [中文]
### 用途:
@ -32,7 +32,7 @@ async def fetch_one_video(request: Request,
- Video data
# [示例/Example]
aweme_id = "7345492945006595379"
aweme_id = "7372484719365098803"
"""
try:
data = await DouyinWebCrawler.fetch_one_video(aweme_id)
@ -470,7 +470,7 @@ async def handler_user_profile(request: Request,
response_model=ResponseModel,
summary="获取单个视频评论数据/Get single video comments data")
async def fetch_video_comments(request: Request,
aweme_id: str = Query(example="7345492945006595379", description="作品id/Video id"),
aweme_id: str = Query(example="7372484719365098803", description="作品id/Video id"),
cursor: int = Query(default=0, description="游标/Cursor"),
count: int = Query(default=20, description="数量/Number")):
"""
@ -495,7 +495,7 @@ async def fetch_video_comments(request: Request,
- Comments data
# [示例/Example]
aweme_id = "7345492945006595379"
aweme_id = "7372484719365098803"
cursor = 0
count = 20
"""
@ -740,7 +740,7 @@ async def generate_x_bogus(request: Request,
summary="使用接口网址生成A-Bogus参数/Generate A-Bogus parameter using API URL")
async def generate_a_bogus(request: Request,
url: str = Query(
example="https://www.douyin.com/aweme/v1/web/aweme/detail/?device_platform=webapp&aid=6383&channel=channel_pc_web&pc_client_type=1&version_code=190500&version_name=19.5.0&cookie_enabled=true&browser_language=zh-CN&browser_platform=Win32&browser_name=Firefox&browser_online=true&engine_name=Gecko&os_name=Windows&os_version=10&platform=PC&screen_width=1920&screen_height=1080&browser_version=124.0&engine_version=122.0.0.0&cpu_core_num=12&device_memory=8&aweme_id=7345492945006595379"),
example="https://www.douyin.com/aweme/v1/web/aweme/detail/?device_platform=webapp&aid=6383&channel=channel_pc_web&pc_client_type=1&version_code=190500&version_name=19.5.0&cookie_enabled=true&browser_language=zh-CN&browser_platform=Win32&browser_name=Firefox&browser_online=true&engine_name=Gecko&os_name=Windows&os_version=10&platform=PC&screen_width=1920&screen_height=1080&browser_version=124.0&engine_version=122.0.0.0&cpu_core_num=12&device_memory=8&aweme_id=7372484719365098803"),
user_agent: str = Query(
example="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.212 Safari/537.36")):
"""
@ -759,7 +759,7 @@ async def generate_a_bogus(request: Request,
- user_agent: User agent, temporarily does not support customization, just use the default value.
# [示例/Example]
url = "https://www.douyin.com/aweme/v1/web/aweme/detail/?device_platform=webapp&aid=6383&channel=channel_pc_web&pc_client_type=1&version_code=190500&version_name=19.5.0&cookie_enabled=true&browser_language=zh-CN&browser_platform=Win32&browser_name=Firefox&browser_online=true&engine_name=Gecko&os_name=Windows&os_version=10&platform=PC&screen_width=1920&screen_height=1080&browser_version=124.0&engine_version=122.0.0.0&cpu_core_num=12&device_memory=8&aweme_id=7345492945006595379"
url = "https://www.douyin.com/aweme/v1/web/aweme/detail/?device_platform=webapp&aid=6383&channel=channel_pc_web&pc_client_type=1&version_code=190500&version_name=19.5.0&cookie_enabled=true&browser_language=zh-CN&browser_platform=Win32&browser_name=Firefox&browser_online=true&engine_name=Gecko&os_name=Windows&os_version=10&platform=PC&screen_width=1920&screen_height=1080&browser_version=124.0&engine_version=122.0.0.0&cpu_core_num=12&device_memory=8&aweme_id=7372484719365098803"
user_agent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.212 Safari/537.36"
"""
try:

View File

@ -4,7 +4,7 @@ import zipfile
import aiofiles
import httpx
import yaml
from fastapi import APIRouter, Request # 导入FastAPI组件
from fastapi import APIRouter, Request, Query # 导入FastAPI组件
from starlette.responses import FileResponse
from app.api.models.APIResponseModel import ErrorResponseModel # 导入响应模型
@ -31,12 +31,47 @@ async def fetch_data(url: str, headers: dict = None):
@router.get("/download", summary="在线下载抖音|TikTok视频/图片/Online download Douyin|TikTok video/image")
async def download_file_hybrid(request: Request,
url: str, prefix: bool = True, with_watermark: bool = False):
url: str = Query(
example="https://www.douyin.com/video/7372484719365098803",
description="视频或图片的URL地址也支持抖音|TikTok的分享链接例如https://v.douyin.com/e4J8Q7A/"),
prefix: bool = True,
with_watermark: bool = False):
"""
# [中文]
### 用途:
- 在线下载抖音|TikTok 无水印或有水印的视频/图片
- 通过传入的视频URL参数获取对应的视频或图片数据然后下载到本地
- 如果你在尝试直接访问TikTok单一视频接口的JSON数据中的视频播放地址时遇到HTTP403错误那么你可以使用此接口来下载视频
- 这个接口会占用一定的服务器资源所以在Demo站点是默认关闭的你可以在本地部署后调用此接口
### 参数:
- url: 视频或图片的URL地址也支持抖音|TikTok的分享链接例如https://v.douyin.com/e4J8Q7A/
- prefix: 下载文件的前缀默认为True可以在配置文件中修改
- with_watermark: 是否下载带水印的视频或图片默认为False
### 返回:
- 返回下载的视频或图片文件响应
# [English]
### Purpose:
- Download Douyin|TikTok video/image with or without watermark online.
- By passing the video URL parameter, get the corresponding video or image data, and then download it to the local.
- If you encounter an HTTP403 error when trying to access the video playback address in the JSON data of the TikTok single video interface directly, you can use this interface to download the video.
- This interface will occupy a certain amount of server resources, so it is disabled by default on the Demo site, you can call this interface after deploying it locally.
### Parameters:
- url: The URL address of the video or image, also supports Douyin|TikTok sharing links, for example: https://v.douyin.com/e4J8Q7A/.
- prefix: The prefix of the downloaded file, the default is True, and can be modified in the configuration file.
- with_watermark: Whether to download videos or images with watermarks, the default is False.
### Returns:
- Return the response of the downloaded video or image file.
# [示例/Example]
url: https://www.douyin.com/video/7372484719365098803
"""
# 是否开启此端点/Whether to enable this endpoint
if not config["API"]["Download_Switch"]:
code = 400
message = "Download endpoint is disabled."
return ErrorResponseModel(code=code, message=message, router=request.url.path, params=dict(request.query_params))
message = "Download endpoint is disabled in the configuration file. | 配置文件中已禁用下载端点。"
return ErrorResponseModel(code=code, message=message, router=request.url.path,
params=dict(request.query_params))
# 开始解析数据/Start parsing data
try:
@ -68,7 +103,8 @@ async def download_file_hybrid(request: Request,
return FileResponse(path=file_path, media_type='video/mp4', filename=file_name)
# 获取视频文件
response = await fetch_data(url) if platform == 'douyin' else await fetch_data(url, headers=await HybridCrawler.TikTokWebCrawler.get_tiktok_headers())
response = await fetch_data(url) if platform == 'douyin' else await fetch_data(url,
headers=await HybridCrawler.TikTokWebCrawler.get_tiktok_headers())
# 保存文件
async with aiofiles.open(file_path, 'wb') as out_file:
@ -118,4 +154,3 @@ async def download_file_hybrid(request: Request,
print(e)
code = 400
return ErrorResponseModel(code=code, message=str(e), router=request.url.path, params=dict(request.query_params))

View File

@ -17,14 +17,49 @@ def api_document_pop_window():
put_markdown("----")
put_markdown(t("> 更多接口",
"> More APIs"))
put_markdown(t("如果你想要使用更多且更稳定的API服务可以使用付费API服务",
"If you want to use more and more stable API services, you can use paid API services"))
put_link('[TikHub API]', 'https://api.tikhub.io', new_window=True)
put_markdown(
t("[TikHub.io](https://beta-web.tikhub.io/en-us/users/signin)是一个API平台提供包括Douyin、TikTok在内的各种公开数据接口如果您想支持 [Douyin_TikTok_Download_API](https://github.com/Evil0ctal/Douyin_TikTok_Download_API) 项目的开发,我们强烈建议您选择[TikHub.io](https://beta-web.tikhub.io/en-us/users/signin)。",
"[TikHub.io](https://beta-web.tikhub.io/en-us/users/signin) is an API platform that provides various public data interfaces including Douyin and TikTok. If you want to support the development of the [Douyin_TikTok_Download_API](https://github.com/Evil0ctal/Douyin_TikTok_Download_API) project, we strongly recommend that you choose [TikHub.io](https://beta-web.tikhub.io/en-us/users/signin)."))
put_markdown(
t("#### 特点:",
"#### Features:"))
put_markdown(
t("> 📦 开箱即用",
"> 📦 Ready to use"))
put_markdown(
t("简化使用流程利用封装好的SDK迅速开展开发工作。所有API接口均依据RESTful架构设计并使用OpenAPI规范进行描述和文档化附带示例参数确保调用更加简便。",
"Simplify the use process and quickly carry out development work using the encapsulated SDK. All API interfaces are designed based on the RESTful architecture and described and documented using the OpenAPI specification, with example parameters attached to ensure easier calls."))
put_markdown(
t("> 💰 成本优势",
"> 💰 Cost advantage"))
put_markdown(
t("不预设套餐限制,没有月度使用门槛,所有消费按实际使用量即时计费,并且根据用户每日的请求量进行阶梯式计费,同时可以通过每日签到在用户后台进行签到获取免费的额度,并且这些免费额度不会过期。",
"There is no preset package limit, no monthly usage threshold, all consumption is billed in real time according to the actual usage, and billed in a step-by-step manner according to the user's daily request volume. At the same time, you can sign in daily in the user background to get free quotas, and these free quotas will not expire."))
put_markdown(
t("> ⚡️ 快速支持",
"> ⚡️ Quick support"))
put_markdown(
t("我们有一个庞大的Discord社区服务器管理员和其他用户会在服务器中快速的回复你帮助你快速解决当前的问题。",
"We have a huge Discord community server, where administrators and other users will quickly reply to you in the server and help you quickly solve the current problem."))
put_markdown(
t("> 🎉 拥抱开源",
"> 🎉 Embrace open source"))
put_markdown(
t("TikHub的部分源代码会开源在Github上并且会赞助一些开源项目的作者。",
"Some of TikHub's source code will be open sourced on Github, and will sponsor some open source project authors."))
put_markdown(
t("#### 链接:",
"#### Links:"))
put_markdown(
t("- Github: [TikHub Github](https://github.com/TikHubIO)",
"- Github: [TikHub Github](https://github.com/TikHubIO)"))
put_markdown(
t("- Discord: [TikHub Discord](https://discord.com/invite/aMEAS8Xsvz)",
"- Discord: [TikHub Discord](https://discord.com/invite/aMEAS8Xsvz)"))
put_markdown(
t("- Register: [TikHub signup](https://beta-web.tikhub.io/en-us/users/signup)",
"- Register: [TikHub signup](https://beta-web.tikhub.io/en-us/users/signup)"))
put_markdown(
t("- API Docs: [TikHub API Docs](https://api.tikhub.io/)",
"- API Docs: [TikHub API Docs](https://api.tikhub.io/)"))
put_markdown("----")
put_markdown(t("> 限时免费测试",
"> Free test for a limited time"))
put_markdown(t("这里也有一个测试版的API服务你可以直接免费使用",
"There is also a beta version of the API service, which you can use for free"))
put_markdown(t("测试接口只会保留一段时间,不保证数据的稳定性",
"The test interface will only be retained for a period of time, and the stability of the data is not guaranteed"))
put_link('[TikHub Beta API]', 'https://beta.tikhub.io', new_window=True)

View File

@ -116,38 +116,38 @@ class TokenManager:
msToken = str(httpx.Cookies(response.cookies).get("msToken"))
if len(msToken) not in [120, 128]:
raise APIResponseError("{0} 内容不符合要求".format("msToken"))
raise APIResponseError("响应内容:{0} Douyin msToken API 的响应内容不符合要求。".format(msToken))
return msToken
except httpx.RequestError as exc:
# 捕获所有与 httpx 请求相关的异常情况 (Captures all httpx request-related exceptions)
raise APIConnectionError(
"请求端点失败,请检查当前网络环境。 链接:{0},代理:{1},异常类名:{2},异常详细信息:{3}"
.format(cls.token_conf["url"], cls.proxies, cls.__name__, exc)
)
# except httpx.RequestError as exc:
# # 捕获所有与 httpx 请求相关的异常情况 (Captures all httpx request-related exceptions)
# raise APIConnectionError(
# "请求端点失败,请检查当前网络环境。 链接:{0},代理:{1},异常类名:{2},异常详细信息:{3}"
# .format(cls.token_conf["url"], cls.proxies, cls.__name__, exc)
# )
#
# except httpx.HTTPStatusError as e:
# # 捕获 httpx 的状态代码错误 (captures specific status code errors from httpx)
# if e.response.status_code == 401:
# raise APIUnauthorizedError(
# "参数验证失败,请更新 Douyin_TikTok_Download_API 配置文件中的 {0},以匹配 {1} 新规则"
# .format("msToken", "douyin")
# )
#
# elif e.response.status_code == 404:
# raise APINotFoundError("{0} 无法找到API端点".format("msToken"))
# else:
# raise APIResponseError(
# "链接:{0},状态码 {1}{2} ".format(
# e.response.url, e.response.status_code, e.response.text
# )
# )
except httpx.HTTPStatusError as e:
# 捕获 httpx 的状态代码错误 (captures specific status code errors from httpx)
if e.response.status_code == 401:
raise APIUnauthorizedError(
"参数验证失败,请更新 F2 配置文件中的 {0},以匹配 {1} 新规则"
.format("msToken", "douyin")
)
elif e.response.status_code == 404:
raise APINotFoundError("{0} 无法找到API端点".format("msToken"))
else:
raise APIResponseError(
"链接:{0},状态码 {1}{2} ".format(
e.response.url, e.response.status_code, e.response.text
)
)
except APIError as e:
except Exception as e:
# 返回虚假的msToken (Return a fake msToken)
logger.error("msToken API错误{0}".format(e))
logger.info("生成虚假msToken")
logger.error("请求Douyin msToken API时发生错误{0}".format(e))
logger.info("将使用本地生成的虚假msToken参数以继续请求。")
return cls.gen_false_msToken()
@classmethod
@ -184,7 +184,7 @@ class TokenManager:
# 捕获 httpx 的状态代码错误 (captures specific status code errors from httpx)
if e.response.status_code == 401:
raise APIUnauthorizedError(
"参数验证失败,请更新 F2 配置文件中的 {0},以匹配 {1} 新规则"
"参数验证失败,请更新 Douyin_TikTok_Download_API 配置文件中的 {0},以匹配 {1} 新规则"
.format("ttwid", "douyin")
)

View File

@ -37,8 +37,6 @@ import asyncio # 异步I/O
import os # 系统操作
import time # 时间操作
from urllib.parse import urlencode, quote # URL编码
import httpx
import yaml # 配置文件
# 基础爬虫客户端和抖音API端点
@ -117,9 +115,17 @@ class DouyinWebCrawler:
base_crawler = BaseCrawler(proxies=kwargs["proxies"], crawler_headers=kwargs["headers"])
async with base_crawler as crawler:
params = UserPost(sec_user_id=sec_user_id, max_cursor=max_cursor, count=count)
endpoint = BogusManager.xb_model_2_endpoint(
DouyinAPIEndpoints.USER_POST, params.dict(), kwargs["headers"]["User-Agent"]
)
# endpoint = BogusManager.xb_model_2_endpoint(
# DouyinAPIEndpoints.USER_POST, params.dict(), kwargs["headers"]["User-Agent"]
# )
# response = await crawler.fetch_get_json(endpoint)
# 生成一个用户发布作品数据的带有a_bogus加密参数的Endpoint
params_dict = params.dict()
params_dict["msToken"] = ''
a_bogus = BogusManager.ab_model_2_endpoint(params_dict, kwargs["headers"]["User-Agent"])
endpoint = f"{DouyinAPIEndpoints.USER_POST}?{urlencode(params_dict)}&a_bogus={a_bogus}"
response = await crawler.fetch_get_json(endpoint)
return response
@ -129,9 +135,16 @@ class DouyinWebCrawler:
base_crawler = BaseCrawler(proxies=kwargs["proxies"], crawler_headers=kwargs["headers"])
async with base_crawler as crawler:
params = UserLike(sec_user_id=sec_user_id, max_cursor=max_cursor, count=count)
endpoint = BogusManager.xb_model_2_endpoint(
DouyinAPIEndpoints.USER_FAVORITE_A, params.dict(), kwargs["headers"]["User-Agent"]
)
# endpoint = BogusManager.xb_model_2_endpoint(
# DouyinAPIEndpoints.USER_FAVORITE_A, params.dict(), kwargs["headers"]["User-Agent"]
# )
# response = await crawler.fetch_get_json(endpoint)
params_dict = params.dict()
params_dict["msToken"] = ''
a_bogus = BogusManager.ab_model_2_endpoint(params_dict, kwargs["headers"]["User-Agent"])
endpoint = f"{DouyinAPIEndpoints.USER_FAVORITE_A}?{urlencode(params_dict)}&a_bogus={a_bogus}"
response = await crawler.fetch_get_json(endpoint)
return response
@ -339,7 +352,7 @@ class DouyinWebCrawler:
"""-------------------------------------------------------handler接口列表-------------------------------------------------------"""
# 获取单一视频信息
# aweme_id = "7345492945006595379"
# aweme_id = "7372484719365098803"
# result = await self.fetch_one_video(aweme_id)
# print(result)