// ==UserScript==
// @name         Advanced Web Crawler Enhanced Pro
// @namespace    http://tampermonkey.net/
// @version      6.0
// @description  고급 웹 크롤러 스크립트 with Enhanced Settings, Improved Accuracy, Resizable UI, and Additional Features
// @author
// @match        https://www.google.com/search*
// @match        https://www.bing.com/search*
// @match        https://search.yahoo.com/search*
// @match        https://duckduckgo.com/*
// @match        https://www.baidu.com/s*
// @match        https://www.yandex.com/search/*
// @match        https://search.naver.com/search*
// @match        https://search.daum.net/search*
// @match        https://www.ask.com/*
// @match        https://www.ecosia.org/search*
// @match        https://www.startpage.com/*
// @match        https://searx.me/*
// @match        https://www.dogpile.com/*
// @match        https://www.bing.com/images*
// @grant        GM_xmlhttpRequest
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_deleteValue
// @connect      *
// @require      https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js
// @require      https://cdn.jsdelivr.net/npm/chart.js
// @require      https://cdnjs.cloudflare.com/ajax/libs/xlsx/0.18.5/xlsx.full.min.js
// ==/UserScript==

(function() {
    'use strict';

    // 기본 설정
    const defaultSettings = {
        // 기존 설정 유지
        /* ... 기존 defaultSettings 코드 ... */
    };

    // 현재 설정 로드
    const settings = {
        // 기존 설정 로드 유지
        /* ... 기존 settings 로드 코드 ... */
    };

    // 크롤링 상태 복원
    let visitedUrls = GM_getValue('visitedUrls', []);
    let logs = GM_getValue('logs', []);
    let data = GM_getValue('data', []);
    let isCrawling = GM_getValue('isCrawling', false);
    let currentPage = GM_getValue('currentPage', 1);
    let totalPages = GM_getValue('totalPages', settings.maxPages);
    let progress = GM_getValue('progress', 0);
    let currentDepth = GM_getValue('currentDepth', 0);
    let activeRequests = 0;
    let requestQueue = [];

    // UI 생성
    function createUI() {
        const style = `
            /* 기존 스타일 유지 */
            /* ... 기존 스타일 코드 ... */
        `;
        $('head').append(`<style>${style}</style>`);

        const container = $(`
            <div id="crawler-container">
                <div id="crawler-tabs">
                    <div class="crawler-tab ${currentTab() === 'logs' ? 'active' : ''}" data-tab="logs" title="로그 보기">📜 로그</div>
                    <div class="crawler-tab ${currentTab() === 'data' ? 'active' : ''}" data-tab="data" title="수집된 데이터 보기">📊 데이터</div>
                    <div class="crawler-tab ${currentTab() === 'settings' ? 'active' : ''}" data-tab="settings" title="설정 변경">⚙️ 설정</div>
                </div>
                <div id="crawler-content">
                    <div id="tab-logs" style="display: ${currentTab() === 'logs' ? 'block' : 'none'};">
                        <div class="progress-bar">
                            <div class="progress-bar-fill" style="width: ${progress.toFixed(2)}%;"></div>
                        </div>
                        <div class="status-text">진행률: ${progress.toFixed(2)}% (${currentPage}/${totalPages} 페이지)</div>
                        <div class="control-buttons">
                            <button id="start-crawling" title="크롤링을 시작합니다." ${isCrawling ? 'disabled' : ''}>▶ 시작</button>
                            <button id="stop-crawling" title="크롤링을 중지합니다." ${isCrawling ? '' : 'disabled'}>■ 중지</button>
                            ${settings.exportFormats.csv ? `<button id="download-csv" title="수집된 데이터를 CSV 파일로 다운로드합니다.">📥 CSV</button>` : ''}
                            ${settings.exportFormats.json ? `<button id="download-json" title="수집된 데이터를 JSON 파일로 다운로드합니다.">📥 JSON</button>` : ''}
                            ${settings.exportFormats.xml ? `<button id="download-xml" title="수집된 데이터를 XML 파일로 다운로드합니다.">📥 XML</button>` : ''}
                            ${settings.exportFormats.xls ? `<button id="download-xls" title="수집된 데이터를 XLS 파일로 다운로드합니다.">📥 XLS</button>` : ''}
                            <!-- 데이터 초기화 버튼 추가 -->
                            <button id="reset-data" title="수집된 데이터와 로그를 초기화합니다." style="background-color: #f44336; color: white;">🗑️ 데이터 초기화</button>
                        </div>
                        <div id="log-messages" style="height: 250px; overflow-y: auto; background: #f9f9f9; padding: 10px; border: 1px solid #ccc; border-radius: 4px;">로그 내용이 여기에 표시됩니다.</div>
                    </div>
                    <div id="tab-data" style="display: ${currentTab() === 'data' ? 'block' : 'none'};">
                        <!-- 기존 데이터 탭 내용 유지 -->
                        <!-- ... 기존 데이터 탭 코드 ... -->
                    </div>
                    <div id="tab-settings" style="display: ${currentTab() === 'settings' ? 'block' : 'none'};">
                        <!-- 기존 설정 탭 내용 유지 -->
                        <!-- ... 기존 설정 탭 코드 ... -->
                    </div>
                </div>

                <!-- 모달 창 -->
                <div id="html-modal" class="modal">
                    <div class="modal-content">
                        <span class="close">&times;</span>
                        <pre id="modal-html-content"></pre>
                    </div>
                </div>
            </div>
        `);
        $('body').append(container);

        // 탭 전환 이벤트
        $('.crawler-tab').on('click', function() {
            const tab = $(this).data('tab');
            $('.crawler-tab').removeClass('active');
            $(this).addClass('active');
            $('#crawler-content > div').hide();
            $(`#tab-${tab}`).show();
            saveCurrentTab(tab);
        });

        // 단축키 이벤트
        $(document).on('keydown', function(e) {
            if (e.ctrlKey) {
                switch (e.key) {
                    case '1':
                        $('.crawler-tab[data-tab="logs"]').click();
                        break;
                    case '2':
                        $('.crawler-tab[data-tab="data"]').click();
                        break;
                    case '3':
                        $('.crawler-tab[data-tab="settings"]').click();
                        break;
                    case 's':
                        e.preventDefault();
                        saveSettings();
                        break;
                    case 'p':
                        e.preventDefault();
                        if (!isCrawling) startCrawling();
                        break;
                    case 'q':
                        e.preventDefault();
                        if (isCrawling) stopCrawling();
                        break;
                    case 'd':
                        e.preventDefault();
                        downloadCSV();
                        break;
                    case 'j':
                        e.preventDefault();
                        downloadJSON();
                        break;
                    case 'x':
                        e.preventDefault();
                        downloadXML();
                        break;
                    case 'l':
                        e.preventDefault();
                        downloadXLS();
                        break;
                }
            }
        });

        // 설정 저장 버튼 이벤트
        $('#save-settings').on('click', saveSettings);
        $('#reset-settings').on('click', resetSettings);

        // 추가/제거 사용자 에이전트 버튼 이벤트
        $('#add-user-agent').on('click', function() {
            const newUA = prompt("새 사용자 에이전트를 입력하세요:");
            if (newUA) {
                settings.userAgents.push(newUA);
                GM_setValue('userAgents', settings.userAgents);
                updateUserAgentSelect();
                log('새 사용자 에이전트가 추가되었습니다.', 'success');
            }
        });

        $('#remove-user-agent').on('click', function() {
            const selectedOptions = $('#user-agent-select').val();
            if (selectedOptions && selectedOptions.length > 0) {
                settings.userAgents = settings.userAgents.filter((_, idx) => !selectedOptions.includes(idx.toString()));
                GM_setValue('userAgents', settings.userAgents);
                settings.userAgentIndex = 0;
                GM_setValue('userAgentIndex', settings.userAgentIndex);
                updateUserAgentSelect();
                log('선택한 사용자 에이전트가 제거되었습니다.', 'success');
            } else {
                alert('제거할 사용자 에이전트를 선택하세요.');
            }
        });

        // 다운로드 버튼 이벤트
        $('#download-csv').on('click', downloadCSV);
        $('#download-json').on('click', downloadJSON);
        $('#download-xml').on('click', downloadXML);
        $('#download-xls').on('click', downloadXLS);

        // 데이터 초기화 버튼 이벤트 추가
        $('#reset-data').on('click', resetData);

        // 검색 필터 기능
        $('#search-filter').on('input', function() {
            filterTable($(this).val());
        });

        // 스케줄링 옵션 표시/숨기기
        $('#schedule-enabled').on('change', function() {
            if ($(this).is(':checked')) {
                $('#schedule-options').slideDown();
            } else {
                $('#schedule-options').slideUp();
            }
        });

        // 데이터 시각화 토글
        $('#data-visualization-checkbox').on('change', function() {
            settings.dataVisualization = $(this).is(':checked');
            GM_setValue('dataVisualization', settings.dataVisualization);
            $('#data-visualization').toggle(settings.dataVisualization);
            log('데이터 시각화 설정이 변경되었습니다.', 'success');
            if (settings.dataVisualization) {
                renderChart();
            }
        });

        // CAPTCHA 처리 토글
        $('#enable-captchas-checkbox').on('change', function() {
            settings.enableCaptchas = $(this).is(':checked');
            GM_setValue('enableCaptchas', settings.enableCaptchas);
            log('CAPTCHA 처리 설정이 변경되었습니다.', 'success');
        });

        // 모달 창 기능
        const modal = document.getElementById("html-modal");
        const span = document.getElementsByClassName("close")[0];
        span.onclick = function() { modal.style.display = "none"; }
        window.onclick = function(event) {
            if (event.target == modal) {
                modal.style.display = "none";
            }
        }

        // 시작 및 중지 버튼 이벤트
        $('#start-crawling').on('click', startCrawling);
        $('#stop-crawling').on('click', stopCrawling);

        // export 버튼 추가 이벤트
        // XML 다운로드
        $('#download-xml').on('click', downloadXML);
        // XLS 다운로드
        $('#download-xls').on('click', downloadXLS);

        // 초기 UI 업데이트
        updateLogsUI();
        updateDataUI();
        if (settings.dataVisualization) {
            renderChart();
        }
    }

    function currentTab() {
        return GM_getValue('currentTab', 'logs');
    }

    function saveCurrentTab(tab) {
        GM_setValue('currentTab', tab);
    }

    function updateUserAgentSelect() {
        $('#user-agent-select').empty();
        settings.userAgents.forEach((ua, idx) => {
            const selected = idx === settings.userAgentIndex ? 'selected' : '';
            $('#user-agent-select').append(`<option value="${idx}" ${selected}>${ua}</option>`);
        });
    }

    // 사용자 지정 키워드 텍스트 생성 함수
    function getCustomKeywordsText() {
        const customKeywords = settings.customKeywords || {};
        return Object.entries(customKeywords).map(([category, kws]) => `${category}:${kws.join(',')}`).join('\n');
    }

    // 사용자 지정 키워드 저장 및 로드 함수
    function parseCustomKeywords(text) {
        const lines = text.split('\n').map(line => line.trim()).filter(line => line);
        const customKeywords = {};
        lines.forEach(line => {
            const [category, kws] = line.split(':');
            if (category && kws) {
                customKeywords[category.trim()] = kws.split(',').map(kw => kw.trim()).filter(kw => kw);
            }
        });
        return customKeywords;
    }

    function combineKeywords() {
        const standardKeywords = {
            '교육': ['학교', '교육', '학생', '교수', '강의'],
            '기술': ['기술', '소프트웨어', '하드웨어', '프로그래밍', '개발'],
            '뉴스': ['뉴스', '속보', '기사', '보도'],
            '엔터테인먼트': ['영화', '음악', '연예', '게임', '드라마'],
            '스포츠': ['축구', '야구', '농구', '경기', '토너먼트'],
            '비즈니스': ['비즈니스', '경제', '주식', '투자', '시장'],
            '건강': ['건강', '의료', '헬스', '운동', '영양'],
            '과학': ['과학', '연구', '실험', '발견', '이론'],
            '문학': ['소설', '시', '문학', '작가', '책'],
            '여행': ['여행', '관광', '숙박', '비행기', '여권']
            // 추가 키워드 및 카테고리
        };
        const customKeywords = settings.customKeywords || {};

        for (const [category, kws] of Object.entries(customKeywords)) {
            if (standardKeywords[category]) {
                standardKeywords[category] = [...new Set([...standardKeywords[category], ...kws])];
            } else {
                standardKeywords[category] = kws;
            }
        }

        return standardKeywords;
    }

    // 로깅 함수
    function log(message, type = 'info') {
        const timestamp = new Date().toLocaleString();
        logs.push({ timestamp, message, type });
        GM_setValue('logs', logs);
        updateLogsUI();

        if (settings.advancedLogging) {
            // type을 콘솔 메서드로 매핑
            switch(type) {
                case 'info':
                    console.info(`[${timestamp}] [INFO] ${message}`);
                    break;
                case 'warning':
                    console.warn(`[${timestamp}] [WARNING] ${message}`);
                    break;
                case 'error':
                    console.error(`[${timestamp}] [ERROR] ${message}`);
                    break;
                case 'success':
                    console.log(`%c[${timestamp}] [SUCCESS] ${message}`, 'color: green; font-weight: bold;');
                    break;
                default:
                    console.log(`[${timestamp}] [${type.toUpperCase()}] ${message}`);
            }
        }
    }

    function updateLogsUI() {
        const logHtml = logs.map(log => `<div>[${log.timestamp}] [${log.type.toUpperCase()}] ${log.message}</div>`).join('');
        $('#log-messages').html(logHtml);
        $('.progress-bar-fill').css('width', `${progress.toFixed(2)}%`);
        $('.status-text').text(`진행률: ${progress.toFixed(2)}% (${currentPage}/${totalPages} 페이지)`);
    }

    // 데이터 업데이트 함수
    function updateDataUI() {
        const filter = $('#search-filter').val().toLowerCase();
        let filteredData = data;
        if (filter) {
            filteredData = data.filter(item =>
                (settings.dataFields.name && item.name.toLowerCase().includes(filter)) ||
                (settings.dataFields.url && item.url.toLowerCase().includes(filter)) ||
                (settings.dataFields.description && item.description.toLowerCase().includes(filter)) ||
                (settings.dataFields.html && item.html.toLowerCase().includes(filter)) ||
                (settings.dataFields.category && item.category.toLowerCase().includes(filter)) ||
                (settings.dataFields.language && item.language && item.language.toLowerCase().includes(filter)) ||
                (settings.dataFields.wordCount && item.wordCount.toString().includes(filter)) ||
                (settings.dataFields.metaTags && item.metaTags && Object.values(item.metaTags).some(v => v.toLowerCase().includes(filter))) ||
                (settings.dataFields.headings && item.headings && item.headings.some(h => h.toLowerCase().includes(filter)))
            );
        }

        let tableHtml = filteredData.map(item => `
            <tr>
                ${settings.dataFields.name ? `<td>${escapeHTML(item.name)}</td>` : ''}
                ${settings.dataFields.url ? `<td><a href="${escapeHTML(item.url)}" target="_blank">${escapeHTML(item.url)}</a></td>` : ''}
                ${settings.dataFields.description ? `<td>${escapeHTML(item.description)}</td>` : ''}
                ${settings.dataFields.html ? `<td><button class="view-html" data-html="${encodeURIComponent(item.html)}" title="HTML 내용을 봅니다.">📄 보기</button></td>` : ''}
                ${settings.dataFields.category ? `<td>${escapeHTML(item.category)}</td>` : ''}
                ${settings.dataFields.quality ? `<td>${escapeHTML(item.quality.toString())}</td>` : ''}
                ${settings.dataFields.language ? `<td>${escapeHTML(item.language)}</td>` : ''}
                ${settings.dataFields.wordCount ? `<td>${escapeHTML(item.wordCount.toString())}</td>` : ''}
                ${settings.dataFields.metaTags ? `<td>${escapeHTML(JSON.stringify(item.metaTags))}</td>` : ''}
                ${settings.dataFields.headings ? `<td>${escapeHTML(item.headings.join(', '))}</td>` : ''}
            </tr>
        `).join('');
        $('#data-table-body').html(tableHtml);

        // HTML 보기 모달
        $('.view-html').off('click').on('click', function() {
            const htmlContent = decodeURIComponent($(this).data('html'));
            $('#modal-html-content').text(htmlContent);
            $('#html-modal').show();
        });

        if (settings.dataVisualization) {
            renderChart();
        }
    }

    // 차트 렌더링 함수
    function renderChart() {
        if (!document.getElementById('data-chart').getContext) return;

        const ctx = document.getElementById('data-chart').getContext('2d');

        // 기존 차트 제거
        if (window.dataChartInstance) {
            window.dataChartInstance.destroy();
        }

        const categories = {};
        data.forEach(item => {
            const category = item.category || '기타';
            if (categories[category]) {
                categories[category]++;
            } else {
                categories[category] = 1;
            }
        });

        const chartData = {
            labels: Object.keys(categories),
            datasets: [{
                label: '카테고리별 데이터 수',
                data: Object.values(categories),
                backgroundColor: generateColors(Object.keys(categories).length)
            }]
        };

        window.dataChartInstance = new Chart(ctx, {
            type: 'pie',
            data: chartData,
            options: {
                responsive: true
            }
        });
    }

    // 색상 생성 함수
    function generateColors(count) {
        const colors = [];
        for(let i=0; i<count; i++) {
            const hue = i * (360 / count);
            colors.push(`hsl(${hue}, 70%, 50%)`);
        }
        return colors;
    }

    // HTML 이스케이프 함수
    function escapeHTML(str) {
        if (!str) return '';
        return str.replace(/&/g, "&amp;")
                  .replace(/</g, "&lt;")
                  .replace(/>/g, "&gt;")
                  .replace(/"/g, "&quot;")
                  .replace(/'/g, "&#039;");
    }

    // CSV, JSON, XML, XLS 다운로드 함수
    function downloadCSV() {
        if (data.length === 0) {
            log('다운로드할 데이터가 없습니다.', 'warning');
            return;
        }
        const headers = [];
        if (settings.dataFields.name) headers.push("사이트 이름");
        if (settings.dataFields.url) headers.push("주소");
        if (settings.dataFields.description) headers.push("설명");
        if (settings.dataFields.html) headers.push("📄 HTML 내용");
        if (settings.dataFields.category) headers.push("분류");
        if (settings.dataFields.quality) headers.push("품질점수");
        if (settings.dataFields.language) headers.push("언어");
        if (settings.dataFields.wordCount) headers.push("단어 수");
        if (settings.dataFields.metaTags) headers.push("메타 태그");
        if (settings.dataFields.headings) headers.push("헤딩");

        const csvRows = [
            headers.join(",")
        ];

        data.forEach(d => {
            const row = [];
            if (settings.dataFields.name) row.push(`"${escapeCSV(d.name)}"`);
            if (settings.dataFields.url) row.push(`"${escapeCSV(d.url)}"`);
            if (settings.dataFields.description) row.push(`"${escapeCSV(d.description)}"`);
            if (settings.dataFields.html) row.push(`"${escapeCSV(d.html)}"`);
            if (settings.dataFields.category) row.push(`"${escapeCSV(d.category)}"`);
            if (settings.dataFields.quality) row.push(`"${d.quality}"`);
            if (settings.dataFields.language) row.push(`"${escapeCSV(d.language)}"`);
            if (settings.dataFields.wordCount) row.push(`"${d.wordCount}"`);
            if (settings.dataFields.metaTags) row.push(`"${escapeCSV(JSON.stringify(d.metaTags))}"`);
            if (settings.dataFields.headings) row.push(`"${escapeCSV(d.headings.join('; '))}"`);
            csvRows.push(row.join(","));
        });

        const csvContent = "data:text/csv;charset=utf-8," + csvRows.join("\n");
        const encodedUri = encodeURI(csvContent);
        const link = document.createElement("a");
        link.setAttribute("href", encodedUri);
        const filename = "data.csv";
        link.setAttribute("download", filename);
        document.body.appendChild(link);
        link.click();
        document.body.removeChild(link);
        log('CSV 파일이 다운로드되었습니다.', 'success');
    }

    function downloadJSON() {
        if (data.length === 0) {
            log('다운로드할 데이터가 없습니다.', 'warning');
            return;
        }
        const jsonContent = JSON.stringify(data, null, 2);
        const blob = new Blob([jsonContent], { type: "application/json" });
        const url = URL.createObjectURL(blob);
        const link = document.createElement("a");
        link.setAttribute("href", url);
        const filename = "data.json";
        link.setAttribute("download", filename);
        document.body.appendChild(link);
        link.click();
        document.body.removeChild(link);
        log('JSON 파일이 다운로드되었습니다.', 'success');
    }

    function downloadXML() {
        if (data.length === 0) {
            log('다운로드할 데이터가 없습니다.', 'warning');
            return;
        }
        let xmlContent = '<?xml version="1.0" encoding="UTF-8"?>\n<data>\n';
        data.forEach(d => {
            xmlContent += `  <entry>\n`;
            if (settings.dataFields.name) xmlContent += `    <name>${escapeXML(d.name)}</name>\n`;
            if (settings.dataFields.url) xmlContent += `    <url>${escapeXML(d.url)}</url>\n`;
            if (settings.dataFields.description) xmlContent += `    <description>${escapeXML(d.description)}</description>\n`;
            if (settings.dataFields.html) xmlContent += `    <html><![CDATA[${d.html}]]></html>\n`;
            if (settings.dataFields.category) xmlContent += `    <category>${escapeXML(d.category)}</category>\n`;
            if (settings.dataFields.quality) xmlContent += `    <quality>${d.quality}</quality>\n`;
            if (settings.dataFields.language) xmlContent += `    <language>${escapeXML(d.language)}</language>\n`;
            if (settings.dataFields.wordCount) xmlContent += `    <wordCount>${d.wordCount}</wordCount>\n`;
            if (settings.dataFields.metaTags) xmlContent += `    <metaTags>${escapeXML(JSON.stringify(d.metaTags))}</metaTags>\n`;
            if (settings.dataFields.headings) xmlContent += `    <headings>${escapeXML(d.headings.join('; '))}</headings>\n`;
            xmlContent += `  </entry>\n`;
        });
        xmlContent += '</data>';

        const blob = new Blob([xmlContent], { type: "application/xml" });
        const url = URL.createObjectURL(blob);
        const link = document.createElement("a");
        link.setAttribute("href", url);
        const filename = "data.xml";
        link.setAttribute("download", filename);
        document.body.appendChild(link);
        link.click();
        document.body.removeChild(link);
        log('XML 파일이 다운로드되었습니다.', 'success');
    }

    function downloadXLS() {
        if (data.length === 0) {
            log('다운로드할 데이터가 없습니다.', 'warning');
            return;
        }
        const worksheet = XLSX.utils.json_to_sheet(data.map(d => {
            const entry = {};
            if (settings.dataFields.name) entry['사이트 이름'] = d.name;
            if (settings.dataFields.url) entry['주소'] = d.url;
            if (settings.dataFields.description) entry['설명'] = d.description;
            if (settings.dataFields.html) entry['📄 HTML 내용'] = d.html;
            if (settings.dataFields.category) entry['분류'] = d.category;
            if (settings.dataFields.quality) entry['품질점수'] = d.quality;
            if (settings.dataFields.language) entry['언어'] = d.language;
            if (settings.dataFields.wordCount) entry['단어 수'] = d.wordCount;
            if (settings.dataFields.metaTags) entry['메타 태그'] = JSON.stringify(d.metaTags);
            if (settings.dataFields.headings) entry['헤딩'] = d.headings.join('; ');
            return entry;
        }));
        const workbook = XLSX.utils.book_new();
        XLSX.utils.book_append_sheet(workbook, worksheet, "Data");
        XLSX.writeFile(workbook, "data.xlsx");
        log('XLS 파일이 다운로드되었습니다.', 'success');
    }

    // XML 이스케이프 함수
    function escapeXML(str) {
        if (!str) return '';
        return str.replace(/&/g, "&amp;")
                  .replace(/</g, "&lt;")
                  .replace(/>/g, "&gt;")
                  .replace(/"/g, "&quot;")
                  .replace(/'/g, "&#039;");
    }

    // CSV 이스케이프 함수
    function escapeCSV(str) {
        if (!str) return '';
        return str.replace(/"/g, '""');
    }

    // 테이블 필터링 함수
    function filterTable(keyword) {
        $('#data-table-body tr').each(function() {
            const rowText = $(this).text().toLowerCase();
            if (rowText.includes(keyword.toLowerCase())) {
                $(this).show();
            } else {
                $(this).hide();
            }
        });
    }

    // 설정 저장 함수
    function saveSettings() {
        const delay = parseInt($('#delay-input').val(), 10);
        settings.delay = isNaN(delay) ? defaultSettings.delay : delay;

        const delayRandom = parseInt($('#delay-random-input').val(), 10);
        settings.delayRandom = isNaN(delayRandom) ? defaultSettings.delayRandom : delayRandom;

        const maxPages = parseInt($('#max-pages-input').val(), 10);
        settings.maxPages = isNaN(maxPages) ? defaultSettings.maxPages : maxPages;

        // 사용자 에이전트 선택
        const selectedUAIndex = parseInt($('#user-agent-select').val(), 10);
        settings.userAgentIndex = isNaN(selectedUAIndex) ? 0 : selectedUAIndex;

        const contentInclude = $('#content-include-input').val().split(',').map(ct => ct.trim()).filter(ct => ct);
        const contentExclude = $('#content-exclude-input').val().split(',').map(ct => ct.trim()).filter(ct => ct);
        settings.contentTypes.include = contentInclude.length > 0 ? contentInclude : defaultSettings.contentTypes.include;
        settings.contentTypes.exclude = contentExclude.length > 0 ? contentExclude : defaultSettings.contentTypes.exclude;

        const timeout = parseInt($('#timeout-input').val(), 10);
        settings.timeout = isNaN(timeout) ? defaultSettings.timeout : timeout;

        const includeKeywords = $('#include-keywords-input').val().split('\n').map(kw => kw.trim()).filter(kw => kw);
        settings.includeKeywords = includeKeywords;

        const excludeKeywords = $('#exclude-keywords-input').val().split('\n').map(kw => kw.trim()).filter(kw => kw);
        settings.excludeKeywords = excludeKeywords;

        const regexInclude = $('#regex-include-input').val().split('\n').map(rx => rx.trim()).filter(rx => rx);
        settings.regexInclude = regexInclude;

        const regexExclude = $('#regex-exclude-input').val().split('\n').map(rx => rx.trim()).filter(rx => rx);
        settings.regexExclude = regexExclude;

        const language = $('#language-select').val() || [];
        settings.language = language;

        const minWordCount = parseInt($('#min-word-count-input').val(), 10);
        settings.minWordCount = isNaN(minWordCount) ? defaultSettings.minWordCount : minWordCount;

        const maxWordCount = parseInt($('#max-word-count-input').val(), 10);
        settings.maxWordCount = isNaN(maxWordCount) ? defaultSettings.maxWordCount : maxWordCount;

        const maxDepth = parseInt($('#max-depth-input').val(), 10);
        settings.maxDepth = isNaN(maxDepth) ? defaultSettings.maxDepth : maxDepth;

        const proxy = $('#proxy-input').val().trim();
        settings.proxy = proxy || defaultSettings.proxy;

        // 데이터 필드 설정
        settings.dataFields.name = $('#field-name').is(':checked');
        settings.dataFields.url = $('#field-url').is(':checked');
        settings.dataFields.description = $('#field-description').is(':checked');
        settings.dataFields.html = $('#field-html').is(':checked');
        settings.dataFields.category = $('#field-category').is(':checked');
        settings.dataFields.quality = $('#field-quality').is(':checked');
        settings.dataFields.language = $('#field-language').is(':checked');
        settings.dataFields.wordCount = $('#field-wordCount').is(':checked');
        settings.dataFields.metaTags = $('#field-metaTags').is(':checked');
        settings.dataFields.headings = $('#field-headings').is(':checked');

        // 재시도 설정
        const retryAttempts = parseInt($('#retry-attempts-input').val(), 10);
        settings.retryAttempts = isNaN(retryAttempts) ? defaultSettings.retryAttempts : retryAttempts;

        const retryDelay = parseInt($('#retry-delay-input').val(), 10);
        settings.retryDelay = isNaN(retryDelay) ? defaultSettings.retryDelay : retryDelay;

        // 스케줄링 설정
        settings.schedule.enabled = $('#schedule-enabled').is(':checked');
        const startTime = $('#start-time-input').val();
        settings.schedule.startTime = startTime || defaultSettings.schedule.startTime;
        const interval = parseInt($('#interval-input').val(), 10);
        settings.schedule.interval = isNaN(interval) ? defaultSettings.schedule.interval : interval * 60000; // 분을 밀리초로 변환

        // 검색 엔진 설정
        const searchEngines = {
            google: $('#engine-google').is(':checked'),
            bing: $('#engine-bing').is(':checked'),
            yahoo: $('#engine-yahoo').is(':checked'),
            duckduckgo: $('#engine-duckduckgo').is(':checked'),
            baidu: $('#engine-baidu').is(':checked'),
            yandex: $('#engine-yandex').is(':checked'),
            naver: $('#engine-naver').is(':checked'),
            daum: $('#engine-daum').is(':checked'),
            ask: $('#engine-ask').is(':checked'),
            ecosia: $('#engine-ecosia').is(':checked'),
            startpage: $('#engine-startpage').is(':checked'),
            searx: $('#engine-searx').is(':checked'),
            dogpile: $('#engine-dogpile').is(':checked'),
            bingimages: $('#engine-bingimages').is(':checked')
            // 추가 검색 엔진 옵션
        };
        settings.searchEngines = searchEngines;

        // robots.txt 준수 설정
        settings.respectRobots = $('#respect-robots-checkbox').is(':checked');

        const blacklist = $('#blacklist-input').val().split('\n').map(d => d.trim()).filter(d => d);
        settings.blacklist = blacklist;

        const whitelist = $('#whitelist-input').val().split('\n').map(d => d.trim()).filter(d => d);
        settings.whitelist = whitelist;

        const maxConcurrentRequests = parseInt($('#max-concurrent-requests-input').val(), 10);
        settings.maxConcurrentRequests = isNaN(maxConcurrentRequests) ? defaultSettings.maxConcurrentRequests : maxConcurrentRequests;

        // 데이터 시각화 설정
        settings.dataVisualization = $('#data-visualization-checkbox').is(':checked');
        GM_setValue('dataVisualization', settings.dataVisualization);

        // CAPTCHA 처리 설정
        settings.enableCaptchas = $('#enable-captchas-checkbox').is(':checked');
        GM_setValue('enableCaptchas', settings.enableCaptchas);

        // 내보내기 형식 설정
        settings.exportFormats.csv = $('#export-csv').is(':checked');
        settings.exportFormats.json = $('#export-json').is(':checked');
        settings.exportFormats.xml = $('#export-xml').is(':checked');
        settings.exportFormats.xls = $('#export-xls').is(':checked');

        // 고급 로깅 설정
        settings.advancedLogging = $('#advanced-logging-checkbox').is(':checked');
        GM_setValue('advancedLogging', settings.advancedLogging);

        // 사용자 지정 키워드 분류 저장
        const customKeywordsText = $('#custom-keywords-input').val();
        const customKeywords = parseCustomKeywords(customKeywordsText);
        settings.customKeywords = customKeywords;
        GM_setValue('customKeywords', customKeywords);

        // 저장
        GM_setValue('delay', settings.delay);
        GM_setValue('delayRandom', settings.delayRandom);
        GM_setValue('maxPages', settings.maxPages);
        GM_setValue('userAgentIndex', settings.userAgentIndex);
        GM_setValue('contentTypes', settings.contentTypes);
        GM_setValue('timeout', settings.timeout);
        GM_setValue('includeKeywords', settings.includeKeywords);
        GM_setValue('excludeKeywords', settings.excludeKeywords);
        GM_setValue('regexInclude', settings.regexInclude);
        GM_setValue('regexExclude', settings.regexExclude);
        GM_setValue('language', settings.language);
        GM_setValue('minWordCount', settings.minWordCount);
        GM_setValue('maxWordCount', settings.maxWordCount);
        GM_setValue('maxDepth', settings.maxDepth);
        GM_setValue('proxy', settings.proxy);
        GM_setValue('dataFields', settings.dataFields);
        GM_setValue('retryAttempts', settings.retryAttempts);
        GM_setValue('retryDelay', settings.retryDelay);
        GM_setValue('schedule', settings.schedule);
        GM_setValue('searchEngines', settings.searchEngines);
        GM_setValue('blacklist', settings.blacklist);
        GM_setValue('whitelist', settings.whitelist);
        GM_setValue('respectRobots', settings.respectRobots);
        GM_setValue('maxConcurrentRequests', settings.maxConcurrentRequests);
        GM_setValue('exportFormats', settings.exportFormats);

        // totalPages 갱신
        totalPages = settings.maxPages;
        GM_setValue('totalPages', totalPages);

        log('설정이 저장되었습니다.', 'success');

        // 스케줄링 설정이 활성화된 경우 스케줄링 시작
        if (settings.schedule.enabled) {
            scheduleCrawling();
        }

        // UI 업데이트
        updateDataUI();
        if (settings.dataVisualization) {
            renderChart();
        }
    }

    // 설정 초기화 함수
    function resetSettings() {
        if (confirm("기본 설정으로 초기화하시겠습니까?")) {
            // 기본 설정으로 되돌리기
            GM_deleteValue('delay');
            GM_deleteValue('delayRandom');
            GM_deleteValue('maxPages');
            GM_deleteValue('userAgents');
            GM_deleteValue('userAgentIndex');
            GM_deleteValue('blacklist');
            GM_deleteValue('whitelist');
            GM_deleteValue('contentTypes');
            GM_deleteValue('timeout');
            GM_deleteValue('includeKeywords');
            GM_deleteValue('excludeKeywords');
            GM_deleteValue('regexInclude');
            GM_deleteValue('regexExclude');
            GM_deleteValue('language');
            GM_deleteValue('minWordCount');
            GM_deleteValue('maxWordCount');
            GM_deleteValue('maxDepth');
            GM_deleteValue('proxy');
            GM_deleteValue('dataFields');
            GM_deleteValue('retryAttempts');
            GM_deleteValue('retryDelay');
            GM_deleteValue('schedule');
            GM_deleteValue('searchEngines');
            GM_deleteValue('exportFormats');
            GM_deleteValue('respectRobots');
            GM_deleteValue('maxConcurrentRequests');
            GM_deleteValue('dataVisualization');
            GM_deleteValue('advancedLogging');
            GM_deleteValue('enableCaptchas');
            GM_deleteValue('customKeywords');

            // 기본 설정 적용
            Object.assign(settings, defaultSettings);
            GM_setValue('userAgents', settings.userAgents);
            GM_setValue('userAgentIndex', settings.userAgentIndex);
            GM_setValue('contentTypes', settings.contentTypes);
            GM_setValue('dataFields', settings.dataFields);
            GM_setValue('exportFormats', settings.exportFormats);
            GM_setValue('advancedLogging', settings.advancedLogging);
            GM_setValue('enableCaptchas', settings.enableCaptchas);

            // UI 업데이트
            $('input, textarea, select').each(function() {
                const id = $(this).attr('id');
                if (settings.hasOwnProperty(id)) {
                    $(this).val(settings[id]);
                }
            });

            // 사용자 지정 키워드 초기화
            $('#custom-keywords-input').val('');

            updateUserAgentSelect();
            updateDataUI();
            renderChart();
            log('기본 설정으로 초기화되었습니다.', 'success');
        }
    }

    // 데이터 초기화 함수
    function resetData() {
        if (confirm("수집된 데이터와 로그를 초기화하시겠습니까? 이 작업은 되돌릴 수 없습니다.")) {
            // 데이터 삭제
            GM_setValue('data', []);
            GM_setValue('logs', []);
            GM_setValue('visitedUrls', []);
            GM_setValue('progress', 0);
            GM_setValue('currentPage', 1);
            GM_setValue('currentDepth', 0);
            GM_setValue('isCrawling', false);

            // 변수 초기화
            data = [];
            logs = [];
            visitedUrls = [];
            progress = 0;
            currentPage = 1;
            currentDepth = 0;
            isCrawling = false;
            requestQueue = [];
            activeRequests = 0;

            // UI 업데이트
            updateLogsUI();
            updateDataUI();
            setButtonStates();

            log('수집된 데이터와 로그가 초기화되었습니다.', 'success');
        }
    }

    // 크롤링 시작 함수
    async function startCrawling() {
        if (isCrawling) {
            log('이미 크롤링이 진행 중입니다.', 'warning');
            return;
        }
        // 활성화된 검색 엔진 확인
        if (!isAnySearchEngineEnabled()) {
            log('사용할 검색 엔진이 하나도 선택되지 않았습니다.', 'error');
            return;
        }
        isCrawling = true;
        GM_setValue('isCrawling', isCrawling);
        log('크롤링을 시작합니다.', 'info');
        setButtonStates();
        currentPage = GM_getValue('currentPage', 1);
        totalPages = GM_getValue('totalPages', settings.maxPages);
        GM_setValue('totalPages', totalPages);
        currentDepth = GM_getValue('currentDepth', 0);
        await crawlPage(currentDepth);
    }

    // 크롤링 중지 함수
    function stopCrawling() {
        if (!isCrawling) {
            log('크롤링이 진행 중이지 않습니다.', 'warning');
            return;
        }
        isCrawling = false;
        GM_setValue('isCrawling', isCrawling);
        log('크롤링이 중지되었습니다.', 'warning');
        setButtonStates();
    }

    // 버튼 상태 설정 함수
    function setButtonStates() {
        if (isCrawling) {
            $('#start-crawling').prop('disabled', true);
            $('#stop-crawling').prop('disabled', false);
        } else {
            $('#start-crawling').prop('disabled', false);
            $('#stop-crawling').prop('disabled', true);
        }
    }

    // 크롤링 함수
    async function crawlPage(currentDepthLevel) {
        try {
            while (isCrawling && currentPage <= totalPages && currentDepthLevel <= settings.maxDepth) {
                log(`페이지 ${currentPage} 크롤링 시작. (깊이: ${currentDepthLevel})`, 'info');

                // robots.txt 준수 체크
                if (settings.respectRobots) {
                    const robotsAllowed = await checkRobotsTxt(window.location.href);
                    if (!robotsAllowed) {
                        log(`robots.txt에 의해 크롤링이 금지된 페이지: ${window.location.href}`, 'warning');
                        break;
                    }
                }

                const searchResults = extractSearchResults();
                if (!searchResults) {
                    log('검색 결과를 찾을 수 없습니다.', 'error');
                    break;
                }

                for (let result of searchResults) {
                    if (!isCrawling) break;

                    const name = result.name;
                    const link = result.url;
                    const description = result.description;

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

                    // 블랙리스트/화이트리스트 검사
                    if (settings.whitelist.length > 0 && !settings.whitelist.some(domain => link.includes(domain))) {
                        log(`화이트리스트에 없는 도메인: ${link}`, 'warning');
                        continue;
                    }
                    if (settings.blacklist.some(domain => link.includes(domain))) {
                        log(`블랙리스트에 있는 도메인: ${link}`, 'warning');
                        continue;
                    }

                    // 언어 필터링 검사
                    if (settings.language.length > 0) {
                        const pageLanguage = await getPageLanguage(link);
                        if (!settings.language.includes(pageLanguage)) {
                            log(`선택된 언어에 맞지 않는 페이지: ${link} (${pageLanguage})`, 'warning');
                            continue;
                        }
                    }

                    // 키워드 포함/제외 검사
                    if (settings.includeKeywords.length > 0 && !settings.includeKeywords.some(kw => link.includes(kw) || description.includes(kw))) {
                        log(`포함 키워드가 없는 URL: ${link}`, 'warning');
                        continue;
                    }
                    if (settings.excludeKeywords.some(kw => link.includes(kw) || description.includes(kw))) {
                        log(`제외 키워드가 포함된 URL: ${link}`, 'warning');
                        continue;
                    }

                    // 정규 표현식 필터링
                    if (settings.regexInclude.length > 0) {
                        const matchesInclude = settings.regexInclude.some(pattern => new RegExp(pattern).test(link) || new RegExp(pattern).test(description));
                        if (!matchesInclude) {
                            log(`정규 표현식 포함 조건에 맞지 않는 URL: ${link}`, 'warning');
                            continue;
                        }
                    }
                    if (settings.regexExclude.length > 0) {
                        const matchesExclude = settings.regexExclude.some(pattern => new RegExp(pattern).test(link) || new RegExp(pattern).test(description));
                        if (matchesExclude) {
                            log(`정규 표현식 제외 조건에 맞는 URL: ${link}`, 'warning');
                            continue;
                        }
                    }

                    // 큐에 추가
                    requestQueue.push({ url: link, depth: currentDepthLevel });

                    // 최대 동시 요청 도달 시 대기
                    if (activeRequests >= settings.maxConcurrentRequests) {
                        break;
                    }
                }

                // 큐 처리 시작
                processQueue();

                // 진행률 업데이트
                progress = (currentPage / totalPages) * 100;
                GM_setValue('progress', progress);
                updateLogsUI();

                // 다음 페이지로 이동
                if (currentPage < totalPages && isCrawling) {
                    const nextPageUrl = getNextPageUrl();
                    if (nextPageUrl) {
                        currentPage++;
                        GM_setValue('currentPage', currentPage);
                        currentDepth++;
                        GM_setValue('currentDepth', currentDepth);
                        log(`다음 페이지로 이동: ${currentPage}`, 'info');
                        await dynamicDelay();
                        window.location.href = nextPageUrl;
                        return; // 페이지 이동 후 크롤링 이어서 하기 위해 함수 종료
                    } else {
                        log('다음 페이지 링크를 찾을 수 없습니다.', 'error');
                        break;
                    }
                } else {
                    break;
                }
            }

            // 큐가 비어있고 모든 요청이 완료된 경우 크롤링 완료
            if (isCrawling && currentPage > totalPages && activeRequests === 0 && requestQueue.length === 0) {
                log('크롤링이 완료되었습니다.', 'success');
                isCrawling = false;
                GM_setValue('isCrawling', isCrawling);
                setButtonStates();
                GM_setValue('progress', 100);
                $('.progress-bar-fill').css('width', `100%`);
                $('.status-text').text(`진행률: 100% (${currentPage}/${totalPages} 페이지)`);
            }
        } catch (error) {
            log(`크롤링 중 오류 발생: ${error.message}`, 'error');
        } finally {
            // 크롤링이 아직 진행 중인 경우 state를 유지
            if (!isCrawling) {
                // 크롤링이 중지된 경우에만 상태를 초기화
                GM_setValue('isCrawling', isCrawling);
                setButtonStates();
                log('크롤링이 중지되었습니다.', 'info');
            }
            // 페이지 이동 시에는 상태를 유지하고, 다음 페이지에서 계속 크롤링
        }
    }

    // 동시 요청 제어 함수
    async function processQueue() {
        while (requestQueue.length > 0 && activeRequests < settings.maxConcurrentRequests) {
            const { url, depth } = requestQueue.shift();
            activeRequests++;
            try {
                await handleRequest(url, depth);
            } catch (error) {
                log(`요청 처리 중 오류 발생: ${url} - ${error.message}`, 'error');
            } finally {
                activeRequests--;
                processQueue();
            }
        }
    }

    async function handleRequest(url, depthLevel) {
        if (!isCrawling) return;

        // 중복 검사
        if (visitedUrls.includes(url)) {
            log(`중복된 URL 발견: ${url}`, 'warning');
            return;
        }

        // 블랙리스트/화이트리스트 검사
        if (settings.whitelist.length > 0 && !settings.whitelist.some(domain => url.includes(domain))) {
            log(`화이트리스트에 없는 도메인: ${url}`, 'warning');
            return;
        }
        if (settings.blacklist.some(domain => url.includes(domain))) {
            log(`블랙리스트에 있는 도메인: ${url}`, 'warning');
            return;
        }

        // 언어 필터링 검사
        if (settings.language.length > 0) {
            const pageLanguage = await getPageLanguage(url);
            if (!settings.language.includes(pageLanguage)) {
                log(`선택된 언어에 맞지 않는 페이지: ${url} (${pageLanguage})`, 'warning');
                return;
            }
        }

        // 키워드 포함/제외 검사
        if (settings.includeKeywords.length > 0 && !settings.includeKeywords.some(kw => url.includes(kw))) {
            log(`포함 키워드가 없는 URL: ${url}`, 'warning');
            return;
        }
        if (settings.excludeKeywords.some(kw => url.includes(kw))) {
            log(`제외 키워드가 포함된 URL: ${url}`, 'warning');
            return;
        }

        // 정규 표현식 필터링
        if (settings.regexInclude.length > 0) {
            const matchesInclude = settings.regexInclude.some(pattern => new RegExp(pattern).test(url));
            if (!matchesInclude) {
                log(`정규 표현식 포함 조건에 맞지 않는 URL: ${url}`, 'warning');
                return;
            }
        }
        if (settings.regexExclude.length > 0) {
            const matchesExclude = settings.regexExclude.some(pattern => new RegExp(pattern).test(url));
            if (matchesExclude) {
                log(`정규 표현식 제외 조건에 맞는 URL: ${url}`, 'warning');
                return;
            }
        }

        // 데이터 수집
        try {
            const response = await fetchWithRetry(url, settings.retryAttempts, settings.retryDelay);
            const contentType = getContentType(response);
            if (!isContentTypeAllowed(contentType)) {
                log(`허용되지 않은 콘텐츠 유형(${contentType})의 URL: ${url}`, 'warning');
                return;
            }

            const pageHtml = response.responseText;
            const parser = new DOMParser();
            const pageDoc = parser.parseFromString(pageHtml, 'text/html');
            const textContent = pageDoc.body ? pageDoc.body.innerText : '';

            // CAPTCHA 감지 및 처리
            if (settings.enableCaptchas && detectCaptcha(pageDoc)) {
                await handleCaptcha(url);
                return;
            }

            // 단어 수 검사
            const wordCount = textContent.split(/\s+/).length;
            if (wordCount < settings.minWordCount) {
                log(`단어 수가 최소 기준 (${settings.minWordCount})에 미치지 못하는 URL: ${url} (${wordCount} 단어)`, 'warning');
                return;
            }
            if (wordCount > settings.maxWordCount) {
                log(`단어 수가 최대 기준 (${settings.maxWordCount})을 초과하는 URL: ${url} (${wordCount} 단어)`, 'warning');
                return;
            }

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

            // 언어 추출
            const language = pageDoc.documentElement.lang || 'unknown';

            // 키워드 분류
            const category = classifyKeywords(textContent);

            // 메타 태그 및 헤딩 추출
            const metaTags = extractMetaTags(pageDoc);
            const headings = extractHeadings(pageDoc);

            // 조건에 맞는 경우에만 데이터 추가
            if (category !== '기타' || qualityScore > 100) { // 예시 조건
                const dataEntry = {};
                if (settings.dataFields.name) dataEntry.name = extractName(pageDoc) || 'No Title';
                if (settings.dataFields.url) dataEntry.url = url;
                if (settings.dataFields.description) dataEntry.description = extractDescription(pageDoc) || 'No Description';
                if (settings.dataFields.html) dataEntry.html = pageHtml;
                if (settings.dataFields.category) dataEntry.category = category;
                if (settings.dataFields.quality) dataEntry.quality = qualityScore;
                if (settings.dataFields.language) dataEntry.language = language;
                if (settings.dataFields.wordCount) dataEntry.wordCount = wordCount;
                if (settings.dataFields.metaTags) dataEntry.metaTags = metaTags;
                if (settings.dataFields.headings) dataEntry.headings = headings;

                data.push(dataEntry);
                GM_setValue('data', data);
                updateDataUI();
                log(`데이터 수집 완료: ${url}`, 'success');

                // 데이터 시각화 업데이트
                if (settings.dataVisualization) {
                    renderChart();
                }
            } else {
                log(`조건에 맞지 않아 데이터에 추가되지 않은 URL: ${url}`, 'info');
            }

            // 링크 추적을 위한 추가 로직 (심층 크롤링)
            if (depthLevel < settings.maxDepth) {
                const additionalLinks = extractLinks(pageDoc);
                for (let additionalLink of additionalLinks) {
                    if (!visitedUrls.includes(additionalLink) && isValidURL(additionalLink)) {
                        requestQueue.push({ url: additionalLink, depth: depthLevel + 1 });
                    }
                }
            }

        } catch (error) {
            log(`데이터 수집 실패: ${url} - ${error.message}`, 'error');
            // 실패한 경우 data에 추가하지 않음
        }

        visitedUrls.push(url);
        GM_setValue('visitedUrls', visitedUrls);
    }

    // 크롤링 함수에 재시도 추가
    async function fetchWithRetry(url, attempts, delayMs) {
        for (let attempt = 1; attempt <= attempts; attempt++) {
            try {
                const response = await GM_xmlhttpRequestPromise(url);
                return response;
            } catch (error) {
                log(`요청 실패 (${attempt}/${attempts}): ${url} - ${error.message}`, 'warning');
                if (attempt < attempts) {
                    await delay(delayMs);
                } else {
                    throw error;
                }
            }
        }
    }

    // 활성화된 검색 엔진이 있는지 확인
    function isAnySearchEngineEnabled() {
        return Object.values(settings.searchEngines).some(enabled => enabled);
    }

    // 사용자 지정 키워드 분류 통합
    function classifyKeywords(text) {
        const standardKeywords = combineKeywords();
        for (const [category, kws] of Object.entries(standardKeywords)) {
            if (kws.some(kw => text.includes(kw))) {
                return category;
            }
        }
        return '기타';
    }

    // 검색 결과 추출 함수 (각 검색 엔진에 맞게 수정됨)
    function extractSearchResults() {
        let results = [];
        // 구글 검색 결과 추출 (업데이트된 셀렉터)
        if (window.location.hostname.includes('google.com') && settings.searchEngines.google) {
            const items = document.querySelectorAll('div.g, div[data-hveid]');
            items.forEach(item => {
                const nameElement = item.querySelector('h3');
                const linkElement = item.querySelector('a');
                let descElement = item.querySelector('.aCOpRe');
                if (!descElement) {
                    descElement = item.querySelector('.VwiC3b');
                }

                const name = nameElement ? nameElement.innerText.trim() : 'No Title';
                const link = linkElement ? linkElement.href : '';
                const description = descElement ? descElement.innerText.trim() : 'No Description';

                if (link && isValidURL(link)) results.push({ name, url: link, description });
            });
        }
        // 빙 검색 결과 추출 (업데이트된 셀렉터)
        else if (window.location.hostname.includes('bing.com') && settings.searchEngines.bing) {
            const items = document.querySelectorAll('.b_algo');
            items.forEach(item => {
                const nameElement = item.querySelector('h2');
                const linkElement = nameElement ? nameElement.querySelector('a') : null;
                const descElement = item.querySelector('.b_caption p');

                const name = nameElement ? nameElement.innerText.trim() : 'No Title';
                const link = linkElement ? linkElement.href : '';
                const description = descElement ? descElement.innerText.trim() : 'No Description';

                if (link && isValidURL(link)) results.push({ name, url: link, description });
            });
        }
        // 야후 검색 결과 추출 (업데이트된 셀렉터)
        else if (window.location.hostname.includes('search.yahoo.com') && settings.searchEngines.yahoo) {
            const items = document.querySelectorAll('div.dd.algo');
            items.forEach(item => {
                const nameElement = item.querySelector('h3.title');
                const linkElement = nameElement ? nameElement.querySelector('a') : null;
                const descElement = item.querySelector('.compText p');

                const name = nameElement ? nameElement.innerText.trim() : 'No Title';
                const link = linkElement ? linkElement.href : '';
                const description = descElement ? descElement.innerText.trim() : 'No Description';

                if (link && isValidURL(link)) results.push({ name, url: link, description });
            });
        }
        // 네이버 검색 결과 추출 (업데이트된 셀렉터)
        else if (window.location.hostname.includes('naver.com') && settings.searchEngines.naver) {
            const items = document.querySelectorAll('.news.mynews.section._prs_nws');
            items.forEach(item => {
                const nameElement = item.querySelector('.news_area .news_tit');
                const linkElement = nameElement ? nameElement.href : '';
                const descElement = item.querySelector('.dsc_wrap');

                const name = nameElement ? nameElement.innerText.trim() : 'No Title';
                const link = linkElement ? linkElement : '';
                const description = descElement ? descElement.innerText.trim() : 'No Description';

                if (link && isValidURL(link)) results.push({ name, url: link, description });
            });
        }
        // 다음 검색 결과 추출 (업데이트된 셀렉터)
        else if (window.location.hostname.includes('daum.net') && settings.searchEngines.daum) {
            const items = document.querySelectorAll('.wrap_cont div');
            items.forEach(item => {
                const nameElement = item.querySelector('a.f_link_b');
                const linkElement = nameElement ? nameElement.href : '';
                const descElement = item.querySelector('.desc');

                const name = nameElement ? nameElement.innerText.trim() : 'No Title';
                const link = linkElement ? linkElement : '';
                const description = descElement ? descElement.innerText.trim() : 'No Description';

                if (link && isValidURL(link)) results.push({ name, url: link, description });
            });
        }
        // DuckDuckGo 검색 결과 추출 (업데이트된 셀렉터)
        else if (window.location.hostname.includes('duckduckgo.com') && settings.searchEngines.duckduckgo) {
            const items = document.querySelectorAll('.result');
            items.forEach(item => {
                const nameElement = item.querySelector('h2 a.result__a');
                const linkElement = nameElement ? nameElement.href : '';
                const descElement = item.querySelector('.result__snippet');

                const name = nameElement ? nameElement.innerText.trim() : 'No Title';
                const link = linkElement ? linkElement : '';
                const description = descElement ? descElement.innerText.trim() : 'No Description';

                if (link && isValidURL(link)) results.push({ name, url: link, description });
            });
        }
        // Baidu 검색 결과 추출 (예시)
        else if (window.location.hostname.includes('baidu.com') && settings.searchEngines.baidu) {
            const items = document.querySelectorAll('.result');
            items.forEach(item => {
                const nameElement = item.querySelector('h3 a');
                const linkElement = nameElement ? nameElement.href : '';
                const descElement = item.querySelector('.c-abstract');

                const name = nameElement ? nameElement.innerText.trim() : 'No Title';
                const link = linkElement ? linkElement : '';
                const description = descElement ? descElement.innerText.trim() : 'No Description';

                if (link && isValidURL(link)) results.push({ name, url: link, description });
            });
        }
        // Yandex 검색 결과 추출 (예시)
        else if (window.location.hostname.includes('yandex.com') && settings.searchEngines.yandex) {
            const items = document.querySelectorAll('.serp-item');
            items.forEach(item => {
                const nameElement = item.querySelector('.organic__title-link');
                const linkElement = nameElement ? nameElement.href : '';
                const descElement = item.querySelector('.text-container');

                const name = nameElement ? nameElement.innerText.trim() : 'No Title';
                const link = linkElement ? linkElement : '';
                const description = descElement ? descElement.innerText.trim() : 'No Description';

                if (link && isValidURL(link)) results.push({ name, url: link, description });
            });
        }
        // Ask.com 검색 결과 추출 (추가된 검색 엔진)
        else if (window.location.hostname.includes('ask.com') && settings.searchEngines.ask) {
            const items = document.querySelectorAll('.PartialSearchResults-item');
            items.forEach(item => {
                const nameElement = item.querySelector('h2 a');
                const linkElement = nameElement ? nameElement.href : '';
                const descElement = item.querySelector('.PartialSearchResults-item-abstract');

                const name = nameElement ? nameElement.innerText.trim() : 'No Title';
                const link = linkElement ? linkElement : '';
                const description = descElement ? descElement.innerText.trim() : 'No Description';

                if (link && isValidURL(link)) results.push({ name, url: link, description });
            });
        }
        // Ecosia 검색 결과 추출 (추가된 검색 엔진)
        else if (window.location.hostname.includes('ecosia.org') && settings.searchEngines.ecosia) {
            const items = document.querySelectorAll('.result-item');
            items.forEach(item => {
                const nameElement = item.querySelector('.result-title a');
                const linkElement = nameElement ? nameElement.href : '';
                const descElement = item.querySelector('.result-description');

                const name = nameElement ? nameElement.innerText.trim() : 'No Title';
                const link = linkElement ? linkElement : '';
                const description = descElement ? descElement.innerText.trim() : 'No Description';

                if (link && isValidURL(link)) results.push({ name, url: link, description });
            });
        }
        // Startpage 검색 결과 추출 (추가된 검색 엔진)
        else if (window.location.hostname.includes('startpage.com') && settings.searchEngines.startpage) {
            const items = document.querySelectorAll('.w-gl__result');
            items.forEach(item => {
                const nameElement = item.querySelector('.w-gl__result-title a');
                const linkElement = nameElement ? nameElement.href : '';
                const descElement = item.querySelector('.w-gl__result-text');

                const name = nameElement ? nameElement.innerText.trim() : 'No Title';
                const link = linkElement ? linkElement : '';
                const description = descElement ? descElement.innerText.trim() : 'No Description';

                if (link && isValidURL(link)) results.push({ name, url: link, description });
            });
        }
        // Searx 검색 결과 추출 (추가된 검색 엔진)
        else if (window.location.hostname.includes('searx') && settings.searchEngines.searx) {
            const items = document.querySelectorAll('.result');
            items.forEach(item => {
                const nameElement = item.querySelector('.result_title a');
                const linkElement = nameElement ? nameElement.href : '';
                const descElement = item.querySelector('.result_desc');

                const name = nameElement ? nameElement.innerText.trim() : 'No Title';
                const link = linkElement ? linkElement : '';
                const description = descElement ? descElement.innerText.trim() : 'No Description';

                if (link && isValidURL(link)) results.push({ name, url: link, description });
            });
        }
        // Dogpile 검색 결과 추출 (추가된 검색 엔진)
        else if (window.location.hostname.includes('dogpile.com') && settings.searchEngines.dogpile) {
            const items = document.querySelectorAll('.result_info');
            items.forEach(item => {
                const nameElement = item.querySelector('h3 a');
                const linkElement = nameElement ? nameElement.href : '';
                const descElement = item.querySelector('.result_snippet');

                const name = nameElement ? nameElement.innerText.trim() : 'No Title';
                const link = linkElement ? linkElement : '';
                const description = descElement ? descElement.innerText.trim() : 'No Description';

                if (link && isValidURL(link)) results.push({ name, url: link, description });
            });
        }
        // Bing Images 검색 결과 추출 (추가된 검색 엔진)
        else if (window.location.hostname.includes('bing.com') && settings.searchEngines.bingimages) {
            const items = document.querySelectorAll('.iusc');
            items.forEach(item => {
                const nameElement = item.querySelector('a.iusc');
                const linkElement = nameElement ? nameElement.href : '';
                const descElement = item.querySelector('.txt');

                const name = nameElement ? nameElement.innerText.trim() : 'No Title';
                const link = linkElement ? linkElement : '';
                const description = descElement ? descElement.innerText.trim() : 'No Description';

                if (link && isValidURL(link)) results.push({ name, url: link, description });
            });
        }
        // 기타 검색 엔진에 대한 추가 로직 필요
        else {
            log('지원되지 않거나 비활성화된 검색 엔진입니다.', 'warning');
        }

        return results.length > 0 ? results : null;
    }

    // 다음 페이지 URL 얻는 함수 (각 검색 엔진에 맞게 수정됨)
    function getNextPageUrl() {
        let nextPageUrl = null;
        // 구글 다음 페이지 링크
        if (window.location.hostname.includes('google.com') && settings.searchEngines.google) {
            const nextLink = document.querySelector('a#pnnext');
            if (nextLink) nextPageUrl = nextLink.href;
        }
        // 빙 다음 페이지 링크
        else if (window.location.hostname.includes('bing.com') && settings.searchEngines.bing) {
            const nextLink = document.querySelector('a.sb_pagN');
            if (nextLink) nextPageUrl = nextLink.href;
        }
        // 야후 다음 페이지 링크
        else if (window.location.hostname.includes('search.yahoo.com') && settings.searchEngines.yahoo) {
            const nextLink = document.querySelector('a.next');
            if (nextLink) nextPageUrl = nextLink.href;
        }
        // 네이버 다음 페이지 링크
        else if (window.location.hostname.includes('naver.com') && settings.searchEngines.naver) {
            const nextLink = document.querySelector('.sc_page_inner a.btn_next');
            if (nextLink) nextPageUrl = nextLink.href;
        }
        // 다음 다음 페이지 링크
        else if (window.location.hostname.includes('daum.net') && settings.searchEngines.daum) {
            const nextLink = document.querySelector('.paging a.btn_next');
            if (nextLink) nextPageUrl = nextLink.href;
        }
        // DuckDuckGo 다음 페이지 링크 (예시)
        else if (window.location.hostname.includes('duckduckgo.com') && settings.searchEngines.duckduckgo) {
            const nextLink = document.querySelector('a.js-next-page');
            if (nextLink) nextPageUrl = nextLink.href;
        }
        // Baidu 다음 페이지 링크 (예시)
        else if (window.location.hostname.includes('baidu.com') && settings.searchEngines.baidu) {
            const nextLink = document.querySelector('.n');
            if (nextLink) nextPageUrl = nextLink.href;
        }
        // Yandex 다음 페이지 링크 (예시)
        else if (window.location.hostname.includes('yandex.com') && settings.searchEngines.yandex) {
            const nextLink = document.querySelector('.pager__item_kind_next a');
            if (nextLink) nextPageUrl = nextLink.href;
        }
        // Ask.com 다음 페이지 링크 (추가된 검색 엔진)
        else if (window.location.hostname.includes('ask.com') && settings.searchEngines.ask) {
            const nextLink = document.querySelector('.pagination a.next');
            if (nextLink) nextPageUrl = nextLink.href;
        }
        // Ecosia 다음 페이지 링크 (추가된 검색 엔진)
        else if (window.location.hostname.includes('ecosia.org') && settings.searchEngines.ecosia) {
            const nextLink = document.querySelector('.pagination a.next');
            if (nextLink) nextPageUrl = nextLink.href;
        }
        // Startpage 다음 페이지 링크 (추가된 검색 엔진)
        else if (window.location.hostname.includes('startpage.com') && settings.searchEngines.startpage) {
            const nextLink = document.querySelector('.pagination a.next');
            if (nextLink) nextPageUrl = nextLink.href;
        }
        // Searx 다음 페이지 링크 (추가된 검색 엔진)
        else if (window.location.hostname.includes('searx') && settings.searchEngines.searx) {
            const nextLink = document.querySelector('.pagination a.next');
            if (nextLink) nextPageUrl = nextLink.href;
        }
        // Dogpile 다음 페이지 링크 (추가된 검색 엔진)
        else if (window.location.hostname.includes('dogpile.com') && settings.searchEngines.dogpile) {
            const nextLink = document.querySelector('.pagination a.next');
            if (nextLink) nextPageUrl = nextLink.href;
        }
        // Bing Images 다음 페이지 링크 (추가된 검색 엔진)
        else if (window.location.hostname.includes('bing.com') && settings.searchEngines.bingimages) {
            const nextLink = document.querySelector('a.sb_pagN');
            if (nextLink) nextPageUrl = nextLink.href;
        }
        // 기타 검색 엔진에 대한 추가 로직 필요

        log(`다음 페이지 URL: ${nextPageUrl || '없음'}`, 'info');
        return nextPageUrl;
    }

    // GM_xmlhttpRequest를 Promise로 래핑
    function GM_xmlhttpRequestPromise(url) {
        return new Promise((resolve, reject) => {
            let requestUrl = url;
            if (settings.proxy) {
                // 프록시 요청을 위한 URL 구성
                requestUrl = `${settings.proxy}?url=${encodeURIComponent(url)}`;
            }

            GM_xmlhttpRequest({
                method: "GET",
                url: requestUrl,
                headers: {
                    'User-Agent': getCurrentUserAgent(),
                    'Accept': '*/*'
                },
                timeout: settings.timeout,
                onload: function(response) {
                    if (response.status >= 200 && response.status < 300) {
                        resolve(response);
                    } else {
                        reject(new Error(`HTTP Status ${response.status}`));
                    }
                },
                onerror: function(error) {
                    reject(new Error('Request failed'));
                },
                ontimeout: function() {
                    reject(new Error('Request timed out'));
                }
            });
        });
    }

    // 현재 사용자 에이전트 얻는 함수
    function getCurrentUserAgent() {
        return settings.userAgents[settings.userAgentIndex % settings.userAgents.length];
    }

    // 콘텐츠 유형 얻는 함수
    function getContentType(response) {
        const contentTypeHeader = response.responseHeaders.split('\n').find(header => header.toLowerCase().startsWith('content-type'));
        return contentTypeHeader ? contentTypeHeader.split(':')[1].trim().split(';')[0] : '';
    }

    // 콘텐츠 유형 허용 여부 검사
    function isContentTypeAllowed(contentType) {
        if (settings.contentTypes.include.length > 0 && !settings.contentTypes.include.includes(contentType)) {
            return false;
        }
        if (settings.contentTypes.exclude.length > 0 && settings.contentTypes.exclude.includes(contentType)) {
            return false;
        }
        return true;
    }

    // 콘텐츠 품질 평가 함수
    function evaluateContentQuality(text) {
        const wordCount = text.split(/\s+/).length;
        return wordCount;
    }

    // 키워드 분류 함수 (사용자 지정 키워드 포함)
    function classifyKeywords(text) {
        const standardKeywords = combineKeywords();
        for (const [category, kws] of Object.entries(standardKeywords)) {
            if (kws.some(kw => text.includes(kw))) {
                return category;
            }
        }
        return '기타';
    }

    // 메타 태그 추출 함수
    function extractMetaTags(pageDoc) {
        const metaTags = {};
        const metas = pageDoc.querySelectorAll('meta');
        metas.forEach(meta => {
            const name = meta.getAttribute('name') || meta.getAttribute('property');
            const content = meta.getAttribute('content');
            if (name && content) {
                metaTags[name.toLowerCase()] = content;
            }
        });
        return metaTags;
    }

    // 헤딩 추출 함수
    function extractHeadings(pageDoc) {
        const headings = [];
        for (let i = 1; i <= 6; i++) {
            const hTags = pageDoc.querySelectorAll(`h${i}`);
            hTags.forEach(h => {
                headings.push(h.innerText.trim());
            });
        }
        return headings;
    }

    // URL 유효성 검사 함수
    function isValidURL(url) {
        try {
            new URL(url);
            return true;
        } catch (_) {
            return false;
        }
    }

    // robots.txt 확인 함수
    async function checkRobotsTxt(url) {
        try {
            const parsedUrl = new URL(url);
            const robotsUrl = `${parsedUrl.origin}/robots.txt`;
            const response = await GM_xmlhttpRequestPromise(robotsUrl);
            const robotsText = response.responseText;
            const parser = new RobotsParser(robotsUrl, robotsText);
            return parser.isAllowed(url, getCurrentUserAgent());
        } catch (error) {
            log(`robots.txt 확인 실패: ${error.message}`, 'warning');
            // robots.txt를 가져올 수 없으면 기본적으로 허용
            return true;
        }
    }

    // robots-parser 라이브러리 (간단한 구현)
    class RobotsParser {
        constructor(url, text) {
            this.rules = [];
            this.parse(text);
        }

        parse(text) {
            const lines = text.split('\n');
            let userAgents = [];
            for (let line of lines) {
                line = line.trim();
                if (line.startsWith('#') || line === '') continue;
                const parts = line.split(':');
                if (parts.length < 2) continue;
                const key = parts[0].trim().toLowerCase();
                const value = parts.slice(1).join(':').trim();
                if (key === 'user-agent') {
                    userAgents = [value.toLowerCase()];
                } else if (key === 'disallow') {
                    this.rules.push({ userAgents: [...userAgents], disallow: value });
                } else if (key === 'allow') {
                    this.rules.push({ userAgents: [...userAgents], allow: value });
                }
            }
        }

        isAllowed(url, ua) {
            const path = new URL(url).pathname;
            let allowed = true;
            for (let rule of this.rules) {
                if (rule.userAgents.includes('*') || rule.userAgents.includes(ua.toLowerCase())) {
                    if (rule.disallow && path.startsWith(rule.disallow)) {
                        allowed = false;
                    }
                    if (rule.allow && path.startsWith(rule.allow)) {
                        allowed = true;
                    }
                }
            }
            return allowed;
        }
    }

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

    // 설정 초기화 함수 이미 구현됨

    // 모달 창 기능 이미 구현됨

    // 시작 및 중지 버튼 기능 이미 구현됨

    // 기타 함수 이미 포함됨

    function init() {
        createUI();
        if (isCrawling) {
            log('크롤링이 이전에 중단된 상태에서 다시 시작됩니다.', 'info');
            setButtonStates();
            crawlPage(currentDepth); // 페이지 로드 후 크롤링 재개
        } else {
            log('크롤러가 초기화되었습니다.', 'info');
        }
    }

    // 페이지 로드 시 초기화
    window.addEventListener('load', init);

    // 추가적인 기능: HTML 내용 수집 및 텍스트 추출, 데이터 테이블에 표시, 모달 창으로 HTML 보기
    // 이미 기존 스크립트에서 구현됨

})();                                                                                                                                                                                    이걸를 바탕으로                                                                                                                            // ==UserScript==
// @name         Advanced Web Crawler
// @namespace    http://tampermonkey.net/
// @version      2.2
// @description  자동 웹 크롤러 with UI, logging, and various enhanced features
// @author       Your Name
// @match        *://*/*
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_download
// @grant        GM_xmlhttpRequest
// @connect      *
// @require      https://code.jquery.com/jquery-3.6.0.min.js
// @require      https://cdnjs.cloudflare.com/ajax/libs/xlsx/0.18.5/xlsx.full.min.js
// ==/UserScript==

(function() {
    'use strict';

    // ====== 기본 설정 ======
    const defaultSettings = {
        delay: 3000, // 딜레이 (밀리초)
        blacklist: [], // 블랙리스트 도메인
        whitelist: [], // 화이트리스트 도메인
        searchEngines: ['google'], // 기본 검색 엔진
        defaultQuery: '예시 검색어' // 기본 검색어
    };

    const settings = GM_getValue('settings', defaultSettings);

    // ====== 크롤링 상태 관리 ======
    let crawling = false;
    let crawledUrls = new Set(GM_getValue('crawledUrls', []));
    let totalUrls = 0;

    // ====== UI 생성 ======
    const createUI = () => {
        // 스타일 추가
        const style = `
            /* UI 스타일링 */
            #crawler-ui {
                position: fixed;
                top: 10px;
                right: 10px;
                width: 600px;
                height: 800px;
                background: white;
                border: 2px solid #333;
                border-radius: 8px;
                z-index: 10000;
                font-family: Arial, sans-serif;
                box-shadow: 0 4px 8px rgba(0,0,0,0.2);
                resize: both;
                overflow: hidden;
                min-width: 400px;
                min-height: 600px;
            }
            #crawler-header {
                background: #333;
                color: white;
                padding: 10px;
                cursor: move;
                user-select: none;
                display: flex;
                justify-content: space-between;
                align-items: center;
            }
            #crawler-header .title {
                font-size: 16px;
                font-weight: bold;
            }
            #crawler-header .close-btn {
                cursor: pointer;
                font-size: 18px;
                font-weight: bold;
            }
            #crawler-tabs {
                display: flex;
                border-bottom: 1px solid #ccc;
            }
            .crawler-tab {
                flex: 1;
                padding: 10px;
                text-align: center;
                cursor: pointer;
                background: #f9f9f9;
                user-select: none;
            }
            .crawler-tab.active {
                background: #e0e0e0;
                font-weight: bold;
            }
            #crawler-content {
                padding: 10px;
                height: calc(100% - 80px);
                overflow: auto;
            }
            .progress-bar {
                width: 100%;
                background: #f3f3f3;
                border: 1px solid #ccc;
                height: 20px;
                position: relative;
                margin-bottom: 10px;
            }
            .progress {
                background: #4caf50;
                height: 100%;
                width: 0%;
            }
            table {
                width: 100%;
                border-collapse: collapse;
                margin-top: 10px;
            }
            th, td {
                border: 1px solid #ddd;
                padding: 8px;
                text-align: left;
                word-break: break-word;
            }
            th {
                background-color: #f2f2f2;
            }
            tr:hover {
                background-color: #f1f1f1;
            }
            /* 버튼 스타일 */
            .btn {
                padding: 8px 12px;
                margin: 5px 2px;
                border: none;
                border-radius: 4px;
                cursor: pointer;
                font-size: 14px;
            }
            .btn-start { background-color: #4caf50; color: white; }
            .btn-stop { background-color: #f44336; color: white; }
            .btn-reset { background-color: #ff9800; color: white; }
            .btn-download { background-color: #2196f3; color: white; }
            /* 모달 스타일 */
            .modal {
                display: none;
                position: fixed;
                z-index: 10001;
                left: 0;
                top: 0;
                width: 100%;
                height: 100%;
                overflow: auto;
                background-color: rgba(0,0,0,0.4);
            }
            .modal-content {
                background-color: #fff;
                margin: 5% auto;
                padding: 20px;
                border: 1px solid #888;
                width: 80%;
                height: 80%;
                overflow: auto;
                border-radius: 8px;
                position: relative;
                font-family: monospace;
            }
            .close {
                color: #aaa;
                position: absolute;
                top: 10px;
                right: 25px;
                font-size: 28px;
                font-weight: bold;
                cursor: pointer;
            }
            .close:hover,
            .close:focus {
                color: black;
                text-decoration: none;
                cursor: pointer;
            }
            /* 로그 스타일 */
            #main-log-container {
                margin-bottom: 10px;
            }
            #main-log {
                background: #e7f3fe;
                color: #31708f;
                padding: 5px;
                border: 1px solid #bce8f1;
                border-radius: 4px;
                max-height: 200px;
                overflow-y: auto;
                white-space: pre-wrap;
            }
            #bug-log-container {
                margin-top: 10px;
            }
            #bug-log {
                background: #f8d7da;
                color: #a94442;
                padding: 5px;
                border: 1px solid #f5c6cb;
                border-radius: 4px;
                max-height: 200px;
                overflow-y: auto;
                white-space: pre-wrap;
            }
        `;
        $('head').append(`<style>${style}</style>`);

        // UI 구조
        const ui = `
            <div id="crawler-ui">
                <div id="crawler-header">
                    <span class="title">🔍 Advanced Web Crawler</span>
                    <span class="close-btn">&times;</span>
                </div>
                <div id="crawler-tabs">
                    <div class="crawler-tab active" data-tab="logs">📜 로그</div>
                    <div class="crawler-tab" data-tab="data">📊 데이터</div>
                    <div class="crawler-tab" data-tab="settings">⚙️ 설정</div>
                    <div class="crawler-tab" data-tab="saved">💾 저장된 사이트</div>
                </div>
                <div id="crawler-content">
                    <div id="logs" class="tab-content">
                        <div id="main-log-container">
                            <div style="font-weight: bold;">✅ 종합 로그</div>
                            <div id="main-log" readonly></div>
                        </div>
                        <div id="bug-log-container">
                            <div style="font-weight: bold;">❌ 버그 로그</div>
                            <div id="bug-log" readonly></div>
                        </div>
                    </div>
                    <div id="data" class="tab-content" style="display:none;">
                        <div class="progress-bar">
                            <div class="progress" id="progress"></div>
                        </div>
                        <table id="data-table">
                            <thead>
                                <tr>
                                    <th>사이트 이름</th>
                                    <th>주소</th>
                                    <th>설명</th>
                                    <th>분류</th>
                                    <th>질 점수</th>
                                    <th>텍스트 내용 🗨️</th>
                                    <th>HTML 내용 📄</th>
                                </tr>
                            </thead>
                            <tbody></tbody>
                        </table>
                        <button class="btn btn-download" id="download-csv">CSV 다운로드</button>
                        <button class="btn btn-download" id="download-json">JSON 다운로드</button>
                    </div>
                    <div id="settings" class="tab-content" style="display:none;">
                        <h3>크롤링 설정</h3>
                        <label>딜레이 (밀리초):</label>
                        <input type="number" id="delay-input" value="${settings.delay}" title="크롤링 딜레이 설정"><br><br>
                        <label>블랙리스트 도메인 (콤마로 구분):</label>
                        <input type="text" id="blacklist-input" value="${settings.blacklist.join(',')}" title="블랙리스트 도메인"><br><br>
                        <label>화이트리스트 도메인 (콤마로 구분):</label>
                        <input type="text" id="whitelist-input" value="${settings.whitelist.join(',')}" title="화이트리스트 도메인"><br><br>
                        <label>검색 엔진 선택:</label><br>
                        <select id="search-engine-select" multiple size="4">
                            <option value="google" ${settings.searchEngines.includes('google') ? 'selected' : ''}>Google</option>
                            <option value="bing" ${settings.searchEngines.includes('bing') ? 'selected' : ''}>Bing</option>
                            <option value="duckduckgo" ${settings.searchEngines.includes('duckduckgo') ? 'selected' : ''}>DuckDuckGo</option>
                            <option value="yahoo" ${settings.searchEngines.includes('yahoo') ? 'selected' : ''}>Yahoo</option>
                            <!-- 추가 검색 엔진 옵션 -->
                        </select><br><br>
                        <button class="btn btn-start" id="start-crawling">시작</button>
                        <button class="btn btn-stop" id="stop-crawling">중지</button>
                        <button class="btn btn-reset" id="reset-crawling">초기화</button>
                        <button class="btn btn-download" id="save-settings">설정 저장 (Ctrl+S)</button>
                    </div>
                    <div id="saved" class="tab-content" style="display:none;">
                        <h3>저장된 사이트 관리</h3>
                        <table id="saved-table">
                            <thead>
                                <tr>
                                    <th>사이트 이름</th>
                                    <th>주소</th>
                                    <th>삭제</th>
                                </tr>
                            </thead>
                            <tbody></tbody>
                        </table>
                        <button class="btn btn-download" id="clear-saved">모두 삭제</button>
                    </div>
                </div>
            </div>
            <!-- 모달 -->
            <div id="html-modal" class="modal">
                <div class="modal-content">
                    <span class="close">&times;</span>
                    <pre id="modal-content"></pre>
                </div>
            </div>
        `;
        $('body').append(ui);

        // 드래그 가능한 헤더 설정
        makeDraggable(document.getElementById("crawler-ui"), document.getElementById("crawler-header"));

        // 탭 이벤트
        $('.crawler-tab').on('click', function() {
            $('.crawler-tab').removeClass('active');
            $(this).addClass('active');
            const tab = $(this).data('tab');
            $('.tab-content').hide();
            $(`#${tab}`).show();
        });

        // 모달 이벤트
        $('.close').on('click', function() {
            $('#html-modal').hide();
        });
        $(window).on('click', function(event) {
            if (event.target.id === 'html-modal') {
                $('#html-modal').hide();
            }
        });

        // 설정 저장 이벤트
        $('#save-settings').on('click', saveSettings);

        // 크롤링 제어 버튼 이벤트
        $('#start-crawling').on('click', startCrawling);
        $('#stop-crawling').on('click', stopCrawling);
        $('#reset-crawling').on('click', resetCrawling);

        // 다운로드 버튼 이벤트
        $('#download-csv').on('click', downloadCSV);
        $('#download-json').on('click', downloadJSON);

        // 저장된 사이트 관리 이벤트
        $('#clear-saved').on('click', clearSavedSites);
        populateSavedSites();

        // 키보드 단축키 설정
        setupKeyboardShortcuts();
    };

    // ====== 드래그 가능 UI ======
    function makeDraggable(elmnt, handle) {
        let pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0;
        handle.onmousedown = dragMouseDown;

        function dragMouseDown(e) {
            e = e || window.event;
            e.preventDefault();
            // Get the mouse cursor position at startup:
            pos3 = e.clientX;
            pos4 = e.clientY;
            document.onmouseup = closeDragElement;
            document.onmousemove = elementDrag;
        }
        function elementDrag(e) {
            e = e || window.event;
            e.preventDefault();
            // Calculate the new cursor position:
            pos1 = pos3 - e.clientX;
            pos2 = pos4 - e.clientY;
            pos3 = e.clientX;
            pos4 = e.clientY;
            // Set the element's new position:
            elmnt.style.top = (elmnt.offsetTop - pos2) + "px";
            elmnt.style.left = (elmnt.offsetLeft - pos1) + "px";
        }
        function closeDragElement() {
            // Stop moving when mouse button is released:
            document.onmouseup = null;
            document.onmousemove = null;
        }
    }

    // ====== 설정 저장 ======
    const saveSettings = () => {
        const delay = parseInt($('#delay-input').val()) || 3000;
        const blacklist = $('#blacklist-input').val().split(',').map(s => s.trim()).filter(s => s);
        const whitelist = $('#whitelist-input').val().split(',').map(s => s.trim()).filter(s => s);
        const searchEngines = $('#search-engine-select').val() || [];

        settings.delay = delay;
        settings.blacklist = blacklist;
        settings.whitelist = whitelist;
        settings.searchEngines = searchEngines;

        GM_setValue('settings', settings);
        log('설정이 저장되었습니다.', 'info');
        refreshSearchEngineOptions();
    };

    // ====== 설정 로드 ======
    const loadSettings = () => {
        const saved = GM_getValue('settings');
        if (saved) {
            settings.delay = saved.delay;
            settings.blacklist = saved.blacklist;
            settings.whitelist = saved.whitelist;
            settings.searchEngines = saved.searchEngines;
        }
    };

    // ====== 로그 기능 ======
    const log = (message, type = 'info') => {
        const timestamp = new Date().toLocaleTimeString();
        const formattedMessage = `${timestamp} [${type.toUpperCase()}] ${message}`;
        if (type === 'error') {
            $('#bug-log').prepend(formattedMessage + '\n');
        } else {
            $('#main-log').prepend(formattedMessage + '\n');
        }
    };

    // ====== 데이터 추가 ======
    const addData = (data) => {
        const row = `
            <tr>
                <td>${escapeHtml(data.name)}</td>
                <td><a href="${escapeHtml(data.url)}" target="_blank">${escapeHtml(data.url)}</a></td>
                <td>${escapeHtml(data.description)}</td>
                <td>${escapeHtml(data.category)}</td>
                <td>${data.score}</td>
                <td><button class="btn view-text" data-text="${escapeHtml(data.text)}">보기</button></td>
                <td><button class="btn view-html" data-html="${escapeHtml(data.html)}">보기</button></td>
            </tr>
        `;
        $('#data-table tbody').append(row);
        // 저장된 데이터 업데이트
        let savedData = GM_getValue('savedData', []);
        savedData.push(data);
        GM_setValue('savedData', savedData);
    };

    // ====== HTML 및 텍스트 보기 ======
    $(document).on('click', '.view-html, .view-text', function() {
        const content = $(this).data($(this).hasClass('view-html') ? 'html' : 'text');
        $('#modal-content').text(unescapeHtml(content));
        $('#html-modal').show();
    });

    // ====== Escape/Unescape HTML ======
    const escapeHtml = (unsafe) => {
        return unsafe
             .replace(/&/g, "&amp;")
             .replace(/</g, "&lt;")
             .replace(/>/g, "&gt;")
             .replace(/"/g, "&quot;")
             .replace(/'/g, "&#039;");
    };

    const unescapeHtml = (safe) => {
        return safe
             .replace(/&lt;/g, "<")
             .replace(/&gt;/g, ">")
             .replace(/&amp;/g, "&")
             .replace(/&quot;/g, "\"")
             .replace(/&#039;/g, "'");
    };

    // ====== 다운로드 기능 ======
    const downloadCSV = () => {
        const ws = XLSX.utils.table_to_sheet(document.getElementById('data-table'));
        const wb = XLSX.utils.book_new();
        XLSX.utils.book_append_sheet(wb, ws, "데이터");
        XLSX.writeFile(wb, "data.csv");
        log('CSV 파일이 다운로드되었습니다.', 'info');
    };

    const downloadJSON = () => {
        const data = GM_getValue('savedData', []);
        const blob = new Blob([JSON.stringify(data, null, 2)], {type : 'application/json'});
        GM_download({
            url: URL.createObjectURL(blob),
            name: 'data.json',
            saveAs: true
        });
        log('JSON 파일이 다운로드되었습니다.', 'info');
    };

    // ====== 키보드 단축키 ======
    const setupKeyboardShortcuts = () => {
        $(document).on('keydown', function(e) {
            if (e.ctrlKey) {
                switch (e.key.toLowerCase()) {
                    case 's':
                        e.preventDefault();
                        saveSettings();
                        break;
                    case 'p':
                        e.preventDefault();
                        if (!crawling) startCrawling();
                        else stopCrawling();
                        break;
                    case 'q':
                        e.preventDefault();
                        stopCrawling();
                        break;
                    case 'd':
                        e.preventDefault();
                        downloadCSV();
                        break;
                    case 'j':
                        e.preventDefault();
                        downloadJSON();
                        break;
                    case '1':
                        e.preventDefault();
                        $('.crawler-tab[data-tab="logs"]').click();
                        break;
                    case '2':
                        e.preventDefault();
                        $('.crawler-tab[data-tab="data"]').click();
                        break;
                    case '3':
                        e.preventDefault();
                        $('.crawler-tab[data-tab="settings"]').click();
                        break;
                    case '4':
                        e.preventDefault();
                        $('.crawler-tab[data-tab="saved"]').click();
                        break;
                }
            }
        });
    };

    // ====== 크롤링 기능 ======
    const startCrawling = () => {
        if (crawling) {
            log('이미 크롤링이 진행 중입니다.', 'warning');
            return;
        }
        const query = settings.defaultQuery || '예시 검색어'; // 기본 검색어 사용
        if (settings.searchEngines.length === 0) {
            log('적어도 하나의 검색 엔진을 선택해야 합니다.', 'error');
            return;
        }
        crawling = true;
        log('크롤링을 시작합니다.', 'info');
        performSearch(query); // 기본 검색어로 크롤링 시작
    };

    const stopCrawling = () => {
        if (!crawling) {
            log('현재 진행 중인 크롤링이 없습니다.', 'warning');
            return;
        }
        crawling = false;
        log('크롤링이 중지되었습니다.', 'info');
    };

    const resetCrawling = () => {
        crawling = false;
        crawledUrls.clear();
        GM_setValue('crawledUrls', []);
        $('#data-table tbody').empty();
        $('#progress').css('width', '0%');
        log('크롤링 상태가 초기화되었습니다.', 'info');
    };

    const performSearch = async (query) => {
        for (const engine of settings.searchEngines) {
            if (!crawling) break;
            let searchUrl = '';
            switch(engine) {
                case 'google':
                    searchUrl = `https://www.google.com/search?q=${encodeURIComponent(query)}&num=100`;
                    break;
                case 'bing':
                    searchUrl = `https://www.bing.com/search?q=${encodeURIComponent(query)}&count=50`;
                    break;
                case 'duckduckgo':
                    searchUrl = `https://duckduckgo.com/html/?q=${encodeURIComponent(query)}`;
                    break;
                case 'yahoo':
                    searchUrl = `https://search.yahoo.com/search?p=${encodeURIComponent(query)}`;
                    break;
                // 추가 검색 엔진 대응 가능
                default:
                    log(`지원되지 않는 검색 엔진: ${engine}`, 'warning');
                    continue;
            }
            try {
                const response = await new Promise((resolve, reject) => {
                    GM_xmlhttpRequest({
                        method: "GET",
                        url: searchUrl,
                        onload: function(res) {
                            if (res.status === 200) resolve(res.responseText);
                            else reject(new Error(`Status Code: ${res.status}`));
                        },
                        onerror: function(err) {
                            reject(err);
                        }
                    });
                });
                const parser = new DOMParser();
                const doc = parser.parseFromString(response, 'text/html');
                const results = extractLinks(doc, engine);
                log(`${engine}에서 ${results.length}개의 URL을 찾았습니다.`, 'info');
                totalUrls += results.length;
                await processResults(results);
                await delay(settings.delay);
            } catch (error) {
                log(`검색 엔진 ${engine} 사용 중 오류 발생: ${error.message}`, 'error');
            }
        }
        if (crawling) {
            log('모든 크롤링 작업이 완료되었습니다.', 'info');
            crawling = false;
        }
    };

    // ====== 링크 추출 ======
    const extractLinks = (doc, engine) => {
        let links = [];
        switch(engine) {
            case 'google':
                $('a', doc).each(function() {
                    const href = $(this).attr('href');
                    if (href && href.startsWith('/url?q=')) {
                        const url = href.split('/url?q=')[1].split('&')[0];
                        links.push(decodeURIComponent(url));
                    }
                });
                break;
            case 'bing':
                $('li.b_algo h2 a', doc).each(function() {
                    const href = $(this).attr('href');
                    if (href && href.startsWith('http')) {
                        links.push(href);
                    }
                });
                break;
            case 'duckduckgo':
                $('a.result__a', doc).each(function() {
                    const href = $(this).attr('href');
                    if (href && href.startsWith('http')) {
                        links.push(href);
                    }
                });
                break;
            case 'yahoo':
                $('a', doc).each(function() {
                    const href = $(this).attr('href');
                    if (href && href.startsWith('http')) {
                        links.push(href);
                    }
                });
                break;
            // 추가 검색 엔진 대응 가능
        }
        // 필터링
        links = links.filter(url => !isBlacklisted(url) && isWhitelisted(url));
        return links;
    };

    // ====== 페이지 분석 ======
    const analyzePage = () => {
        if (!crawling) return;
        log('페이지 분석을 시작합니다.', 'info');
        const results = [];
        $('a').each(function() {
            const url = $(this).attr('href');
            if (url && url.startsWith('http') && !isBlacklisted(url) && isWhitelisted(url)) {
                results.push(url);
            }
        });
        log(`찾은 URL 개수: ${results.length}`, 'info');
        processResults(results);
    };

    const isBlacklisted = (url) => {
        return settings.blacklist.some(domain => url.includes(domain));
    };

    const isWhitelisted = (url) => {
        if (settings.whitelist.length === 0) return true;
        return settings.whitelist.some(domain => url.includes(domain));
    };

    // ====== 결과 처리 ======
    const processResults = async (urls) => {
        for (let i = 0; i < urls.length; i++) {
            if (!crawling) break;
            const url = urls[i];
            if (crawledUrls.has(url)) {
                log(`이미 크롤링한 URL: ${url}`, 'warning');
                continue;
            }
            crawledUrls.add(url);
            GM_setValue('crawledUrls', Array.from(crawledUrls));
            try {
                log(`크롤링 중: ${url}`, 'info');
                const response = await new Promise((resolve, reject) => {
                    GM_xmlhttpRequest({
                        method: "GET",
                        url: url,
                        onload: function(res) {
                            if (res.status === 200) resolve(res.responseText);
                            else reject(new Error(`Status Code: ${res.status}`));
                        },
                        onerror: function(err) {
                            reject(err);
                        }
                    });
                });
                const parser = new DOMParser();
                const doc = parser.parseFromString(response, 'text/html');
                const name = doc.querySelector('title') ? doc.querySelector('title').innerText : 'No Title';
                const description = doc.querySelector('meta[name="description"]') ? doc.querySelector('meta[name="description"]').getAttribute('content') : 'No Description';
                const textContent = doc.body ? doc.body.innerText.substring(0, 500) + '...' : 'No Text Content';
                const category = classifyKeywords(description);
                const score = evaluateContentQuality(description);
                addData({ name, url, description, category, score, html: response, text: textContent });
                updateProgress((crawledUrls.size / totalUrls) * 100);
                await delay(settings.delay);
            } catch (error) {
                log(`에러 발생: ${url} - ${error.message}`, 'error');
                // 재시도 로직 추가 가능
            }
        }
    };

    // ====== 진행률 업데이트 ======
    const updateProgress = (percentage) => {
        $('#progress').css('width', `${percentage}%`);
    };

    // ====== 딜레이 ======
    const delay = (ms) => new Promise(resolve => setTimeout(resolve, ms));

    // ====== 콘텐츠 품질 평가 ======
    const evaluateContentQuality = (text) => {
        const wordCount = text.split(/\s+/).length;
        return wordCount;
    };

    // ====== 키워드 분류 ======
    const classifyKeywords = (text) => {
        const categories = {
            '교육': ['학생', '학교', '교육', '학습'],
            '기술': ['기술', '소프트웨어', '프로그래밍', '개발'],
            '비즈니스': ['비즈니스', '회사', '시장', '경제'],
            // 추가 카테고리 및 키워드 추가 가능
        };
        for (const [category, keywords] of Object.entries(categories)) {
            for (const keyword of keywords) {
                if (text.includes(keyword)) {
                    return category;
                }
            }
        }
        return '기타';
    };

    // ====== 초기화 ======
    const init = () => {
        createUI();
        loadSettings();
    };

    // ====== 저장된 사이트 관리 ======
    const populateSavedSites = () => {
        const savedData = GM_getValue('savedData', []);
        $('#saved-table tbody').empty();
        savedData.forEach((data, index) => {
            const row = `
                <tr>
                    <td>${escapeHtml(data.name)}</td>
                    <td><a href="${escapeHtml(data.url)}" target="_blank">${escapeHtml(data.url)}</a></td>
                    <td><button class="btn btn-stop remove-saved" data-index="${index}">삭제</button></td>
                </tr>
            `;
            $('#saved-table tbody').append(row);
        });
    };

    $(document).on('click', '.remove-saved', function() {
        const index = $(this).data('index');
        let savedData = GM_getValue('savedData', []);
        savedData.splice(index, 1);
        GM_setValue('savedData', savedData);
        populateSavedSites();
        log('저장된 사이트가 삭제되었습니다.', 'info');
    });

    const clearSavedSites = () => {
        GM_setValue('savedData', []);
        populateSavedSites();
        log('모든 저장된 사이트가 삭제되었습니다.', 'info');
    };

    // ====== 저장된 데이터 다시 로드 ======
    const loadSavedData = () => {
        const savedData = GM_getValue('savedData', []);
        savedData.forEach(data => {
            addData(data);
        });
    };

    // ====== 검색 엔진 옵션 새로고침 ======
    const refreshSearchEngineOptions = () => {
        $('#search-engine-select option').each(function() {
            const value = $(this).val();
            if (settings.searchEngines.includes(value)) {
                $(this).prop('selected', true);
            } else {
                $(this).prop('selected', false);
            }
        });
    };

    // ====== 이벤트 핸들러 ======
    $(document).ready(function() {
        init();
        loadSavedData();
    });

})();                                                     이거를 말끔하게 해줘봐