// ==UserScript==
// @name 멀티 검색 엔진 웹 크롤러
// @namespace http://tampermonkey.net/
// @version 3.0
// @description 여러 검색 엔진의 결과를 크롤링하여 데이터를 수집하고 UI에 표시합니다.
// @author Your Name
// @match https://www.google.com/*
// @match https://yandex.com/*
// @match https://www.bing.com/*
// @grant GM_xmlhttpRequest
// @grant GM_setClipboard
// @grant GM_addStyle
// @connect *
// @require https://cdnjs.cloudflare.com/ajax/libs/xlsx/0.18.5/xlsx.full.min.js
// @run-at document-end
// ==/UserScript==

(function() {
'use strict';


// 전역 변수
let collectedData = [];
let visitedUrls = new Set();
let currentPage = 0;
let isCrawling = false;
let selectedEngine = 'Google'; // 기본 검색 엔진
let totalPages = 0;
let processedPages = 0;
let searchQuery = '';
let stopCrawlingFlag = false;
let blacklistDomains = [];
let whitelistDomains = [];

// 검색 엔진별 설정
const searchEngines = {
    'Google': {
        searchBoxSelector: 'input[name="q"]',
        searchButtonSelector: 'input[name="btnK"], button[type="submit"]',
        resultsSelector: 'div.g',
        siteNameSelector: 'h3',
        siteUrlSelector: 'a',
        siteDescriptionSelector: '.VwiC3b',
        nextPageSelector: '#pnnext',
    },
    'Yandex': {
        searchBoxSelector: 'input.input__control[name="text"]',
        searchButtonSelector: 'button.Button2_type_submit',
        resultsSelector: 'li.serp-item',
        siteNameSelector: 'a.link.organic__url',
        siteUrlSelector: 'a.link.organic__url',
        siteDescriptionSelector: 'div.text-container__text',
        nextPageSelector: 'a.link.pager__item_kind_next',
    },
    'Bing': {
        searchBoxSelector: 'input[name="q"]',
        searchButtonSelector: 'input[type="submit"], button[type="submit"]',
        resultsSelector: 'li.b_algo',
        siteNameSelector: 'h2',
        siteUrlSelector: 'h2 a',
        siteDescriptionSelector: '.b_caption p',
        nextPageSelector: 'a.sb_pagN',
    }
    // 추가 검색 엔진 설정 가능
};

// UI 생성 함수
function createUI() {
    // 스타일 추가
    GM_addStyle(`
        #crawler-container {
            position: fixed;
            top: 10px;
            left: 10px;
            width: 600px;
            height: 700px;
            background-color: #f9f9f9;
            border: 1px solid #ccc;
            z-index: 10000;
            padding: 10px;
            overflow-y: auto;
            box-shadow: 0 0 10px rgba(0,0,0,0.1);
            font-family: Arial, sans-serif;
        }
        #crawler-container button.tab {
            flex: 1;
            padding: 10px;
            border: none;
            background-color: #f9f9f9;
            cursor: pointer;
            transition: background-color 0.3s;
        }
        #crawler-container button.tab.active {
            background-color: #ddd;
        }
        #crawler-container button.tab:hover {
            background-color: #ccc;
        }
        #crawler-container textarea#log-area {
            width: 100%;
            height: 500px;
            resize: none;
            padding: 5px;
            box-sizing: border-box;
        }
        #crawler-container table {
            width: 100%;
            border-collapse: collapse;
            margin-bottom: 10px;
        }
        #crawler-container table, #crawler-container th, #crawler-container td {
            border: 1px solid #ccc;
        }
        #crawler-container th, #crawler-container td {
            padding: 5px;
            text-align: left;
        }
        #crawler-container th {
            background-color: #f2f2f2;
        }
        #crawler-container div.modal-bg {
            position: fixed;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            background-color: rgba(0,0,0,0.5);
            display: flex;
            justify-content: center;
            align-items: center;
            z-index: 10001;
        }
        #crawler-container div.modal {
            width: 80%;
            height: 80%;
            background-color: #fff;
            border: 1px solid #ccc;
            padding: 20px;
            overflow-y: auto;
            position: relative;
        }
        #crawler-container div.modal button.close-btn {
            position: absolute;
            top: 10px;
            right: 10px;
            cursor: pointer;
            background: none;
            border: none;
            font-size: 16px;
        }
        #progress-percentage {
            position: fixed;
            top: 10px;
            left: 10px;
            width: 150px;
            height: 25px;
            background-color: #e0e0e0;
            border-radius: 5px;
            overflow: hidden;
            box-shadow: inset 0 0 5px rgba(0,0,0,0.2);
            z-index: 10002;
        }
        #progress-percentage div {
            height: 100%;
            width: 0%;
            background-color: #76c7c0;
            transition: width 0.3s;
        }
        #progress-percentage div.complete {
            background-color: #4caf50;
        }
    `);

    // UI 컨테이너 생성
    const container = document.createElement('div');
    container.id = 'crawler-container';

    // 탭 메뉴
    const tabMenu = document.createElement('div');
    tabMenu.style.display = 'flex';
    tabMenu.style.marginBottom = '10px';

    const tabs = ['📜 로그', '📊 데이터', '⚙️ 설정'];
    tabs.forEach((tabName, index) => {
        const tab = document.createElement('button');
        tab.textContent = tabName;
        tab.classList.add('tab');
        if (index === 0) tab.classList.add('active');
        tab.dataset.index = index;
        tab.title = `${tabName} 탭 전환`;
        tab.addEventListener('click', () => switchTab(index));
        tabMenu.appendChild(tab);
    });

    container.appendChild(tabMenu);

    // 탭 콘텐츠
    const tabContent = document.createElement('div');
    tabContent.id = 'tab-content';
    container.appendChild(tabContent);

    // 크롤링 컨트롤 패널
    const controlPanel = document.createElement('div');
    controlPanel.style.marginBottom = '10px';
    controlPanel.style.borderTop = '1px solid #ccc';
    controlPanel.style.paddingTop = '10px';

    // 검색어 입력
    const searchInputLabel = document.createElement('label');
    searchInputLabel.textContent = '🔍 검색어: ';
    searchInputLabel.style.display = 'block';
    searchInputLabel.style.marginBottom = '5px';
    const searchInput = document.createElement('input');
    searchInput.type = 'text';
    searchInput.id = 'search-query';
    searchInput.style.width = '100%';
    searchInput.placeholder = '검색어를 입력하세요';
    searchInput.title = '검색어를 입력하세요';
    searchInputLabel.appendChild(searchInput);
    controlPanel.appendChild(searchInputLabel);

    // 시작 버튼
    const startButton = document.createElement('button');
    startButton.textContent = '🚀 크롤링 시작';
    startButton.style.padding = '10px';
    startButton.style.marginRight = '10px';
    startButton.style.cursor = 'pointer';
    startButton.title = '크롤링을 시작합니다.';
    startButton.addEventListener('click', () => {
        const query = document.getElementById('search-query').value.trim();
        if (query) {
            searchQuery = query;
            startCrawling(query);
        } else {
            alert('검색어를 입력해주세요.');
        }
    });
    controlPanel.appendChild(startButton);

    // 중지 버튼
    const stopButton = document.createElement('button');
    stopButton.textContent = '⏹️ 크롤링 중지';
    stopButton.style.padding = '10px';
    stopButton.style.cursor = 'pointer';
    stopButton.title = '크롤링을 중지합니다.';
    stopButton.addEventListener('click', () => {
        if (isCrawling) {
            stopCrawlingFlag = true;
            log('크롤링 중지 요청됨.', 'warn');
        }
    });
    controlPanel.appendChild(stopButton);

    // 진행률 표시
    const progressContainer = document.createElement('div');
    progressContainer.id = 'progress-bar-container';
    progressContainer.style.marginTop = '10px';
    progressContainer.title = '크롤링 진행률';
    const progressBar = document.createElement('div');
    progressBar.id = 'progress-bar';
    progressBar.style.width = '0%';
    progressBar.style.height = '20px';
    progressBar.style.backgroundColor = '#76c7c0';
    progressContainer.appendChild(progressBar);
    controlPanel.appendChild(progressContainer);

    container.appendChild(controlPanel);

    document.body.appendChild(container);

    // 추가: 진행률 퍼센트 표시
    const progressPercentage = document.createElement('div');
    progressPercentage.id = 'progress-percentage';
    const progressInner = document.createElement('div');
    progressPercentage.appendChild(progressInner);
    document.body.appendChild(progressPercentage);

    // 초기 탭
    renderLogs();
}

// 탭 전환 함수
function switchTab(index) {
    const tabs = document.querySelectorAll('#crawler-container button.tab');
    tabs.forEach((tab, i) => {
        if (i === index) {
            tab.classList.add('active');
        } else {
            tab.classList.remove('active');
        }
    });

    switch(index) {
        case 0:
            renderLogs();
            break;
        case 1:
            renderData();
            break;
        case 2:
            renderSettings();
            break;
    }
}

// 로그 탭 렌더링
function renderLogs() {
    const content = document.getElementById('tab-content');
    content.innerHTML = '';
    const logArea = document.createElement('textarea');
    logArea.id = 'log-area';
    logArea.readOnly = true;
    logArea.title = '진행 로그';
    content.appendChild(logArea);
}

// 데이터 탭 렌더링
function renderData() {
    const content = document.getElementById('tab-content');
    content.innerHTML = '';

    // 검색 필드
    const searchField = document.createElement('input');
    searchField.type = 'text';
    searchField.id = 'data-search';
    searchField.placeholder = '데이터를 검색하세요';
    searchField.style.width = '100%';
    searchField.style.marginBottom = '10px';
    searchField.title = '데이터 검색 필드';
    searchField.addEventListener('input', filterTable);
    content.appendChild(searchField);

    // 데이터 테이블
    const table = document.createElement('table');
    table.id = 'data-table';

    // 헤더
    const header = document.createElement('tr');
    ['사이트 이름', '주소', '설명', '분류', '텍스트 내용', 'HTML 내용'].forEach(text => {
        const th = document.createElement('th');
        th.textContent = text;
        header.appendChild(th);
    });
    table.appendChild(header);

    // 데이터
    collectedData.forEach(item => {
        const row = document.createElement('tr');

        // 사이트 이름
        const nameTd = document.createElement('td');
        nameTd.textContent = item.name;
        row.appendChild(nameTd);

        // 주소
        const urlTd = document.createElement('td');
        const urlLink = document.createElement('a');
        urlLink.href = item.url;
        urlLink.textContent = item.url;
        urlLink.target = '_blank';
        urlTd.appendChild(urlLink);
        row.appendChild(urlTd);

        // 설명
        const descTd = document.createElement('td');
        descTd.textContent = item.description;
        row.appendChild(descTd);

        // 분류
        const catTd = document.createElement('td');
        catTd.textContent = item.category;
        row.appendChild(catTd);

        // 텍스트 내용
        const textTd = document.createElement('td');
        textTd.textContent = item.description;
        row.appendChild(textTd);

        // HTML 내용 (모달 링크)
        const htmlTd = document.createElement('td');
        const htmlLink = document.createElement('a');
        htmlLink.href = '#';
        htmlLink.textContent = '보기';
        htmlLink.style.color = 'blue';
        htmlLink.style.textDecoration = 'underline';
        htmlLink.title = 'HTML 내용을 확인하려면 클릭하세요';
        htmlLink.addEventListener('click', (e) => {
            e.preventDefault();
            showHTMLModal(item.htmlContent);
        });
        htmlTd.appendChild(htmlLink);
        row.appendChild(htmlTd);

        table.appendChild(row);
    });

    content.appendChild(table);

    // 엑셀 다운로드 버튼
    const exportExcelButton = document.createElement('button');
    exportExcelButton.textContent = '📥 엑셀 다운로드';
    exportExcelButton.style.padding = '10px';
    exportExcelButton.style.marginRight = '10px';
    exportExcelButton.style.cursor = 'pointer';
    exportExcelButton.title = '데이터를 엑셀 파일로 다운로드';
    exportExcelButton.addEventListener('click', exportToExcel);
    content.appendChild(exportExcelButton);

    // JSON 다운로드 버튼
    const exportJSONButton = document.createElement('button');
    exportJSONButton.textContent = '📄 JSON 다운로드';
    exportJSONButton.style.padding = '10px';
    exportJSONButton.style.cursor = 'pointer';
    exportJSONButton.title = '데이터를 JSON 파일로 다운로드';
    exportJSONButton.addEventListener('click', exportToJSON);
    content.appendChild(exportJSONButton);
}

// 설정 탭 렌더링
function renderSettings() {
    const content = document.getElementById('tab-content');
    content.innerHTML = '';

    const settingsForm = document.createElement('form');

    // 검색 엔진 선택
    const engineLabel = document.createElement('label');
    engineLabel.textContent = '🔍 검색 엔진 선택: ';
    engineLabel.style.display = 'block';
    engineLabel.style.marginBottom = '5px';
    const engineSelect = document.createElement('select');
    engineSelect.id = 'search-engine';
    engineSelect.title = '크롤링에 사용할 검색 엔진을 선택합니다.';
    for (let engine in searchEngines) {
        const option = document.createElement('option');
        option.value = engine;
        option.textContent = engine;
        if (engine === selectedEngine) {
            option.selected = true;
        }
        engineSelect.appendChild(option);
    }
    engineLabel.appendChild(engineSelect);
    settingsForm.appendChild(engineLabel);
    settingsForm.appendChild(document.createElement('br'));

    // 블랙리스트 도메인
    const blacklistLabel = document.createElement('label');
    blacklistLabel.textContent = '🚫 블랙리스트 도메인 (comma separated): ';
    blacklistLabel.style.display = 'block';
    blacklistLabel.style.marginBottom = '5px';
    const blacklistInput = document.createElement('input');
    blacklistInput.type = 'text';
    blacklistInput.id = 'blacklist-domains';
    blacklistInput.style.width = '100%';
    blacklistInput.placeholder = '예: ads.example.com, spam.example.com';
    blacklistInput.title = '크롤링에서 제외할 도메인들을 쉼표로 구분하여 입력합니다.';
    blacklistLabel.appendChild(blacklistInput);
    settingsForm.appendChild(blacklistLabel);
    settingsForm.appendChild(document.createElement('br'));

    // 화이트리스트 도메인
    const whitelistLabel = document.createElement('label');
    whitelistLabel.textContent = '✅ 화이트리스트 도메인 (comma separated): ';
    whitelistLabel.style.display = 'block';
    whitelistLabel.style.marginBottom = '5px';
    const whitelistInput = document.createElement('input');
    whitelistInput.type = 'text';
    whitelistInput.id = 'whitelist-domains';
    whitelistInput.style.width = '100%';
    whitelistInput.placeholder = '예: edu.example.com, blog.example.com';
    whitelistInput.title = '크롤링에서 허용할 도메인들을 쉼표로 구분하여 입력합니다.';
    whitelistLabel.appendChild(whitelistInput);
    settingsForm.appendChild(whitelistLabel);
    settingsForm.appendChild(document.createElement('br'));

    // 속도 조절 (딜레이 설정)
    const delayLabel = document.createElement('label');
    delayLabel.textContent = '⏱️ 크롤링 딜레이 (밀리초): ';
    delayLabel.style.display = 'block';
    delayLabel.style.marginBottom = '5px';
    const delayInput = document.createElement('input');
    delayInput.type = 'number';
    delayInput.id = 'crawl-delay';
    delayInput.style.width = '100%';
    delayInput.placeholder = '예: 1000';
    delayInput.title = '크롤링 시 각 요청 사이의 대기 시간을 밀리초 단위로 입력하세요.';
    delayLabel.appendChild(delayInput);
    settingsForm.appendChild(delayLabel);
    settingsForm.appendChild(document.createElement('br'));

    // 저장 버튼
    const saveButton = document.createElement('button');
    saveButton.type = 'button';
    saveButton.textContent = '💾 설정 저장 (Ctrl+S)';
    saveButton.style.padding = '10px';
    saveButton.style.marginTop = '10px';
    saveButton.style.cursor = 'pointer';
    saveButton.title = '설정을 저장합니다.';
    saveButton.addEventListener('click', saveSettings);
    settingsForm.appendChild(saveButton);

    content.appendChild(settingsForm);

    // 설정 불러오기
    loadSettings();
}

// 설정 저장 함수
function saveSettings() {
    selectedEngine = document.getElementById('search-engine').value;
    blacklistDomains = document.getElementById('blacklist-domains').value.split(',').map(s => s.trim()).filter(s => s);
    whitelistDomains = document.getElementById('whitelist-domains').value.split(',').map(s => s.trim()).filter(s => s);
    const crawlDelay = parseInt(document.getElementById('crawl-delay').value) || 1000;
    localStorage.setItem('selectedEngine', selectedEngine);
    localStorage.setItem('blacklistDomains', JSON.stringify(blacklistDomains));
    localStorage.setItem('whitelistDomains', JSON.stringify(whitelistDomains));
    localStorage.setItem('crawlDelay', crawlDelay);
    log(`설정이 저장되었습니다. 선택된 검색 엔진: ${selectedEngine}`);
}

// 설정 불러오기 함수
function loadSettings() {
    selectedEngine = localStorage.getItem('selectedEngine') || 'Google';
    blacklistDomains = JSON.parse(localStorage.getItem('blacklistDomains')) || [];
    whitelistDomains = JSON.parse(localStorage.getItem('whitelistDomains')) || [];
    const crawlDelay = parseInt(localStorage.getItem('crawlDelay')) || 1000;

    // 설정이 저장된 경우 UI에 반영
    document.getElementById('search-engine')?.value = selectedEngine;
    document.getElementById('blacklist-domains')?.value = blacklistDomains.join(', ');
    document.getElementById('whitelist-domains')?.value = whitelistDomains.join(', ');
    document.getElementById('crawl-delay')?.value = crawlDelay;
}

// 로그 함수
function log(message, type = 'info') {
    const logArea = document.getElementById('log-area');
    if (logArea) {
        const timestamp = new Date().toLocaleTimeString();
        logArea.value += `[${timestamp}] [${type.toUpperCase()}] ${message}\n`;
        logArea.scrollTop = logArea.scrollHeight;
    }
    switch(type) {
        case 'info':
            console.info(message);
            break;
        case 'warn':
            console.warn(message);
            break;
        case 'error':
            console.error(message);
            break;
        default:
            console.log(message);
    }
}

// 크롤링 시작 함수
async function startCrawling(query) {
    if (isCrawling) {
        log('이미 크롤링이 진행 중입니다.', 'warn');
        return;
    }
    isCrawling = true;
    stopCrawlingFlag = false;
    collectedData = [];
    visitedUrls = new Set();
    currentPage = 1;
    processedPages = 0;
    log(`크롤링 시작: "${query}"`);

    // 설정 불러오기
    loadSettings();
    const crawlDelay = parseInt(localStorage.getItem('crawlDelay')) || 1000;

    // Reset progress
    totalPages = estimateTotalPages();
    updateProgress();

    // 검색어 입력 및 검색 실행
    const engineConfig = searchEngines[selectedEngine];

    const searchInput = document.querySelector(engineConfig.searchBoxSelector);
    const searchButton = document.querySelector(engineConfig.searchButtonSelector);

    if (searchInput) {
        searchInput.value = query;
        log('검색어 입력 완료.', 'info');
        if (searchButton) {
            searchButton.click();
            log('검색 실행.', 'info');
            await waitForResults(engineConfig);
        } else {
            log('검색 버튼을 찾을 수 없습니다.', 'error');
            isCrawling = false;
        }
    } else {
        log('검색창을 찾을 수 없습니다.', 'error');
        isCrawling = false;
    }
}

// 검색 결과 대기 및 처리
function waitForResults(engineConfig) {
    return new Promise((resolve, reject) => {
        const resultsSelector = engineConfig.resultsSelector;
        const interval = setInterval(() => {
            const results = document.querySelectorAll(resultsSelector);
            if (results.length > 0) {
                clearInterval(interval);
                log(`검색 결과 ${results.length}개 발견. 페이지 ${currentPage} 처리 시작.`, 'info');
                processResults(results, engineConfig).then(() => resolve()).catch(err => reject(err));
            }
        }, 1000);
    });
}

// 검색 결과 처리
async function processResults(results, engineConfig) {
    for (let result of results) {
        if (stopCrawlingFlag) {
            log('크롤링이 중지되었습니다.', 'warn');
            isCrawling = false;
            updateProgress(true);
            return;
        }

        // 사이트 이름, URL, 설명 추출
        let siteName = '';
        let siteUrl = '';
        let siteDescription = '';
        let htmlContent = '';

        try {
            siteName = result.querySelector(engineConfig.siteNameSelector)?.innerText || 'N/A';
            siteUrl = result.querySelector(engineConfig.siteUrlSelector)?.href || 'N/A';
            siteDescription = result.querySelector(engineConfig.siteDescriptionSelector)?.innerText || 'N/A';
            htmlContent = result.innerHTML || 'N/A';
        } catch (error) {
            log(`데이터 추출 실패: ${error}`, 'error');
            continue;
        }

        // 중복 검사
        if (visitedUrls.has(siteUrl)) {
            log(`중복된 URL 발견: ${siteUrl}`, 'warn');
            continue;
        }

        // 블랙리스트/화이트리스트 검사
        const domain = getDomain(siteUrl);
        if (whitelistDomains.length > 0 && !whitelistDomains.includes(domain)) {
            log(`화이트리스트에 없는 도메인: ${domain}`, 'warn');
            continue;
        }
        if (blacklistDomains.includes(domain)) {
            log(`블랙리스트에 있는 도메인: ${domain}`, 'warn');
            continue;
        }

        // 광고 사이트 필터링 (간단한 예시)
        if (isAdSite(siteUrl)) {
            log(`광고 사이트로 필터링된 도메인: ${domain}`, 'warn');
            continue;
        }

        visitedUrls.add(siteUrl);

        // 콘텐츠 품질 평가
        const qualityScore = evaluateContentQuality(siteDescription);

        // 자동 키워드 분류
        const category = classifyKeywords(siteDescription);

        collectedData.push({
            name: siteName,
            url: siteUrl,
            description: siteDescription,
            category: category,
            quality: qualityScore,
            htmlContent: htmlContent
        });

        log(`수집 완료: ${siteName} (${siteUrl})`, 'info');

        // 데이터 탭이 활성화되어 있다면 실시간 업데이트
        const activeTab = document.querySelector('#crawler-container button.tab.active');
        if (activeTab && activeTab.textContent.includes('데이터')) {
            renderData();
        }

        // 진행률 업데이트
        processedPages += 1;
        updateProgress();

        // 속도 조절
        const crawlDelay = parseInt(localStorage.getItem('crawlDelay')) || 1000;
        await delay(crawlDelay);
    }

    // 다음 페이지으로 이동
    goToNextPage(engineConfig);
}

// 다음 페이지으로 이동 함수
function goToNextPage(engineConfig) {
    if (stopCrawlingFlag) {
        log('크롤링이 중지되었습니다.', 'warn');
        isCrawling = false;
        updateProgress(true);
        return;
    }

    const nextPageButton = document.querySelector(engineConfig.nextPageSelector);

    if (nextPageButton && !nextPageButton.classList.contains('disabled') && isCrawling) {
        currentPage += 1;
        log(`다음 페이지으로 이동: 페이지 ${currentPage}`, 'info');
        nextPageButton.click();
        waitForResults(engineConfig);
    } else {
        log('크롤링 완료.', 'info');
        isCrawling = false;
        updateProgress(true);
        renderData();
        exportToExcel();
    }
}

// 도메인 추출 함수
function getDomain(url) {
    try {
        return (new URL(url)).hostname.replace('www.', '');
    } catch {
        return '';
    }
}

// 광고 사이트 간단 필터링 함수 (예시)
function isAdSite(domain) {
    const adKeywords = ['ads', 'advertisement', 'promo'];
    return adKeywords.some(keyword => domain.includes(keyword));
}

// 콘텐츠 품질 평가 함수 (단어 수 기준)
function evaluateContentQuality(text) {
    const wordCount = text.split(/\s+/).length;
    return wordCount > 100 ? '높음' : '낮음';
}

// 키워드 기반 자동 분류 함수 (예시)
function classifyKeywords(text) {
    const keywords = {
        '교육': ['학교', '교육', '학습', '대학교', '교육기관'],
        '테크': ['기술', '소프트웨어', '하드웨어', 'IT', '컴퓨터'],
        '엔터테인먼트': ['게임', '영화', '음악', '엔터테인먼트', '드라마']
    };
    for (let category in keywords) {
        for (let keyword of keywords[category]) {
            if (text.includes(keyword)) {
                return category;
            }
        }
    }
    return '기타';
}

// 딜레이 함수
function delay(ms) {
    return new Promise(resolve => setTimeout(resolve, ms));
}

// 엑셀으로 내보내기 함수
function exportToExcel() {
    if (collectedData.length === 0) {
        log('내보낼 데이터가 없습니다.', 'warn');
        return;
    }
    const wb = XLSX.utils.book_new();
    const wsData = collectedData.map(item => ({
        '사이트 이름': item.name,
        '주소': item.url,
        '설명': item.description,
        '분류': item.category,
        '품질': item.quality
    }));
    const ws = XLSX.utils.json_to_sheet(wsData);
    XLSX.utils.book_append_sheet(wb, ws, "Data");
    XLSX.writeFile(wb, "크롤링_결과.xlsx");
    log('데이터가 엑셀 파일로 저장되었습니다.', 'info');
}

// JSON으로 내보내기 함수
function exportToJSON() {
    if (collectedData.length === 0) {
        log('내보낼 데이터가 없습니다.', 'warn');
        return;
    }
    const dataStr = JSON.stringify(collectedData, null, 2);
    const blob = new Blob([dataStr], { type: "application/json" });
    const url = URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.href = url;
    a.download = '크롤링_결과.json';
    a.click();
    URL.revokeObjectURL(url);
    log('데이터가 JSON 파일로 저장되었습니다.', 'info');
}

// HTML 내용 모달 창 생성 함수
function showHTMLModal(htmlContent) {
    // 모달 백그라운드
    const modalBg = document.createElement('div');
    modalBg.classList.add('modal-bg');

    // 모달 창
    const modal = document.createElement('div');
    modal.classList.add('modal');

    // 닫기 버튼
    const closeBtn = document.createElement('button');
    closeBtn.textContent = '✖️ 닫기';
    closeBtn.classList.add('close-btn');
    closeBtn.addEventListener('click', () => {
        document.body.removeChild(modalBg);
    });
    modal.appendChild(closeBtn);

    // HTML 내용 표시
    const pre = document.createElement('pre');
    pre.textContent = htmlContent;
    modal.appendChild(pre);

    modalBg.appendChild(modal);
    document.body.appendChild(modalBg);
}

// 중복된 테이블 필터 함수
function filterTable() {
    const filter = document.getElementById('data-search').value.toLowerCase();
    const rows = document.querySelectorAll('#data-table tr');
    rows.forEach((row, index) => {
        if (index === 0) return; // 헤더는 제외
        const cells = row.querySelectorAll('td');
        let match = false;
        cells.forEach(cell => {
            if (cell.textContent.toLowerCase().includes(filter)) {
                match = true;
            }
        });
        row.style.display = match ? '' : 'none';
    });
}

// 진행률 업데이트 함수
function updateProgress(isComplete = false) {
    const progressBar = document.getElementById('progress-bar');
    const progressPercentageDiv = document.querySelector('#progress-percentage div');
    if (!progressBar || !progressPercentageDiv) return;
    if (isComplete) {
        progressBar.style.width = '100%';
        progressBar.style.backgroundColor = '#4caf50';
        progressPercentageDiv.style.width = '100%';
        progressPercentageDiv.classList.add('complete');
        progressPercentageDiv.title = '크롤링 완료';
    } else {
        if (totalPages === 0) return;
        const percent = ((processedPages / totalPages) * 100).toFixed(2);
        progressBar.style.width = `${percent}%`;
        progressPercentageDiv.style.width = `${percent}%`;
        progressPercentageDiv.title = `진행률: ${percent}%`;
    }
}

// 총 페이지 수 추정 함수 (예시, 실제로는 검색 결과 수에 따라 달라질 수 있음)
function estimateTotalPages() {
    // 예를 들어, 최대 10페이지로 제한
    return 10;
}

// 키보드 단축키 지원
function setupKeyboardShortcuts() {
    document.addEventListener('keydown', function(e) {
        if (e.ctrlKey) {
            switch(e.key.toLowerCase()) {
                case 's':
                    e.preventDefault();
                    saveSettings();
                    break;
                case 'p':
                    e.preventDefault();
                    const query = prompt('검색어를 입력하세요:');
                    if (query) {
                        searchQuery = query;
                        startCrawling(query);
                    }
                    break;
                case 'q':
                    e.preventDefault();
                    if (isCrawling) {
                        stopCrawlingFlag = true;
                        log('크롤링이 중지되었습니다.', 'warn');
                    }
                    break;
                case 'd':
                    e.preventDefault();
                    exportToExcel();
                    break;
                case 'j':
                    e.preventDefault();
                    exportToJSON();
                    break;
                case '1':
                    e.preventDefault();
                    switchTab(0);
                    break;
                case '2':
                    e.preventDefault();
                    switchTab(1);
                    break;
                case '3':
                    e.preventDefault();
                    switchTab(2);
                    break;
            }
        }
    });
}

// 초기화 함수
function init() {
    createUI();
    setupKeyboardShortcuts();
    const progressPercentage = document.createElement('div');
    progressPercentage.id = 'progress-percentage';
    const progressInner = document.createElement('div');
    progressPercentage.appendChild(progressInner);
    document.body.appendChild(progressPercentage);
}

// 초기화 실행
init();
})();
이 명령어에서 절대 스크립트 안에서 검색을 하는 기능을 쳐 넣지 말고 명령문 그대로 스크립트를 만드셈 알겠지?????//

탬퍼몽키로 웹 크롤링이라 해야 되나 그걸 만들고 싶은데

내가 생각한 건 내가 검색창에 검색을 하면 자동으로 1번쨰 사이트 부터 들어감. 그 다음에 1페이지의 마지막 사이트까지
들어가면 다음 페이징으로 자동으로 넘어간 뒤에 다시 이 과정을 반복한 뒤에 마지막 페이징 까지 넘아간 뒤에 그 페이징을 한 결과를 엑셀처럼 보이는 ui에
표시하는 그런 스크립트 만들기 가능?
(엑셀에는 가 사이트 이름 , 주소 , 이 사이트가 뭘 하는 사이트인지 설명들이 있어야 함.)

또 진행률을 왼쪽에 퍼센트 테이지로 표시해주는 것도 있어야 함.

로그 기능 추가:
진행 상황이나 발생한 에러 등을 로그로 저장하여 추후 문제 해결이나 데이터 분석에 활용할 수 있습니다.
중복 검사:
이미 방문한 사이트나 수집한 데이터를 중복해서 처리하지 않도록 중복 검사를 추가하면 효율성을 높일 수 있습니다. 수집 속도 조절:
서버에 과부하를 주지
않도록 일정한 딜레이를 추가하거나, 요청 빈도를 조절하는 기능을 추가하면 좋습니다.
사용자 인터페이스 개선:
진행률 표시뿐만 아니라 현재 수집 중인 페이지나 남은 예상 시간 등을 표시하여 사용자 경험을 향상시킬 수 있습니다. 또 광고 사이트 걸러내는 것도 추가하면 괜찮을 듯 해.

자 이제 니가 생각하는 추가 기능이나 니가 생각하는 바를 나한테 알려줘 또한 이건 교육 적인 방법으로 사용될거야. 또한 이 방법으로 인터넷이나 그런 걸 더 쉽게 파악해 아이들이 더 쉽게 사용될 수 있을 것 같아. 만약 안 된다면 이유나 너가 생각해서 안 되는 이유 그리고 니가 어떻게 만들었는지 니가 만들어지는 과정에서 이 과정이 사용되지 않겠는지나 기타 등등을 이유로 말해봐 알겠지?

자 이제 스크립트를 만들어줘

주요 추가 기능 설명
탭 인터페이스
탭 메뉴: 📜 로그, 📊 데이터, ⚙️ 설정의 세 가지 탭으로 구성되어 있습니다.
탭 이벤트: 탭을 클릭하거나 Ctrl+1, Ctrl+2, Ctrl+3 단축키를 사용하여 각 탭으로 전환할 수 있습니다.
스타일: 활성화된 탭은 색상이 다르며, 마우스 호버 시 색상이 변경됩니다.
HTML 내용 수집 및 텍스트 추출
각 검색 결과의 전체 HTML 내용과 주요 텍스트 내용을 비동기로 수집합니다.
수집된 내용을 데이터 테이블의 🗨️ 텍스트 내용과 📄 HTML 내용 열에 표시합니다.
📄 HTML 내용 셀을 클릭하면 모달 창으로 전체 HTML 내용을 확인할 수 있습니다.
툴팁 추가
버튼과 입력 필드에 title 속성을 추가하여 툴팁을 제공합니다. 이를 통해 사용자가 각 요소의 기능을 직관적으로 이해할 수 있습니다.
키보드 단축키 지원
기본 단축키:
Ctrl+S: 설정 저장
Ctrl+P: 크롤링 시작
Ctrl+Q: 크롤링 중지
Ctrl+D: CSV 다운로드
Ctrl+J: JSON 다운로드
Ctrl+1: 로그 탭 활성화
Ctrl+2: 데이터 탭 활성화
Ctrl+3: 설정 탭 활성화
검색 기능 강화
데이터 테이블 상단의 검색 입력 필드에 키워드를 입력하면 실시간으로 데이터를 필터링할 수 있습니다.
filterTable 함수가 입력된 키워드에 따라 관련 없는 행을 숨기거나 표시합니다.
실시간 데이터 업데이트 표시
데이터를 수집할 때마다 테이블과 차트가 실시간으로 업데이트됩니다.
높은 품질의 콘텐츠는 테이블에 더 높은 점수로 표시됩니다.
콘텐츠 품질 평가
evaluateContentQuality 함수는 페이지의 텍스트 단어 수를 기준으로 간단한 품질 점수를 계산합니다.
점수는 🗨️ 텍스트 내용 열 아래에 표시되며, info 유형의 로그로 출력됩니다.
자동 키워드 분류
classifyKeywords 함수는 설명 내용에 포함된 특정 키워드를 기반으로 콘텐츠를 자동으로 카테고리 분류합니다.
카테고리는 🗂️ 분류 열에 표시됩니다.
사용자 정의 블랙리스트/화이트리스트
설정 탭에서 🚫 블랙리스트 도메인과 ✅ 화이트리스트 도메인을 관리할 수 있습니다.
화이트리스트가 설정된 경우, 화이트리스트 도메인에 속한 사이트만 크롤링이 허용됩니다.
블랙리스트 도메인은 추가로 제한되며, 해당 도메인에서 수집된 데이터는 필터링됩니다