ipclick 0.1.2__tar.gz → 0.2.3__tar.gz
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.
Potentially problematic release.
This version of ipclick might be problematic. Click here for more details.
- {ipclick-0.1.2 → ipclick-0.2.3}/LICENSE +21 -21
- ipclick-0.2.3/PKG-INFO +421 -0
- ipclick-0.2.3/README.md +390 -0
- ipclick-0.2.3/pyproject.toml +145 -0
- ipclick-0.2.3/src/ipclick/__init__.py +55 -0
- {ipclick-0.1.2 → ipclick-0.2.3}/src/ipclick/__main__.py +5 -4
- ipclick-0.2.3/src/ipclick/adapters/base.py +266 -0
- ipclick-0.2.3/src/ipclick/adapters/curl_cffi_adapter.py +205 -0
- ipclick-0.2.3/src/ipclick/adapters/httpx_adapter.py +188 -0
- ipclick-0.2.3/src/ipclick/adapters/registry.py +49 -0
- {ipclick-0.1.2 → ipclick-0.2.3}/src/ipclick/cli/__init__.py +4 -4
- ipclick-0.2.3/src/ipclick/cli/main.py +85 -0
- {ipclick-0.1.2 → ipclick-0.2.3}/src/ipclick/config_loader/__init__.py +4 -4
- {ipclick-0.1.2 → ipclick-0.2.3}/src/ipclick/config_loader/loader.py +48 -48
- {ipclick-0.1.2 → ipclick-0.2.3}/src/ipclick/configs/default_config.toml +154 -131
- {ipclick-0.1.2 → ipclick-0.2.3}/src/ipclick/dto/__init__.py +4 -4
- ipclick-0.2.3/src/ipclick/dto/models.py +356 -0
- ipclick-0.2.3/src/ipclick/dto/proto/generate.py +52 -0
- {ipclick-0.1.2 → ipclick-0.2.3}/src/ipclick/dto/proto/task.proto +94 -91
- ipclick-0.2.3/src/ipclick/dto/proto/task_pb2.py +60 -0
- {ipclick-0.1.2 → ipclick-0.2.3}/src/ipclick/dto/proto/task_pb2.pyi +140 -140
- {ipclick-0.1.2 → ipclick-0.2.3}/src/ipclick/dto/proto/task_pb2_grpc.py +109 -108
- {ipclick-0.1.2 → ipclick-0.2.3}/src/ipclick/dto/response.py +181 -173
- ipclick-0.2.3/src/ipclick/exceptions.py +48 -0
- ipclick-0.2.3/src/ipclick/py.typed +0 -0
- ipclick-0.2.3/src/ipclick/sdk.py +307 -0
- {ipclick-0.1.2 → ipclick-0.2.3}/src/ipclick/server.py +175 -154
- {ipclick-0.1.2 → ipclick-0.2.3}/src/ipclick/services/__init__.py +4 -3
- ipclick-0.2.3/src/ipclick/services/task_service.py +277 -0
- {ipclick-0.1.2 → ipclick-0.2.3}/src/ipclick/utils/__init__.py +35 -36
- {ipclick-0.1.2 → ipclick-0.2.3}/src/ipclick/utils/config_util.py +80 -80
- {ipclick-0.1.2 → ipclick-0.2.3}/src/ipclick/utils/log_util.py +404 -347
- {ipclick-0.1.2 → ipclick-0.2.3}/src/ipclick/utils/path_util.py +37 -37
- {ipclick-0.1.2 → ipclick-0.2.3}/src/ipclick/utils/secure_util.py +50 -43
- ipclick-0.2.3/src/ipclick/utils/url_util.py +153 -0
- ipclick-0.1.2/PKG-INFO +0 -285
- ipclick-0.1.2/README.md +0 -256
- ipclick-0.1.2/pyproject.toml +0 -101
- ipclick-0.1.2/src/ipclick/__init__.py +0 -23
- ipclick-0.1.2/src/ipclick/adapters/base.py +0 -176
- ipclick-0.1.2/src/ipclick/adapters/curl_cffi_adapter.py +0 -159
- ipclick-0.1.2/src/ipclick/adapters/httpx_adapter.py +0 -165
- ipclick-0.1.2/src/ipclick/adapters/registry.py +0 -38
- ipclick-0.1.2/src/ipclick/cli/main.py +0 -65
- ipclick-0.1.2/src/ipclick/dto/models.py +0 -266
- ipclick-0.1.2/src/ipclick/dto/proto/task_pb2.py +0 -60
- ipclick-0.1.2/src/ipclick/sdk.py +0 -176
- ipclick-0.1.2/src/ipclick/services/task_service.py +0 -219
- {ipclick-0.1.2 → ipclick-0.2.3}/src/ipclick/adapters/__init__.py +0 -0
- {ipclick-0.1.2 → ipclick-0.2.3}/src/ipclick/configs/__init__.py +0 -0
- {ipclick-0.1.2 → ipclick-0.2.3}/src/ipclick/dto/proto/__init__.py +0 -0
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2025 元气码农少女酱
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 元气码农少女酱
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
ipclick-0.2.3/PKG-INFO
ADDED
|
@@ -0,0 +1,421 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: ipclick
|
|
3
|
+
Version: 0.2.3
|
|
4
|
+
Summary: IPClick Tool
|
|
5
|
+
License: MIT
|
|
6
|
+
License-File: LICENSE
|
|
7
|
+
Author: 元气码农少女酱
|
|
8
|
+
Author-email: 1276162429@qq.com
|
|
9
|
+
Requires-Python: >=3.11
|
|
10
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
16
|
+
Requires-Dist: click (>=8.4.2,<9.0.0)
|
|
17
|
+
Requires-Dist: curl-cffi (>=0.16.0,<0.17.0)
|
|
18
|
+
Requires-Dist: fake-useragent (>=2.2.0,<3.0.0)
|
|
19
|
+
Requires-Dist: grpcio (>=1.83.0,<2.0.0)
|
|
20
|
+
Requires-Dist: httpx (>=0.28.1,<0.29.0)
|
|
21
|
+
Requires-Dist: loguru (>=0.7.3,<0.8.0)
|
|
22
|
+
Requires-Dist: protobuf (>=6.33.2,<8.0.0)
|
|
23
|
+
Requires-Dist: python-box[all] (>=7.4.1,<8.0.0)
|
|
24
|
+
Requires-Dist: typing-extensions (>=4.15.0,<5.0.0)
|
|
25
|
+
Requires-Dist: uuid-utils (>=0.17.0,<0.18.0)
|
|
26
|
+
Project-URL: Bug Tracker, https://github.com/yuanqimanong/IPClick/issues
|
|
27
|
+
Project-URL: Homepage, https://github.com/yuanqimanong/IPClick
|
|
28
|
+
Project-URL: Repository, https://github.com/yuanqimanong/IPClick
|
|
29
|
+
Description-Content-Type: text/markdown
|
|
30
|
+
|
|
31
|
+
# IPClick
|
|
32
|
+
|
|
33
|
+

|
|
34
|
+
|
|
35
|
+
> IPClick 名字灵感来源于动画《Link Click》(时光代理人)。正如时光代理人穿梭于不同的时空执行任务,IPClick 帮助您将 HTTP 请求分发到不同的节点高效执行。
|
|
36
|
+
|
|
37
|
+
## 📖 简介
|
|
38
|
+
|
|
39
|
+
IPClick 是一个轻量级、高性能的分布式 HTTP 请求代理工具,基于 gRPC 协议构建。它提供了统一的请求接口,支持多种 HTTP
|
|
40
|
+
客户端适配器,帮助开发者更高效地处理网络请求。
|
|
41
|
+
|
|
42
|
+
## ✨ 特性
|
|
43
|
+
|
|
44
|
+
- **多适配器支持**:内置 `curl_cffi`、`httpx` 适配器,并可注册自定义适配器
|
|
45
|
+
- **浏览器指纹伪装**:基于 `curl_cffi` 实现浏览器指纹模拟,有效绕过反爬检测
|
|
46
|
+
- **gRPC 通信**:使用 gRPC 协议进行高效的客户端-服务端通信
|
|
47
|
+
- **连接复用**:客户端复用 gRPC channel,服务端复用适配器与 HTTP 连接池
|
|
48
|
+
- **代理支持**:灵活的代理配置,支持 HTTP/HTTPS 代理
|
|
49
|
+
- **自动重试**:内置请求重试机制,支持指数退避 + 抖动,并可按状态码重试
|
|
50
|
+
- **SSRF 防护**:服务端对目标 URL 做协议白名单与内网/元数据地址拦截
|
|
51
|
+
- **命令行工具**:提供便捷的 CLI 工具,支持快速启动服务和查看配置
|
|
52
|
+
- **Docker 支持**:多阶段构建、非 root 运行的镜像
|
|
53
|
+
- **完整类型标注**:随包提供 `py.typed`,下游可直接享受类型检查
|
|
54
|
+
|
|
55
|
+
## 📦 安装
|
|
56
|
+
|
|
57
|
+
### 从 PyPI 安装
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
pip install ipclick
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### 从源码安装
|
|
64
|
+
|
|
65
|
+
本项目使用 [uv](https://docs.astral.sh/uv/) 管理依赖:
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
git clone https://github.com/yuanqimanong/IPClick.git
|
|
69
|
+
cd IPClick
|
|
70
|
+
uv sync
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
也可以直接用 pip 以可编辑方式安装:
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
pip install -e .
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## 🔧 系统要求
|
|
80
|
+
|
|
81
|
+
- Python >= 3.11
|
|
82
|
+
- 主要依赖:
|
|
83
|
+
- curl-cffi >= 0.16.0
|
|
84
|
+
- grpcio >= 1.83.0
|
|
85
|
+
- protobuf >= 6.33.2
|
|
86
|
+
- click >= 8.4.2
|
|
87
|
+
- httpx >= 0.28.1
|
|
88
|
+
- fake-useragent >= 2.2.0
|
|
89
|
+
- loguru >= 0.7.3
|
|
90
|
+
- python-box >= 7.4.1
|
|
91
|
+
- uuid-utils >= 0.17.0
|
|
92
|
+
|
|
93
|
+
## 🚀 快速开始
|
|
94
|
+
|
|
95
|
+
### 启动服务端
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
# 使用默认配置启动
|
|
99
|
+
ipclick run
|
|
100
|
+
|
|
101
|
+
# 指定端口和地址
|
|
102
|
+
ipclick run --host 0.0.0.0 --port 9527
|
|
103
|
+
|
|
104
|
+
# 使用自定义配置文件(TOML 格式)
|
|
105
|
+
ipclick run --config /path/to/ipclick.toml
|
|
106
|
+
|
|
107
|
+
# 显示详细日志
|
|
108
|
+
ipclick run --verbose
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
查看当前生效的配置:
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
ipclick config-info
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### 客户端使用
|
|
118
|
+
|
|
119
|
+
```python
|
|
120
|
+
from ipclick import Downloader, HttpMethod
|
|
121
|
+
|
|
122
|
+
# 创建下载器实例(建议用 with,退出时自动关闭 gRPC 连接)
|
|
123
|
+
with Downloader() as downloader:
|
|
124
|
+
# 发送 GET 请求
|
|
125
|
+
response = downloader.get("https://httpbin.org/ip")
|
|
126
|
+
print(response.text)
|
|
127
|
+
print(response.json())
|
|
128
|
+
|
|
129
|
+
# 发送带参数的请求
|
|
130
|
+
response = downloader.request(
|
|
131
|
+
method=HttpMethod.GET,
|
|
132
|
+
url="https://httpbin.org/get",
|
|
133
|
+
headers={"User-Agent": "IPClick/1.0"},
|
|
134
|
+
params={"key": "value"},
|
|
135
|
+
timeout=30,
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
# 检查请求是否成功
|
|
139
|
+
if response.is_success():
|
|
140
|
+
print(f"请求成功,耗时: {response.elapsed_ms}ms")
|
|
141
|
+
else:
|
|
142
|
+
print(f"请求失败: {response.error}")
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
`request()` 及各便捷方法不会因为网络问题抛异常,失败时返回 `status_code == -1`
|
|
146
|
+
且 `error` 非空的 `DownloadResponse`。参数本身非法(如 URL 为空)会抛 `ValidationError`。
|
|
147
|
+
|
|
148
|
+
### 使用代理
|
|
149
|
+
|
|
150
|
+
```python
|
|
151
|
+
from ipclick import Downloader, ProxyConfig, HttpMethod
|
|
152
|
+
|
|
153
|
+
downloader = Downloader()
|
|
154
|
+
|
|
155
|
+
# 方式一:使用代理配置对象
|
|
156
|
+
proxy = ProxyConfig(
|
|
157
|
+
scheme="http",
|
|
158
|
+
host="proxy.example.com",
|
|
159
|
+
port=8080,
|
|
160
|
+
auth_key="username",
|
|
161
|
+
auth_password="password",
|
|
162
|
+
)
|
|
163
|
+
|
|
164
|
+
response = downloader.request(
|
|
165
|
+
method=HttpMethod.GET,
|
|
166
|
+
url="https://httpbin.org/ip",
|
|
167
|
+
proxy=proxy,
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
# 方式二:使用代理 URL 字符串
|
|
171
|
+
response = downloader.request(
|
|
172
|
+
method=HttpMethod.GET,
|
|
173
|
+
url="https://httpbin.org/ip",
|
|
174
|
+
proxy="http://user:pass@proxy.example.com:8080",
|
|
175
|
+
)
|
|
176
|
+
|
|
177
|
+
# 方式三:使用配置文件 [PROXY] 中的代理(设置 proxy=True)
|
|
178
|
+
response = downloader.request(
|
|
179
|
+
method=HttpMethod.GET,
|
|
180
|
+
url="https://httpbin.org/ip",
|
|
181
|
+
proxy=True,
|
|
182
|
+
)
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
> 适配器默认**不读取**环境变量里的 `HTTP_PROXY` / `ALL_PROXY`。代理必须由调用方显式指定,
|
|
186
|
+
> 以免请求意外走到服务端所在机器的环境代理出口。
|
|
187
|
+
|
|
188
|
+
### 使用全局下载器
|
|
189
|
+
|
|
190
|
+
```python
|
|
191
|
+
from ipclick import downloader
|
|
192
|
+
|
|
193
|
+
# 使用默认的全局下载器实例(首次使用时才真正建立连接)
|
|
194
|
+
response = downloader.get("https://httpbin.org/ip")
|
|
195
|
+
print(response.text)
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
### 指定适配器
|
|
199
|
+
|
|
200
|
+
```python
|
|
201
|
+
from ipclick import IPClickAdapter, downloader
|
|
202
|
+
|
|
203
|
+
response = downloader.get("https://httpbin.org/get", adapter=IPClickAdapter.HTTPX)
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
## 📂 项目结构
|
|
207
|
+
|
|
208
|
+
```
|
|
209
|
+
IPClick/
|
|
210
|
+
├── src/
|
|
211
|
+
│ └── ipclick/
|
|
212
|
+
│ ├── __init__.py # 包入口,导出公共 API
|
|
213
|
+
│ ├── __main__.py # 模块入口
|
|
214
|
+
│ ├── sdk.py # SDK 客户端实现
|
|
215
|
+
│ ├── server.py # gRPC 服务端实现
|
|
216
|
+
│ ├── exceptions.py # 异常层次
|
|
217
|
+
│ ├── py.typed # 类型标注标记
|
|
218
|
+
│ ├── adapters/ # HTTP 客户端适配器
|
|
219
|
+
│ │ ├── base.py # 适配器基类与重试装饰器
|
|
220
|
+
│ │ ├── curl_cffi_adapter.py
|
|
221
|
+
│ │ ├── httpx_adapter.py
|
|
222
|
+
│ │ └── registry.py # 适配器注册表
|
|
223
|
+
│ ├── cli/ # 命令行工具
|
|
224
|
+
│ ├── config_loader/ # 配置加载器
|
|
225
|
+
│ ├── configs/ # 默认配置文件
|
|
226
|
+
│ ├── dto/ # 数据传输对象
|
|
227
|
+
│ │ ├── models.py # 数据模型定义
|
|
228
|
+
│ │ ├── response.py # 统一响应对象
|
|
229
|
+
│ │ └── proto/ # Protobuf 定义与生成脚本
|
|
230
|
+
│ ├── services/ # gRPC 服务实现
|
|
231
|
+
│ └── utils/ # 工具模块(日志、配置、URL 安全校验等)
|
|
232
|
+
├── docker/ # Docker 相关文件
|
|
233
|
+
├── examples/ # 示例代码
|
|
234
|
+
├── tests/ # 测试代码
|
|
235
|
+
├── pyproject.toml # 项目配置
|
|
236
|
+
└── README.md
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
## ⚙️ 配置说明
|
|
240
|
+
|
|
241
|
+
配置文件为 TOML 格式,加载顺序(后者覆盖前者):
|
|
242
|
+
|
|
243
|
+
1. 包内默认配置 `src/ipclick/configs/default_config.toml`
|
|
244
|
+
2. 用户目录配置 `~/.ipclick/config.toml`
|
|
245
|
+
3. `--config` 指定的文件,或当前目录下的 `ipclick.toml` / `.ipclick.toml`
|
|
246
|
+
4. 环境变量 `IPCLICK_HOST`、`IPCLICK_PORT`(优先级最高)
|
|
247
|
+
|
|
248
|
+
### 服务端配置
|
|
249
|
+
|
|
250
|
+
| 配置项 | 说明 | 默认值 |
|
|
251
|
+
|----------------------|-------------------|---------|
|
|
252
|
+
| `SERVER.host` | 服务绑定地址 | `[::]` |
|
|
253
|
+
| `SERVER.port` | 服务端口 | `9527` |
|
|
254
|
+
| `SERVER.max_workers` | 最大工作线程数(每请求占用一个) | `100` |
|
|
255
|
+
|
|
256
|
+
### 安全配置
|
|
257
|
+
|
|
258
|
+
服务端会代替调用方请求任意 URL。**若监听在公网或不可信网络,请开启内网拦截**,
|
|
259
|
+
否则本服务可被当作内网跳板(SSRF)。
|
|
260
|
+
|
|
261
|
+
| 配置项 | 说明 | 默认值 |
|
|
262
|
+
|-------------------------------------|--------------------------|----------------------|
|
|
263
|
+
| `SECURITY.allowed_schemes` | 允许的 URL 协议 | `["http", "https"]` |
|
|
264
|
+
| `SECURITY.block_metadata_endpoints` | 拦截云元数据地址(169.254.169.254 等) | `true` |
|
|
265
|
+
| `SECURITY.block_private_networks` | 拦截回环 / 私网 / 保留地址 | `false` |
|
|
266
|
+
| `SECURITY.allowlist` | 即便开启拦截也放行的主机名或 IP | `[]` |
|
|
267
|
+
|
|
268
|
+
### 代理配置
|
|
269
|
+
|
|
270
|
+
| 配置项 | 说明 |
|
|
271
|
+
|------------------------|-------------|
|
|
272
|
+
| `PROXY.scheme` | 协议,默认 `http` |
|
|
273
|
+
| `PROXY.host` / `.port` | 代理地址与端口 |
|
|
274
|
+
| `PROXY.auth_key` | 认证用户名 |
|
|
275
|
+
| `PROXY.auth_password` | 认证密码 |
|
|
276
|
+
| `PROXY.tunnel_server` | 隧道服务器(覆盖 host:port) |
|
|
277
|
+
|
|
278
|
+
## 🐳 Docker 部署
|
|
279
|
+
|
|
280
|
+
构建上下文是**仓库根目录**,不是 `docker/` 目录:
|
|
281
|
+
|
|
282
|
+
```bash
|
|
283
|
+
docker build -f docker/Dockerfile -t ipclick:latest .
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
运行:
|
|
287
|
+
|
|
288
|
+
```bash
|
|
289
|
+
docker run -d -p 9527:9527 --name ipclick ipclick:latest
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
挂载自定义配置:
|
|
293
|
+
|
|
294
|
+
```bash
|
|
295
|
+
docker run -d -p 9527:9527 --name ipclick -v /你的路径/:/home/ipclick/.ipclick/ ipclick:latest
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
## 📚 API 参考
|
|
299
|
+
|
|
300
|
+
### DownloadTask / request() 参数
|
|
301
|
+
|
|
302
|
+
| 参数 | 类型 | 默认值 | 说明 |
|
|
303
|
+
|---------------------|------------------------|--------------|-------------|
|
|
304
|
+
| `url` | `str` | — | 请求 URL(必填) |
|
|
305
|
+
| `method` | `HttpMethod` | `GET` | HTTP 方法 |
|
|
306
|
+
| `adapter` | `IPClickAdapter / str` | `CURL_CFFI` | 使用的适配器 |
|
|
307
|
+
| `headers` | `dict` | `None` | 请求头 |
|
|
308
|
+
| `cookies` | `dict / str` | `None` | Cookies |
|
|
309
|
+
| `params` | `dict` | `None` | URL 查询参数 |
|
|
310
|
+
| `data` | `Any` | `None` | 表单数据 |
|
|
311
|
+
| `json` | `dict` | `None` | JSON 数据 |
|
|
312
|
+
| `proxy` | `ProxyConfig/str/bool` | `None` | 代理配置 |
|
|
313
|
+
| `timeout` | `float` | `60` | 超时时间(秒) |
|
|
314
|
+
| `max_retries` | `int` | `3` | 最大重试次数 |
|
|
315
|
+
| `retry_backoff` | `float` | `2.0` | 重试退避基数(秒) |
|
|
316
|
+
| `verify` | `bool` | `True` | 是否验证 SSL 证书 |
|
|
317
|
+
| `allow_redirects` | `bool` | `True` | 是否跟随重定向 |
|
|
318
|
+
| `impersonate` | `str` | `chrome` | 浏览器指纹伪装 |
|
|
319
|
+
| `allowed_status_codes` | `list[int]` | `[200, 404]` | 不触发重试的状态码 |
|
|
320
|
+
|
|
321
|
+
### DownloadResponse 属性
|
|
322
|
+
|
|
323
|
+
| 属性 | 类型 | 说明 |
|
|
324
|
+
|----------------|---------|----------------|
|
|
325
|
+
| `status_code` | `int` | HTTP 状态码,`-1` 表示本地/传输失败 |
|
|
326
|
+
| `headers` | `dict` | 响应头 |
|
|
327
|
+
| `content` | `bytes` | 响应内容(二进制) |
|
|
328
|
+
| `text` | `str` | 响应内容(文本) |
|
|
329
|
+
| `url` | `str` | 最终 URL |
|
|
330
|
+
| `elapsed_ms` | `int` | 请求耗时(毫秒) |
|
|
331
|
+
| `adapter_type` | `str` | 实际使用的适配器名称 |
|
|
332
|
+
| `error` | `str` | 错误信息 |
|
|
333
|
+
|
|
334
|
+
### DownloadResponse 方法
|
|
335
|
+
|
|
336
|
+
- `json()` - 解析 JSON 响应
|
|
337
|
+
- `is_success()` / `ok` - 判断请求是否成功
|
|
338
|
+
- `raise_for_status()` - 状态码异常时抛出 `RequestError`
|
|
339
|
+
|
|
340
|
+
### 异常
|
|
341
|
+
|
|
342
|
+
所有异常都继承自 `IPClickError`:
|
|
343
|
+
|
|
344
|
+
| 异常 | 场景 |
|
|
345
|
+
|---------------------|-----------------------|
|
|
346
|
+
| `ConfigError` | 配置缺失或非法 |
|
|
347
|
+
| `AdapterError` | 适配器不存在或依赖未安装 |
|
|
348
|
+
| `TransportError` | 与服务端的 gRPC 通信失败 |
|
|
349
|
+
| `RequestError` | 目标站点返回错误 |
|
|
350
|
+
| `ValidationError` | 任务参数校验失败(同时继承 `ValueError`) |
|
|
351
|
+
| `URLNotAllowedError`| 目标 URL 被安全策略拒绝 |
|
|
352
|
+
|
|
353
|
+
## 🚧 尚未实现 / 已知限制
|
|
354
|
+
|
|
355
|
+
为免误导,这里如实列出**当前还没有实现**的部分。配置文件里出现但落在本节的项,
|
|
356
|
+
改了不会有任何效果。
|
|
357
|
+
|
|
358
|
+
### 配置节:仅 4 个真正生效
|
|
359
|
+
|
|
360
|
+
| 配置节 | 状态 |
|
|
361
|
+
|---|---|
|
|
362
|
+
| `[SERVER]` `[PROXY]` `[LOG]` `[SECURITY]` | ✅ 生效 |
|
|
363
|
+
| `[GENERAL]` | ❌ `mode` / `debug` 无消费方 |
|
|
364
|
+
| `[CLUSTER]` | ❌ `load_balancer` / `nodes` / `db_uri` 无消费方 |
|
|
365
|
+
| `[DOWNLOADER]` | ❌ `connect_timeout` / `download_timeout` / `retry.*` / `concurrency.*` / `rate_limit.*` / `chunk.*` / `storage.*` 全部无消费方 |
|
|
366
|
+
| `[BROWSER]` | ❌ 无消费方 |
|
|
367
|
+
| `[MONITOR]` | ❌ `health_check` 无消费方 |
|
|
368
|
+
|
|
369
|
+
> 超时与重试目前只能**按请求**传参(`timeout` / `max_retries` / `retry_backoff`),
|
|
370
|
+
> 配置文件里的对应项不生效。
|
|
371
|
+
|
|
372
|
+
### 功能
|
|
373
|
+
|
|
374
|
+
- **分布式 / 集群**:尽管项目定位是"分布式",目前客户端只连**单个** `host:port`。
|
|
375
|
+
没有节点池、负载均衡、故障转移或服务发现。`[CLUSTER]` 是预留配置。
|
|
376
|
+
- **服务端鉴权**:gRPC 使用 `insecure_channel`,**没有任何鉴权**。任何能连到端口的
|
|
377
|
+
调用方都能使用本服务。部署到非可信网络时,请自行用防火墙 / 网络策略限制来源,
|
|
378
|
+
并开启 `[SECURITY].block_private_networks`。
|
|
379
|
+
- **适配器**:`IPClickAdapter` 枚举列出 6 种,实际只实现 **`curl_cffi`** 和 **`httpx`**。
|
|
380
|
+
请求 `requests` / `DrissionPage` / `undetected_chromedriver` / `playwright` 会抛
|
|
381
|
+
`AdapterError`。
|
|
382
|
+
- **浏览器渲染**:`automation_config` / `automation_script` 字段贯穿了整条调用链,
|
|
383
|
+
但末端没有任何消费方,属于预留接口。
|
|
384
|
+
- **流式下载**:`stream` 参数目前不改变行为。响应体经由单个 protobuf 消息整体传输
|
|
385
|
+
(上限 500MB),大文件会完整驻留内存,不适合下载超大文件。
|
|
386
|
+
- **文件上传**:`files` 参数会抛 `NotImplementedError`。
|
|
387
|
+
- **批量请求**:一次 RPC 只处理一个 URL,没有批量 / 流式接口。
|
|
388
|
+
- **异步客户端**:只有同步 `Downloader`,没有 `grpc.aio` 版本。
|
|
389
|
+
- **Cookie 持久化**:请求之间不共享 cookie jar,每次请求相互独立。
|
|
390
|
+
- **客户端重试**:重试只发生在服务端适配器内部;客户端到服务端这一跳失败不会重试。
|
|
391
|
+
- **可观测性**:没有 metrics,也没有实现 gRPC 标准健康检查协议。Docker 的
|
|
392
|
+
healthcheck 只是探测端口 TCP 可连。
|
|
393
|
+
|
|
394
|
+
## 🛠️ 开发
|
|
395
|
+
|
|
396
|
+
```bash
|
|
397
|
+
uv sync --all-groups # 安装含开发依赖
|
|
398
|
+
uv run pytest # 运行测试
|
|
399
|
+
uv run pytest --cov=ipclick # 带覆盖率
|
|
400
|
+
uv run ruff check src/ tests/ # 代码检查
|
|
401
|
+
uv run ruff format src/ tests/# 代码格式化
|
|
402
|
+
uv run basedpyright src/ # 类型检查
|
|
403
|
+
```
|
|
404
|
+
|
|
405
|
+
修改 `src/ipclick/dto/proto/task.proto` 后需重新生成代码:
|
|
406
|
+
|
|
407
|
+
```bash
|
|
408
|
+
uv run python src/ipclick/dto/proto/generate.py
|
|
409
|
+
```
|
|
410
|
+
|
|
411
|
+
## 🤝 贡献
|
|
412
|
+
|
|
413
|
+
欢迎贡献代码、报告问题或提出建议!请通过 [GitHub Issues](https://github.com/yuanqimanong/IPClick/issues)
|
|
414
|
+
或 [Pull Requests](https://github.com/yuanqimanong/IPClick/pulls) 参与项目开发。
|
|
415
|
+
|
|
416
|
+
## 📄 许可证
|
|
417
|
+
|
|
418
|
+
本项目采用 [MIT License](https://github.com/yuanqimanong/IPClick/blob/master/LICENSE) 开源许可证。
|
|
419
|
+
|
|
420
|
+
Copyright (c) 2025 元气码农少女酱
|
|
421
|
+
|