imperium-crawl 2.4.0 → 2.5.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 +130 -11
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +23 -3
- package/dist/cli.js.map +1 -1
- package/dist/constants.d.ts +1 -1
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +31 -1
- package/dist/constants.js.map +1 -1
- package/dist/flows/engine.d.ts +7 -0
- package/dist/flows/engine.d.ts.map +1 -0
- package/dist/flows/engine.js +183 -0
- package/dist/flows/engine.js.map +1 -0
- package/dist/flows/index.d.ts +6 -0
- package/dist/flows/index.d.ts.map +1 -0
- package/dist/flows/index.js +6 -0
- package/dist/flows/index.js.map +1 -0
- package/dist/flows/server.d.ts +11 -0
- package/dist/flows/server.d.ts.map +1 -0
- package/dist/flows/server.js +81 -0
- package/dist/flows/server.js.map +1 -0
- package/dist/flows/smart-target.d.ts +9 -0
- package/dist/flows/smart-target.d.ts.map +1 -0
- package/dist/flows/smart-target.js +84 -0
- package/dist/flows/smart-target.js.map +1 -0
- package/dist/flows/storage.d.ts +26 -0
- package/dist/flows/storage.d.ts.map +1 -0
- package/dist/flows/storage.js +118 -0
- package/dist/flows/storage.js.map +1 -0
- package/dist/flows/templates.d.ts +4 -0
- package/dist/flows/templates.d.ts.map +1 -0
- package/dist/flows/templates.js +35 -0
- package/dist/flows/templates.js.map +1 -0
- package/dist/flows/types.d.ts +3356 -0
- package/dist/flows/types.d.ts.map +1 -0
- package/dist/flows/types.js +133 -0
- package/dist/flows/types.js.map +1 -0
- package/dist/knowledge/store.d.ts +19 -0
- package/dist/knowledge/store.d.ts.map +1 -1
- package/dist/knowledge/store.js +63 -4
- package/dist/knowledge/store.js.map +1 -1
- package/dist/sessions/browser-connect.d.ts +30 -0
- package/dist/sessions/browser-connect.d.ts.map +1 -0
- package/dist/sessions/browser-connect.js +68 -0
- package/dist/sessions/browser-connect.js.map +1 -0
- package/dist/sessions/browser-state.d.ts +35 -0
- package/dist/sessions/browser-state.d.ts.map +1 -0
- package/dist/sessions/browser-state.js +74 -0
- package/dist/sessions/browser-state.js.map +1 -0
- package/dist/sessions/inject-cookies.d.ts +20 -0
- package/dist/sessions/inject-cookies.d.ts.map +1 -0
- package/dist/sessions/inject-cookies.js +57 -0
- package/dist/sessions/inject-cookies.js.map +1 -0
- package/dist/sessions/manager.d.ts +11 -1
- package/dist/sessions/manager.d.ts.map +1 -1
- package/dist/sessions/manager.js +40 -6
- package/dist/sessions/manager.js.map +1 -1
- package/dist/snapshot/store.d.ts +8 -0
- package/dist/snapshot/store.d.ts.map +1 -1
- package/dist/snapshot/store.js +48 -0
- package/dist/snapshot/store.js.map +1 -1
- package/dist/stealth/antibot-detector.d.ts +1 -1
- package/dist/stealth/antibot-detector.d.ts.map +1 -1
- package/dist/stealth/antibot-detector.js +56 -0
- package/dist/stealth/antibot-detector.js.map +1 -1
- package/dist/stealth/browser-image-extract.d.ts +43 -0
- package/dist/stealth/browser-image-extract.d.ts.map +1 -0
- package/dist/stealth/browser-image-extract.js +268 -0
- package/dist/stealth/browser-image-extract.js.map +1 -0
- package/dist/stealth/browser.d.ts +5 -0
- package/dist/stealth/browser.d.ts.map +1 -1
- package/dist/stealth/browser.js +82 -1
- package/dist/stealth/browser.js.map +1 -1
- package/dist/stealth/chrome-profile.d.ts +1 -0
- package/dist/stealth/chrome-profile.d.ts.map +1 -1
- package/dist/stealth/chrome-profile.js +28 -5
- package/dist/stealth/chrome-profile.js.map +1 -1
- package/dist/stealth/detector.d.ts +10 -1
- package/dist/stealth/detector.d.ts.map +1 -1
- package/dist/stealth/detector.js +117 -25
- package/dist/stealth/detector.js.map +1 -1
- package/dist/stealth/headers.d.ts +1 -1
- package/dist/stealth/headers.d.ts.map +1 -1
- package/dist/stealth/headers.js +94 -2
- package/dist/stealth/headers.js.map +1 -1
- package/dist/stealth/index.d.ts +4 -0
- package/dist/stealth/index.d.ts.map +1 -1
- package/dist/stealth/index.js +207 -25
- package/dist/stealth/index.js.map +1 -1
- package/dist/stealth/proxy.d.ts +40 -1
- package/dist/stealth/proxy.d.ts.map +1 -1
- package/dist/stealth/proxy.js +90 -6
- package/dist/stealth/proxy.js.map +1 -1
- package/dist/tools/action-executor.d.ts +2 -0
- package/dist/tools/action-executor.d.ts.map +1 -1
- package/dist/tools/action-executor.js +38 -0
- package/dist/tools/action-executor.js.map +1 -1
- package/dist/tools/batch-download.d.ts +33 -0
- package/dist/tools/batch-download.d.ts.map +1 -0
- package/dist/tools/batch-download.js +208 -0
- package/dist/tools/batch-download.js.map +1 -0
- package/dist/tools/browser.d.ts +100 -0
- package/dist/tools/browser.d.ts.map +1 -0
- package/dist/tools/browser.js +448 -0
- package/dist/tools/browser.js.map +1 -0
- package/dist/tools/download.d.ts +35 -2
- package/dist/tools/download.d.ts.map +1 -1
- package/dist/tools/download.js +245 -44
- package/dist/tools/download.js.map +1 -1
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +23 -0
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/inspect-flow.d.ts +24 -0
- package/dist/tools/inspect-flow.d.ts.map +1 -0
- package/dist/tools/inspect-flow.js +23 -0
- package/dist/tools/inspect-flow.js.map +1 -0
- package/dist/tools/interact.d.ts +28 -15
- package/dist/tools/interact.d.ts.map +1 -1
- package/dist/tools/interact.js +48 -1
- package/dist/tools/interact.js.map +1 -1
- package/dist/tools/list-flows.d.ts +21 -0
- package/dist/tools/list-flows.d.ts.map +1 -0
- package/dist/tools/list-flows.js +18 -0
- package/dist/tools/list-flows.js.map +1 -0
- package/dist/tools/manifest.d.ts.map +1 -1
- package/dist/tools/manifest.js +43 -0
- package/dist/tools/manifest.js.map +1 -1
- package/dist/tools/monitor.d.ts +46 -0
- package/dist/tools/monitor.d.ts.map +1 -0
- package/dist/tools/monitor.js +213 -0
- package/dist/tools/monitor.js.map +1 -0
- package/dist/tools/pdf-extract.d.ts +38 -0
- package/dist/tools/pdf-extract.d.ts.map +1 -0
- package/dist/tools/pdf-extract.js +244 -0
- package/dist/tools/pdf-extract.js.map +1 -0
- package/dist/tools/record-flow.d.ts +39 -0
- package/dist/tools/record-flow.d.ts.map +1 -0
- package/dist/tools/record-flow.js +406 -0
- package/dist/tools/record-flow.js.map +1 -0
- package/dist/tools/run-flow.d.ts +54 -0
- package/dist/tools/run-flow.d.ts.map +1 -0
- package/dist/tools/run-flow.js +47 -0
- package/dist/tools/run-flow.js.map +1 -0
- package/dist/tools/run-skill.d.ts +2 -2
- package/dist/tools/run-skill.d.ts.map +1 -1
- package/dist/tools/run-skill.js +1 -0
- package/dist/tools/run-skill.js.map +1 -1
- package/dist/tools/scrape.d.ts.map +1 -1
- package/dist/tools/scrape.js +17 -1
- package/dist/tools/scrape.js.map +1 -1
- package/dist/tools/serve-flow.d.ts +36 -0
- package/dist/tools/serve-flow.d.ts.map +1 -0
- package/dist/tools/serve-flow.js +42 -0
- package/dist/tools/serve-flow.js.map +1 -0
- package/dist/tools/validate-flow.d.ts +24 -0
- package/dist/tools/validate-flow.d.ts.map +1 -0
- package/dist/tools/validate-flow.js +23 -0
- package/dist/tools/validate-flow.js.map +1 -0
- package/dist/tools/watch.d.ts +68 -0
- package/dist/tools/watch.d.ts.map +1 -0
- package/dist/tools/watch.js +224 -0
- package/dist/tools/watch.js.map +1 -0
- package/dist/utils/fetcher.d.ts +13 -4
- package/dist/utils/fetcher.d.ts.map +1 -1
- package/dist/utils/fetcher.js +121 -24
- package/dist/utils/fetcher.js.map +1 -1
- package/package.json +15 -4
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Browser-based image extraction engine for imperium-crawl v2.5.1
|
|
3
|
+
* Executes inside the browser via page.evaluate() for 100% coverage.
|
|
4
|
+
*
|
|
5
|
+
* Discovers images from:
|
|
6
|
+
* - <img> tags (src, data-src, data-lazy-src, srcset)
|
|
7
|
+
* - <picture> + <source srcset>
|
|
8
|
+
* - Inline style="background-image:url(...)"
|
|
9
|
+
* - All <style> tags → parsed CSS rules → background-image
|
|
10
|
+
* - Shadow DOM — recursively walks all web components
|
|
11
|
+
* - JSON-LD <script type="application/ld+json"> → image/images fields
|
|
12
|
+
* - Inline <script> window.__INITIAL_STATE__ / __DATA__ → regex image URLs
|
|
13
|
+
* - Same-origin iframes → recursive DOM scan
|
|
14
|
+
*
|
|
15
|
+
* Triggers lazy-load via scroll, optionally auto-clicks "load more" buttons.
|
|
16
|
+
*/
|
|
17
|
+
import { debugLog } from "../utils/debug.js";
|
|
18
|
+
// ── Browser-side extraction script (runs inside page.evaluate) ──
|
|
19
|
+
const EXTRACTION_SCRIPT = `
|
|
20
|
+
(() => {
|
|
21
|
+
const results = [];
|
|
22
|
+
const seen = new Set();
|
|
23
|
+
|
|
24
|
+
function resolveUrl(href, base) {
|
|
25
|
+
if (!href || href.startsWith("data:")) return null;
|
|
26
|
+
try {
|
|
27
|
+
return new URL(href, base).href;
|
|
28
|
+
} catch { return null; }
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function addImage(url, alt, width, height, selector, source) {
|
|
32
|
+
if (!url || seen.has(url)) return;
|
|
33
|
+
seen.add(url);
|
|
34
|
+
results.push({ url, alt: alt || "", width: width || 0, height: height || 0, selector, source });
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function extractFromRoot(root, docUrl, prefix) {
|
|
38
|
+
// 1. <img> tags
|
|
39
|
+
root.querySelectorAll("img").forEach((img) => {
|
|
40
|
+
const src = img.currentSrc || img.src || img.dataset.src || img.dataset.lazySrc || img.getAttribute("data-src");
|
|
41
|
+
if (src) {
|
|
42
|
+
const url = resolveUrl(src, docUrl);
|
|
43
|
+
if (url) addImage(url, img.alt, img.naturalWidth, img.naturalHeight, prefix + getSelector(img), "img");
|
|
44
|
+
}
|
|
45
|
+
// srcset
|
|
46
|
+
const srcset = img.srcset || img.getAttribute("data-srcset");
|
|
47
|
+
if (srcset) {
|
|
48
|
+
srcset.split(",").forEach((s) => {
|
|
49
|
+
const u = s.trim().split(/\\s+/)[0];
|
|
50
|
+
const url = resolveUrl(u, docUrl);
|
|
51
|
+
if (url) addImage(url, img.alt, img.naturalWidth, img.naturalHeight, prefix + getSelector(img), "img");
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
// 2. <picture> + <source srcset>
|
|
57
|
+
root.querySelectorAll("picture").forEach((pic) => {
|
|
58
|
+
pic.querySelectorAll("source").forEach((source) => {
|
|
59
|
+
const srcset = source.srcset || source.getAttribute("data-srcset");
|
|
60
|
+
if (srcset) {
|
|
61
|
+
srcset.split(",").forEach((s) => {
|
|
62
|
+
const u = s.trim().split(/\\s+/)[0];
|
|
63
|
+
const url = resolveUrl(u, docUrl);
|
|
64
|
+
if (url) addImage(url, "", 0, 0, prefix + getSelector(pic), "picture");
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
// 3. Inline style background-image
|
|
71
|
+
root.querySelectorAll("*[style]").forEach((el) => {
|
|
72
|
+
const style = el.getAttribute("style") || "";
|
|
73
|
+
const match = style.match(/background-image\\s*:\\s*url\\([\"']?([^\"')]+)[\"']?\\)/i);
|
|
74
|
+
if (match) {
|
|
75
|
+
const url = resolveUrl(match[1], docUrl);
|
|
76
|
+
if (url) addImage(url, "", 0, 0, prefix + getSelector(el), "background-inline");
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
// 4. <style> tag CSS parsing
|
|
81
|
+
root.querySelectorAll("style").forEach((styleTag) => {
|
|
82
|
+
const css = styleTag.textContent || "";
|
|
83
|
+
const regex = /background-image\\s*:\\s*url\\([\"']?([^\"')]+)[\"']?\\)/gi;
|
|
84
|
+
let m;
|
|
85
|
+
while ((m = regex.exec(css)) !== null) {
|
|
86
|
+
const url = resolveUrl(m[1], docUrl);
|
|
87
|
+
if (url) addImage(url, "", 0, 0, prefix + "<style>", "background-css");
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
// 5. JSON-LD
|
|
92
|
+
root.querySelectorAll('script[type="application/ld+json"]').forEach((script) => {
|
|
93
|
+
try {
|
|
94
|
+
const data = JSON.parse(script.textContent || "{}");
|
|
95
|
+
const extractImages = (obj) => {
|
|
96
|
+
if (!obj) return;
|
|
97
|
+
if (typeof obj === "string" && /^https?:\\/\\//.test(obj) && /\\.(jpg|jpeg|png|gif|webp|svg|avif|bmp)(\\?|$)/i.test(obj)) {
|
|
98
|
+
addImage(obj, "", 0, 0, prefix + "<json-ld>", "jsonld");
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
if (Array.isArray(obj)) { obj.forEach(extractImages); return; }
|
|
102
|
+
if (typeof obj === "object") {
|
|
103
|
+
if (obj.image) extractImages(obj.image);
|
|
104
|
+
if (obj.images) extractImages(obj.images);
|
|
105
|
+
if (obj.thumbnailUrl) extractImages(obj.thumbnailUrl);
|
|
106
|
+
if (obj.contentUrl && /\\.(jpg|jpeg|png|gif|webp|svg|avif|bmp)(\\?|$)/i.test(obj.contentUrl)) {
|
|
107
|
+
addImage(obj.contentUrl, obj.name || "", 0, 0, prefix + "<json-ld>", "jsonld");
|
|
108
|
+
}
|
|
109
|
+
Object.values(obj).forEach((v) => {
|
|
110
|
+
if (typeof v === "string" && /^https?:\\/\\//.test(v) && /\\.(jpg|jpeg|png|gif|webp|svg|avif|bmp)(\\?|$)/i.test(v)) {
|
|
111
|
+
addImage(v, "", 0, 0, prefix + "<json-ld>", "jsonld");
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
extractImages(data);
|
|
117
|
+
} catch {}
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
// 6. Inline scripts — window.__INITIAL_STATE__, __DATA__, etc.
|
|
121
|
+
root.querySelectorAll("script:not([src])").forEach((script) => {
|
|
122
|
+
const text = script.textContent || "";
|
|
123
|
+
const regex = /https?:\\/\\/[^\\s\"'<>]+\\.(jpg|jpeg|png|gif|webp|svg|avif|bmp)(\\?[^\\s\"'<>]*)?/gi;
|
|
124
|
+
let m;
|
|
125
|
+
while ((m = regex.exec(text)) !== null) {
|
|
126
|
+
addImage(m[0], "", 0, 0, prefix + "<inline-script>", "inline-script");
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
// 7. Shadow DOM recursion
|
|
131
|
+
root.querySelectorAll("*").forEach((el) => {
|
|
132
|
+
if (el.shadowRoot) {
|
|
133
|
+
extractFromRoot(el.shadowRoot, docUrl, prefix + getSelector(el) + "::shadow ");
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function getSelector(el) {
|
|
139
|
+
if (el.id) return "#" + el.id;
|
|
140
|
+
let path = el.tagName.toLowerCase();
|
|
141
|
+
if (el.className && typeof el.className === "string") {
|
|
142
|
+
path += "." + el.className.split(/\\s+/).filter(Boolean).join(".");
|
|
143
|
+
}
|
|
144
|
+
return path;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// Main extraction
|
|
148
|
+
extractFromRoot(document, document.location.href, "");
|
|
149
|
+
|
|
150
|
+
return results;
|
|
151
|
+
})()
|
|
152
|
+
`;
|
|
153
|
+
// ── Auto-click script (runs inside page.evaluate) ──
|
|
154
|
+
const AUTOCLICK_SCRIPT = `
|
|
155
|
+
(() => {
|
|
156
|
+
const keywords = [
|
|
157
|
+
"show more", "load more", "gallery", "view images", "view photos",
|
|
158
|
+
"prikaži više", "učitaj još", "galerija", "slike", "fotografije",
|
|
159
|
+
"photos", "images", "see more", "more images", "more photos",
|
|
160
|
+
"expand", "prikazi jos", "jos slika", "vise slika",
|
|
161
|
+
];
|
|
162
|
+
const clicked = [];
|
|
163
|
+
const buttons = Array.from(document.querySelectorAll("button, a, [role=button], .btn, .button, [class*=gallery], [class*=image], [class*=photo], [class*=more], [class*=load]"));
|
|
164
|
+
for (const btn of buttons) {
|
|
165
|
+
const text = (btn.textContent || btn.title || btn.getAttribute("aria-label") || "").toLowerCase();
|
|
166
|
+
const found = keywords.some((k) => text.includes(k));
|
|
167
|
+
if (found && btn.offsetParent !== null) {
|
|
168
|
+
try {
|
|
169
|
+
btn.click();
|
|
170
|
+
btn.scrollIntoView({ behavior: "instant", block: "center" });
|
|
171
|
+
clicked.push(text.slice(0, 100));
|
|
172
|
+
} catch {}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
return clicked;
|
|
176
|
+
})()
|
|
177
|
+
`;
|
|
178
|
+
// ── Node.js API ──
|
|
179
|
+
export async function extractImagesFromPage(page, // Playwright Page
|
|
180
|
+
options = {}) {
|
|
181
|
+
const { scrollFull = false, autoClick = false, iframeScan = false, waitForSelector, minWidth = 0, maxWidth = 99999, limit = 500, } = options;
|
|
182
|
+
// Wait for selector if requested
|
|
183
|
+
if (waitForSelector) {
|
|
184
|
+
try {
|
|
185
|
+
await page.waitForSelector(waitForSelector, { timeout: 10_000 });
|
|
186
|
+
}
|
|
187
|
+
catch {
|
|
188
|
+
debugLog("image-extract", `waitForSelector "${waitForSelector}" timed out, continuing anyway`);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
// Scroll full page to trigger lazy loading
|
|
192
|
+
if (scrollFull) {
|
|
193
|
+
await scrollFullPage(page);
|
|
194
|
+
}
|
|
195
|
+
// Auto-click "load more" buttons
|
|
196
|
+
if (autoClick) {
|
|
197
|
+
for (let i = 0; i < 5; i++) {
|
|
198
|
+
const clicked = await page.evaluate(AUTOCLICK_SCRIPT);
|
|
199
|
+
if (clicked.length === 0)
|
|
200
|
+
break;
|
|
201
|
+
debugLog("image-extract", `Auto-clicked: ${clicked.join(", ")}`);
|
|
202
|
+
await page.waitForTimeout(2500);
|
|
203
|
+
if (scrollFull)
|
|
204
|
+
await scrollFullPage(page);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
// Main extraction
|
|
208
|
+
let images = await page.evaluate(EXTRACTION_SCRIPT);
|
|
209
|
+
// Iframe scan (same-origin only)
|
|
210
|
+
if (iframeScan) {
|
|
211
|
+
const iframeImages = await extractFromIframes(page, options);
|
|
212
|
+
images = images.concat(iframeImages);
|
|
213
|
+
}
|
|
214
|
+
// Deduplicate by URL
|
|
215
|
+
const unique = new Map();
|
|
216
|
+
for (const img of images) {
|
|
217
|
+
if (!unique.has(img.url))
|
|
218
|
+
unique.set(img.url, img);
|
|
219
|
+
}
|
|
220
|
+
images = Array.from(unique.values());
|
|
221
|
+
// Filter by dimensions
|
|
222
|
+
images = images.filter((img) => img.width >= minWidth && img.width <= maxWidth);
|
|
223
|
+
// Limit
|
|
224
|
+
if (limit > 0 && images.length > limit) {
|
|
225
|
+
images = images.slice(0, limit);
|
|
226
|
+
}
|
|
227
|
+
debugLog("image-extract", `Discovered ${images.length} unique images`);
|
|
228
|
+
return images;
|
|
229
|
+
}
|
|
230
|
+
async function scrollFullPage(page) {
|
|
231
|
+
await page.evaluate(async () => {
|
|
232
|
+
const delay = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
233
|
+
const height = document.body.scrollHeight;
|
|
234
|
+
const viewport = window.innerHeight;
|
|
235
|
+
let current = 0;
|
|
236
|
+
while (current < height) {
|
|
237
|
+
current += viewport;
|
|
238
|
+
window.scrollTo(0, current);
|
|
239
|
+
await delay(800);
|
|
240
|
+
}
|
|
241
|
+
window.scrollTo(0, 0);
|
|
242
|
+
await delay(500);
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
async function extractFromIframes(page, options) {
|
|
246
|
+
const frames = page.frames();
|
|
247
|
+
const results = [];
|
|
248
|
+
for (const frame of frames) {
|
|
249
|
+
if (frame === page.mainFrame())
|
|
250
|
+
continue;
|
|
251
|
+
try {
|
|
252
|
+
const url = frame.url();
|
|
253
|
+
if (!url || url === "about:blank")
|
|
254
|
+
continue;
|
|
255
|
+
// Only same-origin frames (cross-origin frames block evaluate)
|
|
256
|
+
const frameImages = await frame.evaluate(EXTRACTION_SCRIPT);
|
|
257
|
+
for (const img of frameImages) {
|
|
258
|
+
img.source = "iframe";
|
|
259
|
+
results.push(img);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
catch {
|
|
263
|
+
// Cross-origin or detached frame — skip
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
return results;
|
|
267
|
+
}
|
|
268
|
+
//# sourceMappingURL=browser-image-extract.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"browser-image-extract.js","sourceRoot":"","sources":["../../src/stealth/browser-image-extract.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { StoredCookie } from "../sessions/types.js";
|
|
1
2
|
export interface BrowserFetchResult {
|
|
2
3
|
html: string;
|
|
3
4
|
status: number;
|
|
@@ -10,6 +11,10 @@ export interface BrowserFetchOptions {
|
|
|
10
11
|
solveCaptcha?: boolean;
|
|
11
12
|
proxyUrl?: string;
|
|
12
13
|
chromeProfile?: string;
|
|
14
|
+
/** Cookies to inject into browser context before navigation */
|
|
15
|
+
cookies?: StoredCookie[];
|
|
16
|
+
/** Block images/fonts/css/tracking for faster loads (default: true for scrape, false for screenshot) */
|
|
17
|
+
blockResources?: boolean;
|
|
13
18
|
}
|
|
14
19
|
export declare function isPlaywrightAvailable(): Promise<boolean>;
|
|
15
20
|
export declare function browserFetch(url: string, options?: BrowserFetchOptions): Promise<BrowserFetchResult & {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"browser.d.ts","sourceRoot":"","sources":["../../src/stealth/browser.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"browser.d.ts","sourceRoot":"","sources":["../../src/stealth/browser.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AA2BzD,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,mBAAmB;IAClC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,+DAA+D;IAC/D,OAAO,CAAC,EAAE,YAAY,EAAE,CAAC;IACzB,wGAAwG;IACxG,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B;AAeD,wBAAsB,qBAAqB,IAAI,OAAO,CAAC,OAAO,CAAC,CAE9D;AA4BD,wBAAsB,YAAY,CAChC,GAAG,EAAE,MAAM,EACX,OAAO,CAAC,EAAE,mBAAmB,GAC5B,OAAO,CAAC,kBAAkB,GAAG;IAAE,UAAU,CAAC,EAAE,MAAM,CAAC;IAAC,aAAa,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC,CAiHhF"}
|
package/dist/stealth/browser.js
CHANGED
|
@@ -2,6 +2,29 @@ import { getTwoCaptchaApiKey } from "../config.js";
|
|
|
2
2
|
import { HUMAN_DELAY_MIN_MS, HUMAN_DELAY_MAX_MS } from "../constants.js";
|
|
3
3
|
import { trySolveCaptcha, hasCaptcha } from "../captcha/index.js";
|
|
4
4
|
import { acquirePage } from "./chrome-profile.js";
|
|
5
|
+
// ── Known tracking/ad domains to block for faster page loads ──
|
|
6
|
+
const BLOCKED_DOMAINS = [
|
|
7
|
+
"google-analytics.com", "googletagmanager.com", "googlesyndication.com",
|
|
8
|
+
"doubleclick.net", "google-analytics.com", "googleadservices.com",
|
|
9
|
+
"facebook.com", "facebook.net", "fbcdn.net",
|
|
10
|
+
"twitter.com", "t.co",
|
|
11
|
+
"linkedin.com",
|
|
12
|
+
"analytics.tiktok.com",
|
|
13
|
+
"snap.licdn.com",
|
|
14
|
+
"bat.bing.com", "clarity.ms",
|
|
15
|
+
"hotjar.com", "mouseflow.com", "fullstory.com",
|
|
16
|
+
"segment.io", "segment.com",
|
|
17
|
+
"mixpanel.com", "amplitude.com", "heap.io",
|
|
18
|
+
"sentry.io", "bugsnag.com",
|
|
19
|
+
"newrelic.com", "nr-data.net",
|
|
20
|
+
"optimizely.com",
|
|
21
|
+
"criteo.com", "criteo.net",
|
|
22
|
+
"outbrain.com", "taboola.com",
|
|
23
|
+
"adnxs.com", "rubiconproject.com",
|
|
24
|
+
"moatads.com", "quantserve.com",
|
|
25
|
+
];
|
|
26
|
+
// Resource types to block for scraping (not screenshots)
|
|
27
|
+
const BLOCKED_RESOURCE_TYPES = new Set(["image", "font", "stylesheet", "media"]);
|
|
5
28
|
let playwrightAvailable = null;
|
|
6
29
|
async function checkPlaywright() {
|
|
7
30
|
if (playwrightAvailable !== null)
|
|
@@ -18,6 +41,22 @@ async function checkPlaywright() {
|
|
|
18
41
|
export async function isPlaywrightAvailable() {
|
|
19
42
|
return checkPlaywright();
|
|
20
43
|
}
|
|
44
|
+
/**
|
|
45
|
+
* Convert StoredCookie[] to Playwright's cookie format for injection.
|
|
46
|
+
*/
|
|
47
|
+
function toPlaywrightCookies(cookies, url) {
|
|
48
|
+
const parsed = new URL(url);
|
|
49
|
+
return cookies.map((c) => ({
|
|
50
|
+
name: c.name,
|
|
51
|
+
value: c.value,
|
|
52
|
+
domain: c.domain || parsed.hostname,
|
|
53
|
+
path: c.path || "/",
|
|
54
|
+
expires: c.expires,
|
|
55
|
+
httpOnly: c.httpOnly,
|
|
56
|
+
secure: c.secure ?? parsed.protocol === "https:",
|
|
57
|
+
sameSite: c.sameSite,
|
|
58
|
+
}));
|
|
59
|
+
}
|
|
21
60
|
export async function browserFetch(url, options) {
|
|
22
61
|
const available = await checkPlaywright();
|
|
23
62
|
if (!available) {
|
|
@@ -28,7 +67,35 @@ export async function browserFetch(url, options) {
|
|
|
28
67
|
proxyUrl: options?.proxyUrl,
|
|
29
68
|
});
|
|
30
69
|
try {
|
|
31
|
-
const { page } = handle;
|
|
70
|
+
const { page, context } = handle;
|
|
71
|
+
// ── Inject accumulated cookies before navigation ──
|
|
72
|
+
if (options?.cookies && options.cookies.length > 0) {
|
|
73
|
+
const pwCookies = toPlaywrightCookies(options.cookies, url);
|
|
74
|
+
await context.addCookies(pwCookies);
|
|
75
|
+
}
|
|
76
|
+
// ── Resource blocking for faster scraping ──
|
|
77
|
+
const shouldBlockResources = options?.blockResources ?? !options?.screenshot;
|
|
78
|
+
if (shouldBlockResources) {
|
|
79
|
+
await page.route("**/*", (route) => {
|
|
80
|
+
const resourceType = route.request().resourceType();
|
|
81
|
+
const requestUrl = route.request().url();
|
|
82
|
+
// Block heavy resource types
|
|
83
|
+
if (BLOCKED_RESOURCE_TYPES.has(resourceType)) {
|
|
84
|
+
return route.abort();
|
|
85
|
+
}
|
|
86
|
+
// Block known tracking domains
|
|
87
|
+
try {
|
|
88
|
+
const reqHost = new URL(requestUrl).hostname;
|
|
89
|
+
if (BLOCKED_DOMAINS.some((d) => reqHost.endsWith(d))) {
|
|
90
|
+
return route.abort();
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
catch {
|
|
94
|
+
// Invalid URL, let it through
|
|
95
|
+
}
|
|
96
|
+
return route.continue();
|
|
97
|
+
});
|
|
98
|
+
}
|
|
32
99
|
// Use "load" instead of "networkidle" — streaming sites (BBC, Binance, etc.)
|
|
33
100
|
// never reach networkidle. After load, we briefly wait for networkidle as a bonus
|
|
34
101
|
// but don't fail if it doesn't happen.
|
|
@@ -41,6 +108,20 @@ export async function browserFetch(url, options) {
|
|
|
41
108
|
page.waitForLoadState("networkidle").catch(() => { }),
|
|
42
109
|
new Promise((r) => setTimeout(r, 5000)),
|
|
43
110
|
]);
|
|
111
|
+
// ── SPA content-readiness check ──
|
|
112
|
+
// Poll for meaningful content (>200 chars text in body) up to 10s for SPAs
|
|
113
|
+
try {
|
|
114
|
+
await page.waitForFunction(() => {
|
|
115
|
+
const body = document.body;
|
|
116
|
+
if (!body)
|
|
117
|
+
return false;
|
|
118
|
+
const text = body.innerText || "";
|
|
119
|
+
return text.length > 200;
|
|
120
|
+
}, { timeout: 10_000 }).catch(() => { });
|
|
121
|
+
}
|
|
122
|
+
catch {
|
|
123
|
+
// Content readiness check timed out — proceed with what we have
|
|
124
|
+
}
|
|
44
125
|
// ── Human-like delay — anti-bot systems flag instant page reads ──
|
|
45
126
|
const humanDelay = HUMAN_DELAY_MIN_MS + Math.random() * (HUMAN_DELAY_MAX_MS - HUMAN_DELAY_MIN_MS);
|
|
46
127
|
await new Promise((r) => setTimeout(r, humanDelay));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"browser.js","sourceRoot":"","sources":["../../src/stealth/browser.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AACnD,OAAO,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AACzE,OAAO,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAClE,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"browser.js","sourceRoot":"","sources":["../../src/stealth/browser.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chrome-profile.d.ts","sourceRoot":"","sources":["../../src/stealth/chrome-profile.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAMH,KAAK,IAAI,GAAG,OAAO,sBAAsB,EAAE,IAAI,CAAC;AAChD,KAAK,cAAc,GAAG,OAAO,sBAAsB,EAAE,cAAc,CAAC;AAGpE,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,IAAI,CAAC;IACX,OAAO,EAAE,cAAc,CAAC;IACxB,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7B,SAAS,EAAE,OAAO,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,kBAAkB;IACjC,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"chrome-profile.d.ts","sourceRoot":"","sources":["../../src/stealth/chrome-profile.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAMH,KAAK,IAAI,GAAG,OAAO,sBAAsB,EAAE,IAAI,CAAC;AAChD,KAAK,cAAc,GAAG,OAAO,sBAAsB,EAAE,cAAc,CAAC;AAGpE,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,IAAI,CAAC;IACX,OAAO,EAAE,cAAc,CAAC;IACxB,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7B,SAAS,EAAE,OAAO,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,kBAAkB;IACjC,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAG1E;AAqJD;;;;;GAKG;AACH,wBAAsB,WAAW,CAAC,OAAO,CAAC,EAAE,kBAAkB,GAAG,OAAO,CAAC,UAAU,CAAC,CAUnF"}
|
|
@@ -46,11 +46,11 @@ async function withProfileLock(profilePath, fn) {
|
|
|
46
46
|
}
|
|
47
47
|
}
|
|
48
48
|
// ── Profile Page Acquisition ──
|
|
49
|
-
async function acquireProfilePage(profilePath, proxyUrl) {
|
|
49
|
+
async function acquireProfilePage(profilePath, proxyUrl, headless = true) {
|
|
50
50
|
const { chromium } = await import("rebrowser-playwright");
|
|
51
51
|
const { timeZone, locale } = Intl.DateTimeFormat().resolvedOptions();
|
|
52
52
|
const launchOptions = {
|
|
53
|
-
headless
|
|
53
|
+
headless,
|
|
54
54
|
args: STEALTH_ARGS,
|
|
55
55
|
timezoneId: timeZone,
|
|
56
56
|
locale,
|
|
@@ -82,7 +82,30 @@ async function acquireProfilePage(profilePath, proxyUrl) {
|
|
|
82
82
|
};
|
|
83
83
|
}
|
|
84
84
|
// ── Pool Page Acquisition (existing behavior) ──
|
|
85
|
-
async function acquirePoolPage(proxyUrl) {
|
|
85
|
+
async function acquirePoolPage(proxyUrl, headless = true) {
|
|
86
|
+
if (!headless) {
|
|
87
|
+
const { chromium } = await import("rebrowser-playwright");
|
|
88
|
+
const { timeZone, locale } = Intl.DateTimeFormat().resolvedOptions();
|
|
89
|
+
const browser = await chromium.launch({
|
|
90
|
+
headless: false,
|
|
91
|
+
args: STEALTH_ARGS,
|
|
92
|
+
...(proxyUrl && { proxy: { server: proxyUrl } }),
|
|
93
|
+
});
|
|
94
|
+
const context = await browser.newContext({
|
|
95
|
+
viewport: DEFAULT_VIEWPORT,
|
|
96
|
+
timezoneId: timeZone,
|
|
97
|
+
locale,
|
|
98
|
+
});
|
|
99
|
+
const page = await context.newPage();
|
|
100
|
+
return {
|
|
101
|
+
page,
|
|
102
|
+
context,
|
|
103
|
+
isProfile: false,
|
|
104
|
+
cleanup: async () => {
|
|
105
|
+
await browser.close();
|
|
106
|
+
},
|
|
107
|
+
};
|
|
108
|
+
}
|
|
86
109
|
const pool = getPool();
|
|
87
110
|
const browser = await pool.acquire(proxyUrl);
|
|
88
111
|
let page;
|
|
@@ -133,8 +156,8 @@ async function acquirePoolPage(proxyUrl) {
|
|
|
133
156
|
export async function acquirePage(options) {
|
|
134
157
|
const profilePath = resolveChromeProfile(options?.chromeProfile);
|
|
135
158
|
if (profilePath) {
|
|
136
|
-
return withProfileLock(profilePath, () => acquireProfilePage(profilePath, options?.proxyUrl));
|
|
159
|
+
return withProfileLock(profilePath, () => acquireProfilePage(profilePath, options?.proxyUrl, options?.headless ?? true));
|
|
137
160
|
}
|
|
138
|
-
return acquirePoolPage(options?.proxyUrl);
|
|
161
|
+
return acquirePoolPage(options?.proxyUrl, options?.headless ?? true);
|
|
139
162
|
}
|
|
140
163
|
//# sourceMappingURL=chrome-profile.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chrome-profile.js","sourceRoot":"","sources":["../../src/stealth/chrome-profile.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AACpD,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACjE,OAAO,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"chrome-profile.js","sourceRoot":"","sources":["../../src/stealth/chrome-profile.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,8 +1,17 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Detect if a response is blocked by anti-bot protection.
|
|
3
|
-
*
|
|
3
|
+
*
|
|
4
|
+
* Uses a layered approach to minimize false positives:
|
|
5
|
+
* 1. Status codes (403/429/503) only trigger block if paired with anti-bot signals
|
|
6
|
+
* 2. Content-based checks only run on small pages (<5KB text)
|
|
7
|
+
* 3. Large pages with navigation are treated as legitimate regardless
|
|
4
8
|
*/
|
|
5
9
|
export declare function isBlocked(html: string, status: number, headers?: Record<string, string>): boolean;
|
|
10
|
+
/**
|
|
11
|
+
* Detect if a page contains a CAPTCHA challenge.
|
|
12
|
+
* Separate from isBlocked — a CAPTCHA page CAN be solved, it's not a hard block.
|
|
13
|
+
*/
|
|
14
|
+
export declare function isCaptchaPage(html: string): boolean;
|
|
6
15
|
/**
|
|
7
16
|
* Detect if a page requires JavaScript rendering to get content.
|
|
8
17
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"detector.d.ts","sourceRoot":"","sources":["../../src/stealth/detector.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"detector.d.ts","sourceRoot":"","sources":["../../src/stealth/detector.ts"],"names":[],"mappings":"AAuGA;;;;;;;GAOG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,OAAO,CA4DjG;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAUnD;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAUtD"}
|