This function does :
- Visit a URL
- Scroll to load dynamic content
- Dismiss cookie popups
- Take a full-page screenshot
- Extract metadata (title, content, language)
- Save it locally and/or to the database
- Recursively crawl internal links
private async crawlPageImage(
        browser: Browser,
        url: string,
        user: User,
        depth: number,
        visited: Set<string>,
        parent: any,
    ) {
        if (visited.has(url) || depth < 0) return;
        visited.add(url);
        this.logger.log(`🔍 Crawling URL: ${url} (Depth: ${depth})`);
        const existingEntryURL = await this.KnowledgeBaseDetailRepo.findOne({
            where: {
                url,
                user: { id: user.id },
            },
            relations: ['user'],
        });
        const page = await browser.newPage();
        try {
            await page.setUserAgent(
                'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36',
            );
            await page.setExtraHTTPHeaders({ 'Accept-Language': 'en-US,en;q=0.9' });
            await page.setDefaultNavigationTimeout(60000);
            await page.goto(url, { waitUntil: 'networkidle2' });
            // Dismiss cookie modals and popups
            const rejectButtonSelectors = [
                '[id*="reject"]', '[id*="deny"]', '[id*="decline"]',
                '[class*="reject"]', '[class*="deny"]', '[class*="decline"]',
                'button:text("Reject")', 'button:text("Deny")', 'button:text("Decline")',
                'button:text("Manage preferences")'
            ];
            for (const selector of rejectButtonSelectors) {
                try {
                    const button = await page.waitForSelector(selector, { timeout: 2000 });
                    if (button) {
                        await button.click();
                        this.logger.log(`✅ Rejected popup on ${url}`);
                        break;
                    }
                } catch {
                    continue;
                }
            }
            // Scroll slowly to bottom (simulate user scroll to load dynamic content)
            await page.evaluate(async () => {
                const delay = (ms: number) => new Promise((res) => setTimeout(res, ms));
                let totalHeight = 0;
                const distance = 200;
                while (totalHeight < document.body.scrollHeight) {
                    window.scrollBy(0, distance);
                    await delay(500);
                    totalHeight += distance;
                }
            });
            // Scroll to top again for clean screenshot
            await page.evaluate(() => window.scrollTo(0, 0));
            await new Promise((res) => setTimeout(res, 4000));
            // Optional: Force header to be visible (if some JS hides it)
            await page.evaluate(() => {
                const header = document.querySelector('header');
                if (header) {
                    header.style.position = 'relative';
                    header.style.top = '0';
                    header.style.left = '0';
                    header.style.width = '100%';
                    header.style.zIndex = '9999';
                    header.style.display = 'block';
                    header.style.visibility = 'visible';
                    header.style.opacity = '1';
                }
            });
            const { title, content } = await page.evaluate(() => ({
                title: document.title || 'Untitled',
                content: document.body?.innerText || '',
            }));
            const language = langdetect.detectOne(content.slice(0, 5000)) || 'unknown';
            const screenshotBuffer: Buffer = (await page.screenshot({ fullPage: true })) as Buffer;
            const filename = `${(title || 'page')
                .replace(/[^a-zA-Z0-9-_]/g, '_')
                .slice(0, 300)}.png`;
            const folderName = user?.id?.toString();
            const screenshotsDir = path.resolve(__dirname, '..', '..', 'screenshots', folderName);
            if (!fs.existsSync(screenshotsDir)) {
                fs.mkdirSync(screenshotsDir, { recursive: true });
            }
            if (!existingEntryURL) {
                const s3path = path.join(screenshotsDir, filename);
                fs.writeFileSync(s3path, screenshotBuffer);
                // const s3path = await this.s3Service.uploadBuffer(screenshotBuffer, filename, folderName);
                await this.KnowledgeBaseDetailRepo.save(
                    this.KnowledgeBaseDetailRepo.create({
                        url,
                        title,
                        content,
                        language,
                        s3path,
                        status: true,
                        uploadDocument: false,
                        user,
                        parent
                    }),
                );
                this.logger.log(`✅ Saved crawled data: ${url}`);
            }
            if (depth > 0) {
                const internalLinks: string[] = await page.evaluate((currentUrl) => {
                    const anchors = Array.from(document.querySelectorAll('a[href]'));
                    const urls = anchors
                        .map((a) => a.getAttribute('href'))
                        .filter(Boolean)
                        .map((href) => {
                            try {
                                return new URL(href!, currentUrl).href;
                            } catch (e) {
                                return null;
                            }
                        })
                        .filter((href) => href !== null)
                        .filter((href) => href!.startsWith(new URL(currentUrl).origin))
                        .map((href) => href!.split('#')[0]) // remove hash fragments
                        .filter((v, i, self) => self.indexOf(v) === i); // deduplicate
                    return urls;
                }, url);
                for (const link of internalLinks) {
                    if (!visited.has(link)) {
                        await this.crawlPage(browser, link, user, depth - 1, visited, parent);
                    }
                }
            }
        } catch (error) {
            this.logger.warn(`⚠️ Failed to crawl ${url}: ${error.message}`);
        } finally {
            await page.close();
        }
    }
 
 
 
 Posts
Posts
 
 
 
 
 
 
No comments:
Post a Comment