tt-help-cli-ycl 1.4.48 → 1.4.49
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/package.json +1 -1
- package/src/lib/shop-scraper.mjs +41 -83
package/package.json
CHANGED
package/src/lib/shop-scraper.mjs
CHANGED
|
@@ -83,100 +83,58 @@ function extractJsonByKey(html, keyPattern) {
|
|
|
83
83
|
|
|
84
84
|
/**
|
|
85
85
|
* Windows: 后台服务的 Playwright 浏览器在 Session 0(隔离桌面),
|
|
86
|
-
* RDP
|
|
86
|
+
* RDP 看不到。改为用 start 命令推到交互桌面,然后轮询 undici 等 IP 恢复信用。
|
|
87
87
|
*/
|
|
88
|
-
let winRecoveryRunning = false;
|
|
89
|
-
|
|
90
88
|
async function resolveCaptchaWindows(url, proxyUrl) {
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
89
|
+
// 用 Windows start 命令在交互桌面打开 Edge
|
|
90
|
+
const edgeUrl = url.replace("shop.tiktok.com", "www.tiktok.com/shop/pdp");
|
|
91
|
+
try {
|
|
92
|
+
execSync(`start "" "microsoft-edge:${edgeUrl}"`, {
|
|
93
|
+
shell: "cmd.exe",
|
|
94
|
+
timeout: 5000,
|
|
95
|
+
});
|
|
96
|
+
console.error(
|
|
97
|
+
"[shop-scraper] Windows: 已用 start 命令在交互桌面打开 Edge,请在 RDP 中完成验证码",
|
|
98
|
+
);
|
|
99
|
+
} catch (e) {
|
|
100
|
+
console.error("[shop-scraper] Windows: start 命令失败:", e.message);
|
|
94
101
|
}
|
|
95
|
-
winRecoveryRunning = true;
|
|
96
102
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
103
|
+
// 轮询 undici 等待 IP 信用恢复(用户解完验证码后代理 IP 就有信用了)
|
|
104
|
+
const proxy = proxyUrl || DEFAULT_PROXY;
|
|
105
|
+
const agent = proxy ? new ProxyAgent(proxy) : null;
|
|
106
|
+
const POLL_MS = 5000;
|
|
107
|
+
const MAX_WAIT_MS = 3600_000; // 最长等 1 小时
|
|
108
|
+
const start = Date.now();
|
|
100
109
|
|
|
101
|
-
|
|
102
|
-
|
|
110
|
+
while (Date.now() - start < MAX_WAIT_MS) {
|
|
111
|
+
await new Promise((r) => setTimeout(r, POLL_MS));
|
|
103
112
|
try {
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
shell: "cmd.exe",
|
|
108
|
-
});
|
|
109
|
-
} catch {}
|
|
110
|
-
|
|
111
|
-
// 获取当天日期,格式 yyyy/MM/dd
|
|
112
|
-
const today = execSync(
|
|
113
|
-
`powershell -NoProfile -Command "Get-Date -Format yyyy/MM/dd"`,
|
|
114
|
-
{ shell: "cmd.exe", timeout: 3000, encoding: "utf-8" },
|
|
115
|
-
).trim();
|
|
116
|
-
|
|
117
|
-
execSync(
|
|
118
|
-
`schtasks /create /tn "${taskName}" /tr "msedge \\"${edgeUrl}\\"" /sc once /sd ${today} /st 23:59 /it /f`,
|
|
119
|
-
{ shell: "cmd.exe", timeout: 10000 },
|
|
120
|
-
);
|
|
121
|
-
execSync(`schtasks /run /tn "${taskName}"`, {
|
|
122
|
-
shell: "cmd.exe",
|
|
123
|
-
timeout: 10000,
|
|
113
|
+
const { body } = await request(url, {
|
|
114
|
+
headers: PDP_HEADERS,
|
|
115
|
+
...(agent ? { dispatcher: agent } : {}),
|
|
124
116
|
});
|
|
125
|
-
|
|
117
|
+
const html = await body.text();
|
|
118
|
+
if (
|
|
119
|
+
html.includes('"product_model"') ||
|
|
120
|
+
html.includes('"review_ratings"')
|
|
121
|
+
) {
|
|
122
|
+
console.error("[shop-scraper] Captcha solved, IP credit restored");
|
|
123
|
+
return true;
|
|
124
|
+
}
|
|
125
|
+
} catch {
|
|
126
|
+
// 继续轮询
|
|
127
|
+
}
|
|
128
|
+
// 每 60 秒打印一次状态
|
|
129
|
+
if (Math.round((Date.now() - start) / 60000) % 5 === 0) {
|
|
126
130
|
console.error(
|
|
127
|
-
|
|
131
|
+
`[shop-scraper] 等待验证码解决中... 已等待 ${Math.round((Date.now() - start) / 60000)} 分钟`,
|
|
128
132
|
);
|
|
129
|
-
} catch (e) {
|
|
130
|
-
console.error("[shop-scraper] Windows: 计划任务失败:", e.message);
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
// 延迟删除任务
|
|
134
|
-
if (taskCreated) {
|
|
135
|
-
setTimeout(() => {
|
|
136
|
-
try {
|
|
137
|
-
execSync(`schtasks /delete /tn "${taskName}" /f 2>$null`, {
|
|
138
|
-
shell: "cmd.exe",
|
|
139
|
-
});
|
|
140
|
-
} catch {}
|
|
141
|
-
}, 10000);
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
// 2. 轮询 undici 等待 IP 信用恢复
|
|
145
|
-
const proxy = proxyUrl || DEFAULT_PROXY;
|
|
146
|
-
const agent = proxy ? new ProxyAgent(proxy) : null;
|
|
147
|
-
const POLL_MS = 5000;
|
|
148
|
-
const MAX_WAIT_MS = 3600_000;
|
|
149
|
-
const startTime = Date.now();
|
|
150
|
-
|
|
151
|
-
while (Date.now() - startTime < MAX_WAIT_MS) {
|
|
152
|
-
await new Promise((r) => setTimeout(r, POLL_MS));
|
|
153
|
-
try {
|
|
154
|
-
const { body } = await request(url, {
|
|
155
|
-
headers: PDP_HEADERS,
|
|
156
|
-
...(agent ? { dispatcher: agent } : {}),
|
|
157
|
-
});
|
|
158
|
-
const html = await body.text();
|
|
159
|
-
if (
|
|
160
|
-
html.includes('"product_model"') ||
|
|
161
|
-
html.includes('"review_ratings"')
|
|
162
|
-
) {
|
|
163
|
-
console.error("[shop-scraper] Captcha solved, IP credit restored");
|
|
164
|
-
return true;
|
|
165
|
-
}
|
|
166
|
-
} catch {}
|
|
167
|
-
const waited = Math.round((Date.now() - startTime) / 60000);
|
|
168
|
-
if (waited > 0 && waited % 5 === 0) {
|
|
169
|
-
console.error(
|
|
170
|
-
`[shop-scraper] 等待验证码解决中... 已等待 ${waited} 分钟`,
|
|
171
|
-
);
|
|
172
|
-
}
|
|
173
133
|
}
|
|
174
|
-
|
|
175
|
-
console.error("[shop-scraper] Windows: 等待验证码超时");
|
|
176
|
-
return false;
|
|
177
|
-
} finally {
|
|
178
|
-
winRecoveryRunning = false;
|
|
179
134
|
}
|
|
135
|
+
|
|
136
|
+
console.error("[shop-scraper] Windows: 等待验证码超时");
|
|
137
|
+
return false;
|
|
180
138
|
}
|
|
181
139
|
|
|
182
140
|
/**
|