// ==UserScript== // 📕备份- 👉实用工具👉压缩包导出 备份预设,仅导出js请自行更新js数据 // @name 网页区域文本提取导出工具 V4.9(无日志) // @namespace http://tampermonkey.net/ // @version 4.90 // @license MIT // @description 抓取指定元素文字,过滤特定内容并导出为TXT - 支持源码内置预设 // @author 能用就行 // @match *://*/* // @grant GM_setValue // @grant GM_getValue // @run-at document-end // @downloadURL https://update.greasyfork.icu/scripts/572884/%E7%BD%91%E9%A1%B5%E5%8C%BA%E5%9F%9F%E6%96%87%E6%9C%AC%E6%8F%90%E5%8F%96%E5%AF%BC%E5%87%BA%E5%B7%A5%E5%85%B7%20V49%EF%BC%88%E6%97%A0%E6%97%A5%E5%BF%97%EF%BC%89.user.js // @updateURL https://update.greasyfork.icu/scripts/572884/%E7%BD%91%E9%A1%B5%E5%8C%BA%E5%9F%9F%E6%96%87%E6%9C%AC%E6%8F%90%E5%8F%96%E5%AF%BC%E5%87%BA%E5%B7%A5%E5%85%B7%20V49%EF%BC%88%E6%97%A0%E6%97%A5%E5%BF%97%EF%BC%89.meta.js // ==/UserScript== (function() { 'use strict'; // 🔹🔹🔹 【用户编辑区】预设网站配置 🔹🔹🔹 // 格式: '纯域名': { title: '显示名称', countSelector: '统计选择器(可留空)', targetSelector: '抓取选择器', blacklist: ['词1', '词2'] } const SITE_PRESETS = { 'example.com': { title: '示例', countSelector: '1', targetSelector: '1', blacklist: ['1', '2'] }, // 在此下方继续添加您的预设网站... // 'another-site.net': { title: '另一个站', countSelector: '', targetSelector: 'h2.title', blacklist: ['VIP', '付费'] } }; // 🔹🔹🔹 预设配置结束(无需修改下方代码)🔹🔹🔹 const STORAGE_KEY_BL = 'scraper_blacklist_rules_v4'; const STORAGE_KEY_SEL = 'scraper_selector_rules_v4'; const STORAGE_KEY_INC = 'scraper_include_filter_rules_v4'; const STORAGE_KEY_INC_MATCH = 'scraper_include_match_rules_v4'; let CONFIG = { filterKeyword: '', exportFileName: 'scraped_data.txt', blacklistRules: { common: [], sites: {} }, selectorRules: {}, includeFilterRules: { common: [], sites: {} }, includeMatchRules: { common: [], sites: {} } }; function loadAllConfigs() { try { const bl = GM_getValue(STORAGE_KEY_BL, null); if (bl && typeof bl === 'object') { CONFIG.blacklistRules.common = Array.isArray(bl.common) ? bl.common : []; CONFIG.blacklistRules.sites = (bl.sites && typeof bl.sites === 'object') ? bl.sites : {}; } const sel = GM_getValue(STORAGE_KEY_SEL, null); if (sel && typeof sel === 'object') { for (const host in sel) { if (typeof sel[host] === 'string') { sel[host] = { title: host, countSelector: '', targetSelector: sel[host] }; } } CONFIG.selectorRules = sel; } const inc = GM_getValue(STORAGE_KEY_INC, null); if (inc && typeof inc === 'object') { CONFIG.includeFilterRules.common = Array.isArray(inc.common) ? inc.common : []; CONFIG.includeFilterRules.sites = (inc.sites && typeof inc.sites === 'object') ? inc.sites : {}; } const incMatch = GM_getValue(STORAGE_KEY_INC_MATCH, null); if (incMatch && typeof incMatch === 'object') { CONFIG.includeMatchRules.common = Array.isArray(incMatch.common) ? incMatch.common : []; CONFIG.includeMatchRules.sites = (incMatch.sites && typeof incMatch.sites === 'object') ? incMatch.sites : {}; } } catch (e) { } } function saveBlacklistConfig() { try { GM_setValue(STORAGE_KEY_BL, CONFIG.blacklistRules); } catch(e) {} } function saveSelectorConfig() { try { GM_setValue(STORAGE_KEY_SEL, CONFIG.selectorRules); } catch(e) {} } function saveIncludeFilterConfig() { try { GM_setValue(STORAGE_KEY_INC, CONFIG.includeFilterRules); } catch(e) {} } function saveIncludeMatchConfig() { try { GM_setValue(STORAGE_KEY_INC_MATCH, CONFIG.includeMatchRules); } catch(e) {} } function mergePresets() { for (const [host, preset] of Object.entries(SITE_PRESETS)) { if (!CONFIG.selectorRules[host]) { CONFIG.selectorRules[host] = { title: preset.title || host, countSelector: preset.countSelector || '', targetSelector: preset.targetSelector || '' }; } if (preset.blacklist && Array.isArray(preset.blacklist)) { if (!CONFIG.blacklistRules.sites[host]) CONFIG.blacklistRules.sites[host] = []; const existing = CONFIG.blacklistRules.sites[host]; preset.blacklist.forEach(kw => { if (!existing.includes(kw)) existing.push(kw); }); } if (preset.includeFilter && Array.isArray(preset.includeFilter)) { if (!CONFIG.includeFilterRules.sites[host]) CONFIG.includeFilterRules.sites[host] = []; const existing = CONFIG.includeFilterRules.sites[host]; preset.includeFilter.forEach(kw => { if (!existing.includes(kw)) existing.push(kw); }); } if (preset.includeMatch && Array.isArray(preset.includeMatch)) { if (!CONFIG.includeMatchRules.sites[host]) CONFIG.includeMatchRules.sites[host] = []; const existing = CONFIG.includeMatchRules.sites[host]; preset.includeMatch.forEach(kw => { if (!existing.includes(kw)) existing.push(kw); }); } } } loadAllConfigs(); mergePresets(); function getSiteConfig() { const host = window.location.hostname; if (!CONFIG.selectorRules[host]) { CONFIG.selectorRules[host] = { title: document.title || host, countSelector: '', targetSelector: '' }; } return CONFIG.selectorRules[host]; } function getSiteBlacklist() { const host = window.location.hostname; const siteKws = CONFIG.blacklistRules.sites[host] || []; const commonKws = CONFIG.blacklistRules.common || []; return [...new Set([...siteKws, ...commonKws])]; } function getSiteIncludeFilter() { const host = window.location.hostname; const siteKws = CONFIG.includeFilterRules.sites[host] || []; const commonKws = CONFIG.includeFilterRules.common || []; return [...new Set([...siteKws, ...commonKws])]; } let panelRef = null, toggleBtnRef = null; let currentPickerTargetId = null, pickerMode = false, highlightEl = null, infoEl = null; let selectorPanelRef = null, currentSelectorLevels = [], previewHighlights = [], scrollUpdateTimer = null; function createControlPanel() { const globalStyle = document.createElement('style'); globalStyle.id = 'scraper-global-style'; globalStyle.textContent = `#scraper-panel { position: fixed; top: 20px; right: 20px; background: linear-gradient(145deg, #1e293b 0%, #0f172a 100%); padding: 20px; border-radius: 16px; box-shadow: 0 25px 80px rgba(0,0,0,0.5), 0 0 0 1px rgba(255,255,255,0.08), inset 0 1px 0 rgba(255,255,255,0.1); z-index: 2147483647; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; color: white; min-width: 320px; max-width: 380px; max-height: calc(100vh - 40px); overflow-y: auto; overflow-x: hidden; } #scraper-panel.draggable { cursor: default; } #scraper-panel.dragging { opacity: 0.9; } #scraper-panel::-webkit-scrollbar { width: 6px; } #scraper-panel::-webkit-scrollbar-track { background: rgba(255,255,255,0.05); border-radius: 3px; } #scraper-panel::-webkit-scrollbar-thumb { background: linear-gradient(135deg, #a78bfa 0%, #f472b6 100%); border-radius: 3px; } #scraper-panel h3 { margin:0 0 16px 0; font-size:18px; font-weight:700; background:linear-gradient(135deg,#a78bfa 0%,#f472b6 100%); -webkit-background-clip:text; -webkit-text-fill-color:transparent; display:flex; align-items:center; gap:8px; cursor:move; user-select:none; } #scraper-panel h3::before { content:'🔍'; -webkit-text-fill-color:initial; } #scraper-panel .config-row { margin-bottom:14px; display:flex; align-items:center; gap:8px; } #scraper-panel .config-row label { font-size:13px; color:#cbd5e1; white-space:nowrap; min-width:70px; font-weight:500; flex-shrink:0; } #scraper-panel input[type="text"] { flex:1; padding:10px 14px; background:rgba(255,255,255,0.06); border:1px solid rgba(255,255,255,0.1); border-radius:10px; font-size:13px; color:white; box-sizing:border-box; min-width:0; } #scraper-panel input[type="text"]:focus { outline:none; background:rgba(255,255,255,0.1); border-color:#a78bfa; box-shadow:0 0 0 4px rgba(167,139,250,0.15); } #scraper-panel input[type="text"]::placeholder { color:#64748b; } #scraper-panel .btn-row { display:flex; align-items:center; gap:8px; margin-bottom:10px; } #scraper-panel .collapse-btn { flex:1; background:linear-gradient(135deg,rgba(167,139,250,0.2) 0%,rgba(244,114,182,0.2) 100%); color:#e2e8f0; border:1px solid rgba(167,139,250,0.3); border-radius:8px; padding:8px 14px; font-size:12px; font-weight:500; cursor:pointer; display:flex; align-items:center; justify-content:space-between; } #scraper-panel .collapse-btn .arrow { transition:transform 0.3s; } #scraper-panel .collapse-btn.expanded .arrow { transform:rotate(180deg); } #scraper-panel .btn-blacklist { background:rgba(251,191,36,0.15); color:#fbbf24; border:1px solid rgba(251,191,36,0.3); border-radius:8px; padding:8px 14px; font-size:12px; font-weight:500; cursor:pointer; white-space:nowrap; } #scraper-panel .collapse-content { display:none; padding:12px; background:rgba(0,0,0,0.2); border-radius:10px; margin-bottom:14px; border:1px solid rgba(255,255,255,0.05); } #scraper-panel .collapse-content.show { display:block; } #scraper-panel .btn-picker { background:linear-gradient(135deg,#10b981 0%,#059669 100%); color:white; border:none; border-radius:6px; padding:8px 10px; font-size:14px; cursor:pointer; flex-shrink:0; } #scraper-panel .btn-picker:hover { transform:scale(1.1); box-shadow:0 4px 12px rgba(16,185,129,0.4); } #scraper-panel .btn-picker.active { background:linear-gradient(135deg,#f59e0b 0%,#d97706 100%); } #scraper-element-highlight { position:fixed; pointer-events:none; z-index:2147483646; border:2px solid #f59e0b; background:rgba(245,158,11,0.1); transition:all 0.1s ease; } #scraper-element-info { position:fixed; background:rgba(15,23,42,0.95); color:white; padding:8px 12px; border-radius:6px; font-size:12px; font-family:monospace; z-index:2147483647; pointer-events:none; box-shadow:0 4px 12px rgba(0,0,0,0.3); } #scraper-selector-panel { position:fixed; top:20px; right:20px; background:linear-gradient(145deg,#1e293b 0%,#0f172a 100%); padding:16px; border-radius:12px; box-shadow:0 20px 60px rgba(0,0,0,0.5); z-index:2147483647; color:white; min-width:340px; max-width:400px; border:1px solid rgba(255,255,255,0.1); } #scraper-selector-panel .panel-header { display:flex; align-items:center; gap:8px; margin-bottom:12px; padding-bottom:10px; border-bottom:1px solid rgba(255,255,255,0.1); cursor:move; user-select:none; } #scraper-selector-panel .btn-close { background:rgba(248,113,113,0.2); color:#f87171; border:none; border-radius:6px; width:28px; height:28px; cursor:pointer; display:flex; align-items:center; justify-content:center; } #scraper-selector-panel .rule-list { max-height:300px; overflow-y:auto; margin-bottom:12px; } #scraper-selector-panel .rule-item { display:flex; align-items:center; gap:8px; padding:8px; margin-bottom:4px; background:rgba(255,255,255,0.05); border-radius:6px; cursor:pointer; font-size:12px; font-family:monospace; } #scraper-selector-panel .rule-item.unchecked { opacity:0.4; text-decoration:line-through; } #scraper-selector-panel .rule-checkbox { width:16px; height:16px; border:2px solid rgba(255,255,255,0.3); border-radius:3px; display:flex; align-items:center; justify-content:center; } #scraper-selector-panel .rule-item:not(.unchecked) .rule-checkbox { background:linear-gradient(135deg,#10b981 0%,#059669 100%); border-color:transparent; } #scraper-selector-panel .rule-checkbox::after { content:'✓'; color:white; font-size:10px; } #scraper-selector-panel .rule-item.unchecked .rule-checkbox::after { display:none; } #scraper-selector-panel .rule-text { flex:1; word-break:break-all; } #scraper-selector-panel .panel-buttons { display:flex; gap:8px; } #scraper-selector-panel .btn-action { flex:1; padding:10px; border:none; border-radius:8px; cursor:pointer; } #scraper-selector-panel .btn-select { background:rgba(255,255,255,0.1); color:white; } #scraper-selector-panel .btn-create { background:linear-gradient(135deg,#10b981 0%,#059669 100%); color:white; } #scraper-panel .options-row { display:flex; align-items:center; gap:6px; margin-bottom:10px; } #scraper-panel .filter-toggle { background:rgba(255,255,255,0.06); border:1px solid rgba(255,255,255,0.1); border-radius:8px; padding:6px 8px; font-size:11px; color:#cbd5e1; cursor:pointer; display:flex; align-items:center; gap:5px; } #scraper-panel .filter-toggle .badge { background:rgba(251,191,36,0.2); color:#fbbf24; padding:1px 5px; border-radius:4px; font-size:9px; } #scraper-panel .filter-content { display:none; margin-top:8px; margin-bottom:10px; } #scraper-panel .filter-content.show { display:block; } #scraper-panel .keywords-list { font-size:12px; color:#fbbf24; margin-bottom:10px; padding:12px; background:linear-gradient(135deg,rgba(251,191,36,0.1) 0%,rgba(245,158,11,0.1) 100%); border:1px solid rgba(251,191,36,0.2); border-radius:10px; display:none; } #scraper-panel .keywords-list.show { display:block; } #scraper-panel .blacklist-section { background:rgba(0,0,0,0.15); padding:10px; border-radius:8px; margin-bottom:10px; } #scraper-panel .section-title { font-weight:600; color:#fbbf24; font-size:12px; } #scraper-panel .section-info { font-size:10px; color:#fcd34d; opacity:0.8; } #scraper-panel .blacklist-tags { display:flex; flex-wrap:wrap; gap:4px; margin-bottom:8px; min-height:24px; } #scraper-panel .empty-hint { color:#64748b; font-size:11px; font-style:italic; } #scraper-panel .blacklist-add-row { display:flex; gap:6px; align-items:center; margin-top:8px; } #scraper-panel .blacklist-input { flex:1; padding:6px 10px; background:rgba(255,255,255,0.06); border:1px solid rgba(255,255,255,0.1); border-radius:6px; font-size:11px; color:white; } #scraper-panel .blacklist-input:focus { outline:none; border-color:#fbbf24; } #scraper-panel .blacklist-input::placeholder { color:#64748b; } #scraper-panel .blacklist-add-btn { padding:6px 12px; background:linear-gradient(135deg,#fbbf24 0%,#f59e0b 100%); border:none; border-radius:6px; font-size:11px; font-weight:500; color:#0f172a; cursor:pointer; white-space:nowrap; } #scraper-panel .keyword-tag { display:inline-block; margin:3px 5px 3px 0; padding:4px 8px; background:rgba(251,191,36,0.15); border:1px solid rgba(251,191,36,0.3); border-radius:4px; font-size:11px; color:#fbbf24; cursor:pointer; } #scraper-panel .keyword-tag.deletable { position:relative; padding-right:20px; } #scraper-panel .keyword-tag.deletable::after { content:'×'; position:absolute; right:6px; top:50%; transform:translateY(-50%); font-size:14px; font-weight:bold; } #scraper-panel .btn-delete-site { background:linear-gradient(135deg,#ef4444 0%,#dc2626 100%); color:white; border:none; border-radius:6px; padding:4px 8px; font-size:11px; cursor:pointer; } #scraper-panel .other-site-header { display:flex; justify-content:space-between; align-items:center; padding:6px 10px; cursor:pointer; user-select:none; border-radius:6px; background:rgba(255,255,255,0.03); margin-top:8px; } #scraper-panel .other-site-header:hover { background:rgba(255,255,255,0.06); } #scraper-panel .expand-icon { transition:transform 0.3s; display:inline-block; font-size:10px; margin-right:6px; } #scraper-panel .expand-icon.rotated { transform:rotate(90deg); } #scraper-panel .toggle-item { display:flex; align-items:center; gap:5px; font-size:11px; color:#cbd5e1; cursor:pointer; padding:6px 8px; background:rgba(255,255,255,0.06); border:1px solid rgba(255,255,255,0.1); border-radius:8px; } #scraper-panel .toggle-switch { position:relative; width:28px; height:16px; background:rgba(255,255,255,0.2); border-radius:8px; } #scraper-panel .toggle-switch::after { content:''; position:absolute; top:2px; left:2px; width:12px; height:12px; background:white; border-radius:50%; } #scraper-panel .toggle-item.active .toggle-switch { background:linear-gradient(135deg,#a78bfa 0%,#f472b6 100%); } #scraper-panel .toggle-item.active .toggle-switch::after { transform:translateX(12px); } #scraper-panel .btn-group { display:flex; gap:10px; margin-top:16px; } #scraper-panel .btn-group button { flex:1; padding:12px 16px; border:none; border-radius:10px; font-size:14px; font-weight:600; cursor:pointer; display:flex; align-items:center; justify-content:center; gap:6px; } #scraper-panel .btn-scrape { background:linear-gradient(135deg,#10b981 0%,#059669 100%); color:white; box-shadow:0 4px 15px rgba(16,185,129,0.3); } #scraper-panel .btn-export { background:linear-gradient(135deg,#3b82f6 0%,#2563eb 100%); color:white; box-shadow:0 4px 15px rgba(59,130,246,0.3); } #scraper-panel .btn-close { background:rgba(255,255,255,0.06); color:#cbd5e1; margin-top:12px; width:100%; padding:10px; border:1px solid rgba(255,255,255,0.1); border-radius:10px; font-size:13px; font-weight:500; cursor:pointer; } #scraper-panel .result-area { max-height:160px; overflow-y:auto; background:rgba(0,0,0,0.3); border-radius:10px; padding:12px; margin-top:12px; font-size:11px; font-family:monospace; } #scraper-panel .stats { font-size:13px; margin-top:12px; padding:14px 16px; background:linear-gradient(135deg,rgba(167,139,250,0.1) 0%,rgba(244,114,182,0.1) 100%); border:1px solid rgba(167,139,250,0.2); border-radius:12px; line-height:1.6; } #scraper-panel .stats-row { display:flex; justify-content:space-between; align-items:center; margin-bottom:8px; } #scraper-panel .stats-row:last-child { margin-bottom:0; } #scraper-panel .stats-main { display:flex; gap:20px; align-items:center; } #scraper-panel .stats-item { display:flex; align-items:center; gap:6px; } #scraper-panel .stats-label { opacity:0.9; font-weight:500; } #scraper-panel .stats-value { font-weight:700; font-size:15px; } #scraper-panel .stats-detail { margin-top:10px; padding:12px; background:rgba(0,0,0,0.2); border-radius:8px; display:none; border:1px solid rgba(255,255,255,0.05); } #scraper-panel .stats-detail.show { display:block; } #scraper-panel .stats-detail-row { display:flex; align-items:center; gap:10px; margin-bottom:6px; font-size:12px; padding:4px 0; } #scraper-panel .btn-stats-detail { background:linear-gradient(135deg,rgba(251,191,36,0.2) 0%,rgba(245,158,11,0.2) 100%); color:#fbbf24; border:1px solid rgba(251,191,36,0.3); border-radius:6px; padding:4px 12px; font-size:11px; font-weight:500; cursor:pointer; margin-left:8px; } #scraper-toggle { position:fixed; top:20px; right:20px; background:linear-gradient(135deg,#a78bfa 0%,#f472b6 100%); color:white; border:none; border-radius:50%; width:56px; height:56px; font-size:24px; cursor:move; box-shadow:0 8px 30px rgba(167,139,250,0.4); z-index:2147483646; user-select:none; } #scraper-toggle.dragging { cursor: grabbing; box-shadow:0 16px 50px rgba(167,139,250,0.6); transform:scale(1.05); } #scraper-panel .sel-config-item { display:flex; align-items:center; justify-content:space-between; padding:8px; background:rgba(255,255,255,0.04); border-radius:6px; margin-bottom:6px; font-size:11px; border:1px solid rgba(255,255,255,0.05); } #scraper-panel .sel-config-item:hover { background:rgba(255,255,255,0.08); } #scraper-panel .sel-title { color:#a78bfa; font-weight:500; max-width:100px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; } #scraper-panel .sel-host { color:#94a3b8; flex:1; text-align:center; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; margin:0 4px; } #scraper-panel .sel-divider { color:#64748b; } #scraper-panel .bl-current-header { display:flex; align-items:center; gap:6px; margin-bottom: 6px; } #scraper-panel .bl-current-count { color:#fbbf24; font-weight:500; font-size:12px; white-space:nowrap; } #scraper-panel .bl-current-input { flex:1; padding:6px 10px; background:rgba(255,255,255,0.06); border:1px solid rgba(255,255,255,0.1); border-radius:6px; font-size:11px; color:white; } #scraper-panel .bl-current-input:focus { outline:none; border-color:#fbbf24; } #scraper-panel .bl-add-btn { padding:6px 10px; background:linear-gradient(135deg,#fbbf24 0%,#f59e0b 100%); border:none; border-radius:6px; font-size:11px; font-weight:500; color:#0f172a; cursor:pointer; } #scraper-panel .bl-toggle { color:#94a3b8; font-size:11px; cursor:pointer; user-select:none; margin-left:4px; } #scraper-panel .bl-toggle:hover { color:#fbbf24; } #scraper-panel .bl-keywords { display:none; padding:6px 0 0 0; } #scraper-panel .bl-keywords.show { display:block; } #scraper-panel .bl-common-header { display:flex; align-items:center; gap:6px; padding:6px 0; cursor:pointer; user-select:none; } #scraper-panel .bl-common-header:hover { opacity:0.9; } #scraper-panel .bl-common-count { color:#fbbf24; font-size:11px; cursor: pointer; } #scraper-panel .bl-common-sep { color:#64748b; font-size:11px; } #scraper-panel .bl-other-link { color:#60a5fa; font-size:11px; cursor:pointer; } #scraper-panel .bl-other-link:hover { text-decoration:underline; } #scraper-panel .bl-common-content { display:none; padding:6px 0 0 0; } #scraper-panel .bl-common-content.show { display:block; } #scraper-panel .bl-other-list { display:none; padding:6px 0 0 0; } #scraper-panel .bl-other-list.show { display:block; } #scraper-panel .bl-other-item { display:flex; align-items:center; justify-content:space-between; padding:6px 8px; background:rgba(255,255,255,0.03); border-radius:6px; margin-bottom:4px; font-size:11px; } #scraper-panel .bl-other-item:hover { background:rgba(255,255,255,0.06); } #scraper-panel .bl-other-title { color:#60a5fa; font-weight:500; max-width:80px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; } #scraper-panel .bl-other-host { color:#94a3b8; flex:1; text-align:center; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; margin:0 4px; } #scraper-panel .other-preset-item { background:rgba(255,255,255,0.03); border-radius:6px; margin-bottom:8px; overflow:hidden; } #scraper-panel .other-preset-header { display:flex; align-items:center; justify-content:space-between; padding:8px 10px; cursor:pointer; user-select:none; } #scraper-panel .other-preset-header:hover { background:rgba(255,255,255,0.06); } #scraper-panel .other-preset-title { color:#60a5fa; font-weight:500; max-width:100px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; } #scraper-panel .other-preset-divider { color:#64748b; font-size:10px; margin:0 4px; } #scraper-panel .other-preset-host { color:#94a3b8; flex:1; text-align:center; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; } #scraper-panel .other-preset-arrow { color:#94a3b8; font-size:10px; margin-left:8px; transition:transform 0.3s; } #scraper-panel .other-preset-content { display:none; padding:8px 10px; border-top:1px solid rgba(255,255,255,0.05); } #scraper-panel .other-preset-content.show { display:block; } #scraper-panel .other-preset-detail { display:flex; gap:6px; margin-bottom:4px; font-size:11px; } #scraper-panel .other-preset-detail:last-child { margin-bottom:0; } #scraper-panel .bl-add-row { display:flex; gap:6px; align-items:center; margin-top:6px; } #scraper-panel .blacklist-input { flex:1; padding:6px 10px; background:rgba(255,255,255,0.06); border:1px solid rgba(255,255,255,0.1); border-radius:6px; font-size:11px; color:white; } #scraper-panel .blacklist-input:focus { outline:none; border-color:#fbbf24; } #scraper-panel .blacklist-input::placeholder { color:#64748b; } #scraper-panel .blacklist-add-btn { padding:6px 12px; background:linear-gradient(135deg,#fbbf24 0%,#f59e0b 100%); border:none; border-radius:6px; font-size:11px; font-weight:500; color:#0f172a; cursor:pointer; white-space:nowrap; } #scraper-panel .bl-add-btn { padding:6px 12px; background:linear-gradient(135deg,#fbbf24 0%,#f59e0b 100%); border:none; border-radius:6px; font-size:11px; font-weight:500; color:#0f172a; cursor:pointer; white-space:nowrap; } #scraper-panel .preset-row { display:flex; align-items:center; gap:8px; margin-bottom:10px; } #scraper-panel .preset-row label { font-size:12px; color:#cbd5e1; min-width:60px; } #scraper-panel .preset-stats { font-size:10px; color:#64748b; text-align:center; margin-top:6px; } @keyframes successPulse { 0%,100% { opacity:1; } 50% { opacity:0.8; } }`; document.head.appendChild(globalStyle); const siteCfg = getSiteConfig(); const panel = document.createElement('div'); panel.id = 'scraper-panel'; panel.style.display = 'none'; panelRef = panel; panel.innerHTML = `

元素抓取工具

配置选项
已预设: 0 个站点
去重
包含过滤
精准过滤
包含匹配
等待抓取...
`; document.body.appendChild(panel); initPanelDrag(panel); const toggleBtn = document.createElement('button'); toggleBtn.id = 'scraper-toggle'; toggleBtn.innerHTML = '🔍'; toggleBtn.title = '打开抓取工具'; toggleBtnRef = toggleBtn; document.body.appendChild(toggleBtn); document.getElementById('btn-scrape').addEventListener('click', scrapeElements); document.getElementById('btn-export').addEventListener('click', exportToTXT); document.getElementById('btn-close').addEventListener('click', hidePanel); document.getElementById('btn-blacklist').addEventListener('click', toggleKeywordsList); initToggleDrag(showPanel); document.getElementById('btn-selector-toggle').addEventListener('click', function() { const content = document.getElementById('selector-content'); const isExpanded = content.classList.contains('show'); if (!isExpanded) { const bl = document.getElementById('keywords-list'); const blBtn = document.getElementById('btn-blacklist'); const ps = document.getElementById('preset-content'); const psBtn = document.getElementById('btn-preset-toggle'); if (bl?.classList.contains('show')) { bl.classList.remove('show'); if(blBtn) blBtn.textContent = '🚫 黑名单'; } if (ps?.classList.contains('show')) { ps.classList.remove('show'); if(psBtn) psBtn.textContent = '🌐 站点预设'; } } content.classList.toggle('show'); this.classList.toggle('expanded'); if (content.classList.contains('show')) updateSelectorUI(); }); document.getElementById('btn-preset-toggle').addEventListener('click', function() { const content = document.getElementById('preset-content'); const isExpanded = content.classList.contains('show'); if (!isExpanded) { const bl = document.getElementById('keywords-list'); const blBtn = document.getElementById('btn-blacklist'); const sel = document.getElementById('selector-content'); const selBtn = document.getElementById('btn-selector-toggle'); if (bl?.classList.contains('show')) { bl.classList.remove('show'); if(blBtn) blBtn.textContent = '🚫 黑名单'; } if (sel?.classList.contains('show')) { sel.classList.remove('show'); if(selBtn) { selBtn.classList.remove('expanded'); const a = selBtn.querySelector('.arrow'); if(a) a.style.transform = ''; } } } content.classList.toggle('show'); this.textContent = isExpanded ? '🌐 站点预设' : '✕ 收起预设'; updatePresetStats(); }); document.getElementById('count-selector').addEventListener('input', function() { getSiteConfig().countSelector = this.value; saveSelectorConfig(); }); document.getElementById('target-selector').addEventListener('input', function() { getSiteConfig().targetSelector = this.value; saveSelectorConfig(); }); document.getElementById('toggle-dedup').addEventListener('click', function() { this.classList.toggle('active'); }); document.getElementById('toggle-include-filter').addEventListener('click', function() { this.classList.toggle('active'); }); document.getElementById('toggle-exact-match').addEventListener('click', function() { this.classList.toggle('active'); const incMatch = document.getElementById('toggle-include-match'); if (this.classList.contains('active') && incMatch.classList.contains('active')) { incMatch.classList.remove('active'); } }); document.getElementById('toggle-include-match').addEventListener('click', function () { this.classList.toggle('active'); const exactMatch = document.getElementById('toggle-exact-match'); if (this.classList.contains('active') && exactMatch.classList.contains('active')) { exactMatch.classList.remove('active'); } }); document.querySelectorAll('.btn-picker').forEach(btn => { btn.addEventListener('click', function() { currentPickerTargetId = this.dataset.target; startElementPicker(); }); }); initPresetLogic(); } function toggleKeywordsList() { const list = document.getElementById('keywords-list'); const btn = document.getElementById('btn-blacklist'); if (!list || !btn) return; if (list.classList.contains('show')) { list.classList.remove('show'); btn.textContent = '🚫 黑名单'; } else { const selContent = document.getElementById('selector-content'); const selBtn = document.getElementById('btn-selector-toggle'); const psContent = document.getElementById('preset-content'); const psBtn = document.getElementById('btn-preset-toggle'); if (selContent?.classList.contains('show')) { selContent.classList.remove('show'); if(selBtn) { selBtn.classList.remove('expanded'); const arrow = selBtn.querySelector('.arrow'); if (arrow) arrow.style.transform = ''; } } if (psContent?.classList.contains('show')) { psContent.classList.remove('show'); if(psBtn) psBtn.textContent = '🌐 站点预设'; } updateBlacklistUI(); list.classList.add('show'); btn.textContent = '✕ 收起'; } } function initPresetLogic() { const domainInput = document.getElementById('preset-domain'); const countInput = document.getElementById('preset-count-sel'); const targetInput = document.getElementById('preset-target-sel'); const blInput = document.getElementById('preset-blacklist'); const incInput = document.getElementById('preset-include-filter'); const incMatchInput = document.getElementById('preset-include-match'); const loadBtn = document.getElementById('btn-preset-load'); const saveBtn = document.getElementById('btn-preset-save'); const exportBtn = document.getElementById('btn-preset-export'); const otherBtn = document.getElementById('btn-preset-other'); if (!domainInput || !loadBtn) return; const parseDomain = (d) => d.trim().replace(/^https?:\/\//, '').split('/')[0].split('?')[0]; loadBtn.addEventListener('click', () => { const host = parseDomain(domainInput.value); if (!host) { alert('请输入有效的域名'); return; } domainInput.value = host; const selCfg = CONFIG.selectorRules[host] || { countSelector: '', targetSelector: '' }; countInput.value = selCfg.countSelector || ''; targetInput.value = selCfg.targetSelector || ''; const siteBl = CONFIG.blacklistRules.sites[host] || []; blInput.value = siteBl.join(', '); const siteInc = CONFIG.includeFilterRules.sites[host] || []; incInput.value = siteInc.join(', '); const siteIncMatch = CONFIG.includeMatchRules.sites[host] || []; incMatchInput.value = siteIncMatch.join(', '); }); saveBtn.addEventListener('click', () => { const host = parseDomain(domainInput.value); if (!host) { alert('请先输入目标域名'); return; } if (!CONFIG.selectorRules[host]) CONFIG.selectorRules[host] = { title: host, countSelector: '', targetSelector: '' }; CONFIG.selectorRules[host].countSelector = countInput.value.trim(); CONFIG.selectorRules[host].targetSelector = targetInput.value.trim(); const kws = blInput.value.split(/[,,]/).map(k => k.trim()).filter(k => k); CONFIG.blacklistRules.sites[host] = [...new Set(kws)]; const incKws = incInput.value.split(/[,,]/).map(k => k.trim()).filter(k => k); CONFIG.includeFilterRules.sites[host] = [...new Set(incKws)]; const incMatchKws = incMatchInput.value.split(/[,,]/).map(k => k.trim()).filter(k => k); CONFIG.includeMatchRules.sites[host] = [...new Set(incMatchKws)]; saveSelectorConfig(); saveBlacklistConfig(); saveIncludeFilterConfig(); saveIncludeMatchConfig(); updatePresetStats(); alert(`✅ 站点 [${host}] 预设已保存!`); }); exportBtn.addEventListener('click', () => { const exportText = generatePresetExport(); const blob = new Blob(['\uFEFF' + exportText], { type: 'text/plain;charset=utf-8;' }); const link = document.createElement('a'); link.href = URL.createObjectURL(blob); link.download = 'site_presets.txt'; document.body.appendChild(link); link.click(); document.body.removeChild(link); URL.revokeObjectURL(link.href); }); otherBtn.addEventListener('click', () => { const content = document.getElementById('preset-other-content'); content.classList.toggle('show'); otherBtn.textContent = content.classList.contains('show') ? '✕ 收起' : '📋 其他配置'; if (content.classList.contains('show')) { renderOtherPresets(); } }); } function generatePresetExport() { let text = '// 🔹🔹🔹 【用户编辑区】预设网站配置 🔹🔹🔹\n'; text += '// 格式: \'纯域名\': { title: \'显示名称\', countSelector: \'统计选择器(可留空)\', targetSelector: \'抓取选择器\', blacklist: [\'词1\', \'词2\'], includeFilter: [\'词1\', \'词2\'], includeMatch: [\'词1\', \'词2\'] }\n\n'; text += 'const SITE_PRESETS = {\n'; const allHosts = Object.keys(CONFIG.selectorRules).filter(h => CONFIG.selectorRules[h]?.targetSelector); allHosts.forEach((host, index) => { const cfg = CONFIG.selectorRules[host] || {}; const bl = CONFIG.blacklistRules.sites[host] || []; const inc = CONFIG.includeFilterRules.sites[host] || []; const incMatch = CONFIG.includeMatchRules.sites[host] || []; const title = cfg.title || host; const countSel = cfg.countSelector || ''; const targetSel = cfg.targetSelector || ''; text += ` '${host}': {\n`; text += ` title: '${title.replace(/'/g, "\\'")}',\n`; text += ` countSelector: '${countSel.replace(/'/g, "\\'")}',\n`; text += ` targetSelector: '${targetSel.replace(/'/g, "\\'")}'`; if (bl.length > 0) { text += `,\n blacklist: [${bl.map(k => `'${k.replace(/'/g, "\\'")}'`).join(', ')}]`; } if (inc.length > 0) { text += `,\n includeFilter: [${inc.map(k => `'${k.replace(/'/g, "\\'")}'`).join(', ')}]`; } if (incMatch.length > 0) { text += `,\n includeMatch: [${incMatch.map(k => `'${k.replace(/'/g, "\\'")}'`).join(', ')}]`; } text += index < allHosts.length - 1 ? '\n },\n' : '\n }'; }); text += '\n};\n// 🔹🔹🔹 预设配置结束(无需修改下方代码)🔹🔹🔹\n'; return text; } function checkSiteInJS(host) { for (const [presetHost, preset] of Object.entries(SITE_PRESETS)) { if (presetHost === host) return true; } return false; } function renderOtherPresets() { const container = document.getElementById('preset-other-content'); if (!container) return; const currentHost = window.location.hostname; const allHosts = Object.keys(CONFIG.selectorRules).filter(h => h !== currentHost && CONFIG.selectorRules[h]?.targetSelector); if (allHosts.length === 0) { container.innerHTML = '
暂无其他站点预设
'; return; } container.innerHTML = allHosts.map(host => { const cfg = CONFIG.selectorRules[host] || {}; const bl = CONFIG.blacklistRules.sites[host] || []; const inc = CONFIG.includeFilterRules.sites[host] || []; const incMatch = CONFIG.includeMatchRules.sites[host] || []; const title = cfg.title || host; const inJS = checkSiteInJS(host); return `
${inJS ? '✓ ' : ''}${title} / ${host} /
统计: ${cfg.countSelector || '(空)'}
抓取: ${cfg.targetSelector}
黑名单${bl.length}条: ${bl.length ? bl.join(', ') : '(空)'}
包含过滤${inc.length}条: ${inc.length ? inc.join(', ') : '(空)'}
包含匹配${incMatch.length}条: ${incMatch.length ? incMatch.join(', ') : '(空)'}
`; }).join(''); container.querySelectorAll('.other-preset-header').forEach(header => { header.addEventListener('click', function(e) { if (e.target.classList.contains('btn-delete-site')) return; const host = this.closest('.other-preset-item').dataset.host; const content = container.querySelector(`.other-preset-content[data-host="${host}"]`); const arrow = this.querySelector('.other-preset-arrow'); if (content) { const isShow = content.classList.toggle('show'); arrow.textContent = isShow ? '▲' : '▼'; } }); }); container.querySelectorAll('[data-del-preset]').forEach(btn => { btn.addEventListener('click', function(e) { e.stopPropagation(); const host = this.dataset.delPreset; if (confirm(`确定删除 ${host} 的所有预设配置吗?`)) { delete CONFIG.selectorRules[host]; delete CONFIG.blacklistRules.sites[host]; delete CONFIG.includeFilterRules.sites[host]; delete CONFIG.includeMatchRules.sites[host]; saveSelectorConfig(); saveBlacklistConfig(); saveIncludeFilterConfig(); saveIncludeMatchConfig(); updatePresetStats(); renderOtherPresets(); } }); }); } function updatePresetStats() { const el = document.getElementById('preset-count-info'); if (el) { const count = Object.keys(CONFIG.selectorRules).filter(h => CONFIG.selectorRules[h]?.targetSelector).length; el.textContent = `已预设: ${count} 个站点`; } } function updateBlacklistUI() { const host = window.location.hostname; const blacklists = CONFIG.blacklistRules.sites || {}; const includeFilters = CONFIG.includeFilterRules.sites || {}; const includeMatches = CONFIG.includeMatchRules.sites || {}; const keywordsContent = document.getElementById('keywords-content'); if (!keywordsContent) return; const currentBL = blacklists[host] || []; const currentINC = includeFilters[host] || []; const currentMATCH = includeMatches[host] || []; const commonBL = CONFIG.blacklistRules.common || []; const commonINC = CONFIG.includeFilterRules.common || []; const commonMATCH = CONFIG.includeMatchRules.common || []; keywordsContent.innerHTML = `
精准过滤 ${currentBL.length} 个:
${currentBL.length === 0 ? '暂无精准过滤关键词' : currentBL.map((k, i) => `${k}`).join('')}
包含过滤 ${currentINC.length} 个:
${currentINC.length === 0 ? '暂无包含过滤关键词' : currentINC.map((k, i) => `${k}`).join('')}
包含匹配 ${currentMATCH.length} 个:
${currentMATCH.length === 0 ? '暂无包含匹配关键词' : currentMATCH.map((k, i) => `${k}`).join('')}
公用共 ${commonBL.length + commonINC.length + commonMATCH.length} 个
`; const batchAddCurrent = () => { const blInput = document.getElementById('bl-current-input'); const incInput = document.getElementById('inc-current-input'); const matchInput = document.getElementById('match-current-input'); if (!CONFIG.blacklistRules.sites[host]) CONFIG.blacklistRules.sites[host] = []; if (!CONFIG.includeFilterRules.sites[host]) CONFIG.includeFilterRules.sites[host] = []; if (!CONFIG.includeMatchRules.sites[host]) CONFIG.includeMatchRules.sites[host] = []; let added = false; if (blInput?.value.trim()) { const keywords = blInput.value.split(/[,,]/).map(k => k.trim()).filter(k => k); if (keywords.length > 0) CONFIG.blacklistRules.sites[host].push(...keywords); blInput.value = ''; added = true; } if (incInput?.value.trim()) { const keywords = incInput.value.split(/[,,]/).map(k => k.trim()).filter(k => k); if (keywords.length > 0) CONFIG.includeFilterRules.sites[host].push(...keywords); incInput.value = ''; added = true; } if (matchInput?.value.trim()) { const keywords = matchInput.value.split(/[,,]/).map(k => k.trim()).filter(k => k); if (keywords.length > 0) CONFIG.includeMatchRules.sites[host].push(...keywords); matchInput.value = ''; added = true; } if (added) { saveBlacklistConfig(); saveIncludeFilterConfig(); saveIncludeMatchConfig(); updateKeywordsDisplay(); } }; const batchAddCommon = () => { const blInput = document.getElementById('bl-common-input'); const incInput = document.getElementById('inc-common-input'); const matchInput = document.getElementById('match-common-input'); let added = false; if (blInput?.value.trim()) { const keywords = blInput.value.split(/[,,]/).map(k => k.trim()).filter(k => k); if (keywords.length > 0) CONFIG.blacklistRules.common.push(...keywords); blInput.value = ''; added = true; } if (incInput?.value.trim()) { const keywords = incInput.value.split(/[,,]/).map(k => k.trim()).filter(k => k); if (keywords.length > 0) CONFIG.includeFilterRules.common.push(...keywords); incInput.value = ''; added = true; } if (matchInput?.value.trim()) { const keywords = matchInput.value.split(/[,,]/).map(k => k.trim()).filter(k => k); if (keywords.length > 0) CONFIG.includeMatchRules.common.push(...keywords); matchInput.value = ''; added = true; } if (added) { saveBlacklistConfig(); saveIncludeFilterConfig(); saveIncludeMatchConfig(); updateKeywordsDisplay(); } }; document.getElementById('bl-add-current')?.addEventListener('click', batchAddCurrent); document.getElementById('inc-add-current')?.addEventListener('click', batchAddCurrent); document.getElementById('match-add-current')?.addEventListener('click', batchAddCurrent); document.getElementById('bl-add-common')?.addEventListener('click', batchAddCommon); document.getElementById('inc-add-common')?.addEventListener('click', batchAddCommon); document.getElementById('match-add-common')?.addEventListener('click', batchAddCommon); const commonHeader = document.getElementById('bl-common-header'); const commonContent = document.getElementById('bl-common-content'); if (commonHeader) { commonHeader.addEventListener('click', function() { commonContent.classList.toggle('show'); commonContent.style.display = commonContent.classList.contains('show') ? 'block' : 'none'; }); } keywordsContent.querySelectorAll('.keyword-tag.deletable').forEach(tag => { tag.addEventListener('click', function() { const type = this.dataset.type; const idx = parseInt(this.dataset.index); if (type === 'current-bl') { if (CONFIG.blacklistRules.sites[host]?.[idx] !== undefined) CONFIG.blacklistRules.sites[host].splice(idx, 1); } else if (type === 'current-inc') { if (CONFIG.includeFilterRules.sites[host]?.[idx] !== undefined) CONFIG.includeFilterRules.sites[host].splice(idx, 1); } else if (type === 'current-match') { if (CONFIG.includeMatchRules.sites[host]?.[idx] !== undefined) CONFIG.includeMatchRules.sites[host].splice(idx, 1); } else if (type === 'common-bl') { if (CONFIG.blacklistRules.common?.[idx] !== undefined) CONFIG.blacklistRules.common.splice(idx, 1); } else if (type === 'common-inc') { if (CONFIG.includeFilterRules.common?.[idx] !== undefined) CONFIG.includeFilterRules.common.splice(idx, 1); } else if (type === 'common-match') { if (CONFIG.includeMatchRules.common?.[idx] !== undefined) CONFIG.includeMatchRules.common.splice(idx, 1); } saveBlacklistConfig(); saveIncludeFilterConfig(); saveIncludeMatchConfig(); updateKeywordsDisplay(); }); }); } function updateKeywordsDisplay() { const host = window.location.hostname; const currentBL = CONFIG.blacklistRules.sites[host] || []; const currentINC = CONFIG.includeFilterRules.sites[host] || []; const currentMATCH = CONFIG.includeMatchRules.sites[host] || []; const commonBL = CONFIG.blacklistRules.common || []; const commonINC = CONFIG.includeFilterRules.common || []; const commonMATCH = CONFIG.includeMatchRules.common || []; const blContainer = document.querySelector('#bl-current-input').parentElement.nextElementSibling; if (blContainer) { blContainer.innerHTML = currentBL.length === 0 ? '暂无精准过滤关键词' : currentBL.map((k, i) => `${k}`).join(''); bindDeleteEvents(blContainer, host); } const incContainer = document.querySelector('#inc-current-input').parentElement.nextElementSibling; if (incContainer) { incContainer.innerHTML = currentINC.length === 0 ? '暂无包含过滤关键词' : currentINC.map((k, i) => `${k}`).join(''); bindDeleteEvents(incContainer, host); } const matchContainer = document.querySelector('#match-current-input').parentElement.nextElementSibling; if (matchContainer) { matchContainer.innerHTML = currentMATCH.length === 0 ? '暂无包含匹配关键词' : currentMATCH.map((k, i) => `${k}`).join(''); bindDeleteEvents(matchContainer, host); } const commonContent = document.getElementById('bl-common-content'); if (commonContent) { const commonBlContainer = commonContent.querySelectorAll(':scope > div')[1]; if (commonBlContainer) { commonBlContainer.innerHTML = commonBL.map((k, i) => `${k}`).join('') + (commonBL.length === 0 ? '暂无' : ''); bindDeleteEvents(commonBlContainer, host); } const commonIncContainer = commonContent.querySelectorAll(':scope > div')[3]; if (commonIncContainer) { commonIncContainer.innerHTML = commonINC.map((k, i) => `${k}`).join('') + (commonINC.length === 0 ? '暂无' : ''); bindDeleteEvents(commonIncContainer, host); } const commonMatchContainer = commonContent.querySelectorAll(':scope > div')[5]; if (commonMatchContainer) { commonMatchContainer.innerHTML = commonMATCH.map((k, i) => `${k}`).join('') + (commonMATCH.length === 0 ? '暂无' : ''); bindDeleteEvents(commonMatchContainer, host); } const commonHeader = document.getElementById('bl-common-header'); if (commonHeader) { commonHeader.querySelector('span').textContent = `公用共 ${commonBL.length + commonINC.length + commonMATCH.length} 个`; } } } function bindDeleteEvents(container, host) { container.querySelectorAll('.keyword-tag.deletable').forEach(tag => { tag.addEventListener('click', function() { const type = this.dataset.type; const idx = parseInt(this.dataset.index); if (type === 'current-bl') { if (CONFIG.blacklistRules.sites[host]?.[idx] !== undefined) CONFIG.blacklistRules.sites[host].splice(idx, 1); } else if (type === 'current-inc') { if (CONFIG.includeFilterRules.sites[host]?.[idx] !== undefined) CONFIG.includeFilterRules.sites[host].splice(idx, 1); } else if (type === 'current-match') { if (CONFIG.includeMatchRules.sites[host]?.[idx] !== undefined) CONFIG.includeMatchRules.sites[host].splice(idx, 1); } else if (type === 'common-bl') { if (CONFIG.blacklistRules.common?.[idx] !== undefined) CONFIG.blacklistRules.common.splice(idx, 1); } else if (type === 'common-inc') { if (CONFIG.includeFilterRules.common?.[idx] !== undefined) CONFIG.includeFilterRules.common.splice(idx, 1); } else if (type === 'common-match') { if (CONFIG.includeMatchRules.common?.[idx] !== undefined) CONFIG.includeMatchRules.common.splice(idx, 1); } saveBlacklistConfig(); saveIncludeFilterConfig(); saveIncludeMatchConfig(); updateKeywordsDisplay(); }); }); } function updateSelectorUI() { const host = window.location.hostname; const cfg = getSiteConfig(); document.getElementById('count-selector').value = cfg.countSelector || ''; document.getElementById('target-selector').value = cfg.targetSelector || ''; } function addBlacklistKeyword(type, keyword, host = null) { if (type === 'common') { if (!CONFIG.blacklistRules.common) CONFIG.blacklistRules.common = []; CONFIG.blacklistRules.common.push(keyword); } else { const targetHost = (type === 'current-site') ? window.location.hostname : host; if (!CONFIG.blacklistRules.sites[targetHost]) CONFIG.blacklistRules.sites[targetHost] = []; CONFIG.blacklistRules.sites[targetHost].push(keyword); } saveBlacklistConfig(); updateKeywordsDisplay(); } function deleteBlacklistKeyword(type, host = null, index = 0) { const targetHost = host || window.location.hostname; if (type === 'common') { if (CONFIG.blacklistRules.common?.[index] !== undefined) CONFIG.blacklistRules.common.splice(index, 1); } else { if (CONFIG.blacklistRules.sites?.[targetHost]?.[index] !== undefined) CONFIG.blacklistRules.sites[targetHost].splice(index, 1); } saveBlacklistConfig(); updateKeywordsDisplay(); } function showPanel() { if (panelRef) panelRef.style.display = 'block'; if (toggleBtnRef) toggleBtnRef.style.display = 'none'; } function hidePanel() { if (panelRef) panelRef.style.display = 'none'; if (toggleBtnRef) toggleBtnRef.style.display = 'block'; } function initToggleDrag(showPanelCallback) { if (!toggleBtnRef) return; let isDragging = false, hasMoved = false, startX, startY, buttonX, buttonY; toggleBtnRef.addEventListener('mousedown', e => { if(e.button===0){ isDragging=true; hasMoved=false; startX=e.clientX; startY=e.clientY; const r=toggleBtnRef.getBoundingClientRect(); buttonX=r.left; buttonY=r.top; toggleBtnRef.classList.add('dragging'); e.preventDefault(); }}); document.addEventListener('mousemove', e => { if(!isDragging)return; if(Math.abs(e.clientX-startX)>5||Math.abs(e.clientY-startY)>5)hasMoved=true; toggleBtnRef.style.left=Math.max(0,Math.min(buttonX+e.clientX-startX,window.innerWidth-toggleBtnRef.offsetWidth))+'px'; toggleBtnRef.style.top=Math.max(0,Math.min(buttonY+e.clientY-startY,window.innerHeight-toggleBtnRef.offsetHeight))+'px'; toggleBtnRef.style.right='auto'; toggleBtnRef.style.bottom='auto'; e.preventDefault(); }); document.addEventListener('mouseup', () => { if(isDragging){ isDragging=false; toggleBtnRef.classList.remove('dragging'); const r=toggleBtnRef.getBoundingClientRect(); buttonX=r.left; buttonY=r.top; }}); toggleBtnRef.addEventListener('click', e => { if(hasMoved){e.stopPropagation();e.preventDefault();} else if(typeof showPanelCallback==='function') showPanelCallback(); }); } function initPanelDrag(panel) { if(!panel)return; let isDragging=false, startX, startY, panelX, panelY; const handle = panel.querySelector('.panel-drag-handle'); if(!handle)return; panel.classList.add('draggable'); handle.addEventListener('mousedown', e => { if(e.button===0){ isDragging=true; startX=e.clientX; startY=e.clientY; const r=panel.getBoundingClientRect(); panelX=r.left; panelY=r.top; panel.classList.add('dragging'); e.preventDefault(); }}); document.addEventListener('mousemove', e => { if(!isDragging)return; panel.style.left=Math.max(0,Math.min(panelX+e.clientX-startX,window.innerWidth-panel.offsetWidth))+'px'; panel.style.top=Math.max(0,Math.min(panelY+e.clientY-startY,window.innerHeight-panel.offsetHeight))+'px'; panel.style.right='auto'; panel.style.bottom='auto'; e.preventDefault(); }); document.addEventListener('mouseup', () => { if(isDragging){ isDragging=false; panel.classList.remove('dragging'); const r=panel.getBoundingClientRect(); panelX=r.left; panelY=r.top; }}); } function startElementPicker() { if (pickerMode) return; pickerMode = true; if (panelRef) panelRef.style.display = 'none'; if (!highlightEl) { highlightEl = document.createElement('div'); highlightEl.id = 'scraper-element-highlight'; document.body.appendChild(highlightEl); } if (!infoEl) { infoEl = document.createElement('div'); infoEl.id = 'scraper-element-info'; document.body.appendChild(infoEl); } infoEl.textContent = '点击元素获取选择器 | ESC 退出'; infoEl.style.cssText = 'top:10px;left:50%;transform:translateX(-50%);display:block;'; document.addEventListener('mouseover', handlePickerMouseOver, true); document.addEventListener('mouseout', handlePickerMouseOut, true); document.addEventListener('click', handlePickerClick, true); document.addEventListener('keydown', handlePickerKeyDown, true); } function stopElementPicker() { pickerMode = false; if (highlightEl) highlightEl.style.display = 'none'; if (infoEl) infoEl.style.display = 'none'; document.removeEventListener('mouseover', handlePickerMouseOver, true); document.removeEventListener('mouseout', handlePickerMouseOut, true); document.removeEventListener('click', handlePickerClick, true); document.removeEventListener('keydown', handlePickerKeyDown, true); } function handlePickerMouseOver(e) { if (!pickerMode || e.target.closest('#scraper-panel,#scraper-toggle,.scraper-preview-highlight')) return; e.stopPropagation(); const rect = e.target.getBoundingClientRect(); highlightEl.style.cssText = `display:block;top:${rect.top}px;left:${rect.left}px;width:${rect.width}px;height:${rect.height}px;`; } function handlePickerMouseOut(e) { if (pickerMode) { highlightEl.style.display = 'none'; infoEl.style.display = 'none'; } } function handlePickerClick(e) { if (!pickerMode || e.target.closest('#scraper-panel,#scraper-toggle,.scraper-preview-highlight')) return; e.preventDefault(); e.stopPropagation(); showSelectorPanel(generateSelectorLevels(e.target)); } function handlePickerKeyDown(e) { if (pickerMode && e.key === 'Escape') { e.preventDefault(); stopElementPicker(); closeSelectorPanel(); } } function generateSelectorLevels(el) { const levels = []; let current = el; while (current && current !== document.body) { levels.unshift({ selector: generateSingleLevelSelector(current), checked: true }); current = current.parentElement; } return levels; } function generateSingleLevelSelector(el) { if (el.id) return '#' + el.id; if (el.className && typeof el.className === 'string') { const classes = el.className.trim().split(/\s+/).filter(c => c); for (const cls of classes) if (document.querySelectorAll('.' + cls).length === 1) return '.' + cls; const cs = '.' + classes.join('.'); if (document.querySelectorAll(cs).length === 1) return cs; } const parent = el.parentElement; if (parent) { const siblings = Array.from(parent.children), idx = siblings.indexOf(el) + 1; if (siblings.filter(s => s.tagName === el.tagName).length === 1) return el.tagName.toLowerCase(); return el.tagName.toLowerCase() + ':nth-child(' + idx + ')'; } return el.tagName.toLowerCase(); } function showSelectorPanel(levels) { currentSelectorLevels = levels; closeSelectorPanel(); const panel = document.createElement('div'); panel.id = 'scraper-selector-panel'; selectorPanelRef = panel; panel.innerHTML = `
元素选择器${countMatchedElements(levels)} 个匹配
${levels.map((l, i) => `
${l.selector}
`).join('')}
`; document.body.appendChild(panel); initSelectorPanelDrag(panel); document.getElementById('btn-close-panel').addEventListener('click', () => { clearPreviewHighlights(); closeSelectorPanel(); if(panelRef) panelRef.style.display='block'; }); document.getElementById('btn-reselect').addEventListener('click', () => { clearPreviewHighlights(); closeSelectorPanel(); startElementPicker(); }); document.getElementById('btn-create').addEventListener('click', function() { const sel = buildSelector(currentSelectorLevels); if (sel && currentPickerTargetId) { const input = document.getElementById(currentPickerTargetId); if (input) { input.value = sel; input.dispatchEvent(new Event('input', { bubbles: true })); } } clearPreviewHighlights(); closeSelectorPanel(); if(panelRef) panelRef.style.display='block'; }); panel.querySelectorAll('.rule-item').forEach(item => { item.addEventListener('click', function() { const idx = parseInt(this.dataset.index); currentSelectorLevels[idx].checked = !currentSelectorLevels[idx].checked; this.classList.toggle('unchecked'); panel.querySelector('.panel-count').textContent = countMatchedElements(currentSelectorLevels) + ' 个匹配'; updatePreviewHighlights(); }); }); stopElementPicker(); updatePreviewHighlights(); window.addEventListener('scroll', handleScroll, true); window.addEventListener('resize', handleScroll, true); } function handleScroll() { if (scrollUpdateTimer) cancelAnimationFrame(scrollUpdateTimer); scrollUpdateTimer = requestAnimationFrame(updatePreviewHighlights); } function updatePreviewHighlights() { clearPreviewHighlights(); const sel = buildSelector(currentSelectorLevels); if (!sel) return; try { document.querySelectorAll(sel).forEach(el => { const rect = el.getBoundingClientRect(); const h = document.createElement('div'); h.className = 'scraper-preview-highlight'; h.style.cssText = `position:fixed;top:${rect.top}px;left:${rect.left}px;width:${rect.width}px;height:${rect.height}px;border:2px solid #3b82f6;background:rgba(59,130,246,0.1);pointer-events:none;z-index:2147483646;`; document.body.appendChild(h); previewHighlights.push(h); }); } catch(e){} } function clearPreviewHighlights() { previewHighlights.forEach(el => el.remove()); previewHighlights = []; } function closeSelectorPanel() { window.removeEventListener('scroll', handleScroll, true); window.removeEventListener('resize', handleScroll, true); if (scrollUpdateTimer) { cancelAnimationFrame(scrollUpdateTimer); scrollUpdateTimer = null; } if (selectorPanelRef) { selectorPanelRef.remove(); selectorPanelRef = null; } clearPreviewHighlights(); } function countMatchedElements(levels) { const s = buildSelector(levels); return s ? document.querySelectorAll(s).length : 0; } function buildSelector(levels) { return levels.filter(l => l.checked).map(l => l.selector).join(' > '); } function initSelectorPanelDrag(panel) { if (!panel) return; let drag = false, sx, sy, px, py; const hd = panel.querySelector('.panel-header'); hd.style.cursor = 'move'; panel.classList.add('draggable'); const mv = e => { if(!drag) return; panel.style.left=Math.max(0,Math.min(px+e.clientX-sx,window.innerWidth-panel.offsetWidth))+'px'; panel.style.top=Math.max(0,Math.min(py+e.clientY-sy,window.innerHeight-panel.offsetHeight))+'px'; panel.style.right='auto'; panel.style.bottom='auto'; }; const up = () => { if(!drag) return; drag=false; panel.classList.remove('dragging'); const r=panel.getBoundingClientRect(); px=r.left; py=r.top; }; hd.addEventListener('mousedown', e => { if(e.button!==0||e.target.closest('.btn-close')) return; drag=true; sx=e.clientX; sy=e.clientY; const r=panel.getBoundingClientRect(); px=r.left; py=r.top; panel.classList.add('dragging'); e.preventDefault(); }); document.addEventListener('mousemove', mv); document.addEventListener('mouseup', up); } let scrapedData = []; function scrapeElements() { const cfg = getSiteConfig(); const selector = cfg.targetSelector?.trim() || ''; const countSelector = cfg.countSelector?.trim() || ''; if (!selector) { alert('请先配置当前网站的选择器!'); return; } const includeMatchKeywords = getSiteIncludeMatch(); const includeFilterKeywords = getSiteIncludeFilter(); const useIncludeMatch = document.getElementById('toggle-include-match').classList.contains('active'); const useIncludeFilter = document.getElementById('toggle-include-filter').classList.contains('active'); const useExactMatch = document.getElementById('toggle-exact-match').classList.contains('active'); const useDedup = document.getElementById('toggle-dedup').classList.contains('active'); const exactKeywords = getSiteBlacklist(); try { const elements = document.querySelectorAll(selector); if (elements.length === 0) { document.getElementById('scraper-stats').innerHTML = `❌ 未找到匹配元素`; return; } scrapedData = []; const valid = [], filtered = [], seen = new Set(); let incF=0, exactF=0, matchF=0, dupF=0, vIdx=0; elements.forEach((el, i) => { const txt = el.textContent.trim(); if(!txt) return; let reason = '', filt = false; if (useIncludeMatch) { const matched = includeMatchKeywords.some(k => txt.includes(k)); if (!matched) { reason = '未包含匹配关键词'; filt = true; matchF++; filtered.push(`
[${i+1}] ${escapeHtml(txt.substring(0,35))}${txt.length >35?'...':''} (${reason})
`); } else { if (useExactMatch) { const exactMatched = exactKeywords.some(k => txt === k); if (exactMatched) { reason = `完整匹配 "${exactKeywords.find(k => txt === k)}"`; filt = true; exactF++; filtered.push(`
[${i+1}] ${escapeHtml(txt.substring(0,35))}${txt.length >35?'...':''} (${reason})
`); } } if (useIncludeFilter && !filt && includeFilterKeywords.length > 0) { const filteredOut = includeFilterKeywords.some(k => txt.includes(k)); if (filteredOut) { reason = `包含 "${includeFilterKeywords.find(k => txt.includes(k))}"`; filt = true; incF++; filtered.push(`
[${i+1}] ${escapeHtml(txt.substring(0,35))}${txt.length >35?'...':''} (${reason})
`); } } } } else { if (useExactMatch) { const exactMatched = exactKeywords.some(k => txt === k); if (exactMatched) { reason = `完整匹配 "${exactKeywords.find(k => txt === k)}"`; filt = true; exactF++; filtered.push(`
[${i+1}] ${escapeHtml(txt.substring(0,35))}${txt.length >35?'...':''} (${reason})
`); } } if (useIncludeFilter && !filt && includeFilterKeywords.length > 0) { const filteredOut = includeFilterKeywords.some(k => txt.includes(k)); if (filteredOut) { reason = `包含 "${includeFilterKeywords.find(k => txt.includes(k))}"`; filt = true; incF++; filtered.push(`
[${i+1}] ${escapeHtml(txt.substring(0,35))}${txt.length >35?'...':''} (${reason})
`); } } } if (!filt) { if (useDedup && seen.has(txt)) { dupF++; filtered.push(`
[${i+1}] ${escapeHtml(txt.substring(0,35))}${txt.length >35?'...':''} (重复)
`); } else { if (useDedup) seen.add(txt); vIdx++; scrapedData.push({index:vIdx, text:txt}); valid.push(`
[${vIdx}] ${escapeHtml(txt.substring(0,35))}${txt.length >35?'...':''}
`); } } }); let cnt = 0; if(countSelector) try { cnt = document.querySelectorAll(countSelector).length; } catch(e){} document.getElementById('scraper-stats').innerHTML = `
✅ 找到${elements.length}
${cnt >0?`元素 ${cnt} 个`:'无元素'}
📥 有效${scrapedData.length}
🚫 过滤${incF+exactF+matchF+dupF}
${matchF >0?`
未匹配:${matchF} 条
`:''}${dupF >0?`
重复:${dupF} 条
`:''}${exactF >0?`
精准过滤:${exactF} 条
`:''}${incF >0?`
包含过滤:${incF} 条
`:''}${incF+exactF+matchF+dupF===0?'
暂无过滤数据
':''}
`; const db = document.getElementById('btn-filter-detail'), dc = document.getElementById('filter-detail-content'); if(db && dc) db.addEventListener('click', function() { const ex=this.dataset.expanded==='true'; dc.classList.toggle('show'); this.textContent=ex?'详情':'收起'; this.dataset.expanded=(!ex).toString(); }); document.getElementById('scraper-results').innerHTML = [...valid, ...filtered].join(''); } catch (e) { document.getElementById('scraper-stats').innerHTML = `❌ 错误: ${e.message}`; } } function getSiteIncludeMatch() { const host = window.location.hostname; const siteKws = CONFIG.includeMatchRules.sites[host] || []; const commonKws = CONFIG.includeMatchRules.common || []; return [...new Set([...siteKws, ...commonKws])]; } function exportToTXT() { if (scrapedData.length === 0) { alert('没有数据可导出!请先抓取元素。'); return; } const blob = new Blob(['\uFEFF' + scrapedData.map(d=>d.text).join('\n')], { type: 'text/plain;charset=utf-8;' }); const link = document.createElement('a'); link.href = URL.createObjectURL(blob); link.download = CONFIG.exportFileName; document.body.appendChild(link); link.click(); document.body.removeChild(link); document.getElementById('scraper-stats').innerHTML = `✅ 已导出 ${scrapedData.length} 条数据!`; } function escapeHtml(t) { const d=document.createElement('div'); d.textContent=t; return d.innerHTML; } if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', createControlPanel); else createControlPanel(); })();