smart-image-scraper-mcp 2.4.0 → 2.4.2
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.
- package/README.md +156 -60
- package/package.json +1 -1
- package/src/index.js +9 -1
- package/src/infrastructure/requestQueue.js +24 -10
package/README.md
CHANGED
|
@@ -1,58 +1,90 @@
|
|
|
1
1
|
# 全网智能图片抓取 MCP 服务器
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://www.npmjs.com/package/smart-image-scraper-mcp)
|
|
4
|
+
[](https://opensource.org/licenses/MIT)
|
|
4
5
|
|
|
5
|
-
|
|
6
|
+
基于 Node.js 和 Model Context Protocol (MCP) 开发的**高性能**智能图片抓取工具。让 AI 客户端(如 Claude Desktop、Windsurf)能够通过自然语言指令,实现全网图片资源的搜索、验证、下载与本地化管理。
|
|
6
7
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
-
|
|
11
|
-
-
|
|
12
|
-
-
|
|
13
|
-
-
|
|
8
|
+
## ✨ 核心特性
|
|
9
|
+
|
|
10
|
+
### 🚀 高性能架构 (v2.4.0)
|
|
11
|
+
- **多请求并行**:同时处理 5 个 MCP 请求
|
|
12
|
+
- **并行翻页搜索**:同时获取多页结果,速度提升 5x
|
|
13
|
+
- **HTTP 连接池**:Keep-Alive 复用 TCP 连接
|
|
14
|
+
- **智能缓存**:LRU 缓存减少重复请求
|
|
15
|
+
- **请求队列管理**:自动资源释放,防止内存泄漏
|
|
16
|
+
|
|
17
|
+
### 📷 双模式运行
|
|
18
|
+
- **`link` 模式**:快速返回图片直链(默认跳过 HTTP 验证)
|
|
19
|
+
- **`download` 模式**:下载到本地,自动按质量排序
|
|
20
|
+
|
|
21
|
+
### 🎯 高级功能
|
|
22
|
+
- **批量处理**:逗号分隔多关键词批量搜索
|
|
23
|
+
- **尺寸过滤**:small/medium/large/wallpaper
|
|
24
|
+
- **宽高比过滤**:wide(横屏)/tall(竖屏)/square(正方形)
|
|
25
|
+
- **尺寸统一**:下载后自动裁剪/缩放到指定尺寸
|
|
26
|
+
- **质量优先**:自动按图片质量排序,高清优先
|
|
14
27
|
|
|
15
28
|
## 安装
|
|
16
29
|
|
|
17
|
-
|
|
18
|
-
# 进入项目目录
|
|
19
|
-
cd 图片爬取mcp
|
|
30
|
+
### 方式一:npm 全局安装(推荐)
|
|
20
31
|
|
|
21
|
-
|
|
22
|
-
npm install
|
|
32
|
+
```bash
|
|
33
|
+
npm install -g smart-image-scraper-mcp
|
|
23
34
|
```
|
|
24
35
|
|
|
25
|
-
|
|
36
|
+
### 方式二:本地开发
|
|
26
37
|
|
|
27
|
-
|
|
38
|
+
```bash
|
|
39
|
+
git clone https://github.com/your-repo/smart-image-scraper-mcp.git
|
|
40
|
+
cd smart-image-scraper-mcp
|
|
41
|
+
npm install
|
|
42
|
+
```
|
|
28
43
|
|
|
29
|
-
|
|
30
|
-
|--------|------|--------|
|
|
31
|
-
| `SAVE_ROOT` | 图片下载根目录 | `./images` |
|
|
44
|
+
## 快速开始
|
|
32
45
|
|
|
33
|
-
|
|
46
|
+
### 1. 配置 MCP 客户端
|
|
34
47
|
|
|
35
|
-
|
|
48
|
+
**Windsurf 配置** (`~/.codeium/windsurf/mcp_config.json`):
|
|
36
49
|
|
|
37
|
-
```
|
|
38
|
-
|
|
50
|
+
```json
|
|
51
|
+
{
|
|
52
|
+
"mcpServers": {
|
|
53
|
+
"smart-image-scraper": {
|
|
54
|
+
"command": "npx",
|
|
55
|
+
"args": ["-y", "smart-image-scraper-mcp"]
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
39
59
|
```
|
|
40
60
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
在 Claude Desktop 的配置文件中添加:
|
|
61
|
+
**Claude Desktop 配置** (`claude_desktop_config.json`):
|
|
44
62
|
|
|
45
63
|
```json
|
|
46
64
|
{
|
|
47
65
|
"mcpServers": {
|
|
48
66
|
"smart-image-scraper": {
|
|
49
|
-
"command": "
|
|
50
|
-
"args": ["
|
|
67
|
+
"command": "npx",
|
|
68
|
+
"args": ["-y", "smart-image-scraper-mcp"],
|
|
69
|
+
"env": {
|
|
70
|
+
"SAVE_ROOT": "D:/images"
|
|
71
|
+
}
|
|
51
72
|
}
|
|
52
73
|
}
|
|
53
74
|
}
|
|
54
75
|
```
|
|
55
76
|
|
|
77
|
+
### 2. 重启客户端
|
|
78
|
+
|
|
79
|
+
配置完成后重启 Windsurf 或 Claude Desktop。
|
|
80
|
+
|
|
81
|
+
### 3. 开始使用
|
|
82
|
+
|
|
83
|
+
直接用自然语言描述需求:
|
|
84
|
+
- "帮我搜索 10 张猫咪图片"
|
|
85
|
+
- "下载 20 张高清风景壁纸"
|
|
86
|
+
- "找一些人物头像图片"
|
|
87
|
+
|
|
56
88
|
## Tool Schema
|
|
57
89
|
|
|
58
90
|
### smart_scraper
|
|
@@ -98,30 +130,69 @@ npm start
|
|
|
98
130
|
}
|
|
99
131
|
```
|
|
100
132
|
|
|
133
|
+
## 架构设计
|
|
134
|
+
|
|
135
|
+
```
|
|
136
|
+
┌─────────────────────────────────────────────────────────────────────────┐
|
|
137
|
+
│ MCP Server (index.js) │
|
|
138
|
+
│ - 版本号从 package.json 动态读取 │
|
|
139
|
+
│ - 优雅关闭和资源清理 │
|
|
140
|
+
└─────────────────────────────────────────────────────────────────────────┘
|
|
141
|
+
↓
|
|
142
|
+
┌─────────────────────────────────────────────────────────────────────────┐
|
|
143
|
+
│ RequestQueue (requestQueue.js) │
|
|
144
|
+
│ - 最大并发: 5 个请求 │
|
|
145
|
+
│ - 请求超时: 60 秒 │
|
|
146
|
+
│ - 队列超时: 30 秒 │
|
|
147
|
+
│ - 自动资源释放 │
|
|
148
|
+
└─────────────────────────────────────────────────────────────────────────┘
|
|
149
|
+
↓
|
|
150
|
+
┌─────────────────────────────────────────────────────────────────────────┐
|
|
151
|
+
│ Orchestrator (orchestrator.js) │
|
|
152
|
+
│ - 关键词并发: 3 个/请求 │
|
|
153
|
+
│ - 智能缓存集成 │
|
|
154
|
+
│ - 快速模式/完整验证模式 │
|
|
155
|
+
└─────────────────────────────────────────────────────────────────────────┘
|
|
156
|
+
↓
|
|
157
|
+
┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐
|
|
158
|
+
│ BingScraper │ │ GoogleScraper │ │ LinkValidator │
|
|
159
|
+
│ - 并行翻页(5页) │ │ - 并行翻页(5页) │ │ - 并发验证(30) │
|
|
160
|
+
│ - 最小延迟 50ms │ │ - 最小延迟 50ms │ │ - 超时 1.5s │
|
|
161
|
+
└──────────────────┘ └──────────────────┘ └──────────────────┘
|
|
162
|
+
```
|
|
163
|
+
|
|
101
164
|
## 项目结构
|
|
102
165
|
|
|
103
166
|
```
|
|
104
|
-
|
|
167
|
+
smart-image-scraper-mcp/
|
|
105
168
|
├── src/
|
|
106
|
-
│ ├── index.js
|
|
169
|
+
│ ├── index.js # MCP 服务器入口
|
|
107
170
|
│ ├── config/
|
|
108
|
-
│ │ └── index.js
|
|
171
|
+
│ │ └── index.js # 配置模块
|
|
109
172
|
│ ├── infrastructure/
|
|
110
|
-
│ │ ├── httpClient.js
|
|
111
|
-
│ │ ├──
|
|
112
|
-
│ │ ├──
|
|
113
|
-
│ │
|
|
173
|
+
│ │ ├── httpClient.js # HTTP 客户端(连接池)
|
|
174
|
+
│ │ ├── requestQueue.js # 请求队列管理
|
|
175
|
+
│ │ ├── cache.js # LRU 缓存
|
|
176
|
+
│ │ ├── metrics.js # 性能指标
|
|
177
|
+
│ │ ├── healthCheck.js # 健康检查
|
|
178
|
+
│ │ ├── gracefulShutdown.js # 优雅关闭
|
|
179
|
+
│ │ ├── rateLimiter.js # 速率限制
|
|
180
|
+
│ │ ├── logger.js # 日志模块
|
|
181
|
+
│ │ ├── retry.js # 重试机制
|
|
182
|
+
│ │ ├── errors.js # 错误分类
|
|
183
|
+
│ │ └── proxy.js # 代理支持
|
|
114
184
|
│ ├── providers/
|
|
115
|
-
│ │ ├── baseScraper.js
|
|
116
|
-
│ │ ├── bingScraper.js
|
|
117
|
-
│ │ ├── googleScraper.js
|
|
118
|
-
│ │ └── index.js
|
|
185
|
+
│ │ ├── baseScraper.js # 抽象基类
|
|
186
|
+
│ │ ├── bingScraper.js # Bing 搜索(并行翻页)
|
|
187
|
+
│ │ ├── googleScraper.js # Google 搜索(并行翻页)
|
|
188
|
+
│ │ └── index.js # 提供者索引
|
|
119
189
|
│ └── services/
|
|
120
|
-
│ ├──
|
|
121
|
-
│ ├── linkValidator.js
|
|
122
|
-
│ ├──
|
|
123
|
-
│
|
|
124
|
-
|
|
190
|
+
│ ├── orchestrator.js # 任务编排器
|
|
191
|
+
│ ├── linkValidator.js # 链接验证器
|
|
192
|
+
│ ├── fileManager.js # 文件管理器
|
|
193
|
+
│ ├── imageProcessor.js # 图片处理器
|
|
194
|
+
│ └── index.js # 服务索引
|
|
195
|
+
├── images/ # 图片存储目录
|
|
125
196
|
├── package.json
|
|
126
197
|
└── README.md
|
|
127
198
|
```
|
|
@@ -137,35 +208,37 @@ npm start
|
|
|
137
208
|
|
|
138
209
|
## 生产级功能
|
|
139
210
|
|
|
140
|
-
###
|
|
141
|
-
-
|
|
142
|
-
-
|
|
143
|
-
-
|
|
144
|
-
-
|
|
211
|
+
### 🔄 请求队列管理
|
|
212
|
+
- 最大并发 5 个请求
|
|
213
|
+
- 队列最大 20 个等待
|
|
214
|
+
- 请求超时 60 秒
|
|
215
|
+
- 队列等待超时 30 秒
|
|
216
|
+
- 自动资源释放
|
|
145
217
|
|
|
146
|
-
### 性能指标
|
|
218
|
+
### 📊 性能指标
|
|
147
219
|
- 请求成功率统计
|
|
148
|
-
-
|
|
220
|
+
- 缓存命中率统计
|
|
149
221
|
- 响应时间统计(平均值、P95)
|
|
150
222
|
- 错误分类统计
|
|
151
223
|
|
|
152
|
-
###
|
|
224
|
+
### 💾 智能缓存
|
|
153
225
|
- LRU 缓存实现
|
|
154
|
-
- 搜索结果缓存(
|
|
155
|
-
-
|
|
226
|
+
- 搜索结果缓存(5分钟 TTL)
|
|
227
|
+
- 统一缓存键策略
|
|
156
228
|
- 自动过期清理
|
|
157
229
|
|
|
158
|
-
###
|
|
159
|
-
-
|
|
160
|
-
-
|
|
161
|
-
-
|
|
230
|
+
### ⚡ 高性能优化
|
|
231
|
+
- HTTP 连接池(Keep-Alive)
|
|
232
|
+
- 并行翻页搜索(5页同时)
|
|
233
|
+
- 极速验证(1.5秒超时)
|
|
234
|
+
- 最小请求延迟(50ms)
|
|
162
235
|
|
|
163
|
-
### 错误处理
|
|
236
|
+
### 🛡️ 错误处理
|
|
164
237
|
- 统一错误码体系
|
|
165
238
|
- 错误分类(网络/搜索/下载/验证)
|
|
166
239
|
- 自动重试机制(指数退避)
|
|
167
240
|
|
|
168
|
-
### 优雅关闭
|
|
241
|
+
### 🔒 优雅关闭
|
|
169
242
|
- 信号处理(SIGINT/SIGTERM)
|
|
170
243
|
- 活跃操作追踪
|
|
171
244
|
- 资源清理
|
|
@@ -190,11 +263,34 @@ npm start
|
|
|
190
263
|
npm test
|
|
191
264
|
```
|
|
192
265
|
|
|
266
|
+
## 版本历史
|
|
267
|
+
|
|
268
|
+
| 版本 | 日期 | 主要更新 |
|
|
269
|
+
|------|------|----------|
|
|
270
|
+
| 2.4.0 | 2026-02 | 修复所有分析问题:超时策略统一、缓存键一致、版本号动态读取 |
|
|
271
|
+
| 2.3.0 | 2026-02 | 性能优化:HTTP 连接池、并行翻页搜索、内存泄漏修复 |
|
|
272
|
+
| 2.2.0 | 2026-02 | 请求队列管理系统、自动资源释放 |
|
|
273
|
+
| 2.1.0 | 2026-02 | 极速模式、快速验证、更高并发 |
|
|
274
|
+
| 2.0.0 | 2026-02 | 高性能并发模型、多请求并行 |
|
|
275
|
+
| 1.0.0 | 2026-01 | 初始版本 |
|
|
276
|
+
|
|
193
277
|
## 注意事项
|
|
194
278
|
|
|
195
279
|
1. 请遵守目标网站的使用条款和 robots.txt
|
|
196
280
|
2. 建议适度使用,避免频繁请求
|
|
197
281
|
3. 下载的图片仅供个人学习使用
|
|
282
|
+
4. 首次请求可能较慢(需要建立连接池)
|
|
283
|
+
|
|
284
|
+
## 常见问题
|
|
285
|
+
|
|
286
|
+
### Q: 搜索返回 0 张图片?
|
|
287
|
+
A: 检查网络连接,或尝试更换搜索源(bing → google)
|
|
288
|
+
|
|
289
|
+
### Q: 下载速度慢?
|
|
290
|
+
A: 可以设置 `HTTP_PROXY` 环境变量使用代理
|
|
291
|
+
|
|
292
|
+
### Q: 如何查看队列状态?
|
|
293
|
+
A: 日志中会显示 `[Queue]` 相关信息
|
|
198
294
|
|
|
199
295
|
## License
|
|
200
296
|
|
package/package.json
CHANGED
package/src/index.js
CHANGED
|
@@ -145,7 +145,10 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
145
145
|
|
|
146
146
|
// 注册工具调用处理器
|
|
147
147
|
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
148
|
+
const requestStartTime = Date.now();
|
|
148
149
|
const { name, arguments: args } = request.params;
|
|
150
|
+
|
|
151
|
+
logger.info(`[MCP] 收到请求: ${name}, query="${args?.query?.substring(0, 30)}..."`);
|
|
149
152
|
|
|
150
153
|
if (name !== 'smart_scraper') {
|
|
151
154
|
return {
|
|
@@ -262,6 +265,9 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
262
265
|
|
|
263
266
|
// 格式化输出
|
|
264
267
|
const formattedResult = orchestrator.formatResult(result);
|
|
268
|
+
|
|
269
|
+
const totalTime = Date.now() - requestStartTime;
|
|
270
|
+
logger.info(`[MCP] 请求完成: ${totalTime}ms, requestId=${result.requestId}`);
|
|
265
271
|
|
|
266
272
|
return {
|
|
267
273
|
content: [
|
|
@@ -275,13 +281,15 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
275
281
|
// 记录失败指标
|
|
276
282
|
metrics.recordSearch(source, false, Date.now() - startTime);
|
|
277
283
|
metrics.recordError(innerError);
|
|
284
|
+
logger.error(`[MCP] 内部错误: ${innerError.message}`);
|
|
278
285
|
throw innerError;
|
|
279
286
|
} finally {
|
|
280
287
|
// 结束操作追踪
|
|
281
288
|
operation.end();
|
|
282
289
|
}
|
|
283
290
|
} catch (error) {
|
|
284
|
-
|
|
291
|
+
const totalTime = Date.now() - requestStartTime;
|
|
292
|
+
logger.error(`[MCP] 请求失败: ${totalTime}ms, error=${error.message}`);
|
|
285
293
|
return {
|
|
286
294
|
content: [
|
|
287
295
|
{
|
|
@@ -123,12 +123,22 @@ export class RequestQueue {
|
|
|
123
123
|
|
|
124
124
|
/**
|
|
125
125
|
* 处理队列中的请求
|
|
126
|
+
* 注意:不使用 await,让请求并行执行
|
|
127
|
+
* 每个请求完成后会在 finally 中调用 _processQueue 继续处理
|
|
126
128
|
*/
|
|
127
129
|
_processQueue() {
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
this.
|
|
131
|
-
|
|
130
|
+
// 使用 setImmediate 避免调用栈过深
|
|
131
|
+
setImmediate(() => {
|
|
132
|
+
while (this.queue.length > 0 && this.active.size < this.maxConcurrent) {
|
|
133
|
+
const requestInfo = this.queue.shift();
|
|
134
|
+
if (requestInfo) {
|
|
135
|
+
// 不 await,让请求并行执行
|
|
136
|
+
this._executeRequest(requestInfo).catch(err => {
|
|
137
|
+
logger.error(`[Queue] Unexpected error: ${err.message}`);
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
});
|
|
132
142
|
}
|
|
133
143
|
|
|
134
144
|
/**
|
|
@@ -181,11 +191,15 @@ export class RequestQueue {
|
|
|
181
191
|
// 从活跃列表移除
|
|
182
192
|
this._removeFromActive(id);
|
|
183
193
|
|
|
184
|
-
//
|
|
185
|
-
|
|
194
|
+
// 保存到已完成列表(使用 try-catch 防止错误)
|
|
195
|
+
try {
|
|
196
|
+
this._addToCompleted(requestInfo);
|
|
197
|
+
} catch (e) {
|
|
198
|
+
logger.warn(`[Queue] Failed to add to completed: ${e.message}`);
|
|
199
|
+
}
|
|
186
200
|
|
|
187
|
-
//
|
|
188
|
-
this._processQueue();
|
|
201
|
+
// 继续处理队列(延迟执行,避免阻塞)
|
|
202
|
+
setImmediate(() => this._processQueue());
|
|
189
203
|
}
|
|
190
204
|
}
|
|
191
205
|
|
|
@@ -284,8 +298,8 @@ export class RequestQueue {
|
|
|
284
298
|
// 全局请求队列实例
|
|
285
299
|
export const requestQueue = new RequestQueue({
|
|
286
300
|
maxConcurrent: 5,
|
|
287
|
-
maxQueueSize:
|
|
288
|
-
requestTimeout:
|
|
301
|
+
maxQueueSize: 50, // 增加队列容量
|
|
302
|
+
requestTimeout: 120000, // 增加超时到 2 分钟
|
|
289
303
|
});
|
|
290
304
|
|
|
291
305
|
export default requestQueue;
|