tt-help-cli-ycl 1.3.6 → 1.3.7
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 +17 -17
- package/cli.js +9 -9
- package/package.json +45 -45
- package/src/cli/auto.js +131 -121
- package/src/cli/explore.js +147 -138
- package/src/cli/progress.js +111 -111
- package/src/cli/scrape.js +47 -47
- package/src/cli/utils.js +18 -18
- package/src/cli/videos.js +41 -41
- package/src/cli/watch.js +31 -31
- package/src/lib/args.js +391 -391
- package/src/lib/browser/anti-detect.js +23 -23
- package/src/lib/browser/cdp.js +142 -142
- package/src/lib/browser/launch.js +43 -43
- package/src/lib/browser/page.js +87 -87
- package/src/lib/constants.js +109 -95
- package/src/lib/delay.js +54 -54
- package/src/lib/explore-fetch.js +118 -118
- package/src/lib/fetcher.js +45 -45
- package/src/lib/filter.js +66 -66
- package/src/lib/io.js +54 -54
- package/src/lib/mac-or-uuid.js +82 -0
- package/src/lib/output.js +80 -80
- package/src/lib/parser.js +47 -47
- package/src/lib/retry.js +44 -44
- package/src/lib/scrape.js +40 -40
- package/src/lib/url.js +52 -52
- package/src/main.mjs +221 -221
- package/src/scraper/auto-core.mjs +185 -185
- package/src/scraper/core.mjs +190 -190
- package/src/scraper/explore-core.mjs +162 -162
- package/src/scraper/modules/captcha-handler.mjs +114 -114
- package/src/scraper/modules/comment-extractor.mjs +69 -69
- package/src/scraper/modules/follow-extractor.mjs +121 -121
- package/src/scraper/modules/guess-extractor.mjs +51 -51
- package/src/scraper/modules/page-error-detector.mjs +70 -70
- package/src/scraper/modules/page-helpers.mjs +48 -48
- package/src/scraper/modules/scroll-collector.mjs +189 -189
- package/src/test-auto-follow.cjs +109 -0
- package/src/test-extractors.cjs +75 -0
- package/src/test-follow.cjs +41 -0
- package/src/videos/core.mjs +126 -126
- package/src/watch/data-store.mjs +258 -261
- package/src/watch/public/index.html +466 -465
- package/src/watch/server.mjs +291 -281
- package/src/results/user-videos-bar.lar.lar.moeta.json +0 -37
package/src/lib/args.js
CHANGED
|
@@ -1,392 +1,392 @@
|
|
|
1
|
-
import { readFileSync } from 'fs';
|
|
2
|
-
import { server as defaultServer } from './constants.js';
|
|
3
|
-
import { proxy } from './constants.js';
|
|
4
|
-
|
|
5
|
-
const PRESETS = ['fast', 'normal', 'slow', 'stealth'];
|
|
6
|
-
|
|
7
|
-
function parseScrapeArgs(args) {
|
|
8
|
-
let scrapeUrl = null;
|
|
9
|
-
let scrapePreset = null;
|
|
10
|
-
let scrapeMaxVideos = 20;
|
|
11
|
-
let scrapeMaxComments = 999;
|
|
12
|
-
let scrapeMaxGuess = 10;
|
|
13
|
-
let scrapeSwitchDelay = null;
|
|
14
|
-
let scrapeCommentDelay = null;
|
|
15
|
-
let outputFile = null;
|
|
16
|
-
|
|
17
|
-
const positional = [];
|
|
18
|
-
|
|
19
|
-
for (let i = 0; i < args.length; i++) {
|
|
20
|
-
const arg = args[i];
|
|
21
|
-
if (arg === '-o' || arg === '--output') {
|
|
22
|
-
outputFile = args[++i];
|
|
23
|
-
} else if (arg === '--switch-delay') {
|
|
24
|
-
scrapeSwitchDelay = parseInt(args[++i]) || null;
|
|
25
|
-
} else if (arg === '--comment-delay') {
|
|
26
|
-
scrapeCommentDelay = parseInt(args[++i]) || null;
|
|
27
|
-
} else {
|
|
28
|
-
positional.push(arg);
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
scrapeUrl = positional[0] || null;
|
|
33
|
-
|
|
34
|
-
if (positional[1]) {
|
|
35
|
-
if (PRESETS.includes(positional[1].toLowerCase())) {
|
|
36
|
-
scrapePreset = positional[1].toLowerCase();
|
|
37
|
-
scrapeMaxVideos = parseInt(positional[2]) || 20;
|
|
38
|
-
scrapeMaxComments = parseInt(positional[3]) || 999;
|
|
39
|
-
scrapeMaxGuess = parseInt(positional[4]) || 10;
|
|
40
|
-
} else {
|
|
41
|
-
scrapeMaxVideos = parseInt(positional[1]) || 20;
|
|
42
|
-
scrapeMaxComments = parseInt(positional[2]) || 999;
|
|
43
|
-
scrapeMaxGuess = parseInt(positional[3]) || 10;
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
return {
|
|
48
|
-
subcommand: 'scrape',
|
|
49
|
-
scrapeUrl,
|
|
50
|
-
scrapePreset,
|
|
51
|
-
scrapeMaxVideos,
|
|
52
|
-
scrapeMaxComments,
|
|
53
|
-
scrapeMaxGuess,
|
|
54
|
-
scrapeSwitchDelay,
|
|
55
|
-
scrapeCommentDelay,
|
|
56
|
-
outputFile,
|
|
57
|
-
urls: [],
|
|
58
|
-
outputFormat: 'json',
|
|
59
|
-
exploreCount: 0,
|
|
60
|
-
showConfig: false,
|
|
61
|
-
showHelp: false,
|
|
62
|
-
customProxy: null,
|
|
63
|
-
configAction: null,
|
|
64
|
-
configValue: null,
|
|
65
|
-
pipeMode: false,
|
|
66
|
-
filterStr: null,
|
|
67
|
-
};
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
function parseAutoArgs(args) {
|
|
71
|
-
let autoCollectMax = 1;
|
|
72
|
-
let autoScrapeDepth = 50;
|
|
73
|
-
let autoMaxComments = 200;
|
|
74
|
-
let autoMaxGuess = 10;
|
|
75
|
-
let autoPreset = 'fast';
|
|
76
|
-
let autoSwitchDelay = null;
|
|
77
|
-
let autoCommentDelay = null;
|
|
78
|
-
let serverUrl = defaultServer;
|
|
79
|
-
let autoEnableFollow = false;
|
|
80
|
-
let autoMaxFollowing = 200;
|
|
81
|
-
let autoMaxFollowers = 200;
|
|
82
|
-
|
|
83
|
-
const positional = [];
|
|
84
|
-
const PRESETS = ['fast', 'normal', 'slow', 'stealth'];
|
|
85
|
-
|
|
86
|
-
for (let i = 0; i < args.length; i++) {
|
|
87
|
-
const arg = args[i];
|
|
88
|
-
if (arg === '--server') {
|
|
89
|
-
serverUrl = args[++i];
|
|
90
|
-
} else if (arg === '--switch-delay') {
|
|
91
|
-
autoSwitchDelay = parseInt(args[++i]) || null;
|
|
92
|
-
} else if (arg === '--comment-delay') {
|
|
93
|
-
autoCommentDelay = parseInt(args[++i]) || null;
|
|
94
|
-
} else if (arg === '--enable-follow') {
|
|
95
|
-
autoEnableFollow = true;
|
|
96
|
-
} else if (arg === '--max-following') {
|
|
97
|
-
autoMaxFollowing = parseInt(args[++i]) || 200;
|
|
98
|
-
} else if (arg === '--max-followers') {
|
|
99
|
-
autoMaxFollowers = parseInt(args[++i]) || 200;
|
|
100
|
-
} else {
|
|
101
|
-
positional.push(arg);
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
// 收集用户名(非 preset、非数字的都是用户名)
|
|
106
|
-
const usernames = [];
|
|
107
|
-
let j = 0;
|
|
108
|
-
while (j < positional.length && !PRESETS.includes(positional[j]?.toLowerCase()) && isNaN(positional[j])) {
|
|
109
|
-
usernames.push(positional[j].replace('@', ''));
|
|
110
|
-
j++;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
// preset
|
|
114
|
-
if (j < positional.length && PRESETS.includes(positional[j].toLowerCase())) {
|
|
115
|
-
autoPreset = positional[j].toLowerCase();
|
|
116
|
-
j++;
|
|
117
|
-
autoCollectMax = parseInt(positional[j]) || 1; j++;
|
|
118
|
-
autoScrapeDepth = parseInt(positional[j]) || 50; j++;
|
|
119
|
-
autoMaxComments = parseInt(positional[j]) || 200; j++;
|
|
120
|
-
autoMaxGuess = parseInt(positional[j]) || 10; j++;
|
|
121
|
-
} else if (usernames.length > 0) {
|
|
122
|
-
autoCollectMax = parseInt(positional[j]) || 1; j++;
|
|
123
|
-
autoScrapeDepth = parseInt(positional[j]) || 50; j++;
|
|
124
|
-
autoMaxComments = parseInt(positional[j]) || 200; j++;
|
|
125
|
-
autoMaxGuess = parseInt(positional[j]) || 10;
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
return {
|
|
129
|
-
subcommand: 'auto',
|
|
130
|
-
autoUsernames: usernames,
|
|
131
|
-
autoCollectMax,
|
|
132
|
-
autoScrapeDepth,
|
|
133
|
-
autoMaxComments,
|
|
134
|
-
autoMaxGuess,
|
|
135
|
-
autoPreset,
|
|
136
|
-
autoSwitchDelay,
|
|
137
|
-
autoCommentDelay,
|
|
138
|
-
serverUrl,
|
|
139
|
-
autoEnableFollow,
|
|
140
|
-
autoMaxFollowing,
|
|
141
|
-
autoMaxFollowers,
|
|
142
|
-
urls: [],
|
|
143
|
-
outputFormat: 'json',
|
|
144
|
-
exploreCount: 0,
|
|
145
|
-
showConfig: false,
|
|
146
|
-
showHelp: false,
|
|
147
|
-
customProxy: null,
|
|
148
|
-
configAction: null,
|
|
149
|
-
configValue: null,
|
|
150
|
-
pipeMode: false,
|
|
151
|
-
filterStr: null,
|
|
152
|
-
};
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
function parseExploreArgs(args) {
|
|
156
|
-
let serverUrl = defaultServer;
|
|
157
|
-
let explorePreset = 'normal';
|
|
158
|
-
let exploreMaxComments = 10;
|
|
159
|
-
let exploreMaxGuess = 0;
|
|
160
|
-
let exploreEnableFollow = true;
|
|
161
|
-
let exploreMaxFollowing = 5;
|
|
162
|
-
let exploreMaxFollowers = 5;
|
|
163
|
-
let exploreLocation = 'ES';
|
|
164
|
-
let exploreMaxUsers = 0;
|
|
165
|
-
|
|
166
|
-
const positional = [];
|
|
167
|
-
const PRESETS = ['fast', 'normal', 'slow', 'stealth'];
|
|
168
|
-
|
|
169
|
-
for (let i = 0; i < args.length; i++) {
|
|
170
|
-
const arg = args[i];
|
|
171
|
-
if (arg === '--server') {
|
|
172
|
-
serverUrl = args[++i];
|
|
173
|
-
} else if (arg === '--max-comments') {
|
|
174
|
-
exploreMaxComments = parseInt(args[++i]) || 0;
|
|
175
|
-
} else if (arg === '--max-guess') {
|
|
176
|
-
exploreMaxGuess = parseInt(args[++i]) || 0;
|
|
177
|
-
} else if (arg === '--location') {
|
|
178
|
-
exploreLocation = args[++i];
|
|
179
|
-
} else if (arg === '--enable-follow') {
|
|
180
|
-
exploreEnableFollow = true;
|
|
181
|
-
} else if (arg === '--disable-follow') {
|
|
182
|
-
exploreEnableFollow = false;
|
|
183
|
-
} else if (arg === '--max-following') {
|
|
184
|
-
exploreMaxFollowing = parseInt(args[++i]) || 5;
|
|
185
|
-
} else if (arg === '--max-followers') {
|
|
186
|
-
exploreMaxFollowers = parseInt(args[++i]) || 5;
|
|
187
|
-
} else if (arg === '--max-users') {
|
|
188
|
-
exploreMaxUsers = parseInt(args[++i]) || 0;
|
|
189
|
-
} else {
|
|
190
|
-
positional.push(arg);
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
const usernames = [];
|
|
195
|
-
let j = 0;
|
|
196
|
-
while (j < positional.length && !PRESETS.includes(positional[j]?.toLowerCase()) && isNaN(positional[j])) {
|
|
197
|
-
usernames.push(positional[j].replace('@', ''));
|
|
198
|
-
j++;
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
if (j < positional.length && PRESETS.includes(positional[j].toLowerCase())) {
|
|
202
|
-
explorePreset = positional[j].toLowerCase();
|
|
203
|
-
j++;
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
return {
|
|
207
|
-
subcommand: 'explore',
|
|
208
|
-
exploreUsernames: usernames,
|
|
209
|
-
explorePreset,
|
|
210
|
-
exploreMaxComments,
|
|
211
|
-
exploreMaxGuess,
|
|
212
|
-
exploreEnableFollow,
|
|
213
|
-
exploreMaxFollowing,
|
|
214
|
-
exploreMaxFollowers,
|
|
215
|
-
exploreLocation,
|
|
216
|
-
serverUrl,
|
|
217
|
-
exploreMaxUsers,
|
|
218
|
-
urls: [],
|
|
219
|
-
outputFormat: 'json',
|
|
220
|
-
exploreCount: 0,
|
|
221
|
-
showConfig: false,
|
|
222
|
-
showHelp: false,
|
|
223
|
-
customProxy: null,
|
|
224
|
-
configAction: null,
|
|
225
|
-
configValue: null,
|
|
226
|
-
pipeMode: false,
|
|
227
|
-
filterStr: null,
|
|
228
|
-
};
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
function parseVideosArgs(args) {
|
|
232
|
-
let videosUsername = null;
|
|
233
|
-
let videosMax = 5;
|
|
234
|
-
let outputFile = null;
|
|
235
|
-
|
|
236
|
-
const positional = [];
|
|
237
|
-
|
|
238
|
-
for (let i = 0; i < args.length; i++) {
|
|
239
|
-
const arg = args[i];
|
|
240
|
-
if (arg === '-o' || arg === '--output') {
|
|
241
|
-
outputFile = args[++i];
|
|
242
|
-
} else {
|
|
243
|
-
positional.push(arg);
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
videosUsername = positional[0] ? positional[0].replace('@', '') : null;
|
|
248
|
-
videosMax = parseInt(positional[1]) || 5;
|
|
249
|
-
|
|
250
|
-
return {
|
|
251
|
-
subcommand: 'videos',
|
|
252
|
-
videosUsername,
|
|
253
|
-
videosMax,
|
|
254
|
-
outputFile,
|
|
255
|
-
urls: [],
|
|
256
|
-
outputFormat: 'json',
|
|
257
|
-
exploreCount: 0,
|
|
258
|
-
showConfig: false,
|
|
259
|
-
showHelp: false,
|
|
260
|
-
customProxy: null,
|
|
261
|
-
configAction: null,
|
|
262
|
-
configValue: null,
|
|
263
|
-
pipeMode: false,
|
|
264
|
-
filterStr: null,
|
|
265
|
-
};
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
function parseWatchArgs(args) {
|
|
269
|
-
let outputFile = './result.json';
|
|
270
|
-
let watchPort = 3001;
|
|
271
|
-
|
|
272
|
-
for (let i = 0; i < args.length; i++) {
|
|
273
|
-
const arg = args[i];
|
|
274
|
-
if (arg === '-o' || arg === '--output') {
|
|
275
|
-
outputFile = args[++i];
|
|
276
|
-
} else if (arg === '-p') {
|
|
277
|
-
watchPort = parseInt(args[++i]) || 3001;
|
|
278
|
-
}
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
return {
|
|
282
|
-
subcommand: 'watch',
|
|
283
|
-
outputFile,
|
|
284
|
-
watchPort,
|
|
285
|
-
urls: [],
|
|
286
|
-
outputFormat: 'json',
|
|
287
|
-
exploreCount: 0,
|
|
288
|
-
showConfig: false,
|
|
289
|
-
showHelp: false,
|
|
290
|
-
customProxy: null,
|
|
291
|
-
configAction: null,
|
|
292
|
-
configValue: null,
|
|
293
|
-
pipeMode: false,
|
|
294
|
-
filterStr: null,
|
|
295
|
-
};
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
export function parseArgs() {
|
|
299
|
-
const args = process.argv.slice(2);
|
|
300
|
-
|
|
301
|
-
// Global flags take precedence over subcommands
|
|
302
|
-
if (args.includes('-h') || args.includes('--help')) {
|
|
303
|
-
return { showHelp: true, showVersion: false, subcommand: null, urls: [], outputFile: null, outputFormat: 'json', exploreCount: 0, showConfig: false, customProxy: null, configAction: null, configValue: null, pipeMode: false, filterStr: null };
|
|
304
|
-
}
|
|
305
|
-
if (args.includes('--version')) {
|
|
306
|
-
return { showHelp: false, showVersion: true, subcommand: null, urls: [], outputFile: null, outputFormat: 'json', exploreCount: 0, showConfig: false, customProxy: null, configAction: null, configValue: null, pipeMode: false, filterStr: null };
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
if (args.length > 0 && args[0] === 'scrape') {
|
|
310
|
-
return parseScrapeArgs(args.slice(1));
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
if (args.length > 0 && args[0] === 'videos') {
|
|
314
|
-
return parseVideosArgs(args.slice(1));
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
if (args.length > 0 && args[0] === 'auto') {
|
|
318
|
-
return parseAutoArgs(args.slice(1));
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
if (args.length > 0 && args[0] === 'explore') {
|
|
322
|
-
return parseExploreArgs(args.slice(1));
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
if (args.length > 0 && args[0] === 'watch') {
|
|
326
|
-
return parseWatchArgs(args.slice(1));
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
const urls = [];
|
|
330
|
-
let inputFile = null;
|
|
331
|
-
let outputFile = null;
|
|
332
|
-
let outputFormat = 'json';
|
|
333
|
-
let exploreCount = 0;
|
|
334
|
-
let showConfig = false;
|
|
335
|
-
let showHelp = false;
|
|
336
|
-
let showVersion = false;
|
|
337
|
-
let customProxy = null;
|
|
338
|
-
let configAction = null;
|
|
339
|
-
let configKey = null;
|
|
340
|
-
let configValue = null;
|
|
341
|
-
let pipeMode = false;
|
|
342
|
-
let filterStr = null;
|
|
343
|
-
|
|
344
|
-
for (let i = 0; i < args.length; i++) {
|
|
345
|
-
const arg = args[i];
|
|
346
|
-
|
|
347
|
-
if (arg === '--explore') {
|
|
348
|
-
exploreCount = args[i + 1] && !args[i + 1].startsWith('http')
|
|
349
|
-
? parseInt(args[++i], 10)
|
|
350
|
-
: 100;
|
|
351
|
-
} else if (arg === '--proxy') {
|
|
352
|
-
customProxy = args[++i];
|
|
353
|
-
} else if (arg === '--filter') {
|
|
354
|
-
filterStr = args[++i];
|
|
355
|
-
} else if (arg === 'config') {
|
|
356
|
-
configAction = args[i + 1];
|
|
357
|
-
if (!configAction) {
|
|
358
|
-
configAction = 'show';
|
|
359
|
-
} else if (configAction === 'set' || configAction === 'set-proxy' || configAction === 'set-browser') {
|
|
360
|
-
configKey = args[i + 2];
|
|
361
|
-
configValue = args[i + 3];
|
|
362
|
-
i += 3;
|
|
363
|
-
} else {
|
|
364
|
-
i++;
|
|
365
|
-
}
|
|
366
|
-
} else if (arg === '--pipe') {
|
|
367
|
-
pipeMode = true;
|
|
368
|
-
} else if (arg === '-i' || arg === '--input') {
|
|
369
|
-
inputFile = args[++i];
|
|
370
|
-
} else if (arg === '-o' || arg === '--output') {
|
|
371
|
-
outputFile = args[++i];
|
|
372
|
-
} else if (arg === '-f' || arg === '--format') {
|
|
373
|
-
outputFormat = args[++i];
|
|
374
|
-
} else if (arg === '-c' || arg === '--config') {
|
|
375
|
-
showConfig = true;
|
|
376
|
-
} else if (arg === '-h' || arg === '--help') {
|
|
377
|
-
showHelp = true;
|
|
378
|
-
} else if (arg === '--version') {
|
|
379
|
-
showVersion = true;
|
|
380
|
-
} else if (arg.startsWith('http')) {
|
|
381
|
-
urls.push(arg);
|
|
382
|
-
}
|
|
383
|
-
}
|
|
384
|
-
|
|
385
|
-
if (inputFile) {
|
|
386
|
-
const content = readFileSync(inputFile, 'utf-8');
|
|
387
|
-
const lines = content.split(/\r?\n/).map(l => l.trim()).filter(l => l.startsWith('http'));
|
|
388
|
-
urls.push(...lines);
|
|
389
|
-
}
|
|
390
|
-
|
|
391
|
-
return { urls, outputFile, outputFormat, exploreCount, showConfig, showHelp, showVersion, customProxy, configAction, configKey, configValue, pipeMode, filterStr };
|
|
1
|
+
import { readFileSync } from 'fs';
|
|
2
|
+
import { server as defaultServer } from './constants.js';
|
|
3
|
+
import { proxy } from './constants.js';
|
|
4
|
+
|
|
5
|
+
const PRESETS = ['fast', 'normal', 'slow', 'stealth'];
|
|
6
|
+
|
|
7
|
+
function parseScrapeArgs(args) {
|
|
8
|
+
let scrapeUrl = null;
|
|
9
|
+
let scrapePreset = null;
|
|
10
|
+
let scrapeMaxVideos = 20;
|
|
11
|
+
let scrapeMaxComments = 999;
|
|
12
|
+
let scrapeMaxGuess = 10;
|
|
13
|
+
let scrapeSwitchDelay = null;
|
|
14
|
+
let scrapeCommentDelay = null;
|
|
15
|
+
let outputFile = null;
|
|
16
|
+
|
|
17
|
+
const positional = [];
|
|
18
|
+
|
|
19
|
+
for (let i = 0; i < args.length; i++) {
|
|
20
|
+
const arg = args[i];
|
|
21
|
+
if (arg === '-o' || arg === '--output') {
|
|
22
|
+
outputFile = args[++i];
|
|
23
|
+
} else if (arg === '--switch-delay') {
|
|
24
|
+
scrapeSwitchDelay = parseInt(args[++i]) || null;
|
|
25
|
+
} else if (arg === '--comment-delay') {
|
|
26
|
+
scrapeCommentDelay = parseInt(args[++i]) || null;
|
|
27
|
+
} else {
|
|
28
|
+
positional.push(arg);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
scrapeUrl = positional[0] || null;
|
|
33
|
+
|
|
34
|
+
if (positional[1]) {
|
|
35
|
+
if (PRESETS.includes(positional[1].toLowerCase())) {
|
|
36
|
+
scrapePreset = positional[1].toLowerCase();
|
|
37
|
+
scrapeMaxVideos = parseInt(positional[2]) || 20;
|
|
38
|
+
scrapeMaxComments = parseInt(positional[3]) || 999;
|
|
39
|
+
scrapeMaxGuess = parseInt(positional[4]) || 10;
|
|
40
|
+
} else {
|
|
41
|
+
scrapeMaxVideos = parseInt(positional[1]) || 20;
|
|
42
|
+
scrapeMaxComments = parseInt(positional[2]) || 999;
|
|
43
|
+
scrapeMaxGuess = parseInt(positional[3]) || 10;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return {
|
|
48
|
+
subcommand: 'scrape',
|
|
49
|
+
scrapeUrl,
|
|
50
|
+
scrapePreset,
|
|
51
|
+
scrapeMaxVideos,
|
|
52
|
+
scrapeMaxComments,
|
|
53
|
+
scrapeMaxGuess,
|
|
54
|
+
scrapeSwitchDelay,
|
|
55
|
+
scrapeCommentDelay,
|
|
56
|
+
outputFile,
|
|
57
|
+
urls: [],
|
|
58
|
+
outputFormat: 'json',
|
|
59
|
+
exploreCount: 0,
|
|
60
|
+
showConfig: false,
|
|
61
|
+
showHelp: false,
|
|
62
|
+
customProxy: null,
|
|
63
|
+
configAction: null,
|
|
64
|
+
configValue: null,
|
|
65
|
+
pipeMode: false,
|
|
66
|
+
filterStr: null,
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function parseAutoArgs(args) {
|
|
71
|
+
let autoCollectMax = 1;
|
|
72
|
+
let autoScrapeDepth = 50;
|
|
73
|
+
let autoMaxComments = 200;
|
|
74
|
+
let autoMaxGuess = 10;
|
|
75
|
+
let autoPreset = 'fast';
|
|
76
|
+
let autoSwitchDelay = null;
|
|
77
|
+
let autoCommentDelay = null;
|
|
78
|
+
let serverUrl = defaultServer;
|
|
79
|
+
let autoEnableFollow = false;
|
|
80
|
+
let autoMaxFollowing = 200;
|
|
81
|
+
let autoMaxFollowers = 200;
|
|
82
|
+
|
|
83
|
+
const positional = [];
|
|
84
|
+
const PRESETS = ['fast', 'normal', 'slow', 'stealth'];
|
|
85
|
+
|
|
86
|
+
for (let i = 0; i < args.length; i++) {
|
|
87
|
+
const arg = args[i];
|
|
88
|
+
if (arg === '--server') {
|
|
89
|
+
serverUrl = args[++i];
|
|
90
|
+
} else if (arg === '--switch-delay') {
|
|
91
|
+
autoSwitchDelay = parseInt(args[++i]) || null;
|
|
92
|
+
} else if (arg === '--comment-delay') {
|
|
93
|
+
autoCommentDelay = parseInt(args[++i]) || null;
|
|
94
|
+
} else if (arg === '--enable-follow') {
|
|
95
|
+
autoEnableFollow = true;
|
|
96
|
+
} else if (arg === '--max-following') {
|
|
97
|
+
autoMaxFollowing = parseInt(args[++i]) || 200;
|
|
98
|
+
} else if (arg === '--max-followers') {
|
|
99
|
+
autoMaxFollowers = parseInt(args[++i]) || 200;
|
|
100
|
+
} else {
|
|
101
|
+
positional.push(arg);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// 收集用户名(非 preset、非数字的都是用户名)
|
|
106
|
+
const usernames = [];
|
|
107
|
+
let j = 0;
|
|
108
|
+
while (j < positional.length && !PRESETS.includes(positional[j]?.toLowerCase()) && isNaN(positional[j])) {
|
|
109
|
+
usernames.push(positional[j].replace('@', ''));
|
|
110
|
+
j++;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// preset
|
|
114
|
+
if (j < positional.length && PRESETS.includes(positional[j].toLowerCase())) {
|
|
115
|
+
autoPreset = positional[j].toLowerCase();
|
|
116
|
+
j++;
|
|
117
|
+
autoCollectMax = parseInt(positional[j]) || 1; j++;
|
|
118
|
+
autoScrapeDepth = parseInt(positional[j]) || 50; j++;
|
|
119
|
+
autoMaxComments = parseInt(positional[j]) || 200; j++;
|
|
120
|
+
autoMaxGuess = parseInt(positional[j]) || 10; j++;
|
|
121
|
+
} else if (usernames.length > 0) {
|
|
122
|
+
autoCollectMax = parseInt(positional[j]) || 1; j++;
|
|
123
|
+
autoScrapeDepth = parseInt(positional[j]) || 50; j++;
|
|
124
|
+
autoMaxComments = parseInt(positional[j]) || 200; j++;
|
|
125
|
+
autoMaxGuess = parseInt(positional[j]) || 10;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
return {
|
|
129
|
+
subcommand: 'auto',
|
|
130
|
+
autoUsernames: usernames,
|
|
131
|
+
autoCollectMax,
|
|
132
|
+
autoScrapeDepth,
|
|
133
|
+
autoMaxComments,
|
|
134
|
+
autoMaxGuess,
|
|
135
|
+
autoPreset,
|
|
136
|
+
autoSwitchDelay,
|
|
137
|
+
autoCommentDelay,
|
|
138
|
+
serverUrl,
|
|
139
|
+
autoEnableFollow,
|
|
140
|
+
autoMaxFollowing,
|
|
141
|
+
autoMaxFollowers,
|
|
142
|
+
urls: [],
|
|
143
|
+
outputFormat: 'json',
|
|
144
|
+
exploreCount: 0,
|
|
145
|
+
showConfig: false,
|
|
146
|
+
showHelp: false,
|
|
147
|
+
customProxy: null,
|
|
148
|
+
configAction: null,
|
|
149
|
+
configValue: null,
|
|
150
|
+
pipeMode: false,
|
|
151
|
+
filterStr: null,
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function parseExploreArgs(args) {
|
|
156
|
+
let serverUrl = defaultServer;
|
|
157
|
+
let explorePreset = 'normal';
|
|
158
|
+
let exploreMaxComments = 10;
|
|
159
|
+
let exploreMaxGuess = 0;
|
|
160
|
+
let exploreEnableFollow = true;
|
|
161
|
+
let exploreMaxFollowing = 5;
|
|
162
|
+
let exploreMaxFollowers = 5;
|
|
163
|
+
let exploreLocation = 'ES';
|
|
164
|
+
let exploreMaxUsers = 0;
|
|
165
|
+
|
|
166
|
+
const positional = [];
|
|
167
|
+
const PRESETS = ['fast', 'normal', 'slow', 'stealth'];
|
|
168
|
+
|
|
169
|
+
for (let i = 0; i < args.length; i++) {
|
|
170
|
+
const arg = args[i];
|
|
171
|
+
if (arg === '--server') {
|
|
172
|
+
serverUrl = args[++i];
|
|
173
|
+
} else if (arg === '--max-comments') {
|
|
174
|
+
exploreMaxComments = parseInt(args[++i]) || 0;
|
|
175
|
+
} else if (arg === '--max-guess') {
|
|
176
|
+
exploreMaxGuess = parseInt(args[++i]) || 0;
|
|
177
|
+
} else if (arg === '--location') {
|
|
178
|
+
exploreLocation = args[++i];
|
|
179
|
+
} else if (arg === '--enable-follow') {
|
|
180
|
+
exploreEnableFollow = true;
|
|
181
|
+
} else if (arg === '--disable-follow') {
|
|
182
|
+
exploreEnableFollow = false;
|
|
183
|
+
} else if (arg === '--max-following') {
|
|
184
|
+
exploreMaxFollowing = parseInt(args[++i]) || 5;
|
|
185
|
+
} else if (arg === '--max-followers') {
|
|
186
|
+
exploreMaxFollowers = parseInt(args[++i]) || 5;
|
|
187
|
+
} else if (arg === '--max-users') {
|
|
188
|
+
exploreMaxUsers = parseInt(args[++i]) || 0;
|
|
189
|
+
} else {
|
|
190
|
+
positional.push(arg);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
const usernames = [];
|
|
195
|
+
let j = 0;
|
|
196
|
+
while (j < positional.length && !PRESETS.includes(positional[j]?.toLowerCase()) && isNaN(positional[j])) {
|
|
197
|
+
usernames.push(positional[j].replace('@', ''));
|
|
198
|
+
j++;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
if (j < positional.length && PRESETS.includes(positional[j].toLowerCase())) {
|
|
202
|
+
explorePreset = positional[j].toLowerCase();
|
|
203
|
+
j++;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
return {
|
|
207
|
+
subcommand: 'explore',
|
|
208
|
+
exploreUsernames: usernames,
|
|
209
|
+
explorePreset,
|
|
210
|
+
exploreMaxComments,
|
|
211
|
+
exploreMaxGuess,
|
|
212
|
+
exploreEnableFollow,
|
|
213
|
+
exploreMaxFollowing,
|
|
214
|
+
exploreMaxFollowers,
|
|
215
|
+
exploreLocation,
|
|
216
|
+
serverUrl,
|
|
217
|
+
exploreMaxUsers,
|
|
218
|
+
urls: [],
|
|
219
|
+
outputFormat: 'json',
|
|
220
|
+
exploreCount: 0,
|
|
221
|
+
showConfig: false,
|
|
222
|
+
showHelp: false,
|
|
223
|
+
customProxy: null,
|
|
224
|
+
configAction: null,
|
|
225
|
+
configValue: null,
|
|
226
|
+
pipeMode: false,
|
|
227
|
+
filterStr: null,
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
function parseVideosArgs(args) {
|
|
232
|
+
let videosUsername = null;
|
|
233
|
+
let videosMax = 5;
|
|
234
|
+
let outputFile = null;
|
|
235
|
+
|
|
236
|
+
const positional = [];
|
|
237
|
+
|
|
238
|
+
for (let i = 0; i < args.length; i++) {
|
|
239
|
+
const arg = args[i];
|
|
240
|
+
if (arg === '-o' || arg === '--output') {
|
|
241
|
+
outputFile = args[++i];
|
|
242
|
+
} else {
|
|
243
|
+
positional.push(arg);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
videosUsername = positional[0] ? positional[0].replace('@', '') : null;
|
|
248
|
+
videosMax = parseInt(positional[1]) || 5;
|
|
249
|
+
|
|
250
|
+
return {
|
|
251
|
+
subcommand: 'videos',
|
|
252
|
+
videosUsername,
|
|
253
|
+
videosMax,
|
|
254
|
+
outputFile,
|
|
255
|
+
urls: [],
|
|
256
|
+
outputFormat: 'json',
|
|
257
|
+
exploreCount: 0,
|
|
258
|
+
showConfig: false,
|
|
259
|
+
showHelp: false,
|
|
260
|
+
customProxy: null,
|
|
261
|
+
configAction: null,
|
|
262
|
+
configValue: null,
|
|
263
|
+
pipeMode: false,
|
|
264
|
+
filterStr: null,
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
function parseWatchArgs(args) {
|
|
269
|
+
let outputFile = './result.json';
|
|
270
|
+
let watchPort = 3001;
|
|
271
|
+
|
|
272
|
+
for (let i = 0; i < args.length; i++) {
|
|
273
|
+
const arg = args[i];
|
|
274
|
+
if (arg === '-o' || arg === '--output') {
|
|
275
|
+
outputFile = args[++i];
|
|
276
|
+
} else if (arg === '-p') {
|
|
277
|
+
watchPort = parseInt(args[++i]) || 3001;
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
return {
|
|
282
|
+
subcommand: 'watch',
|
|
283
|
+
outputFile,
|
|
284
|
+
watchPort,
|
|
285
|
+
urls: [],
|
|
286
|
+
outputFormat: 'json',
|
|
287
|
+
exploreCount: 0,
|
|
288
|
+
showConfig: false,
|
|
289
|
+
showHelp: false,
|
|
290
|
+
customProxy: null,
|
|
291
|
+
configAction: null,
|
|
292
|
+
configValue: null,
|
|
293
|
+
pipeMode: false,
|
|
294
|
+
filterStr: null,
|
|
295
|
+
};
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
export function parseArgs() {
|
|
299
|
+
const args = process.argv.slice(2);
|
|
300
|
+
|
|
301
|
+
// Global flags take precedence over subcommands
|
|
302
|
+
if (args.includes('-h') || args.includes('--help')) {
|
|
303
|
+
return { showHelp: true, showVersion: false, subcommand: null, urls: [], outputFile: null, outputFormat: 'json', exploreCount: 0, showConfig: false, customProxy: null, configAction: null, configValue: null, pipeMode: false, filterStr: null };
|
|
304
|
+
}
|
|
305
|
+
if (args.includes('--version')) {
|
|
306
|
+
return { showHelp: false, showVersion: true, subcommand: null, urls: [], outputFile: null, outputFormat: 'json', exploreCount: 0, showConfig: false, customProxy: null, configAction: null, configValue: null, pipeMode: false, filterStr: null };
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
if (args.length > 0 && args[0] === 'scrape') {
|
|
310
|
+
return parseScrapeArgs(args.slice(1));
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
if (args.length > 0 && args[0] === 'videos') {
|
|
314
|
+
return parseVideosArgs(args.slice(1));
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
if (args.length > 0 && args[0] === 'auto') {
|
|
318
|
+
return parseAutoArgs(args.slice(1));
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
if (args.length > 0 && args[0] === 'explore') {
|
|
322
|
+
return parseExploreArgs(args.slice(1));
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
if (args.length > 0 && args[0] === 'watch') {
|
|
326
|
+
return parseWatchArgs(args.slice(1));
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
const urls = [];
|
|
330
|
+
let inputFile = null;
|
|
331
|
+
let outputFile = null;
|
|
332
|
+
let outputFormat = 'json';
|
|
333
|
+
let exploreCount = 0;
|
|
334
|
+
let showConfig = false;
|
|
335
|
+
let showHelp = false;
|
|
336
|
+
let showVersion = false;
|
|
337
|
+
let customProxy = null;
|
|
338
|
+
let configAction = null;
|
|
339
|
+
let configKey = null;
|
|
340
|
+
let configValue = null;
|
|
341
|
+
let pipeMode = false;
|
|
342
|
+
let filterStr = null;
|
|
343
|
+
|
|
344
|
+
for (let i = 0; i < args.length; i++) {
|
|
345
|
+
const arg = args[i];
|
|
346
|
+
|
|
347
|
+
if (arg === '--explore') {
|
|
348
|
+
exploreCount = args[i + 1] && !args[i + 1].startsWith('http')
|
|
349
|
+
? parseInt(args[++i], 10)
|
|
350
|
+
: 100;
|
|
351
|
+
} else if (arg === '--proxy') {
|
|
352
|
+
customProxy = args[++i];
|
|
353
|
+
} else if (arg === '--filter') {
|
|
354
|
+
filterStr = args[++i];
|
|
355
|
+
} else if (arg === 'config') {
|
|
356
|
+
configAction = args[i + 1];
|
|
357
|
+
if (!configAction) {
|
|
358
|
+
configAction = 'show';
|
|
359
|
+
} else if (configAction === 'set' || configAction === 'set-proxy' || configAction === 'set-browser') {
|
|
360
|
+
configKey = args[i + 2];
|
|
361
|
+
configValue = args[i + 3];
|
|
362
|
+
i += 3;
|
|
363
|
+
} else {
|
|
364
|
+
i++;
|
|
365
|
+
}
|
|
366
|
+
} else if (arg === '--pipe') {
|
|
367
|
+
pipeMode = true;
|
|
368
|
+
} else if (arg === '-i' || arg === '--input') {
|
|
369
|
+
inputFile = args[++i];
|
|
370
|
+
} else if (arg === '-o' || arg === '--output') {
|
|
371
|
+
outputFile = args[++i];
|
|
372
|
+
} else if (arg === '-f' || arg === '--format') {
|
|
373
|
+
outputFormat = args[++i];
|
|
374
|
+
} else if (arg === '-c' || arg === '--config') {
|
|
375
|
+
showConfig = true;
|
|
376
|
+
} else if (arg === '-h' || arg === '--help') {
|
|
377
|
+
showHelp = true;
|
|
378
|
+
} else if (arg === '--version') {
|
|
379
|
+
showVersion = true;
|
|
380
|
+
} else if (arg.startsWith('http')) {
|
|
381
|
+
urls.push(arg);
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
if (inputFile) {
|
|
386
|
+
const content = readFileSync(inputFile, 'utf-8');
|
|
387
|
+
const lines = content.split(/\r?\n/).map(l => l.trim()).filter(l => l.startsWith('http'));
|
|
388
|
+
urls.push(...lines);
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
return { urls, outputFile, outputFormat, exploreCount, showConfig, showHelp, showVersion, customProxy, configAction, configKey, configValue, pipeMode, filterStr };
|
|
392
392
|
}
|