redfox-python-sdk 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- redfox/__init__.py +7 -0
- redfox/client.py +138 -0
- redfox/endpoints/__init__.py +1 -0
- redfox/endpoints/douyin.py +164 -0
- redfox/exceptions.py +26 -0
- redfox_python_sdk-0.1.0.dist-info/METADATA +49 -0
- redfox_python_sdk-0.1.0.dist-info/RECORD +9 -0
- redfox_python_sdk-0.1.0.dist-info/WHEEL +5 -0
- redfox_python_sdk-0.1.0.dist-info/top_level.txt +1 -0
redfox/__init__.py
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
"""RedFox Python SDK - 红狐数据平台 Python 客户端"""
|
|
2
|
+
|
|
3
|
+
from .client import RedFoxClient
|
|
4
|
+
from .exceptions import RedFoxAPIError, RedFoxAuthError, RedFoxRateLimitError
|
|
5
|
+
|
|
6
|
+
__version__ = "0.1.0"
|
|
7
|
+
__all__ = ["RedFoxClient", "RedFoxAPIError", "RedFoxAuthError", "RedFoxRateLimitError"]
|
redfox/client.py
ADDED
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
"""RedFox SDK 核心客户端"""
|
|
2
|
+
|
|
3
|
+
import requests
|
|
4
|
+
from typing import Optional, Dict, Any
|
|
5
|
+
|
|
6
|
+
from .exceptions import RedFoxAPIError, RedFoxAuthError, RedFoxRateLimitError
|
|
7
|
+
from .endpoints.douyin import DouyinAPI
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class RedFoxClient:
|
|
11
|
+
"""
|
|
12
|
+
RedFox 红狐数据平台客户端
|
|
13
|
+
|
|
14
|
+
使用示例::
|
|
15
|
+
|
|
16
|
+
from redfox import RedFoxClient
|
|
17
|
+
|
|
18
|
+
client = RedFoxClient(api_key="your_api_key")
|
|
19
|
+
|
|
20
|
+
# 搜索抖音作品
|
|
21
|
+
result = client.douyin.search_articles(keyword="AI")
|
|
22
|
+
|
|
23
|
+
# 获取账号信息
|
|
24
|
+
user = client.douyin.get_user(account_id="dy_user123")
|
|
25
|
+
|
|
26
|
+
:param api_key: RedFox API Key,在 https://redfox.hk/settings/api-keys?source=github 获取
|
|
27
|
+
:param base_url: API 基础地址,默认 https://redfox.hk
|
|
28
|
+
:param timeout: 请求超时时间(秒),默认 30
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
DEFAULT_BASE_URL = "https://redfox.hk"
|
|
32
|
+
DEFAULT_TIMEOUT = 30
|
|
33
|
+
|
|
34
|
+
def __init__(
|
|
35
|
+
self,
|
|
36
|
+
api_key: str,
|
|
37
|
+
base_url: str = DEFAULT_BASE_URL,
|
|
38
|
+
timeout: int = DEFAULT_TIMEOUT,
|
|
39
|
+
):
|
|
40
|
+
if not api_key:
|
|
41
|
+
raise ValueError(
|
|
42
|
+
"api_key 不能为空,请在 https://redfox.hk/settings/api-keys?source=github 获取"
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
self.api_key = api_key
|
|
46
|
+
self.base_url = base_url.rstrip("/")
|
|
47
|
+
self.timeout = timeout
|
|
48
|
+
|
|
49
|
+
self._session = requests.Session()
|
|
50
|
+
self._session.headers.update({
|
|
51
|
+
"Content-Type": "application/json",
|
|
52
|
+
"REDFOX_API_KEY": self.api_key,
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
# 初始化各平台 API 模块
|
|
56
|
+
self.douyin = DouyinAPI(self)
|
|
57
|
+
|
|
58
|
+
def request(
|
|
59
|
+
self,
|
|
60
|
+
method: str,
|
|
61
|
+
path: str,
|
|
62
|
+
params: Optional[Dict[str, Any]] = None,
|
|
63
|
+
data: Optional[Dict[str, Any]] = None,
|
|
64
|
+
) -> dict:
|
|
65
|
+
"""
|
|
66
|
+
发送 API 请求
|
|
67
|
+
|
|
68
|
+
:param method: HTTP 方法(GET/POST)
|
|
69
|
+
:param path: API 路径
|
|
70
|
+
:param params: URL 查询参数
|
|
71
|
+
:param data: 请求体 JSON 数据
|
|
72
|
+
:return: API 响应 data 字段内容
|
|
73
|
+
:raises RedFoxAPIError: API 返回错误时抛出
|
|
74
|
+
"""
|
|
75
|
+
url = f"{self.base_url}{path}"
|
|
76
|
+
|
|
77
|
+
# 过滤掉值为 None 的参数
|
|
78
|
+
if data:
|
|
79
|
+
data = {k: v for k, v in data.items() if v is not None}
|
|
80
|
+
if params:
|
|
81
|
+
params = {k: v for k, v in params.items() if v is not None}
|
|
82
|
+
|
|
83
|
+
try:
|
|
84
|
+
response = self._session.request(
|
|
85
|
+
method=method,
|
|
86
|
+
url=url,
|
|
87
|
+
params=params,
|
|
88
|
+
json=data,
|
|
89
|
+
timeout=self.timeout,
|
|
90
|
+
)
|
|
91
|
+
except requests.exceptions.Timeout:
|
|
92
|
+
raise RedFoxAPIError("请求超时,请稍后重试")
|
|
93
|
+
except requests.exceptions.ConnectionError:
|
|
94
|
+
raise RedFoxAPIError("网络连接失败,请检查网络")
|
|
95
|
+
except requests.exceptions.RequestException as e:
|
|
96
|
+
raise RedFoxAPIError(f"请求异常: {str(e)}")
|
|
97
|
+
|
|
98
|
+
return self._handle_response(response)
|
|
99
|
+
|
|
100
|
+
def post(self, path: str, data: Optional[Dict[str, Any]] = None) -> dict:
|
|
101
|
+
"""发送 POST 请求"""
|
|
102
|
+
return self.request("POST", path, data=data)
|
|
103
|
+
|
|
104
|
+
def get(self, path: str, params: Optional[Dict[str, Any]] = None) -> dict:
|
|
105
|
+
"""发送 GET 请求"""
|
|
106
|
+
return self.request("GET", path, params=params)
|
|
107
|
+
|
|
108
|
+
def _handle_response(self, response: requests.Response) -> dict:
|
|
109
|
+
"""处理 API 响应"""
|
|
110
|
+
# HTTP 级别错误
|
|
111
|
+
if response.status_code == 401:
|
|
112
|
+
raise RedFoxAuthError(
|
|
113
|
+
"API Key 无效或缺失,请在 https://redfox.hk/settings/api-keys?source=github 获取",
|
|
114
|
+
code=401,
|
|
115
|
+
)
|
|
116
|
+
if response.status_code == 429:
|
|
117
|
+
raise RedFoxRateLimitError("请求频率超限,请稍后重试", code=429)
|
|
118
|
+
if response.status_code >= 500:
|
|
119
|
+
raise RedFoxAPIError(f"服务端错误", code=response.status_code)
|
|
120
|
+
|
|
121
|
+
try:
|
|
122
|
+
result = response.json()
|
|
123
|
+
except ValueError:
|
|
124
|
+
raise RedFoxAPIError(f"响应解析失败: {response.text[:200]}")
|
|
125
|
+
|
|
126
|
+
# 业务级别错误
|
|
127
|
+
code = result.get("code")
|
|
128
|
+
if code and code != 2000:
|
|
129
|
+
msg = result.get("msg", "未知错误")
|
|
130
|
+
if code == 401 or code == 4001:
|
|
131
|
+
raise RedFoxAuthError(
|
|
132
|
+
f"{msg},请在 https://redfox.hk/settings/api-keys?source=github 获取有效的 API Key",
|
|
133
|
+
code=code,
|
|
134
|
+
response=result,
|
|
135
|
+
)
|
|
136
|
+
raise RedFoxAPIError(msg, code=code, response=result)
|
|
137
|
+
|
|
138
|
+
return result.get("data", result)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""端点模块"""
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
"""抖音平台 API 端点"""
|
|
2
|
+
|
|
3
|
+
from typing import Optional, Dict, Any
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class DouyinAPI:
|
|
7
|
+
"""
|
|
8
|
+
抖音平台 API 集合
|
|
9
|
+
|
|
10
|
+
包含抖音作品搜索、账号查询、作品详情等接口。
|
|
11
|
+
所有接口均为 POST 请求,参数通过 JSON body 传递。
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
def __init__(self, client):
|
|
15
|
+
self._client = client
|
|
16
|
+
|
|
17
|
+
# ─── 作品相关 ───────────────────────────────────────────
|
|
18
|
+
|
|
19
|
+
def get_work(
|
|
20
|
+
self,
|
|
21
|
+
work_id: str = None,
|
|
22
|
+
work_url: str = None,
|
|
23
|
+
) -> dict:
|
|
24
|
+
"""
|
|
25
|
+
获取抖音作品内容详情(优质库)
|
|
26
|
+
|
|
27
|
+
通过作品 ID 或作品链接查询作品详细信息,包括互动数据、作者信息等。
|
|
28
|
+
work_id 和 work_url 至少传一个。
|
|
29
|
+
|
|
30
|
+
:param work_id: 作品 ID,如 "10000123456789"
|
|
31
|
+
:param work_url: 作品链接,如 "https://www.douyin.com/video/xxx"
|
|
32
|
+
:return: 作品详情字典
|
|
33
|
+
"""
|
|
34
|
+
data = {}
|
|
35
|
+
if work_id:
|
|
36
|
+
data["workId"] = work_id
|
|
37
|
+
if work_url:
|
|
38
|
+
data["workUrl"] = work_url
|
|
39
|
+
|
|
40
|
+
return self._client.post("/story/api/dyData/queryWork", data=data)
|
|
41
|
+
|
|
42
|
+
def search_articles(
|
|
43
|
+
self,
|
|
44
|
+
keyword: str,
|
|
45
|
+
offset: int = 0,
|
|
46
|
+
sort_type: str = None,
|
|
47
|
+
) -> dict:
|
|
48
|
+
"""
|
|
49
|
+
搜索关键词获取抖音作品(优质库)
|
|
50
|
+
|
|
51
|
+
:param keyword: 搜索关键词(必填)
|
|
52
|
+
:param offset: 分页偏移量,从 0 开始,每次 +20
|
|
53
|
+
:param sort_type: 排序方式,如 "default"
|
|
54
|
+
:return: 搜索结果字典,包含 total/hasMore/list
|
|
55
|
+
"""
|
|
56
|
+
data: Dict[str, Any] = {"keyword": keyword}
|
|
57
|
+
if offset is not None:
|
|
58
|
+
data["offset"] = offset
|
|
59
|
+
if sort_type is not None:
|
|
60
|
+
data["sortType"] = sort_type
|
|
61
|
+
|
|
62
|
+
return self._client.post("/story/api/dyData/searchArticle", data=data)
|
|
63
|
+
|
|
64
|
+
def get_user_works(
|
|
65
|
+
self,
|
|
66
|
+
account_id: str = None,
|
|
67
|
+
author_url: str = None,
|
|
68
|
+
sec_user_id: str = None,
|
|
69
|
+
offset: int = 0,
|
|
70
|
+
sort_type: str = None,
|
|
71
|
+
) -> dict:
|
|
72
|
+
"""
|
|
73
|
+
获取抖音账号作品列表(优质库)
|
|
74
|
+
|
|
75
|
+
account_id / author_url / sec_user_id 至少传一个。
|
|
76
|
+
|
|
77
|
+
:param account_id: 抖音号
|
|
78
|
+
:param author_url: 作者主页链接
|
|
79
|
+
:param sec_user_id: 作者 sec_user_id
|
|
80
|
+
:param offset: 偏移量,从 0 开始,每页 +20
|
|
81
|
+
:param sort_type: 排序方式:0=默认,2=最新,4=最热
|
|
82
|
+
:return: 作品列表字典,包含 total/hasMore/list
|
|
83
|
+
"""
|
|
84
|
+
data: Dict[str, Any] = {}
|
|
85
|
+
if account_id:
|
|
86
|
+
data["accountId"] = account_id
|
|
87
|
+
if author_url:
|
|
88
|
+
data["authorUrl"] = author_url
|
|
89
|
+
if sec_user_id:
|
|
90
|
+
data["secUserId"] = sec_user_id
|
|
91
|
+
if offset is not None:
|
|
92
|
+
data["offset"] = offset
|
|
93
|
+
if sort_type is not None:
|
|
94
|
+
data["sortType"] = sort_type
|
|
95
|
+
|
|
96
|
+
return self._client.post("/story/api/dyData/queryWorkList", data=data)
|
|
97
|
+
|
|
98
|
+
# ─── 账号相关 ───────────────────────────────────────────
|
|
99
|
+
|
|
100
|
+
def get_user(self, account_id: str) -> dict:
|
|
101
|
+
"""
|
|
102
|
+
获取抖音账号信息(优质库)
|
|
103
|
+
|
|
104
|
+
:param account_id: 抖音账号 ID(支持 unique_id、short_id、uid 任一匹配)
|
|
105
|
+
:return: 账号信息字典
|
|
106
|
+
"""
|
|
107
|
+
return self._client.post(
|
|
108
|
+
"/story/api/dyData/queryUser",
|
|
109
|
+
data={"accountId": account_id},
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
def search_users(
|
|
113
|
+
self,
|
|
114
|
+
keyword: str,
|
|
115
|
+
offset: int = 0,
|
|
116
|
+
sort_type: str = None,
|
|
117
|
+
) -> dict:
|
|
118
|
+
"""
|
|
119
|
+
搜索关键词获取抖音账号(优质库)
|
|
120
|
+
|
|
121
|
+
:param keyword: 搜索关键词(必填)
|
|
122
|
+
:param offset: 分页偏移量,从 0 开始
|
|
123
|
+
:param sort_type: 排序方式
|
|
124
|
+
:return: 搜索结果字典,包含 total/hasMore/list
|
|
125
|
+
"""
|
|
126
|
+
data: Dict[str, Any] = {"keyword": keyword}
|
|
127
|
+
if offset is not None:
|
|
128
|
+
data["offset"] = offset
|
|
129
|
+
if sort_type is not None:
|
|
130
|
+
data["sortType"] = sort_type
|
|
131
|
+
|
|
132
|
+
return self._client.post("/story/api/dyData/searchUser", data=data)
|
|
133
|
+
|
|
134
|
+
# ─── AI 作品 ────────────────────────────────────────────
|
|
135
|
+
|
|
136
|
+
def search_ai_articles(
|
|
137
|
+
self,
|
|
138
|
+
keyword: str,
|
|
139
|
+
page_num: int = 1,
|
|
140
|
+
page_size: int = 20,
|
|
141
|
+
start_time: str = None,
|
|
142
|
+
end_time: str = None,
|
|
143
|
+
) -> dict:
|
|
144
|
+
"""
|
|
145
|
+
搜索关键词获取抖音 AI 作品(优质库)
|
|
146
|
+
|
|
147
|
+
:param keyword: 搜索关键词(必填)
|
|
148
|
+
:param page_num: 页码,默认 1
|
|
149
|
+
:param page_size: 每页条数,默认 20
|
|
150
|
+
:param start_time: 开始时间,格式 "2026-06-01 00:00:00"
|
|
151
|
+
:param end_time: 结束时间,格式 "2026-06-02 00:00:00"
|
|
152
|
+
:return: 搜索结果字典,包含 total/pageNum/pageSize/pages/list
|
|
153
|
+
"""
|
|
154
|
+
data: Dict[str, Any] = {
|
|
155
|
+
"keyword": keyword,
|
|
156
|
+
"pageNum": page_num,
|
|
157
|
+
"pageSize": page_size,
|
|
158
|
+
}
|
|
159
|
+
if start_time is not None:
|
|
160
|
+
data["startTime"] = start_time
|
|
161
|
+
if end_time is not None:
|
|
162
|
+
data["endTime"] = end_time
|
|
163
|
+
|
|
164
|
+
return self._client.post("/story/api/parseWork/queryDyAiMsgs", data=data)
|
redfox/exceptions.py
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"""RedFox SDK 自定义异常"""
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class RedFoxAPIError(Exception):
|
|
5
|
+
"""RedFox API 调用异常基类"""
|
|
6
|
+
|
|
7
|
+
def __init__(self, message: str, code: int = None, response: dict = None):
|
|
8
|
+
self.message = message
|
|
9
|
+
self.code = code
|
|
10
|
+
self.response = response
|
|
11
|
+
super().__init__(self.message)
|
|
12
|
+
|
|
13
|
+
def __str__(self):
|
|
14
|
+
if self.code:
|
|
15
|
+
return f"RedFoxAPIError(code={self.code}): {self.message}"
|
|
16
|
+
return f"RedFoxAPIError: {self.message}"
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class RedFoxAuthError(RedFoxAPIError):
|
|
20
|
+
"""鉴权失败(API Key 无效或缺失)"""
|
|
21
|
+
pass
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class RedFoxRateLimitError(RedFoxAPIError):
|
|
25
|
+
"""请求频率超限"""
|
|
26
|
+
pass
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: redfox-python-sdk
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: RedFox 红狐数据平台 Python SDK
|
|
5
|
+
Home-page: https://redfox.hk
|
|
6
|
+
Author: RedFox
|
|
7
|
+
Author-email: dev@redfox.hk
|
|
8
|
+
License: UNKNOWN
|
|
9
|
+
Platform: UNKNOWN
|
|
10
|
+
Classifier: Development Status :: 4 - Beta
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Requires-Python: >=3.8
|
|
15
|
+
Description-Content-Type: text/markdown
|
|
16
|
+
Requires-Dist: requests (>=2.25.0)
|
|
17
|
+
|
|
18
|
+
# RedFox Python SDK
|
|
19
|
+
|
|
20
|
+
红狐数据平台 Python SDK,支持通过简洁的 Python 接口调用红狐平台所有数据 API。
|
|
21
|
+
|
|
22
|
+
## 安装
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
pip install redfox-sdk
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## 快速开始
|
|
29
|
+
|
|
30
|
+
```python
|
|
31
|
+
from redfox import RedFoxClient
|
|
32
|
+
|
|
33
|
+
client = RedFoxClient(api_key="your_api_key")
|
|
34
|
+
|
|
35
|
+
# 搜索抖音作品
|
|
36
|
+
result = client.douyin.search_articles(keyword="人工智能")
|
|
37
|
+
for item in result["list"]:
|
|
38
|
+
print(f"{item['title']} - 点赞: {item['likeCount']}")
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## 获取 API Key
|
|
42
|
+
|
|
43
|
+
前往 [API Keys 页面](https://redfox.hk/settings/api-keys?source=github) 获取。
|
|
44
|
+
|
|
45
|
+
## License
|
|
46
|
+
|
|
47
|
+
MIT
|
|
48
|
+
|
|
49
|
+
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
redfox/__init__.py,sha256=IDMBABFLRl6UV3BuvLLVH7mmJOSCwJZuk2uOAocrXwI,285
|
|
2
|
+
redfox/client.py,sha256=HAatJMY-hc_kKF3NNhKWMX9Q2J0SDQBmO_QUiStY6N8,4589
|
|
3
|
+
redfox/exceptions.py,sha256=QWosa7FXJbxNPenMx1CRIPj7z8SFmmtg2_8Qjzika1E,668
|
|
4
|
+
redfox/endpoints/__init__.py,sha256=sjtHfb6bkX_wdd_ryRajkPrBRnno2HMiwys0DcNIqSA,19
|
|
5
|
+
redfox/endpoints/douyin.py,sha256=ND2iECy5EFNlx8HbB4Fom_H6djFQOMpzaPoao3RZb-Y,5506
|
|
6
|
+
redfox_python_sdk-0.1.0.dist-info/METADATA,sha256=qXRQwCN8VFXCkKDOhl7J4HGwyj_Xu9cabYNQEN3aBHg,1081
|
|
7
|
+
redfox_python_sdk-0.1.0.dist-info/WHEEL,sha256=ewwEueio1C2XeHTvT17n8dZUJgOvyCWCt0WVNLClP9o,92
|
|
8
|
+
redfox_python_sdk-0.1.0.dist-info/top_level.txt,sha256=cOIfFv-M-TaEjpiRQy-OygdvvuHeZrlQSoRWMj0DaEM,7
|
|
9
|
+
redfox_python_sdk-0.1.0.dist-info/RECORD,,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
redfox
|