auspex 0.2.1 → 0.3.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.
- package/package.json +3 -1
- package/dist/agent/actions.d.ts +0 -5
- package/dist/agent/actions.d.ts.map +0 -1
- package/dist/agent/actions.js +0 -32
- package/dist/agent/actions.js.map +0 -1
- package/dist/agent/agent.d.ts +0 -26
- package/dist/agent/agent.d.ts.map +0 -1
- package/dist/agent/agent.js +0 -282
- package/dist/agent/agent.js.map +0 -1
- package/dist/agent/logger.d.ts +0 -15
- package/dist/agent/logger.d.ts.map +0 -1
- package/dist/agent/logger.js +0 -70
- package/dist/agent/logger.js.map +0 -1
- package/dist/agent/loop.d.ts +0 -21
- package/dist/agent/loop.d.ts.map +0 -1
- package/dist/agent/loop.js +0 -250
- package/dist/agent/loop.js.map +0 -1
- package/dist/agent/report.d.ts +0 -3
- package/dist/agent/report.d.ts.map +0 -1
- package/dist/agent/report.js +0 -107
- package/dist/agent/report.js.map +0 -1
- package/dist/browser/executor.d.ts +0 -5
- package/dist/browser/executor.d.ts.map +0 -1
- package/dist/browser/executor.js +0 -87
- package/dist/browser/executor.js.map +0 -1
- package/dist/browser/pool.d.ts +0 -33
- package/dist/browser/pool.d.ts.map +0 -1
- package/dist/browser/pool.js +0 -101
- package/dist/browser/pool.js.map +0 -1
- package/dist/browser/snapshot.d.ts +0 -7
- package/dist/browser/snapshot.d.ts.map +0 -1
- package/dist/browser/snapshot.js +0 -201
- package/dist/browser/snapshot.js.map +0 -1
- package/dist/config/defaults.d.ts +0 -17
- package/dist/config/defaults.d.ts.map +0 -1
- package/dist/config/defaults.js +0 -17
- package/dist/config/defaults.js.map +0 -1
- package/dist/config/schema.d.ts +0 -169
- package/dist/config/schema.d.ts.map +0 -1
- package/dist/config/schema.js +0 -53
- package/dist/config/schema.js.map +0 -1
- package/dist/index.d.ts +0 -9
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js +0 -10
- package/dist/index.js.map +0 -1
- package/dist/llm/client.d.ts +0 -23
- package/dist/llm/client.d.ts.map +0 -1
- package/dist/llm/client.js +0 -88
- package/dist/llm/client.js.map +0 -1
- package/dist/llm/prompt.d.ts +0 -15
- package/dist/llm/prompt.d.ts.map +0 -1
- package/dist/llm/prompt.js +0 -82
- package/dist/llm/prompt.js.map +0 -1
- package/dist/llm/vision-models.d.ts +0 -3
- package/dist/llm/vision-models.d.ts.map +0 -1
- package/dist/llm/vision-models.js +0 -30
- package/dist/llm/vision-models.js.map +0 -1
- package/dist/scraper/extractors/content.d.ts +0 -33
- package/dist/scraper/extractors/content.d.ts.map +0 -1
- package/dist/scraper/extractors/content.js +0 -276
- package/dist/scraper/extractors/content.js.map +0 -1
- package/dist/scraper/extractors/ssr.d.ts +0 -18
- package/dist/scraper/extractors/ssr.d.ts.map +0 -1
- package/dist/scraper/extractors/ssr.js +0 -162
- package/dist/scraper/extractors/ssr.js.map +0 -1
- package/dist/scraper/extractors/to-markdown.d.ts +0 -5
- package/dist/scraper/extractors/to-markdown.d.ts.map +0 -1
- package/dist/scraper/extractors/to-markdown.js +0 -103
- package/dist/scraper/extractors/to-markdown.js.map +0 -1
- package/dist/scraper/index.d.ts +0 -35
- package/dist/scraper/index.d.ts.map +0 -1
- package/dist/scraper/index.js +0 -299
- package/dist/scraper/index.js.map +0 -1
- package/dist/scraper/tiers/tier1-http.d.ts +0 -5
- package/dist/scraper/tiers/tier1-http.d.ts.map +0 -1
- package/dist/scraper/tiers/tier1-http.js +0 -116
- package/dist/scraper/tiers/tier1-http.js.map +0 -1
- package/dist/scraper/tiers/tier2-stealth.d.ts +0 -5
- package/dist/scraper/tiers/tier2-stealth.d.ts.map +0 -1
- package/dist/scraper/tiers/tier2-stealth.js +0 -109
- package/dist/scraper/tiers/tier2-stealth.js.map +0 -1
- package/dist/scraper/tiers/tier3-browser.d.ts +0 -11
- package/dist/scraper/tiers/tier3-browser.d.ts.map +0 -1
- package/dist/scraper/tiers/tier3-browser.js +0 -511
- package/dist/scraper/tiers/tier3-browser.js.map +0 -1
- package/dist/scraper/types.d.ts +0 -161
- package/dist/scraper/types.d.ts.map +0 -1
- package/dist/scraper/types.js +0 -3
- package/dist/scraper/types.js.map +0 -1
- package/dist/security/action-validator.d.ts +0 -98
- package/dist/security/action-validator.d.ts.map +0 -1
- package/dist/security/action-validator.js +0 -72
- package/dist/security/action-validator.js.map +0 -1
- package/dist/security/url-validator.d.ts +0 -9
- package/dist/security/url-validator.d.ts.map +0 -1
- package/dist/security/url-validator.js +0 -78
- package/dist/security/url-validator.js.map +0 -1
- package/dist/types.d.ts +0 -168
- package/dist/types.d.ts.map +0 -1
- package/dist/types.js +0 -2
- package/dist/types.js.map +0 -1
|
@@ -1,511 +0,0 @@
|
|
|
1
|
-
import { chromium } from "playwright";
|
|
2
|
-
import { extractContent } from "../extractors/content.js";
|
|
3
|
-
import { htmlToMarkdown } from "../extractors/to-markdown.js";
|
|
4
|
-
// ─── Tier 3: Playwright Chromium (fallback final) ──────────────────────────
|
|
5
|
-
//
|
|
6
|
-
// Acionado quando Tier 1 (HTTP) e Tier 2 (Stealth HTTP) falham.
|
|
7
|
-
// Casos típicos: SPAs complexas, anti-bot pesado (Cloudflare, Akamai, etc.).
|
|
8
|
-
//
|
|
9
|
-
// Estratégias aplicadas:
|
|
10
|
-
// 1. Stealth scripts injetados antes de qualquer script da página
|
|
11
|
-
// 2. Interceptar chamadas de API JSON (melhor para SPAs — dados diretos)
|
|
12
|
-
// 3. Bloquear recursos desnecessários (fonts, media, analytics)
|
|
13
|
-
// 4. Aguardar networkidle ou seletor específico
|
|
14
|
-
// 5. Extrair DOM completo e converter para Markdown
|
|
15
|
-
// ──────────────────────────────────────────────────────────────────────────
|
|
16
|
-
// User-Agent de Chrome real para Windows (OS mais comum = menos suspeito).
|
|
17
|
-
// Atualizar a cada 2-3 versões major do Chrome.
|
|
18
|
-
const CHROME_UA = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.0.0 Safari/537.36";
|
|
19
|
-
// Args que reduzem sinais de automação detectáveis
|
|
20
|
-
const STEALTH_ARGS = [
|
|
21
|
-
"--disable-blink-features=AutomationControlled",
|
|
22
|
-
"--disable-features=IsolateOrigins,site-per-process",
|
|
23
|
-
"--disable-infobars",
|
|
24
|
-
"--no-first-run",
|
|
25
|
-
"--no-sandbox",
|
|
26
|
-
"--disable-setuid-sandbox",
|
|
27
|
-
"--disable-dev-shm-usage",
|
|
28
|
-
"--disable-accelerated-2d-canvas",
|
|
29
|
-
"--no-zygote",
|
|
30
|
-
"--disable-gpu",
|
|
31
|
-
"--window-size=1920,1080",
|
|
32
|
-
"--disable-background-networking",
|
|
33
|
-
"--disable-client-side-phishing-detection",
|
|
34
|
-
"--disable-component-update",
|
|
35
|
-
"--disable-default-apps",
|
|
36
|
-
"--disable-domain-reliability",
|
|
37
|
-
"--disable-extensions",
|
|
38
|
-
"--disable-hang-monitor",
|
|
39
|
-
"--disable-popup-blocking",
|
|
40
|
-
"--disable-prompt-on-repost",
|
|
41
|
-
"--disable-sync",
|
|
42
|
-
"--metrics-recording-only",
|
|
43
|
-
"--safebrowsing-disable-auto-update",
|
|
44
|
-
];
|
|
45
|
-
// ─── Script de anti-detecção (injetado antes de qualquer JS da página) ────────
|
|
46
|
-
//
|
|
47
|
-
// Cobre as principais técnicas usadas por anti-bots modernos
|
|
48
|
-
// (Cloudflare, DataDome, Akamai, PerimeterX, Shape Security):
|
|
49
|
-
//
|
|
50
|
-
// 1. navigator.webdriver → remove o flag mais óbvio
|
|
51
|
-
// 2. navigator.plugins → simula os 3 plugins reais do Chrome
|
|
52
|
-
// 3. Propriedades de hardware → concurrency, memory, maxTouchPoints, vendor, platform
|
|
53
|
-
// 4. window.chrome → objeto completo (runtime, loadTimes, csi, app)
|
|
54
|
-
// 5. Notification.permission → 'default' (headless retorna 'denied')
|
|
55
|
-
// 6. Permission API → 'prompt' para notifications
|
|
56
|
-
// 7. Canvas fingerprint → ruído de 1 bit no toDataURL (quebra fingerprinting)
|
|
57
|
-
// 8. WebGL UNMASKED_VENDOR/RENDERER → GPU Intel realista (em vez de llvmpipe/SwiftShader)
|
|
58
|
-
// 9. Screen.colorDepth/pixelDepth → 24 bits
|
|
59
|
-
// 10. Remoção de artefatos → remove vars de outras ferramentas (Selenium, PhantomJS)
|
|
60
|
-
// ──────────────────────────────────────────────────────────────────────────────
|
|
61
|
-
const STEALTH_INIT_SCRIPT = /* language=javascript */ `
|
|
62
|
-
(function () {
|
|
63
|
-
// ── 1. Remove a flag mais básica de automação ─────────────────────────
|
|
64
|
-
Object.defineProperty(navigator, 'webdriver', {
|
|
65
|
-
get: () => undefined,
|
|
66
|
-
configurable: true,
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
// ── 2. Plugins realistas de um Chrome normal ──────────────────────────
|
|
70
|
-
// navigator.plugins.length === 0 é o maior red-flag de headless.
|
|
71
|
-
const makeMime = (type, suffixes, desc, plugin) => {
|
|
72
|
-
const mt = Object.create(MimeType.prototype);
|
|
73
|
-
Object.defineProperties(mt, {
|
|
74
|
-
type: { value: type, enumerable: true },
|
|
75
|
-
suffixes: { value: suffixes, enumerable: true },
|
|
76
|
-
description: { value: desc, enumerable: true },
|
|
77
|
-
enabledPlugin: { value: plugin, enumerable: true },
|
|
78
|
-
});
|
|
79
|
-
return mt;
|
|
80
|
-
};
|
|
81
|
-
|
|
82
|
-
const makePlugin = (name, desc, filename, mimeSpecs) => {
|
|
83
|
-
const p = Object.create(Plugin.prototype);
|
|
84
|
-
Object.defineProperties(p, {
|
|
85
|
-
name: { value: name, enumerable: true },
|
|
86
|
-
description: { value: desc, enumerable: true },
|
|
87
|
-
filename: { value: filename, enumerable: true },
|
|
88
|
-
length: { value: mimeSpecs.length },
|
|
89
|
-
});
|
|
90
|
-
mimeSpecs.forEach((spec, i) => {
|
|
91
|
-
const mt = makeMime(spec.type, spec.suffixes, spec.desc, p);
|
|
92
|
-
Object.defineProperty(p, i, { value: mt, enumerable: true });
|
|
93
|
-
Object.defineProperty(p, spec.type, { value: mt });
|
|
94
|
-
});
|
|
95
|
-
p.item = (i) => p[i] ?? null;
|
|
96
|
-
p.namedItem = (n) => p[n] ?? null;
|
|
97
|
-
return p;
|
|
98
|
-
};
|
|
99
|
-
|
|
100
|
-
const pdfViewer = makePlugin(
|
|
101
|
-
'PDF Viewer', 'Portable Document Format', 'internal-pdf-viewer',
|
|
102
|
-
[
|
|
103
|
-
{ type: 'application/pdf', suffixes: 'pdf', desc: '' },
|
|
104
|
-
{ type: 'text/pdf', suffixes: 'pdf', desc: '' },
|
|
105
|
-
],
|
|
106
|
-
);
|
|
107
|
-
const chromePDF = makePlugin(
|
|
108
|
-
'Chrome PDF Viewer', '', 'mhjfbmdgcfjbbpaeojofohoefgiehjai',
|
|
109
|
-
[{ type: 'application/pdf', suffixes: 'pdf', desc: '' }],
|
|
110
|
-
);
|
|
111
|
-
const nacl = makePlugin(
|
|
112
|
-
'Native Client', '', 'internal-nacl-plugin',
|
|
113
|
-
[
|
|
114
|
-
{ type: 'application/x-nacl', suffixes: '', desc: 'Native Client Executable' },
|
|
115
|
-
{ type: 'application/x-pnacl', suffixes: '', desc: 'Portable Native Client Executable' },
|
|
116
|
-
],
|
|
117
|
-
);
|
|
118
|
-
|
|
119
|
-
const pluginList = [pdfViewer, chromePDF, nacl];
|
|
120
|
-
const pa = Object.create(PluginArray.prototype);
|
|
121
|
-
Object.defineProperty(pa, 'length', { value: pluginList.length });
|
|
122
|
-
pluginList.forEach((plug, i) => {
|
|
123
|
-
Object.defineProperty(pa, i, { value: plug, enumerable: true });
|
|
124
|
-
Object.defineProperty(pa, plug.name, { value: plug });
|
|
125
|
-
});
|
|
126
|
-
pa.item = (i) => pluginList[i] ?? null;
|
|
127
|
-
pa.namedItem = (n) => pa[n] ?? null;
|
|
128
|
-
pa.refresh = () => {};
|
|
129
|
-
|
|
130
|
-
Object.defineProperty(navigator, 'plugins', { get: () => pa });
|
|
131
|
-
|
|
132
|
-
// ── 3. Propriedades de hardware realistas ─────────────────────────────
|
|
133
|
-
Object.defineProperty(navigator, 'languages', { get: () => ['pt-BR', 'pt', 'en-US', 'en'] });
|
|
134
|
-
Object.defineProperty(navigator, 'hardwareConcurrency', { get: () => 8 });
|
|
135
|
-
Object.defineProperty(navigator, 'deviceMemory', { get: () => 8 });
|
|
136
|
-
Object.defineProperty(navigator, 'maxTouchPoints', { get: () => 0 });
|
|
137
|
-
Object.defineProperty(navigator, 'vendor', { get: () => 'Google Inc.' });
|
|
138
|
-
Object.defineProperty(navigator, 'platform', { get: () => 'Win32' });
|
|
139
|
-
|
|
140
|
-
// ── 4. window.chrome — objeto completo como Chrome real ──────────────
|
|
141
|
-
// Automação headless deixa window.chrome undefined ou com .runtime vazio.
|
|
142
|
-
if (!window.chrome) window.chrome = {};
|
|
143
|
-
|
|
144
|
-
if (!window.chrome.app) {
|
|
145
|
-
window.chrome.app = {
|
|
146
|
-
isInstalled: false,
|
|
147
|
-
getDetails: () => null,
|
|
148
|
-
getIsInstalled: () => false,
|
|
149
|
-
InstallState: { DISABLED: 'disabled', INSTALLED: 'installed', NOT_INSTALLED: 'not_installed' },
|
|
150
|
-
RunningState: { CANNOT_RUN: 'cannot_run', READY_TO_RUN: 'ready_to_run', RUNNING: 'running' },
|
|
151
|
-
};
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
if (!window.chrome.runtime) {
|
|
155
|
-
window.chrome.runtime = {
|
|
156
|
-
id: undefined,
|
|
157
|
-
connect: () => { throw Object.assign(new Error('Could not establish connection.'), { message: 'Could not establish connection. Receiving end does not exist.' }); },
|
|
158
|
-
sendMessage: () => { throw Object.assign(new Error('Could not establish connection.'), { message: 'Could not establish connection. Receiving end does not exist.' }); },
|
|
159
|
-
PlatformOs: { MAC: 'mac', WIN: 'win', ANDROID: 'android', CROS: 'cros', LINUX: 'linux', OPENBSD: 'openbsd' },
|
|
160
|
-
PlatformArch: { ARM: 'arm', ARM64: 'arm64', X86_32: 'x86-32', X86_64: 'x86-64', MIPS: 'mips', MIPS64: 'mips64' },
|
|
161
|
-
};
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
if (!window.chrome.loadTimes) {
|
|
165
|
-
window.chrome.loadTimes = () => {
|
|
166
|
-
const now = Date.now() / 1000;
|
|
167
|
-
return {
|
|
168
|
-
requestTime: now - 1.5 - Math.random() * 0.5,
|
|
169
|
-
startLoadTime: now - 1.2 - Math.random() * 0.3,
|
|
170
|
-
commitLoadTime: now - 0.8 - Math.random() * 0.2,
|
|
171
|
-
finishDocumentLoadTime: now - 0.3 - Math.random() * 0.1,
|
|
172
|
-
finishLoadTime: now - 0.1 - Math.random() * 0.05,
|
|
173
|
-
firstPaintTime: now - 0.9 - Math.random() * 0.2,
|
|
174
|
-
firstPaintAfterLoadTime: now - 0.05,
|
|
175
|
-
navigationType: 'Other',
|
|
176
|
-
wasFetchedViaSpdy: true,
|
|
177
|
-
wasNpnNegotiated: true,
|
|
178
|
-
npnNegotiatedProtocol: 'h2',
|
|
179
|
-
wasAlternateProtocolAvailable: false,
|
|
180
|
-
connectionInfo: 'h2',
|
|
181
|
-
};
|
|
182
|
-
};
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
if (!window.chrome.csi) {
|
|
186
|
-
window.chrome.csi = () => ({
|
|
187
|
-
startE: Date.now() - 1000,
|
|
188
|
-
onloadT: Date.now(),
|
|
189
|
-
pageT: 500 + Math.random() * 1000,
|
|
190
|
-
tran: 15,
|
|
191
|
-
});
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
// ── 5. Notification API — headless retorna 'denied', real retorna 'default' ─
|
|
195
|
-
try {
|
|
196
|
-
if (typeof Notification !== 'undefined') {
|
|
197
|
-
Object.defineProperty(Notification, 'permission', { get: () => 'default' });
|
|
198
|
-
}
|
|
199
|
-
} catch (_) {}
|
|
200
|
-
|
|
201
|
-
// ── 6. Permission API — 'notifications' deve retornar 'prompt' ────────
|
|
202
|
-
if (navigator.permissions) {
|
|
203
|
-
const origQuery = navigator.permissions.query.bind(navigator.permissions);
|
|
204
|
-
navigator.permissions.query = (params) => {
|
|
205
|
-
if (params && params.name === 'notifications') {
|
|
206
|
-
return Promise.resolve({ state: 'prompt', onchange: null, addEventListener: () => {}, removeEventListener: () => {}, dispatchEvent: () => true });
|
|
207
|
-
}
|
|
208
|
-
return origQuery(params);
|
|
209
|
-
};
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
// ── 7. Canvas fingerprint — ruído sutil no último byte do dataURL ─────
|
|
213
|
-
// Técnica: altera 1 bit → output diferente em cada run → quebra fingerprinting.
|
|
214
|
-
// Impacto visual: imperceptível (altera apenas o encoding base64 do último pixel).
|
|
215
|
-
const _origToDataURL = HTMLCanvasElement.prototype.toDataURL;
|
|
216
|
-
HTMLCanvasElement.prototype.toDataURL = function (type, quality) {
|
|
217
|
-
const data = _origToDataURL.call(this, type, quality);
|
|
218
|
-
if (data.length < 12) return data;
|
|
219
|
-
const idx = data.length - 2;
|
|
220
|
-
return data.slice(0, idx) + String.fromCharCode(data.charCodeAt(idx) ^ 0x01) + data.slice(idx + 1);
|
|
221
|
-
};
|
|
222
|
-
|
|
223
|
-
// ── 8. WebGL — GPU Intel realista em vez de llvmpipe/SwiftShader ─────
|
|
224
|
-
// llvmpipe/SwiftShader = fingerprint de VM detectado por todos os anti-bots.
|
|
225
|
-
const WEBGL_VENDOR = 'Google Inc. (Intel)';
|
|
226
|
-
const WEBGL_RENDERER = 'ANGLE (Intel, Intel(R) UHD Graphics 620 Direct3D11 vs_5_0 ps_5_0, D3D11)';
|
|
227
|
-
|
|
228
|
-
const patchWebGL = (Ctx) => {
|
|
229
|
-
if (!Ctx) return;
|
|
230
|
-
const orig = Ctx.prototype.getParameter;
|
|
231
|
-
Ctx.prototype.getParameter = function (param) {
|
|
232
|
-
if (param === 37445) return WEBGL_VENDOR; // UNMASKED_VENDOR_WEBGL
|
|
233
|
-
if (param === 37446) return WEBGL_RENDERER; // UNMASKED_RENDERER_WEBGL
|
|
234
|
-
return orig.call(this, param);
|
|
235
|
-
};
|
|
236
|
-
};
|
|
237
|
-
|
|
238
|
-
patchWebGL(typeof WebGLRenderingContext !== 'undefined' ? WebGLRenderingContext : null);
|
|
239
|
-
patchWebGL(typeof WebGL2RenderingContext !== 'undefined' ? WebGL2RenderingContext : null);
|
|
240
|
-
|
|
241
|
-
// ── 9. Screen depth realista ──────────────────────────────────────────
|
|
242
|
-
try {
|
|
243
|
-
Object.defineProperty(screen, 'colorDepth', { get: () => 24 });
|
|
244
|
-
Object.defineProperty(screen, 'pixelDepth', { get: () => 24 });
|
|
245
|
-
} catch (_) {}
|
|
246
|
-
|
|
247
|
-
// ── 10. Remove artefatos de outras ferramentas de automação ──────────
|
|
248
|
-
const automationVars = ['__nightmare', '_phantom', 'callPhantom',
|
|
249
|
-
'__selenium_evaluate', '__webdriver_evaluate', '_Selenium_IDE_Recorder',
|
|
250
|
-
'__webdriver_script_fn', '__lastWatirAlert', '__lastWatirConfirm'];
|
|
251
|
-
automationVars.forEach(v => { try { delete window[v]; } catch (_) {} });
|
|
252
|
-
|
|
253
|
-
})();
|
|
254
|
-
`;
|
|
255
|
-
// Recursos que bloqueamos para economizar banda/tempo.
|
|
256
|
-
// "image" incluído: extraímos texto/markdown, não renderizamos visualmente.
|
|
257
|
-
const BLOCKED_RESOURCE_TYPES = new Set(["font", "media", "image"]);
|
|
258
|
-
// Padrões de analytics/rastreamento a bloquear
|
|
259
|
-
const BLOCKED_URL_PATTERNS = [
|
|
260
|
-
"google-analytics.com",
|
|
261
|
-
"googletagmanager.com",
|
|
262
|
-
"facebook.net/en_US/fbevents.js",
|
|
263
|
-
"connect.facebook.net",
|
|
264
|
-
"hotjar.com",
|
|
265
|
-
"fullstory.com",
|
|
266
|
-
"segment.com",
|
|
267
|
-
"mixpanel.com",
|
|
268
|
-
"amplitude.com",
|
|
269
|
-
"sentry.io",
|
|
270
|
-
"clarity.ms",
|
|
271
|
-
"doubleclick.net",
|
|
272
|
-
"adnxs.com",
|
|
273
|
-
"criteo.com",
|
|
274
|
-
"taboola.com",
|
|
275
|
-
"outbrain.com",
|
|
276
|
-
];
|
|
277
|
-
export class Tier3Browser {
|
|
278
|
-
browser = null;
|
|
279
|
-
browserPromise = null;
|
|
280
|
-
browserConfig;
|
|
281
|
-
constructor(browserConfig = {}) {
|
|
282
|
-
this.browserConfig = browserConfig;
|
|
283
|
-
}
|
|
284
|
-
// ── Lifecycle do browser (singleton with mutex) ────────────────────────
|
|
285
|
-
async getBrowser() {
|
|
286
|
-
if (this.browser?.isConnected())
|
|
287
|
-
return this.browser;
|
|
288
|
-
if (!this.browserPromise) {
|
|
289
|
-
this.browserPromise = (async () => {
|
|
290
|
-
const launchOptions = {
|
|
291
|
-
headless: this.browserConfig.headless ?? true,
|
|
292
|
-
args: STEALTH_ARGS,
|
|
293
|
-
};
|
|
294
|
-
if (this.browserConfig.executablePath) {
|
|
295
|
-
launchOptions.executablePath = this.browserConfig.executablePath;
|
|
296
|
-
}
|
|
297
|
-
else if (this.browserConfig.channel) {
|
|
298
|
-
launchOptions.channel = this.browserConfig.channel;
|
|
299
|
-
}
|
|
300
|
-
else {
|
|
301
|
-
try {
|
|
302
|
-
const browser = await chromium.launch({ ...launchOptions, channel: "chrome" });
|
|
303
|
-
this.browser = browser;
|
|
304
|
-
this.browserPromise = null;
|
|
305
|
-
return browser;
|
|
306
|
-
}
|
|
307
|
-
catch {
|
|
308
|
-
// Chrome not found → use Playwright's bundled Chromium
|
|
309
|
-
}
|
|
310
|
-
}
|
|
311
|
-
const browser = await chromium.launch(launchOptions);
|
|
312
|
-
this.browser = browser;
|
|
313
|
-
this.browserPromise = null;
|
|
314
|
-
return browser;
|
|
315
|
-
})();
|
|
316
|
-
}
|
|
317
|
-
return this.browserPromise;
|
|
318
|
-
}
|
|
319
|
-
// ── Scraping principal ─────────────────────────────────────────────────
|
|
320
|
-
async scrape(url, options = {}) {
|
|
321
|
-
const startTime = Date.now();
|
|
322
|
-
const browser = await this.getBrowser();
|
|
323
|
-
let context = null;
|
|
324
|
-
try {
|
|
325
|
-
context = await browser.newContext({
|
|
326
|
-
userAgent: CHROME_UA,
|
|
327
|
-
viewport: { width: 1920, height: 1080 },
|
|
328
|
-
locale: "pt-BR",
|
|
329
|
-
timezoneId: "America/Sao_Paulo",
|
|
330
|
-
extraHTTPHeaders: {
|
|
331
|
-
"accept-language": "pt-BR,pt;q=0.9,en-US;q=0.8,en;q=0.7",
|
|
332
|
-
...(options.headers ?? {}),
|
|
333
|
-
},
|
|
334
|
-
javaScriptEnabled: true,
|
|
335
|
-
// Desabilita WebRTC para evitar vazamento de IP real em ambientes com proxy
|
|
336
|
-
// (equivalente a --disable-webrtc nos args, mas via context)
|
|
337
|
-
});
|
|
338
|
-
const page = await context.newPage();
|
|
339
|
-
// ── Injetar stealth script ANTES de qualquer script da página ─────
|
|
340
|
-
await page.addInitScript(STEALTH_INIT_SCRIPT);
|
|
341
|
-
// ── Bloquear recursos desnecessários ──────────────────────────────
|
|
342
|
-
await page.route("**/*", (route) => {
|
|
343
|
-
const req = route.request();
|
|
344
|
-
const type = req.resourceType();
|
|
345
|
-
const reqUrl = req.url();
|
|
346
|
-
if (BLOCKED_RESOURCE_TYPES.has(type)) {
|
|
347
|
-
return route.abort();
|
|
348
|
-
}
|
|
349
|
-
if (type === "script" &&
|
|
350
|
-
BLOCKED_URL_PATTERNS.some((p) => reqUrl.includes(p))) {
|
|
351
|
-
return route.abort();
|
|
352
|
-
}
|
|
353
|
-
return route.continue();
|
|
354
|
-
});
|
|
355
|
-
// ── Interceptação de APIs JSON (fundamental para SPAs) ────────────
|
|
356
|
-
const interceptedAPIs = [];
|
|
357
|
-
const shouldIntercept = options.interceptAPIs !== false;
|
|
358
|
-
if (shouldIntercept) {
|
|
359
|
-
page.on("response", async (response) => {
|
|
360
|
-
try {
|
|
361
|
-
const contentType = response.headers()["content-type"] ?? "";
|
|
362
|
-
if (!contentType.includes("application/json"))
|
|
363
|
-
return;
|
|
364
|
-
const apiUrl = response.url();
|
|
365
|
-
// Ignora analytics e recursos JS/CSS
|
|
366
|
-
if (BLOCKED_URL_PATTERNS.some((p) => apiUrl.includes(p)))
|
|
367
|
-
return;
|
|
368
|
-
if (/\.(js|css|png|jpg|gif|svg|woff)/.test(apiUrl))
|
|
369
|
-
return;
|
|
370
|
-
// Ignora respostas muito grandes (provavelmente não são dados da view)
|
|
371
|
-
const contentLength = parseInt(response.headers()["content-length"] ?? "0", 10);
|
|
372
|
-
if (contentLength > 500_000)
|
|
373
|
-
return;
|
|
374
|
-
const data = await response.json().catch(() => null);
|
|
375
|
-
if (!data)
|
|
376
|
-
return;
|
|
377
|
-
interceptedAPIs.push({
|
|
378
|
-
url: apiUrl,
|
|
379
|
-
method: response.request().method(),
|
|
380
|
-
statusCode: response.status(),
|
|
381
|
-
contentType,
|
|
382
|
-
data,
|
|
383
|
-
});
|
|
384
|
-
}
|
|
385
|
-
catch {
|
|
386
|
-
// Resposta já consumida ou parse inválido — ignora silenciosamente
|
|
387
|
-
}
|
|
388
|
-
});
|
|
389
|
-
}
|
|
390
|
-
// Auto-dismiss dialogs (alert/confirm/prompt) para não travar a navegação
|
|
391
|
-
page.on("dialog", (dialog) => dialog.dismiss().catch(() => { }));
|
|
392
|
-
// ── Navegação com retry ────────────────────────────────────────────
|
|
393
|
-
// Em sites com anti-bot, a 1ª tentativa pode receber um challenge (403/503).
|
|
394
|
-
// A 2ª tentativa (com cookies/state acumulados) frequentemente passa.
|
|
395
|
-
const timeout = options.timeout ?? 30_000;
|
|
396
|
-
let statusCode = 200;
|
|
397
|
-
let lastNavError = null;
|
|
398
|
-
for (let attempt = 1; attempt <= 2; attempt++) {
|
|
399
|
-
try {
|
|
400
|
-
const navResponse = await page.goto(url, {
|
|
401
|
-
waitUntil: "domcontentloaded",
|
|
402
|
-
timeout: Math.min(timeout, 30_000),
|
|
403
|
-
});
|
|
404
|
-
statusCode = navResponse?.status() ?? 200;
|
|
405
|
-
lastNavError = null;
|
|
406
|
-
break; // Sucesso — sai do loop
|
|
407
|
-
}
|
|
408
|
-
catch (navErr) {
|
|
409
|
-
lastNavError = navErr instanceof Error ? navErr : new Error(String(navErr));
|
|
410
|
-
if (attempt < 2) {
|
|
411
|
-
await page.waitForTimeout(1_500).catch(() => { });
|
|
412
|
-
}
|
|
413
|
-
}
|
|
414
|
-
}
|
|
415
|
-
if (lastNavError) {
|
|
416
|
-
throw new Error(`Tier3 Browser: falha na navegação — ${lastNavError.message}`);
|
|
417
|
-
}
|
|
418
|
-
// ── Aguardar conteúdo dinâmico ────────────────────────────────────
|
|
419
|
-
// networkidle sinaliza que a SPA terminou de carregar
|
|
420
|
-
await page
|
|
421
|
-
.waitForLoadState("networkidle", {
|
|
422
|
-
timeout: Math.min(timeout * 0.5, 15_000),
|
|
423
|
-
})
|
|
424
|
-
.catch(() => {
|
|
425
|
-
// Timeout é aceitável — prosseguimos com o que tiver
|
|
426
|
-
});
|
|
427
|
-
// Seletor específico do usuário (ex: '.product-list', '#app')
|
|
428
|
-
if (options.waitForSelector) {
|
|
429
|
-
await page
|
|
430
|
-
.waitForSelector(options.waitForSelector, {
|
|
431
|
-
state: "visible",
|
|
432
|
-
timeout: 10_000,
|
|
433
|
-
})
|
|
434
|
-
.catch(() => {
|
|
435
|
-
// Seletor não apareceu — prosseguimos assim mesmo
|
|
436
|
-
});
|
|
437
|
-
}
|
|
438
|
-
// ── Scroll para ativar lazy-loading ───────────────────────────────
|
|
439
|
-
// Muitos sites usam IntersectionObserver para carregar conteúdo apenas
|
|
440
|
-
// quando o usuário rola até ele. Varrer a página simula esse comportamento
|
|
441
|
-
// e garante que todo o conteúdo seja carregado antes da extração.
|
|
442
|
-
await page
|
|
443
|
-
.evaluate(() => {
|
|
444
|
-
return new Promise((resolve) => {
|
|
445
|
-
const totalHeight = document.body.scrollHeight;
|
|
446
|
-
if (totalHeight <= window.innerHeight) {
|
|
447
|
-
resolve();
|
|
448
|
-
return;
|
|
449
|
-
}
|
|
450
|
-
const step = Math.max(Math.floor(totalHeight / 6), 300);
|
|
451
|
-
let scrolled = 0;
|
|
452
|
-
const tick = () => {
|
|
453
|
-
scrolled += step;
|
|
454
|
-
window.scrollTo({ top: scrolled, behavior: "smooth" });
|
|
455
|
-
if (scrolled < totalHeight) {
|
|
456
|
-
// Intervalo variado simula comportamento humano e dá tempo ao
|
|
457
|
-
// IntersectionObserver disparar e buscar conteúdo novo
|
|
458
|
-
setTimeout(tick, 120 + Math.floor(Math.random() * 130));
|
|
459
|
-
}
|
|
460
|
-
else {
|
|
461
|
-
window.scrollTo({ top: 0, behavior: "instant" });
|
|
462
|
-
resolve();
|
|
463
|
-
}
|
|
464
|
-
};
|
|
465
|
-
setTimeout(tick, 400);
|
|
466
|
-
});
|
|
467
|
-
})
|
|
468
|
-
.catch(() => {
|
|
469
|
-
// Scroll falhou (página sem body ou JS bloqueado) — ignora
|
|
470
|
-
});
|
|
471
|
-
// ── Extração de conteúdo ──────────────────────────────────────────
|
|
472
|
-
const [html, pageTitle] = await Promise.all([
|
|
473
|
-
page.content(),
|
|
474
|
-
page.title(),
|
|
475
|
-
]);
|
|
476
|
-
const finalUrl = page.url();
|
|
477
|
-
const formats = options.formats ?? ["markdown", "text"];
|
|
478
|
-
const extracted = extractContent(html, options.onlyMainContent ?? true, finalUrl);
|
|
479
|
-
const result = {
|
|
480
|
-
url: finalUrl,
|
|
481
|
-
statusCode,
|
|
482
|
-
title: pageTitle || extracted.title,
|
|
483
|
-
description: extracted.description || undefined,
|
|
484
|
-
tier: "browser",
|
|
485
|
-
durationMs: Date.now() - startTime,
|
|
486
|
-
links: extracted.links.length > 0 ? extracted.links : undefined,
|
|
487
|
-
interceptedAPIs: interceptedAPIs.length > 0 ? interceptedAPIs : undefined,
|
|
488
|
-
};
|
|
489
|
-
if (options.getRawHtml)
|
|
490
|
-
result.rawHtml = html;
|
|
491
|
-
if (formats.includes("markdown"))
|
|
492
|
-
result.markdown = htmlToMarkdown(extracted.html);
|
|
493
|
-
if (formats.includes("html"))
|
|
494
|
-
result.html = extracted.html;
|
|
495
|
-
if (formats.includes("text"))
|
|
496
|
-
result.text = extracted.text;
|
|
497
|
-
return result;
|
|
498
|
-
}
|
|
499
|
-
finally {
|
|
500
|
-
await context?.close().catch(() => { });
|
|
501
|
-
}
|
|
502
|
-
}
|
|
503
|
-
// ── Encerrar browser ───────────────────────────────────────────────────
|
|
504
|
-
async close() {
|
|
505
|
-
if (this.browser) {
|
|
506
|
-
await this.browser.close().catch(() => { });
|
|
507
|
-
this.browser = null;
|
|
508
|
-
}
|
|
509
|
-
}
|
|
510
|
-
}
|
|
511
|
-
//# sourceMappingURL=tier3-browser.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"tier3-browser.js","sourceRoot":"","sources":["../../../src/scraper/tiers/tier3-browser.ts"],"names":[],"mappings":"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"}
|