wespy-ts 1.0.0 → 1.1.0

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.
Files changed (2) hide show
  1. package/README.md +560 -52
  2. package/package.json +62 -62
package/README.md CHANGED
@@ -1,9 +1,25 @@
1
- # wespy-ts
1
+ # WeSpy-TS
2
2
 
3
3
  [![npm version](https://img.shields.io/npm/v/wespy-ts)](https://www.npmjs.com/package/wespy-ts)
4
- [![license](https://img.shields.io/npm/l/wespy-ts)](https://github.com/Cuimc/WeSpy-TS/blob/main/LICENSE)
4
+ [![Node.js Support](https://img.shields.io/node/v/wespy-ts)](https://nodejs.org/)
5
+ [![License: MIT](https://img.shields.io/npm/l/wespy-ts)](https://opensource.org/licenses/MIT)
5
6
 
6
- 文章抓取与 Markdown 转换工具 支持微信公众号、掘金等平台。
7
+ WeSpy-TS 是用于获取微信公众号文章并转换为 Markdown 格式的 TypeScript 工具,支持图片防盗链处理、专辑批量下载和多种输出格式。提供 CLI 命令行和 SDK API 两种使用方式。
8
+
9
+ > 本项目由 Python 版 [WeSpy](https://github.com/tianchangNorth/WeSpy) 重构而来,保留了原有核心能力,并提供了更完善的类型定义和模块化架构。详见 [MIGRATION.md](./MIGRATION.md)。
10
+
11
+ ## 特性
12
+
13
+ - 🚀 **智能文章提取**:自动识别文章标题、作者、发布时间和正文内容
14
+ - 📱 **微信公众号支持**:专门优化微信公众号文章的提取,支持长短链接自动转换
15
+ - 🎵 **专辑批量下载**:支持微信公众号专辑文章批量获取和下载
16
+ - 🖼️ **图片防盗链处理**:自动处理图片防盗链问题,确保图片正常显示
17
+ - 📝 **灵活输出配置**:默认只输出 Markdown,可选择 HTML 和 JSON 格式
18
+ - 🌐 **通用网页支持**:支持大多数网站的文章提取(掘金等平台专项优化)
19
+ - 🎯 **命令行友好**:提供简单易用的命令行界面,支持交互模式
20
+ - 📂 **批量处理**:支持批量处理多个文章链接和专辑文章
21
+ - 🔒 **类型安全**:完整的 TypeScript 类型定义,支持 Zod 输入校验
22
+ - 📦 **双模使用**:既可作为 CLI 工具,也可作为 SDK 被其他项目 import
7
23
 
8
24
  ## 支持平台
9
25
 
@@ -15,54 +31,102 @@
15
31
 
16
32
  ## 安装
17
33
 
34
+ ### 使用 npm 全局安装(推荐)
35
+
18
36
  ```bash
19
- # 全局安装(推荐,可直接使用 wespy 命令)
20
37
  npm install -g wespy-ts
38
+ ```
39
+
40
+ ### 作为项目依赖安装
21
41
 
22
- # 或作为项目依赖安装
42
+ ```bash
23
43
  npm install wespy-ts
24
44
  ```
25
45
 
26
- ## CLI 使用
46
+ ### 使用 npx 直接运行(无需安装)
47
+
48
+ ```bash
49
+ npx wespy-ts fetch-article https://mp.weixin.qq.com/s/xxxxx
50
+ ```
51
+
52
+ ### 从源码安装
53
+
54
+ ```bash
55
+ git clone https://github.com/Cuimc/WeSpy-TS.git
56
+ cd WeSpy-TS
57
+ npm install
58
+ npm run build
59
+ ```
60
+
61
+ ## 快速开始
62
+
63
+ ### 命令行使用
27
64
 
28
65
  全局安装后直接使用 `wespy` 命令:
29
66
 
30
67
  ```bash
31
- # 获取单篇文章(默认输出 Markdown)
32
- wespy fetch-article https://mp.weixin.qq.com/s/xxxxx
68
+ # 获取微信公众号文章(默认只输出 Markdown)
69
+ wespy "https://mp.weixin.qq.com/s/xxxxx"
70
+
71
+ # 指定输出目录
72
+ wespy "https://mp.weixin.qq.com/s/xxxxx" -o /path/to/output
73
+
74
+ # 输出 Markdown + HTML 格式
75
+ wespy "https://example.com/article" --html
76
+
77
+ # 输出 Markdown + JSON 格式
78
+ wespy "https://example.com/article" --json
79
+
80
+ # 输出所有格式(HTML + JSON + Markdown)
81
+ wespy "https://example.com/article" --all
82
+
83
+ # 显示详细信息
84
+ wespy "https://example.com/article" -v
85
+
86
+ # === 微信专辑功能 ===
33
87
 
34
- # 指定输出格式和目录
35
- wespy fetch-article https://mp.weixin.qq.com/s/xxxxx -o ./output --all
88
+ # 获取微信专辑文章列表(不下载内容)
89
+ wespy "https://mp.weixin.qq.com/mp/appmsgalbum?__biz=...&album_id=..." --album-only
36
90
 
37
- # 批量下载微信专辑
38
- wespy fetch-album https://mp.weixin.qq.com/mp/appmsgalbum?... --max 5
91
+ # 批量下载微信专辑文章(默认下载前 10 篇)
92
+ wespy "https://mp.weixin.qq.com/mp/appmsgalbum?__biz=...&album_id=..."
39
93
 
40
- # 智能模式(自动判断 URL 类型)
41
- wespy https://mp.weixin.qq.com/s/xxxxx -o ./output --html --json
94
+ # 限制专辑文章下载数量
95
+ wespy "https://mp.weixin.qq.com/mp/appmsgalbum?__biz=...&album_id=..." --max 5
96
+
97
+ # 下载专辑文章并保存所有格式
98
+ wespy "https://mp.weixin.qq.com/mp/appmsgalbum?__biz=...&album_id=..." --max 5 --all
42
99
  ```
43
100
 
44
- 也可以通过 `npx` 直接运行,无需安装:
101
+ 也可以使用子命令方式,更加明确:
45
102
 
46
103
  ```bash
47
- npx wespy-ts fetch-article https://mp.weixin.qq.com/s/xxxxx
104
+ # 获取单篇文章
105
+ wespy fetch-article "https://mp.weixin.qq.com/s/xxxxx" -o ./output --all
106
+
107
+ # 批量下载专辑
108
+ wespy fetch-album "https://mp.weixin.qq.com/mp/appmsgalbum?__biz=...&album_id=..." --max 5
48
109
  ```
49
110
 
50
- ### CLI 参数
111
+ ### 交互式使用
51
112
 
52
- | 参数 | 说明 | 默认值 |
53
- |------|------|--------|
54
- | `-o, --output <dir>` | 输出目录 | `articles` |
55
- | `--html` | 同时保存 HTML 文件 | false |
56
- | `--json` | 同时保存 JSON 文件 | false |
57
- | `--all` | 保存所有格式 | false |
58
- | `--max <n>` | 专辑最大下载数 | 10 |
59
- | `--timeout <ms>` | 请求超时时间 | 30000 |
60
- | `--album-only` | 仅获取文章列表 | false |
113
+ 如果不提供任何参数,程序会进入交互模式:
114
+
115
+ ```bash
116
+ wespy
117
+ ```
118
+
119
+ 然后根据提示输入文章 URL、输出目录和输出格式选择:
61
120
 
62
- ## SDK API
121
+ 1. Markdown(默认)
122
+ 2. Markdown + HTML
123
+ 3. Markdown + JSON
124
+ 4. 全部格式(HTML + JSON + Markdown)
125
+
126
+ ### SDK API 使用
63
127
 
64
128
  ```typescript
65
- import { fetchArticle, fetchAlbum } from 'wespy-ts'
129
+ import { fetchArticle, fetchAlbum, fetchAlbumList } from 'wespy-ts'
66
130
 
67
131
  // 获取单篇文章
68
132
  const result = await fetchArticle({
@@ -73,71 +137,515 @@ const result = await fetchArticle({
73
137
 
74
138
  if (result.ok) {
75
139
  console.log(result.article.title)
140
+ console.log(result.article.author)
76
141
  console.log(result.article.markdown)
77
142
  console.log(result.artifacts)
78
143
  } else {
79
144
  console.error(result.error.code, result.error.message)
80
145
  }
81
146
 
82
- // 批量获取专辑
147
+ // 批量获取专辑文章
83
148
  const albumResult = await fetchAlbum({
84
- url: 'https://mp.weixin.qq.com/mp/appmsgalbum?...',
149
+ url: 'https://mp.weixin.qq.com/mp/appmsgalbum?__biz=...&album_id=...',
85
150
  maxArticles: 10,
86
151
  format: ['markdown'],
152
+ outputDir: './output',
87
153
  })
88
154
 
89
155
  if (albumResult.ok) {
90
156
  console.log(`获取了 ${albumResult.articles.length} 篇文章`)
157
+ for (const article of albumResult.articles) {
158
+ console.log(`- ${article.title}`)
159
+ }
160
+ }
161
+
162
+ // 仅获取专辑文章列表(不下载内容)
163
+ const listResult = await fetchAlbumList({
164
+ url: 'https://mp.weixin.qq.com/mp/appmsgalbum?__biz=...&album_id=...',
165
+ maxArticles: 20,
166
+ })
167
+
168
+ if (listResult.ok) {
169
+ for (const entry of listResult.articles) {
170
+ console.log(`${entry.title} - ${entry.url}`)
171
+ }
172
+ }
173
+ ```
174
+
175
+ ## 输出格式
176
+
177
+ WeSpy-TS 默认只生成 Markdown 文件,但可以通过配置选项选择其他格式:
178
+
179
+ ### 默认输出(仅 Markdown)
180
+
181
+ ```
182
+ articles/
183
+ └── 文章标题_1627834567.md # Markdown 格式
184
+ ```
185
+
186
+ ### 可选格式
187
+
188
+ - **Markdown 文件**:转换后的 Markdown 格式内容(默认生成)
189
+ - **HTML 文件**:原始 HTML 内容(使用 `--html` 选项)
190
+ - **JSON 文件**:文章元数据信息(使用 `--json` 选项)
191
+
192
+ ### 全部格式输出示例
193
+
194
+ ```
195
+ articles/
196
+ ├── 文章标题_1627834567.html # 原始 HTML
197
+ ├── 文章标题_1627834567.md # Markdown 格式
198
+ └── 文章标题_1627834567_info.json # 元数据信息
199
+ ```
200
+
201
+ ### JSON 元数据格式
202
+
203
+ ```json
204
+ {
205
+ "title": "文章标题",
206
+ "author": "作者名称",
207
+ "publishTime": "2023-07-30",
208
+ "url": "https://example.com/article",
209
+ "platform": "wechat",
210
+ "fetchedAt": "2023-07-30T12:34:56.000Z"
91
211
  }
92
212
  ```
93
213
 
94
- ## 输出结构
214
+ ## 命令行选项
215
+
216
+ ### 通用选项
217
+
218
+ | 参数 | 说明 | 默认值 |
219
+ |------|------|--------|
220
+ | `-o, --output <dir>` | 输出目录 | `articles` |
221
+ | `--html` | 同时保存 HTML 文件 | false |
222
+ | `--json` | 同时保存 JSON 文件 | false |
223
+ | `--all` | 保存所有格式(HTML + JSON + Markdown) | false |
224
+ | `--timeout <ms>` | 请求超时时间(毫秒) | `30000` |
225
+ | `--download-images` | 下载图片到本地 | false |
226
+ | `-v, --verbose` | 显示详细信息 | false |
227
+
228
+ ### 专辑专用选项
229
+
230
+ | 参数 | 说明 | 默认值 |
231
+ |------|------|--------|
232
+ | `--max <n>` | 专辑最大下载文章数 | `10` |
233
+ | `--album-only` | 仅获取文章列表,不下载内容 | false |
234
+
235
+ ### 输出格式选项说明
236
+
237
+ - **默认行为**:只生成 Markdown 文件
238
+ - **`--html`**:生成 Markdown + HTML 文件
239
+ - **`--json`**:生成 Markdown + JSON 文件
240
+ - **`--all`**:生成所有格式文件(HTML + JSON + Markdown)
241
+
242
+ ## 微信专辑功能
243
+
244
+ ### 功能介绍
245
+
246
+ WeSpy-TS 支持微信公众号专辑文章的批量获取和下载,可以一次性下载整个专辑中的所有文章。
247
+
248
+ ### 使用方式
249
+
250
+ #### 仅获取文章列表
251
+
252
+ 使用 `--album-only` 参数只获取专辑中的文章列表,不下载具体内容:
253
+
254
+ ```bash
255
+ wespy "https://mp.weixin.qq.com/mp/appmsgalbum?__biz=...&album_id=..." --album-only
256
+ ```
257
+
258
+ 输出示例:
259
+
260
+ ```
261
+ 获取到 25 篇文章:
262
+ 1. 文章标题一
263
+ URL: http://mp.weixin.qq.com/s?__biz=...
264
+ 时间: 1704067200
265
+
266
+ 2. 文章标题二
267
+ URL: http://mp.weixin.qq.com/s?__biz=...
268
+ 时间: 1703980800
269
+ ```
270
+
271
+ 文章列表会保存为 JSON 文件,包含标题、URL、发布时间等完整信息。
272
+
273
+ #### 批量下载专辑文章
274
+
275
+ 直接使用专辑 URL 即可批量下载专辑中的所有文章:
276
+
277
+ ```bash
278
+ # 下载前 10 篇文章(默认)
279
+ wespy "https://mp.weixin.qq.com/mp/appmsgalbum?__biz=...&album_id=..."
280
+
281
+ # 限制下载文章数量
282
+ wespy "https://mp.weixin.qq.com/mp/appmsgalbum?__biz=...&album_id=..." --max 5
283
+
284
+ # 下载并保存所有格式
285
+ wespy "https://mp.weixin.qq.com/mp/appmsgalbum?__biz=...&album_id=..." --max 5 --all
286
+ ```
287
+
288
+ ### 输出结构
289
+
290
+ 专辑文章下载后会创建独立的专辑目录:
291
+
292
+ ```
293
+ articles/
294
+ ├── album_1703980800/ # 专辑专用目录
295
+ │ ├── 文章标题一_1703980800.md # Markdown 格式
296
+ │ ├── 文章标题一_1703980800_info.json # 文章信息
297
+ │ ├── 文章标题二_1703980801.md # 下一篇文章
298
+ │ └── ...
299
+ └── album_1703980800_summary.json # 专辑下载汇总信息
300
+ ```
301
+
302
+ ### 汇总信息
303
+
304
+ 每个专辑下载完成后会生成详细的汇总报告,包含:
305
+
306
+ - 专辑 URL 和下载时间
307
+ - 成功/失败统计
308
+ - 成功下载的文章列表
309
+ - 失败的文章列表和错误信息
310
+
311
+ ### 技术特性
312
+
313
+ - **智能分页**:自动处理微信分页获取,支持大型专辑
314
+ - **错误处理**:分离成功和失败的文章,确保部分失败不影响整体下载
315
+ - **速率控制**:内置延迟机制避免请求过快
316
+ - **进度显示**:实时显示下载进度和统计信息
317
+
318
+ ## 支持的网站
319
+
320
+ ### 完全支持
321
+
322
+ - 微信公众号 (`mp.weixin.qq.com`)
323
+ - 掘金 (`juejin.cn`)
324
+
325
+ ### 通用支持
326
+
327
+ WeSpy-TS 使用智能算法尝试从以下元素中提取内容:
328
+
329
+ - `<article>` 标签
330
+ - 带有 `content`、`article-content`、`post-content` 等 class 的元素
331
+ - `<main>` 标签
332
+ - 标准的 meta 标签信息
333
+
334
+ ## SDK API 参考
335
+
336
+ ### `fetchArticle(input)`
95
337
 
96
- ### ArticleDraft
338
+ 获取单篇文章。
97
339
 
98
340
  ```typescript
99
- interface ArticleDraft {
100
- platform: 'wechat' | 'juejin' | 'generic'
101
- url: string
102
- title: string
103
- author?: string
104
- publishTime?: string
105
- contentHtml?: string
106
- contentText?: string
107
- markdown?: string
108
- metadata: Record<string, unknown>
109
- fetchedAt: string
110
- warnings: string[]
341
+ import { fetchArticle } from 'wespy-ts'
342
+
343
+ const result = await fetchArticle({
344
+ url: 'https://mp.weixin.qq.com/s/xxxxx',
345
+ outputDir: './output', // 输出目录,默认 'articles'
346
+ format: ['markdown'], // 输出格式,默认 ['markdown']
347
+ timeoutMs: 30000, // 超时时间,默认 30000
348
+ downloadImages: false, // 是否下载图片
349
+ userAgent: 'custom-ua', // 自定义 User-Agent
350
+ headers: { 'X-Custom': 'v' }, // 自定义请求头
351
+ })
352
+ ```
353
+
354
+ **返回值:** `FetchArticleResult`
355
+
356
+ ```typescript
357
+ // 成功
358
+ {
359
+ ok: true,
360
+ article: {
361
+ platform: 'wechat',
362
+ url: 'https://mp.weixin.qq.com/s/xxxxx',
363
+ title: '文章标题',
364
+ author: '作者',
365
+ publishTime: '2023-07-30',
366
+ contentHtml: '<div>...</div>',
367
+ contentText: '纯文本内容',
368
+ markdown: '# 标题\n\n正文...',
369
+ metadata: {},
370
+ fetchedAt: '2023-07-30T12:34:56.000Z',
371
+ warnings: [],
372
+ },
373
+ artifacts: [
374
+ { type: 'markdown', path: 'articles/文章标题_1627834567.md' }
375
+ ],
376
+ warnings: [],
377
+ }
378
+
379
+ // 失败
380
+ {
381
+ ok: false,
382
+ error: {
383
+ code: 'NETWORK_ERROR',
384
+ message: '请求失败: ...',
385
+ retryable: true,
386
+ }
111
387
  }
112
388
  ```
113
389
 
114
- ### FetchArticleResult
390
+ ### `fetchAlbum(input)`
391
+
392
+ 批量获取微信专辑文章。
393
+
394
+ ```typescript
395
+ import { fetchAlbum } from 'wespy-ts'
396
+
397
+ const result = await fetchAlbum({
398
+ url: 'https://mp.weixin.qq.com/mp/appmsgalbum?__biz=...&album_id=...',
399
+ outputDir: './output',
400
+ format: ['markdown'],
401
+ maxArticles: 10, // 最大下载数,默认 10
402
+ timeoutMs: 30000,
403
+ downloadImages: false,
404
+ })
405
+ ```
406
+
407
+ **返回值:** `FetchAlbumResult`
115
408
 
116
409
  ```typescript
117
410
  // 成功
118
- { ok: true, article: ArticleDraft, artifacts: OutputArtifact[], warnings: string[] }
411
+ {
412
+ ok: true,
413
+ articles: [/* ArticleDraft[] */],
414
+ artifacts: [/* OutputArtifact[] */],
415
+ warnings: [],
416
+ summaryFile: 'articles/album_1703980800_summary.json',
417
+ failedCount: 0,
418
+ }
119
419
 
120
420
  // 失败
121
- { ok: false, error: { code: string, message: string, retryable: boolean } }
421
+ {
422
+ ok: false,
423
+ error: { code: string, message: string, retryable: boolean }
424
+ }
425
+ ```
426
+
427
+ ### `fetchAlbumList(input)`
428
+
429
+ 仅获取专辑文章列表,不下载内容。
430
+
431
+ ```typescript
432
+ import { fetchAlbumList } from 'wespy-ts'
433
+
434
+ const result = await fetchAlbumList({
435
+ url: 'https://mp.weixin.qq.com/mp/appmsgalbum?__biz=...&album_id=...',
436
+ maxArticles: 20,
437
+ timeoutMs: 30000,
438
+ })
439
+ ```
440
+
441
+ **返回值:** `FetchAlbumListResult`
442
+
443
+ ```typescript
444
+ // 成功
445
+ {
446
+ ok: true,
447
+ articles: [
448
+ {
449
+ title: '文章标题',
450
+ url: 'http://mp.weixin.qq.com/s?__biz=...',
451
+ msgid: '...',
452
+ createTime: '1704067200',
453
+ coverImg: '...',
454
+ itemidx: '1',
455
+ key: '...',
456
+ }
457
+ ],
458
+ warnings: [],
459
+ }
460
+ ```
461
+
462
+ ### 错误码
463
+
464
+ | 错误码 | 说明 | 可重试 |
465
+ |--------|------|--------|
466
+ | `INVALID_INPUT` | 输入参数校验失败 | 否 |
467
+ | `UNSUPPORTED_URL` | 不支持的 URL 格式 | 否 |
468
+ | `NETWORK_ERROR` | 网络请求失败 | 是 |
469
+ | `HTTP_STATUS_ERROR` | HTTP 状态码异常 | 视情况 |
470
+ | `PARSE_EMPTY` | 解析结果为空 | 否 |
471
+ | `TIMEOUT` | 请求超时 | 是 |
472
+ | `FILE_SYSTEM_ERROR` | 文件系统错误 | 否 |
473
+ | `UNKNOWN_ERROR` | 未知错误 | 否 |
474
+
475
+ ## 项目结构
476
+
477
+ ```
478
+ src/
479
+ ├── index.ts # SDK 入口,导出公共 API
480
+ ├── cli/
481
+ │ └── main.ts # CLI 入口(commander)
482
+ ├── core/
483
+ │ ├── types.ts # 核心类型定义 + Zod Schema
484
+ │ ├── errors.ts # 错误工厂函数
485
+ │ └── result.ts # Result<T> 类型工具
486
+ ├── fetcher/
487
+ │ └── http-client.ts # HTTP 客户端(fetch + 编码检测)
488
+ ├── platforms/
489
+ │ ├── detector.ts # URL 平台检测
490
+ │ ├── wechat/
491
+ │ │ ├── wechat-article.extractor.ts # 微信文章提取器
492
+ │ │ ├── wechat-album.extractor.ts # 微信专辑提取器
493
+ │ │ └── wechat.types.ts # 微信类型定义
494
+ │ ├── juejin/
495
+ │ │ ├── juejin-article.extractor.ts # 掘金文章提取器
496
+ │ │ └── juejin.types.ts # 掘金类型定义
497
+ │ └── generic/
498
+ │ └── generic-article.extractor.ts # 通用网页提取器
499
+ ├── sdk/
500
+ │ ├── fetch-article.ts # fetchArticle() SDK 函数
501
+ │ ├── fetch-album.ts # fetchAlbum() SDK 函数
502
+ │ └── fetch-album-list.ts # fetchAlbumList() SDK 函数
503
+ ├── converter/
504
+ │ ├── html-to-markdown.ts # HTML → Markdown 转换
505
+ │ └── sanitize-html.ts # HTML 清理
506
+ └── utils/
507
+ ├── url.ts # URL 工具函数
508
+ ├── fs.ts # 文件系统工具
509
+ └── text.ts # 文本工具函数
122
510
  ```
123
511
 
124
512
  ## 开发
125
513
 
514
+ ### 环境要求
515
+
516
+ - Node.js >= 18
517
+ - npm >= 8
518
+
519
+ ### 开发环境设置
520
+
126
521
  ```bash
127
- git clone git@github.com:Cuimc/WeSpy-TS.git
522
+ git clone https://github.com/Cuimc/WeSpy-TS.git
128
523
  cd WeSpy-TS
129
524
  npm install
525
+ ```
130
526
 
527
+ ### 常用命令
528
+
529
+ ```bash
131
530
  npm run build # 编译 TypeScript
132
- npm run dev # watch 模式
531
+ npm run dev # watch 模式(开发时自动编译)
133
532
  npm test # 运行测试
533
+ npm run test:watch # watch 模式运行测试
134
534
  npm run lint # 类型检查
535
+ npm run clean # 清除编译产物
135
536
  ```
136
537
 
137
- ## Python 原版
538
+ ### 运行测试
539
+
540
+ ```bash
541
+ npm test
542
+ ```
543
+
544
+ ## 依赖说明
545
+
546
+ | 依赖 | 用途 |
547
+ |------|------|
548
+ | `cheerio` | HTML 解析 |
549
+ | `commander` | CLI 框架 |
550
+ | `turndown` | HTML → Markdown 转换 |
551
+ | `undici` | HTTP 请求 |
552
+ | `zod` | 输入参数校验 |
553
+ | `vitest` | 测试框架(开发依赖) |
554
+ | `typescript` | TypeScript 编译器(开发依赖) |
555
+
556
+ ## 与 Python 版的关系
557
+
558
+ 本项目由 Python 版 [WeSpy](https://github.com/tianchangNorth/WeSpy) 重构而来:
559
+
560
+ - **保留了所有核心功能**:文章抓取、专辑下载、多格式输出、交互模式等
561
+ - **改进了架构设计**:分层架构(CLI → SDK → Extractor → HTTP),消除重复代码
562
+ - **增强了类型安全**:完整的 TypeScript 类型定义,Zod 输入校验
563
+ - **优化了错误处理**:结构化错误模型,7 种错误码,Result 返回类型
138
564
 
139
565
  Python 原版代码保留在 `../WeSpy/` 目录中,未被删除。详见 [MIGRATION.md](./MIGRATION.md)。
140
566
 
141
- ## License
567
+ ## 常见问题
568
+
569
+ ### Q: 为什么有些图片无法显示?
570
+
571
+ A: WeSpy-TS 使用 images.weserv.nl 作为代理服务来解决图片防盗链问题。如果仍然无法显示,可能是原图片已被删除或网络问题。
572
+
573
+ ### Q: 支持哪些网站?
574
+
575
+ A: WeSpy-TS 对微信公众号和掘金有特别优化,对大部分使用标准 HTML 结构的网站都有较好的支持。如果某个网站不支持,欢迎提交 issue。
576
+
577
+ ### Q: 如何在自己的项目中使用?
578
+
579
+ A: 安装为依赖后,通过 `import { fetchArticle } from 'wespy-ts'` 引入即可。所有 API 返回结构化结果,不会抛出异常,详见上方 SDK API 参考。
580
+
581
+ ### Q: 与 Python 版有什么区别?
582
+
583
+ A: TypeScript 版提供了完整的类型定义、结构化错误处理、Zod 输入校验,并且既可以作为 CLI 工具也可以作为 SDK 使用。核心功能与 Python 版一致。详见 [MIGRATION.md](./MIGRATION.md)。
584
+
585
+ ## 贡献
586
+
587
+ 欢迎提交 issue 和 pull request!
588
+
589
+ 1. Fork 本仓库
590
+ 2. 创建特性分支 (`git checkout -b feature/AmazingFeature`)
591
+ 3. 提交更改 (`git commit -m 'Add some AmazingFeature'`)
592
+ 4. 推送到分支 (`git push origin feature/AmazingFeature`)
593
+ 5. 开启 Pull Request
594
+
595
+ ## 许可证
596
+
597
+ 本项目使用 MIT 许可证。详见 [LICENSE](LICENSE) 文件。
598
+
599
+ ## 更新日志
600
+
601
+ ### v1.0.0 (2026-06-09)
602
+
603
+ - **🎉 TypeScript 重构发布**:由 Python 版 WeSpy 完整重构为 TypeScript
604
+ - **📦 npm 发布**:支持 `npm install -g wespy-ts` 全局安装
605
+ - **🔧 双模使用**:支持 CLI 命令行和 SDK API 两种使用方式
606
+ - **🎵 专辑功能完整迁移**:支持微信公众号专辑批量下载、仅列表获取
607
+ - **📝 多格式输出**:支持 Markdown、HTML、JSON 三种输出格式
608
+ - **🔒 类型安全**:完整的 TypeScript 类型定义,Zod 输入校验
609
+ - **🏗️ 分层架构**:CLI → SDK → Platform Extractor → HTTP Client 清晰分层
610
+ - **⚠️ 结构化错误**:7 种错误码,Result 返回类型,不依赖异常
611
+ - **🌐 多平台支持**:微信公众号、掘金、通用网页
612
+ - **🧪 测试覆盖**:提供完整的单元测试和 fixture
613
+
614
+ ## 联系方式
615
+
616
+ - GitHub: [https://github.com/Cuimc/WeSpy-TS](https://github.com/Cuimc/WeSpy-TS)
617
+ - Issues: [https://github.com/Cuimc/WeSpy-TS/issues](https://github.com/Cuimc/WeSpy-TS/issues)
618
+ - npm: [https://www.npmjs.com/package/wespy-ts](https://www.npmjs.com/package/wespy-ts)
619
+
620
+ ## 免责声明
621
+
622
+ 本项目仅供学习和研究目的使用。使用本工具时,请务必遵守以下原则:
623
+
624
+ ### 使用责任
625
+
626
+ - 用户需自行承担使用本工具的所有风险和责任
627
+ - 请确保您的使用行为符合目标网站的 robots.txt 文件要求
628
+ - 尊重内容创作者的知识产权,不得用于商业目的
629
+ - 不要对网站服务器造成过大的访问压力
630
+
631
+ ### 法律合规
632
+
633
+ - 请遵守当地法律法规以及目标网站的使用条款
634
+ - 不得将本工具用于任何非法或未经授权的活动
635
+ - 下载的内容应仅用于个人学习、研究或存档目的
636
+
637
+ ### 技术风险
638
+
639
+ - 网站结构可能随时变化,本工具可能无法正常工作
640
+ - 本工具按"原样"提供,不提供任何明示或暗示的保证
641
+ - 开发者不对因使用本工具造成的任何损失承担责任
642
+
643
+ ### 数据安全
644
+
645
+ - 本工具不会收集或上传您的任何个人信息
646
+ - 所有数据处理都在本地完成
647
+ - 请妥善保管您下载的内容
648
+
649
+ ---
142
650
 
143
- MIT
651
+ **重要提醒**:请合理、合法、负责任地使用本工具,尊重网络服务提供者和内容创作者的权益。
package/package.json CHANGED
@@ -1,64 +1,64 @@
1
1
  {
2
- "name": "wespy-ts",
3
- "version": "1.0.0",
4
- "description": "WeSpy - 文章抓取与 Markdown 转换工具 (TypeScript 版)",
5
- "type": "module",
6
- "main": "dist/index.js",
7
- "types": "dist/index.d.ts",
8
- "exports": {
9
- ".": {
10
- "types": "./dist/index.d.ts",
11
- "import": "./dist/index.js"
2
+ "name": "wespy-ts",
3
+ "version": "1.1.0",
4
+ "description": "WeSpy - 文章抓取与 Markdown 转换工具 (TypeScript 版)",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.js"
12
+ }
13
+ },
14
+ "files": [
15
+ "dist"
16
+ ],
17
+ "bin": {
18
+ "wespy": "dist/cli/main.js"
19
+ },
20
+ "scripts": {
21
+ "build": "tsc",
22
+ "dev": "tsc --watch",
23
+ "test": "vitest run",
24
+ "test:watch": "vitest",
25
+ "lint": "tsc --noEmit",
26
+ "clean": "rm -rf dist",
27
+ "prepublishOnly": "npm run build"
28
+ },
29
+ "keywords": [
30
+ "wechat",
31
+ "article",
32
+ "scraper",
33
+ "markdown",
34
+ "juejin",
35
+ "fetch",
36
+ "crawl"
37
+ ],
38
+ "license": "MIT",
39
+ "repository": {
40
+ "type": "git",
41
+ "url": "git+https://github.com/Cuimc/WeSpy-TS.git"
42
+ },
43
+ "bugs": {
44
+ "url": "https://github.com/Cuimc/WeSpy-TS/issues"
45
+ },
46
+ "homepage": "https://github.com/Cuimc/WeSpy-TS#readme",
47
+ "sideEffects": false,
48
+ "engines": {
49
+ "node": ">=18"
50
+ },
51
+ "dependencies": {
52
+ "cheerio": "^1.0.0",
53
+ "commander": "^12.0.0",
54
+ "turndown": "^7.1.0",
55
+ "undici": "^6.0.0",
56
+ "zod": "^3.22.0"
57
+ },
58
+ "devDependencies": {
59
+ "@types/node": "^20.0.0",
60
+ "@types/turndown": "^5.0.1",
61
+ "typescript": "^5.4.0",
62
+ "vitest": "^1.0.0"
12
63
  }
13
- },
14
- "files": [
15
- "dist"
16
- ],
17
- "bin": {
18
- "wespy": "dist/cli/main.js"
19
- },
20
- "scripts": {
21
- "build": "tsc",
22
- "dev": "tsc --watch",
23
- "test": "vitest run",
24
- "test:watch": "vitest",
25
- "lint": "tsc --noEmit",
26
- "clean": "rm -rf dist",
27
- "prepublishOnly": "npm run build"
28
- },
29
- "keywords": [
30
- "wechat",
31
- "article",
32
- "scraper",
33
- "markdown",
34
- "juejin",
35
- "fetch",
36
- "crawl"
37
- ],
38
- "license": "MIT",
39
- "repository": {
40
- "type": "git",
41
- "url": "git+https://github.com/Cuimc/WeSpy-TS.git"
42
- },
43
- "bugs": {
44
- "url": "https://github.com/Cuimc/WeSpy-TS/issues"
45
- },
46
- "homepage": "https://github.com/Cuimc/WeSpy-TS#readme",
47
- "sideEffects": false,
48
- "engines": {
49
- "node": ">=18"
50
- },
51
- "dependencies": {
52
- "cheerio": "^1.0.0",
53
- "commander": "^12.0.0",
54
- "turndown": "^7.1.0",
55
- "undici": "^6.0.0",
56
- "zod": "^3.22.0"
57
- },
58
- "devDependencies": {
59
- "@types/node": "^20.0.0",
60
- "@types/turndown": "^5.0.1",
61
- "typescript": "^5.4.0",
62
- "vitest": "^1.0.0"
63
- }
64
- }
64
+ }