crawlerkeeper-sdk 0.2.1__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.
@@ -0,0 +1,80 @@
1
+ # Build artifacts
2
+ target/
3
+ *.class
4
+ *.jar
5
+ *.war
6
+
7
+ # Maven
8
+ .mvn/
9
+ mvnw
10
+ mvnw.cmd
11
+
12
+ # Node
13
+ node_modules/
14
+ dist/
15
+ pnpm-lock.yaml
16
+ yarn.lock
17
+ package-lock.json
18
+ npm-debug.log*
19
+
20
+ # IDE
21
+ .idea/
22
+ .vscode/
23
+ *.iml
24
+ *.ipr
25
+ *.iws
26
+ .project
27
+ .classpath
28
+ .settings/
29
+ *.swp
30
+ *.swo
31
+
32
+ # OS
33
+ .DS_Store
34
+ Thumbs.db
35
+ *.tmp
36
+
37
+ # Logs
38
+ *.log
39
+ logs/
40
+ !openspec/specs/logs/
41
+
42
+ # Config (sensitive)
43
+ *.env
44
+ *.env.local
45
+ application-local.yml
46
+ application-*.properties
47
+
48
+ # Local config (generated by CLI)
49
+ CLAUDE.local.md
50
+
51
+ # Worktrees
52
+ .worktrees/
53
+ worktrees/
54
+
55
+ # Temp
56
+ tmp/
57
+ temp/
58
+ crawlerkeeper-agent/config/config.yaml
59
+
60
+ # Agent data
61
+ crawlerkeeper-agent/data/
62
+
63
+ .playwright-mcp
64
+ */.playwright-cli/
65
+
66
+ __pycache__/
67
+ settings.local.json.worktrees/
68
+
69
+ findings.md
70
+ progress.md
71
+ task_plan.md
72
+ */superpowers/
73
+ .claude/settings.local.json
74
+ uv.lock
75
+
76
+ out/
77
+ crawlerkeeper-agent/.last_daemon_args
78
+ crawlerkeeper-agent/agent.pid
79
+ .gstack/
80
+ .omo/
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 CrawlerKeeper Team
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.
@@ -0,0 +1,264 @@
1
+ Metadata-Version: 2.5
2
+ Name: crawlerkeeper-sdk
3
+ Version: 0.2.1
4
+ Summary: CrawlerKeeper 爬虫 SDK:日志上报、结果写入、任务参数解析、TLS 指纹请求、代理管理与并发线程池
5
+ Author: CrawlerKeeper Team
6
+ License-Expression: MIT
7
+ License-File: LICENSE
8
+ Keywords: crawler,crawlerkeeper,scraping,spider,tls-fingerprint
9
+ Classifier: Development Status :: 4 - Beta
10
+ Classifier: Intended Audience :: Developers
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Programming Language :: Python :: 3.10
13
+ Classifier: Programming Language :: Python :: 3.11
14
+ Classifier: Programming Language :: Python :: 3.12
15
+ Classifier: Programming Language :: Python :: 3.13
16
+ Classifier: Topic :: Internet :: WWW/HTTP
17
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
18
+ Requires-Python: >=3.10
19
+ Requires-Dist: curl-cffi>=0.7.0
20
+ Description-Content-Type: text/markdown
21
+
22
+ # CrawlerKeeper SDK for Python
23
+
24
+ CrawlerKeeper Python SDK 提供了爬虫与 Agent 通信的封装,支持日志记录、结果写入、参数获取、代理管理和并发爬取。
25
+
26
+ ## 安装
27
+
28
+ ```bash
29
+ pip install crawlerkeeper-sdk
30
+ ```
31
+
32
+ ## 快速开始
33
+
34
+ ```python
35
+ from crawlerkeeper_sdk import params, info, result, StealthSession
36
+
37
+ # 获取任务参数
38
+ config = params()
39
+ info(f"收到参数: {config}")
40
+
41
+ # 使用 StealthSession 发送请求
42
+ session = StealthSession()
43
+ resp = session.get("https://example.com/api/data")
44
+ data = resp.json()
45
+
46
+ # 写入结果
47
+ result({"data": data, "source": "example.com"})
48
+
49
+ info("爬取完成")
50
+ session.close()
51
+ ```
52
+
53
+ ## 核心功能
54
+
55
+ ### 日志记录
56
+
57
+ ```python
58
+ from crawlerkeeper_sdk import info, warning, error
59
+
60
+ info("信息日志")
61
+ warning("警告日志")
62
+ error("错误日志")
63
+ ```
64
+
65
+ ### 结果写入
66
+
67
+ ```python
68
+ from crawlerkeeper_sdk import result
69
+
70
+ result({"count": 100, "items": [...]})
71
+ ```
72
+
73
+ ### 任务参数
74
+
75
+ ```python
76
+ from crawlerkeeper_sdk import params
77
+
78
+ # python spider.py --params '{"keyword": "python", "page": 1}'
79
+ config = params() # {"keyword": "python", "page": 1}
80
+ ```
81
+
82
+ ## StealthSession
83
+
84
+ `StealthSession` 提供隐式浏览器指纹的 HTTP 请求能力,自动管理 TLS 指纹和代理。
85
+
86
+ ```python
87
+ from crawlerkeeper_sdk import StealthSession, result
88
+
89
+ session = StealthSession()
90
+
91
+ # GET 请求
92
+ resp = session.get("https://example.com/api/data")
93
+ print(resp.status_code)
94
+ print(resp.json())
95
+
96
+ # POST 请求
97
+ resp = session.post("https://example.com/api/submit", json={"key": "value"})
98
+
99
+ session.close()
100
+ ```
101
+
102
+ ### 主要方法
103
+
104
+ | 方法 | 说明 |
105
+ |------|------|
106
+ | `get(url, **kwargs)` | 发送 GET 请求 |
107
+ | `post(url, **kwargs)` | 发送 POST 请求 |
108
+ | `close()` | 关闭会话 |
109
+
110
+ ## ThreadPool 并发爬取
111
+
112
+ `ThreadPool` 提供并发爬取能力,内部管理 `StealthSession` 的创建和关闭。
113
+
114
+ ### 推荐用法:使用上下文管理器
115
+
116
+ ```python
117
+ from crawlerkeeper_sdk import ThreadPool, StealthSession, result
118
+
119
+ def crawl(session: StealthSession, keyword: str, page: int):
120
+ """爬虫函数:session 会自动传入"""
121
+ resp = session.get(f"https://example.com/search?kw={keyword}&page={page}")
122
+ return resp.json()
123
+
124
+ # 使用 with 语句自动管理生命周期,不会遗漏 close()
125
+ with ThreadPool(max_workers=5) as pool:
126
+ pool.submit(crawl, keyword="python", page=1)
127
+ pool.submit(crawl, keyword="java", page=1)
128
+ pool.submit(crawl, keyword="go", page=1)
129
+
130
+ # 阻塞等待所有任务完成
131
+ results = pool.wait()
132
+
133
+ # 处理结果
134
+ for data in results:
135
+ if data is not None:
136
+ result(data)
137
+ # with 块结束时自动调用 wait() 和 close()
138
+ ```
139
+
140
+ ### 传统用法
141
+
142
+ ```python
143
+ pool = ThreadPool(max_workers=5)
144
+ pool.submit(crawl, keyword="python", page=1)
145
+ pool.submit(crawl, keyword="java", page=1)
146
+
147
+ results = pool.wait()
148
+ pool.close() # 需要手动调用
149
+ ```
150
+
151
+ ### 环境变量配置并发数
152
+
153
+ ```bash
154
+ export CONCURRENT_NUM=10
155
+ ```
156
+
157
+ ```python
158
+ # 不指定 max_workers 时使用 CONCURRENT_NUM 环境变量
159
+ with ThreadPool() as pool:
160
+ pool.submit(crawl, keyword="python", page=1)
161
+ ```
162
+
163
+ ### ThreadPool 方法
164
+
165
+ | 方法 | 说明 |
166
+ |------|------|
167
+ | `submit(crawl_fn, **kwargs)` | 提交任务,worker 全满时阻塞等待 |
168
+ | `wait()` | 阻塞等待所有任务完成,返回结果列表 |
169
+ | `close()` | 等待所有任务完成后关闭线程池 |
170
+
171
+ ### 异常处理
172
+
173
+ 爬虫函数抛出的异常会被捕获并通过 `output.error()` 记录,任务结果记为 `None`,不中断其他任务。
174
+
175
+ ```python
176
+ def failing_crawl(session):
177
+ raise ValueError("some error")
178
+
179
+ pool = ThreadPool(max_workers=2)
180
+ pool.submit(failing_crawl)
181
+ results = pool.wait() # results[0] is None
182
+ pool.close()
183
+ ```
184
+
185
+ ## 代理配置
186
+
187
+ `StealthSession` 的代理行为由环境变量控制(通常由 Agent 下发),不支持按请求传入代理参数:
188
+
189
+ | 环境变量 | 说明 |
190
+ |---------|------|
191
+ | `PROXY_ENABLED` | `on` 开启代理,其他值直连 |
192
+ | `PROXY_MODE` | `tunnel` 静态代理 / `private` 管理端动态分配 |
193
+ | `PROXY_URL` | tunnel 模式的静态代理 URL |
194
+ | `PROXY_VENDOR` | private 模式的供应商标识 |
195
+ | `PROXY_TARGET_SITE` | private 模式的目标站点标识 |
196
+ | `ADMIN_SERVER_URL` | private 模式下管理端地址 |
197
+ | `AGENT_API_KEY` | private 模式下调用管理端的第三方 Token(`td_` 前缀;代理分配/上报接口仅对第三方 Token 开放,节点 `ck_` Key 无权调用) |
198
+
199
+ ```bash
200
+ # tunnel 模式示例
201
+ export PROXY_ENABLED=on
202
+ export PROXY_MODE=tunnel
203
+ export PROXY_URL=http://proxy:8080
204
+ ```
205
+
206
+ private 模式下代理用尽或持续被目标站封禁时,`get`/`post` 抛出 `ProxyExhaustedError`。
207
+
208
+ ## 运行模式
209
+
210
+ SDK 自动检测运行模式:
211
+
212
+ - **Agent 模式**: 当 `LOG_FILE_PATH` 和 `DATA_FILE_PATH` 环境变量存在时,日志和结果写入文件
213
+ - **开发模式**: 环境变量不存在时,输出到控制台
214
+
215
+ ```bash
216
+ # 开发模式
217
+ python spider.py
218
+
219
+ # Agent 模式
220
+ export LOG_FILE_PATH=/tmp/test.log
221
+ export DATA_FILE_PATH=/tmp/test.json
222
+ python spider.py
223
+ ```
224
+
225
+ ## 完整示例
226
+
227
+ ```python
228
+ #!/usr/bin/env python3
229
+ from crawlerkeeper_sdk import params, info, result, StealthSession, ThreadPool
230
+
231
+ def crawl(session: StealthSession, keyword: str, page: int):
232
+ """爬虫函数"""
233
+ info(f"爬取 {keyword} 第 {page} 页")
234
+ resp = session.get(f"https://example.com/api?kw={keyword}&page={page}")
235
+ return resp.json()
236
+
237
+ def main():
238
+ config = params()
239
+ keywords = config.get("keywords", ["python"])
240
+ max_workers = config.get("max_workers", 3)
241
+
242
+ with ThreadPool(max_workers=max_workers) as pool:
243
+ # 提交所有任务
244
+ for keyword in keywords:
245
+ for page in range(1, 4):
246
+ pool.submit(crawl, keyword=keyword, page=page)
247
+
248
+ # 阻塞等待所有任务完成
249
+ results = pool.wait()
250
+
251
+ # 输出结果
252
+ for data in results:
253
+ if data is not None:
254
+ result(data)
255
+
256
+ info("爬取完成")
257
+
258
+ if __name__ == "__main__":
259
+ main()
260
+ ```
261
+
262
+ ## 许可证
263
+
264
+ MIT License
@@ -0,0 +1,243 @@
1
+ # CrawlerKeeper SDK for Python
2
+
3
+ CrawlerKeeper Python SDK 提供了爬虫与 Agent 通信的封装,支持日志记录、结果写入、参数获取、代理管理和并发爬取。
4
+
5
+ ## 安装
6
+
7
+ ```bash
8
+ pip install crawlerkeeper-sdk
9
+ ```
10
+
11
+ ## 快速开始
12
+
13
+ ```python
14
+ from crawlerkeeper_sdk import params, info, result, StealthSession
15
+
16
+ # 获取任务参数
17
+ config = params()
18
+ info(f"收到参数: {config}")
19
+
20
+ # 使用 StealthSession 发送请求
21
+ session = StealthSession()
22
+ resp = session.get("https://example.com/api/data")
23
+ data = resp.json()
24
+
25
+ # 写入结果
26
+ result({"data": data, "source": "example.com"})
27
+
28
+ info("爬取完成")
29
+ session.close()
30
+ ```
31
+
32
+ ## 核心功能
33
+
34
+ ### 日志记录
35
+
36
+ ```python
37
+ from crawlerkeeper_sdk import info, warning, error
38
+
39
+ info("信息日志")
40
+ warning("警告日志")
41
+ error("错误日志")
42
+ ```
43
+
44
+ ### 结果写入
45
+
46
+ ```python
47
+ from crawlerkeeper_sdk import result
48
+
49
+ result({"count": 100, "items": [...]})
50
+ ```
51
+
52
+ ### 任务参数
53
+
54
+ ```python
55
+ from crawlerkeeper_sdk import params
56
+
57
+ # python spider.py --params '{"keyword": "python", "page": 1}'
58
+ config = params() # {"keyword": "python", "page": 1}
59
+ ```
60
+
61
+ ## StealthSession
62
+
63
+ `StealthSession` 提供隐式浏览器指纹的 HTTP 请求能力,自动管理 TLS 指纹和代理。
64
+
65
+ ```python
66
+ from crawlerkeeper_sdk import StealthSession, result
67
+
68
+ session = StealthSession()
69
+
70
+ # GET 请求
71
+ resp = session.get("https://example.com/api/data")
72
+ print(resp.status_code)
73
+ print(resp.json())
74
+
75
+ # POST 请求
76
+ resp = session.post("https://example.com/api/submit", json={"key": "value"})
77
+
78
+ session.close()
79
+ ```
80
+
81
+ ### 主要方法
82
+
83
+ | 方法 | 说明 |
84
+ |------|------|
85
+ | `get(url, **kwargs)` | 发送 GET 请求 |
86
+ | `post(url, **kwargs)` | 发送 POST 请求 |
87
+ | `close()` | 关闭会话 |
88
+
89
+ ## ThreadPool 并发爬取
90
+
91
+ `ThreadPool` 提供并发爬取能力,内部管理 `StealthSession` 的创建和关闭。
92
+
93
+ ### 推荐用法:使用上下文管理器
94
+
95
+ ```python
96
+ from crawlerkeeper_sdk import ThreadPool, StealthSession, result
97
+
98
+ def crawl(session: StealthSession, keyword: str, page: int):
99
+ """爬虫函数:session 会自动传入"""
100
+ resp = session.get(f"https://example.com/search?kw={keyword}&page={page}")
101
+ return resp.json()
102
+
103
+ # 使用 with 语句自动管理生命周期,不会遗漏 close()
104
+ with ThreadPool(max_workers=5) as pool:
105
+ pool.submit(crawl, keyword="python", page=1)
106
+ pool.submit(crawl, keyword="java", page=1)
107
+ pool.submit(crawl, keyword="go", page=1)
108
+
109
+ # 阻塞等待所有任务完成
110
+ results = pool.wait()
111
+
112
+ # 处理结果
113
+ for data in results:
114
+ if data is not None:
115
+ result(data)
116
+ # with 块结束时自动调用 wait() 和 close()
117
+ ```
118
+
119
+ ### 传统用法
120
+
121
+ ```python
122
+ pool = ThreadPool(max_workers=5)
123
+ pool.submit(crawl, keyword="python", page=1)
124
+ pool.submit(crawl, keyword="java", page=1)
125
+
126
+ results = pool.wait()
127
+ pool.close() # 需要手动调用
128
+ ```
129
+
130
+ ### 环境变量配置并发数
131
+
132
+ ```bash
133
+ export CONCURRENT_NUM=10
134
+ ```
135
+
136
+ ```python
137
+ # 不指定 max_workers 时使用 CONCURRENT_NUM 环境变量
138
+ with ThreadPool() as pool:
139
+ pool.submit(crawl, keyword="python", page=1)
140
+ ```
141
+
142
+ ### ThreadPool 方法
143
+
144
+ | 方法 | 说明 |
145
+ |------|------|
146
+ | `submit(crawl_fn, **kwargs)` | 提交任务,worker 全满时阻塞等待 |
147
+ | `wait()` | 阻塞等待所有任务完成,返回结果列表 |
148
+ | `close()` | 等待所有任务完成后关闭线程池 |
149
+
150
+ ### 异常处理
151
+
152
+ 爬虫函数抛出的异常会被捕获并通过 `output.error()` 记录,任务结果记为 `None`,不中断其他任务。
153
+
154
+ ```python
155
+ def failing_crawl(session):
156
+ raise ValueError("some error")
157
+
158
+ pool = ThreadPool(max_workers=2)
159
+ pool.submit(failing_crawl)
160
+ results = pool.wait() # results[0] is None
161
+ pool.close()
162
+ ```
163
+
164
+ ## 代理配置
165
+
166
+ `StealthSession` 的代理行为由环境变量控制(通常由 Agent 下发),不支持按请求传入代理参数:
167
+
168
+ | 环境变量 | 说明 |
169
+ |---------|------|
170
+ | `PROXY_ENABLED` | `on` 开启代理,其他值直连 |
171
+ | `PROXY_MODE` | `tunnel` 静态代理 / `private` 管理端动态分配 |
172
+ | `PROXY_URL` | tunnel 模式的静态代理 URL |
173
+ | `PROXY_VENDOR` | private 模式的供应商标识 |
174
+ | `PROXY_TARGET_SITE` | private 模式的目标站点标识 |
175
+ | `ADMIN_SERVER_URL` | private 模式下管理端地址 |
176
+ | `AGENT_API_KEY` | private 模式下调用管理端的第三方 Token(`td_` 前缀;代理分配/上报接口仅对第三方 Token 开放,节点 `ck_` Key 无权调用) |
177
+
178
+ ```bash
179
+ # tunnel 模式示例
180
+ export PROXY_ENABLED=on
181
+ export PROXY_MODE=tunnel
182
+ export PROXY_URL=http://proxy:8080
183
+ ```
184
+
185
+ private 模式下代理用尽或持续被目标站封禁时,`get`/`post` 抛出 `ProxyExhaustedError`。
186
+
187
+ ## 运行模式
188
+
189
+ SDK 自动检测运行模式:
190
+
191
+ - **Agent 模式**: 当 `LOG_FILE_PATH` 和 `DATA_FILE_PATH` 环境变量存在时,日志和结果写入文件
192
+ - **开发模式**: 环境变量不存在时,输出到控制台
193
+
194
+ ```bash
195
+ # 开发模式
196
+ python spider.py
197
+
198
+ # Agent 模式
199
+ export LOG_FILE_PATH=/tmp/test.log
200
+ export DATA_FILE_PATH=/tmp/test.json
201
+ python spider.py
202
+ ```
203
+
204
+ ## 完整示例
205
+
206
+ ```python
207
+ #!/usr/bin/env python3
208
+ from crawlerkeeper_sdk import params, info, result, StealthSession, ThreadPool
209
+
210
+ def crawl(session: StealthSession, keyword: str, page: int):
211
+ """爬虫函数"""
212
+ info(f"爬取 {keyword} 第 {page} 页")
213
+ resp = session.get(f"https://example.com/api?kw={keyword}&page={page}")
214
+ return resp.json()
215
+
216
+ def main():
217
+ config = params()
218
+ keywords = config.get("keywords", ["python"])
219
+ max_workers = config.get("max_workers", 3)
220
+
221
+ with ThreadPool(max_workers=max_workers) as pool:
222
+ # 提交所有任务
223
+ for keyword in keywords:
224
+ for page in range(1, 4):
225
+ pool.submit(crawl, keyword=keyword, page=page)
226
+
227
+ # 阻塞等待所有任务完成
228
+ results = pool.wait()
229
+
230
+ # 输出结果
231
+ for data in results:
232
+ if data is not None:
233
+ result(data)
234
+
235
+ info("爬取完成")
236
+
237
+ if __name__ == "__main__":
238
+ main()
239
+ ```
240
+
241
+ ## 许可证
242
+
243
+ MIT License
@@ -0,0 +1,28 @@
1
+ """
2
+ CrawlerKeeper SDK for Python
3
+
4
+ 提供爬虫与 Agent 通信的封装,支持日志记录、结果写入、参数解析和代理管理。
5
+ """
6
+
7
+ from .output import (
8
+ info,
9
+ warning,
10
+ error,
11
+ result,
12
+ )
13
+ from .params import params
14
+ from .stealth_session import StealthSession
15
+ from .proxy import ProxyExhaustedError
16
+ from .pool import ThreadPool
17
+
18
+ __version__ = "0.2.1"
19
+ __all__ = [
20
+ "info",
21
+ "warning",
22
+ "error",
23
+ "result",
24
+ "params",
25
+ "StealthSession",
26
+ "ProxyExhaustedError",
27
+ "ThreadPool",
28
+ ]
@@ -0,0 +1,29 @@
1
+ """
2
+ 环境变量管理模块
3
+
4
+ 统一管理所有环境变量访问。
5
+ """
6
+
7
+ import os
8
+ from typing import Optional
9
+
10
+ # Agent 环境
11
+ LOG_FILE_PATH: Optional[str] = os.environ.get("LOG_FILE_PATH")
12
+ DATA_FILE_PATH: Optional[str] = os.environ.get("DATA_FILE_PATH")
13
+
14
+ # Admin Server
15
+ ADMIN_SERVER_URL: str = os.environ.get("ADMIN_SERVER_URL", "http://localhost:8080")
16
+ AGENT_API_KEY: str = os.environ.get("AGENT_API_KEY", "")
17
+
18
+ # 代理配置
19
+ PROXY_ENABLED: bool = os.environ.get("PROXY_ENABLED", "off").lower() == "on"
20
+ PROXY_MODE: str = os.environ.get("PROXY_MODE", "private").lower()
21
+ PROXY_URL: Optional[str] = os.environ.get("PROXY_URL")
22
+ PROXY_VENDOR: str = os.environ.get("PROXY_VENDOR", "default")
23
+ PROXY_TARGET_SITE: str = os.environ.get("PROXY_TARGET_SITE", "default")
24
+
25
+ # 并发配置
26
+ CONCURRENT_NUM: int = max(1, int(os.environ.get("CONCURRENT_NUM", "1")))
27
+
28
+ # 请求超时配置(秒)
29
+ CRAWL_TIMEOUT: int = max(1, int(os.environ.get("CRAWL_TIMEOUT", "30")))