#!/bin/python #coding: utf-8 # +------------------------------------------------------------------- # | django-vue-lyadmin 专业版 # +------------------------------------------------------------------- # | Author: lybbn # +------------------------------------------------------------------- # | QQ: 1042594286 # +------------------------------------------------------------------- # | Date: 2023-10-21 # +------------------------------------------------------------------- # | version: 1.0 # +------------------------------------------------------------------- # 抖音开放平台接口 # ------------------------------ import json import requests import logging from urllib import parse from django.core.cache import cache logger = logging.getLogger(__name__) class douyin: def __init__(self, client_key: str, client_secret: str): self._client_key = client_key # 应用唯一标识 self._client_secret = client_secret # 应用密钥 self._gate_way = "https://open.douyin.com/" def _request(self, url: str, params: dict, method: str = "post",headers=None,isJson=True) -> json: """ 请求 :param url: 接口url :param params: 业务参数字典,示例:{'client_secret':'xxxx','aaa':'xxxx'} :param method: post 请求、 get请求 """ try: if not headers: headers = {} if isJson: headers.update({'Content-Type': 'application/json'}) headers.update({'Accept': 'application/json'}) else: headers.update({'Content-Type': 'application/x-www-form-urlencoded'}) headers.update({'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Safari/537.36'}) if method == "post": if isJson: param_json = json.dumps(params) else: param_json = parse.urlencode(params) response = requests.post(url=url, data=param_json, headers=headers) else: response = requests.get(url=url, headers=headers) if response.status_code != 200: return None return json.loads(response.content) except Exception as e: logger.error('请求地址:{}报错,{}'.format(url,e)) return None def create_code(self,scope,redirect_uri="",optionalScope="",state="state") -> None: """ 生成授权链接,获取授权码(打开该url后,页面会出现一个二维码,用户扫描该二维码即可授权,在抖音 APP 支持端内唤醒的版本内打开的话会弹出客户端原生授权页面) 生成的url链接给前端用户pc浏览器访问,或ur链接地址转换成二维码用抖音app扫码(注意该URL不是用来请求的, 需要展示给用户用于扫码) 用户允许授权后,将会重定向到redirect_uri的网址上,并且带上code和state参数 scope: 应用授权作用域,多个授权作用域以英文逗号(,)分隔 state: 用于保持请求和回调的状态 redirect_uri:需要配置再抖音的授权回调白名单,否则报错,回调地址一定要采用https协议 """ url = self._gate_way+"platform/oauth/connect/?client_key={}&response_type=code&scope={}&optionalScope={}&redirect_uri={}&state={}".format(self._client_key,scope,optionalScope,redirect_uri,state) return url def callback_code(self,data): """ 验证处理抖音开放平台code授权回调返回的code信息 用户允许授权后,将会重定向到redirect_uri的网址上,并且带上code和state参数 使用apiview获取回调请求数据 """ code = data.get("code") state = data.get("state") return code,state def get_access_token(self,code): """ 获取access_token(过期时间 15 天) 当 access_token 过期(过期时间 15 天)后,可以通过该接口使用 refresh_token(过期时间 30 天)进行刷新。刷新后获得一个有效期为15天的 access_token,但是 refresh_token 的有效期保持不变。 若 refresh_token 过期,获取 access_token 会报错(error_code=10010),此时需要重新引导用户授权。 注意:获取到 access_token 后授权临时票据 (code) 不要再授权刷新,否则会导致上一次获取的 code 过期。 """ url = self._gate_way+"oauth/access_token/" param = { "client_secret":self._client_secret, "code":code, "grant_type":"authorization_code", "client_key":self._client_key } return self._request(url=url,params=param,method="post") def refresh_refresh_token(self,refresh_token): """ 刷新 refresh_token(过期时间 30 天) 该接口用于刷新 refresh_token 的有效期。使用前提:client_key 必须拥有 renew_refresh_token 权限。 刷新操作需要在 refresh_token 过期前进行。 通过旧的 refresh_token 获取新的 refresh_token,调用后旧 refresh_token 会失效,新 refresh_token 有 30 天有效期。最多只能获取 5 次新的 refresh_token,5 次过后需要用户重新授权。 """ url = self._gate_way+"oauth/renew_refresh_token/" param = { "refresh_token":refresh_token, "client_key":self._client_key } return self._request(url=url,params=param,method="post",isJson=False) def create_client_token(self): """ 该接口用于获取接口调用的凭证 client_token client_token 用于不需要用户授权就可以调用的接口。 client_token 的有效时间为 2 个小时,重复获取 client_token 后会使上次的 client_token 失效(但有 5 分钟的缓冲时间,连续多次获取 client_token 只会保留最新的两个 client_token)。 """ url = self._gate_way+"oauth/client_token/" param = { "grant_type":"client_credential", "client_key":self._client_key, "client_secret":self._client_secret, } res = self._request(url=url,params=param,method="post") if res and "error_code" in res["data"] and res["data"]["error_code"] == 0: access_token = res.get('data').get("access_token") expires_in = res.get('data').get("expires_in") cache.set("douyin_client_token"+self._client_key, access_token,expires_in - 3000) return access_token return None def get_client_token(self): """ 获取client_token """ client_token = cache.get("douyin_client_token"+self._client_key) if client_token: return client_token return self.create_client_token() def refresh_access_token(self,refresh_token): """ 该接口用于刷新 access_token 的有效期; 当 access_token 过期(过期时间 15 天)后,可以通过该接口使用 refresh_token(过期时间 30 天)进行刷新。刷新后获得一个有效期为15天的 access_token,但是 refresh_token 的有效期保持不变。 若 refresh_token 过期,获取 access_token 会报错(error_code=10010),此时需要重新引导用户授权。 用户可以在抖音-我-设置(右上角)-帐号与安全-授权管理 中取消对应用的授权,取消授权后原有 access_token 会立即失效。 抖音开放平台会定期对用户授权进行检查,取消不合规的 access_token 授权。 """ url = self._gate_way+"oauth/refresh_token/" param = { "grant_type":"refresh_token", "client_key":self._client_key, "refresh_token":refresh_token, } return self._request(url=url,params=param,method="post",isJson=False) def get_userinfo(self,access_token,open_id): """ 该接口获取用户的抖音公开信息,包含昵称和头像,适用于抖音、抖音极速版 若需要获取用户手机号,需要用户额外授权 mobile_alert 权限,本接口会额外返回 encrypt_mobile 字段 open_id:通过 /oauth/access_token/ 获取的,用户唯一标识 """ url = self._gate_way+"oauth/userinfo/" params = { "access_token":access_token, "open_id":open_id } return self._request(url=url,params=params,method="post",isJson=False) def get_usermobile(self): """ 该接口在用户公开信息的基础上,额外获取用户的手机号。 需要抖音开放平台申请权限。路径:抖音开放平台控制台 > 应用详情 > 能力管理 > 用户权限 > 获取用户手机号 注意事项: 1、用户授权后,获取用户公开信息接口会额外返回 encrypt_mobile 字段。 2、解密手机号,使用 AES 算法解密,秘钥是 client_secret, 向量 lv 是 client_secret 前 16 字节。 """ pass def get_userfans(self,open_id="",date_type=7): """ 获取用户粉丝数 open_id:用户openid date_type:数字型 近7/15天;输入7代表7天、15代表15天、30代表30天 https://developer.open-douyin.com/docs/resource/zh-CN/dop/develop/openapi/data-open-service/user-data/get-user-fans-count """ url = self._gate_way+"data/external/user/fans/" params = { "date_type":date_type, "open_id":open_id } res = self._request(url=url,params=params,method="post",isJson=True) if res and "error_code" in res["data"] and res["data"]["error_code"] == 0: return res["data"]["result_list"] return None def get_billboard_live(self): """ 获取直播榜数据,不需要用户授权(使用client_token) https://developer.open-douyin.com/docs/resource/zh-CN/dop/develop/openapi/data-open-service/tops-data/live-list-data/live-list """ url = self._gate_way+"data/extern/billboard/live/" client_token = self.get_access_token() headers = {} headers.update({'Content-Type': 'application/json'}) headers.update({'access-token': client_token}) res = self._request(url=url,method="get",headers=headers) if res and "error_code" in res["data"] and res["data"]["error_code"] == 0: return res["data"]["list"] return None def video_search(self,access_token,open_id,cursor=0,count=10,keyword=""): """ 通过关键词搜索全站视频,类似抖音端上搜索。使用前请到 管理中心-应用详情-关键词视频管理-关键词管理 创建关键词 该接口只返回最近 1 天的视频 cursor:分页游标, 第一页请求cursor是0, response中会返回下一页请求用到的cursor, 同时response还会返回has_more来表明是否有更多的数据。 count:每页数量 keyword:关键词 https://developer.open-douyin.com/docs/resource/zh-CN/dop/develop/openapi/search-management/keywords-video-list/keywords-video """ url = self._gate_way+"video/search/?open_id="+open_id+"&cursor="+cursor+"&count="+count+"&keyword="+keyword headers = {} headers.update({'Content-Type': 'application/json'}) headers.update({'access-token': access_token}) res = self._request(url=url,method="get",headers=headers) if res and "error_code" in res["data"] and res["data"]["error_code"] == 0: return res["data"] return None