// ==UserScript==
// @name 知网资源批量下载
// @namespace https://cnki.net/
// @version 1.1.0
// @description 知网全自动批量下载PDF(从详情页解析PDF直链)- 断点续传,验证码暂停,批次日志
// @icon data:image/svg+xml,%3Csvg%20xmlns=%22http://www.w3.org/2000/svg%22%20viewBox=%220%200%2032%2032%22%3E%3Crect%20x=%221%22%20y=%221%22%20width=%2230%22%20height=%2230%22%20rx=%228%22%20fill=%22%2312141a%22%20stroke=%22%235a4a24%22%20stroke-width=%222%22/%3E%3Cpath%20d=%22M9%208h14%22%20stroke=%22%238b93a3%22%20stroke-width=%222.6%22%20stroke-linecap=%22round%22/%3E%3Cpath%20d=%22M9%2013h14%22%20stroke=%22%238b93a3%22%20stroke-width=%222.6%22%20stroke-linecap=%22round%22/%3E%3Cpath%20d=%22M16%2017v7%22%20stroke=%22%23f5a524%22%20stroke-width=%223.2%22%20stroke-linecap=%22round%22/%3E%3Cpath%20d=%22M11.5%2020.5L16%2025l4.5-4.5%22%20fill=%22none%22%20stroke=%22%23f5a524%22%20stroke-width=%223.2%22%20stroke-linecap=%22round%22%20stroke-linejoin=%22round%22/%3E%3C/svg%3E
// @match *://*.cnki.net/*
// @match *://*cnki*.*/*
// @match *://*.atrust.seu.edu.cn/*
// @match *://119.45.145.238/*
// @match *://119.45.237.51/*
// @author lunan
// @license MIT
// @grant none
// @run-at document-idle
// ==/UserScript==
(function() {
'use strict';
// 持续检查页面元素(翻页后 DOM 会重建)。
// 找不到时给一次控制台提示,方便区分「脚本没装成功」和「知网改版导致失效」
var pageEnteredAt = Date.now();
var notFoundHinted = false;
var mainInterval = setInterval(function() {
var batchOpsBox = document.querySelector('#batchOpsBox');
var tableBody = document.querySelector('#gridTable table.result-table-list tbody');
if (!batchOpsBox || !tableBody) {
if (!notFoundHinted && Date.now() - pageEnteredAt > 8000) {
notFoundHinted = true;
console.info('[知网资源批量下载] 本页没有检索结果表格(#batchOpsBox / #gridTable),面板只在检索结果页出现;若确认已在结果页,多半是知网改版导致脚本失效。');
}
return;
}
if (document.getElementById('cnki-panel')) return;
clearInterval(mainInterval);
initPanel();
}, 1000);
function initPanel() {
var CONFIG = {
restEvery: 50, restMin: 15, restMax: 30, maxPerSession: 200,
cajFallback: false, // 找不到 PDF 直链时,是否退回下载默认格式(CAJ)
autoCloseTab: false // 触发下载后自动关闭新标签页(默认关,避免误关验证页)
};
var SETTINGS_KEY = 'cnki_settings';
var STORAGE_KEY = 'cnki_downloaded';
function loadSettings() {
try {
var saved = JSON.parse(localStorage.getItem(SETTINGS_KEY));
if (saved) { for (var k in saved) { if (CONFIG.hasOwnProperty(k)) CONFIG[k] = saved[k]; } }
} catch(e) {}
}
function saveSettings() { localStorage.setItem(SETTINGS_KEY, JSON.stringify(CONFIG)); }
loadSettings();
// 记录键归一化:抹平「扩展名 / 副本序号 / CNKI 附带的作者后缀 / 空白差异」
// 页面标题和下载文件名走的是同一套归一化,才能互相对得上
function normTitle(raw) {
var s = String(raw == null ? '' : raw).trim();
s = s.replace(/\.(pdf|caj|nh|kdh|txt)$/i, '');
s = s.replace(/[((]\s*\d+\s*[))]\s*$/, ''); // 题名(1)
s = s.replace(/[-_—]\s*\d+\s*$/, ''); // 题名-1 / 题名_2
// 只处理「下划线 + 2~4 字姓名」这种 CNKI 作者后缀,且要求前面题名够长
// 刻意不动空格和连字符:题名本身常含空格(如「图像隐写 算法」),误删会导致漏下
s = s.replace(/^(.{6,})_[^_\s\u3000]{2,4}$/, '$1');
s = s.replace(/[\s\u3000]+/g, ''); // 去掉所有空白
return s;
}
// 缓存已下载列表,避免高频 JSON.parse
var _downloadedCache = null;
function getDownloaded() {
if (!_downloadedCache) {
var raw = [];
try { raw = JSON.parse(localStorage.getItem(STORAGE_KEY) || '[]'); } catch (e) { raw = []; }
if (!Array.isArray(raw)) raw = [];
var out = [], seen = {}, dirty = false;
raw.forEach(function (t) {
var k = normTitle(t);
if (!k || seen[k]) { dirty = true; return; }
seen[k] = 1;
out.push(k);
if (k !== t) dirty = true;
});
_downloadedCache = out;
if (dirty) localStorage.setItem(STORAGE_KEY, JSON.stringify(out));
}
return _downloadedCache;
}
// 写入前先跟 localStorage 合并:多标签页同时跑时不会互相覆盖记录
function persistDownloaded(extra) {
var fresh = [];
try { fresh = JSON.parse(localStorage.getItem(STORAGE_KEY) || '[]'); } catch (e) { fresh = []; }
if (!Array.isArray(fresh)) fresh = [];
var seen = {}, out = [];
fresh.concat(extra || []).forEach(function (t) {
var k = normTitle(t);
if (!k || seen[k]) return;
seen[k] = 1;
out.push(k);
});
_downloadedCache = out;
localStorage.setItem(STORAGE_KEY, JSON.stringify(out));
return out;
}
function saveDownloaded(title) {
var key = normTitle(title);
if (!key || getDownloaded().indexOf(key) !== -1) return;
persistDownloaded([key]);
}
function isDownloaded(title) { return getDownloaded().indexOf(normTitle(title)) !== -1; }
function getDownloadedCount() { return getDownloaded().length; }
function clearDownloaded() {
_downloadedCache = [];
localStorage.removeItem(STORAGE_KEY);
}
// 别的标签页动了记录就丢掉本地缓存,下次读取时重新加载
window.addEventListener('storage', function (e) {
if (!e.key || e.key === STORAGE_KEY) _downloadedCache = null;
});
// ── CSS ──
var style = document.createElement('style');
style.textContent = '\
#cnki-panel{--bg:#12141a;--bg2:#191c23;--line:#2a2f3a;--ink:#e7e9ee;--dim:#8b93a3;--amber:#f5a524;--green:#35c07a;--red:#f2545b;--blue:#5aa9f7;--violet:#b48ef0;--sans:"PingFang SC","HarmonyOS Sans SC","Source Han Sans SC","Microsoft YaHei",sans-serif;--mono:ui-monospace,"JetBrains Mono","Cascadia Mono",Consolas,monospace;position:fixed;bottom:22px;right:22px;z-index:99999;width:352px;max-height:90vh;overflow-y:auto;padding:0;color:var(--ink);font:13px/1.5 var(--sans);cursor:move;background:linear-gradient(180deg,#171a21,#0f1116);border:1px solid var(--line);border-radius:14px;box-shadow:0 18px 48px rgba(0,0,0,.55),0 1px 0 rgba(255,255,255,.04) inset;-webkit-font-smoothing:antialiased}\
#cnki-panel *{box-sizing:border-box}\
#cnki-panel::-webkit-scrollbar,#cnki-log::-webkit-scrollbar{width:7px;height:7px}\
#cnki-panel::-webkit-scrollbar-track,#cnki-log::-webkit-scrollbar-track{background:transparent}\
#cnki-panel::-webkit-scrollbar-thumb,#cnki-log::-webkit-scrollbar-thumb{background:#2c313c;border-radius:99px}\
#cnki-panel::-webkit-scrollbar-thumb:hover,#cnki-log::-webkit-scrollbar-thumb:hover{background:#3a4150}\
#cnki-head{display:flex;align-items:center;gap:8px;padding:12px 14px;border-bottom:1px solid var(--line);cursor:move}\
#cnki-dot{flex:none;width:8px;height:8px;border-radius:50%;background:var(--dim);box-shadow:0 0 0 3px rgba(139,147,163,.12);transition:background .2s}\
#cnki-title{font:800 13.5px/1.2 var(--sans);letter-spacing:.06em;white-space:nowrap}\
#cnki-count{margin-left:auto;flex:none;font:10px/1 var(--mono);letter-spacing:.03em;color:#ffd479;background:rgba(245,165,36,.1);border:1px solid rgba(245,165,36,.26);border-radius:99px;padding:3px 7px;white-space:nowrap}\
#cnki-ver{flex:none;font:10px/1 var(--mono);letter-spacing:.03em;color:var(--dim);border:1px solid var(--line);border-radius:99px;padding:3px 7px}\
#cnki-panel.is-running #cnki-dot{background:var(--amber);box-shadow:0 0 0 3px rgba(245,165,36,.16);animation:cnkiPulse 1.6s ease-in-out infinite}\
#cnki-panel.is-resting #cnki-dot{background:var(--violet);box-shadow:0 0 0 3px rgba(180,142,240,.16);animation:cnkiPulse 1.1s ease-in-out infinite}\
#cnki-panel.is-paused #cnki-dot{background:var(--blue);box-shadow:0 0 0 3px rgba(90,169,247,.16)}\
#cnki-hud{padding:11px 14px 12px;border-bottom:1px solid var(--line)}\
#cnki-progress{min-height:17px;font-weight:600;font-size:12px;line-height:1.45;word-break:break-all}\
#cnki-countdown{margin-top:3px;min-height:16px;font:11px/1.5 var(--mono);color:var(--dim);word-break:break-all}\
#cnki-rail{margin-top:9px;height:3px;border-radius:99px;background:#22262f;overflow:hidden}\
#cnki-rail i{display:block;height:100%;width:var(--p,0%);border-radius:99px;background:linear-gradient(90deg,var(--amber),#ffd479);transition:width .35s cubic-bezier(.4,0,.2,1)}\
#cnki-panel.is-running #cnki-rail i{background-image:linear-gradient(90deg,var(--amber),#ffe3a3,var(--amber));background-size:200% 100%;animation:cnkiSheen 1.5s linear infinite}\
#cnki-body{display:flex;flex-direction:column;gap:9px;padding:11px 14px 14px}\
#cnki-panel button{display:block;width:100%;padding:10px 12px;border:1px solid transparent;border-radius:9px;cursor:pointer;font:600 13px/1 var(--sans);transition:background .15s,border-color .15s,color .15s,filter .15s,transform .08s}\
#cnki-panel button:active{transform:translateY(1px)}\
#cnki-btn-start{background:linear-gradient(180deg,#f7b53c,#e8960f);color:#241a00;box-shadow:0 6px 18px rgba(245,165,36,.2)}\
#cnki-btn-start:hover{filter:brightness(1.07)}\
#cnki-btn-pause{background:#22262f;color:var(--ink);border-color:var(--line)}\
#cnki-btn-pause:hover{border-color:#3a4150}\
#cnki-btn-stop{background:rgba(242,84,91,.12);color:#ff9197;border-color:rgba(242,84,91,.34)}\
#cnki-btn-stop:hover{background:rgba(242,84,91,.2)}\
#cnki-btn-skip-rest{background:rgba(53,192,122,.14);color:#5cd99a;border-color:rgba(53,192,122,.34)}\
#cnki-btn-skip-rest:hover{background:rgba(53,192,122,.22)}\
#cnki-btn-settings{background:#1c2028;color:var(--dim);border-color:var(--line);font-weight:500}\
#cnki-btn-settings:hover{color:var(--ink);border-color:#3a4150}\
#cnki-btn-import{background:#1c2028;color:#bda9f2;border-color:rgba(180,142,240,.28)}\
#cnki-btn-import:hover{border-color:rgba(180,142,240,.55);color:#d3c6fa}\
#cnki-btn-export{background:#1c2028;color:#9ccbfb;border-color:rgba(90,169,247,.28)}\
#cnki-btn-export:hover{border-color:rgba(90,169,247,.55);color:#c6e0fd}\
#cnki-btn-clear{background:transparent;color:#7b8394;border-color:var(--line);font-weight:500;font-size:12px}\
#cnki-btn-clear:hover{color:#ff9197;border-color:rgba(242,84,91,.4)}\
.cnki-util{display:grid;grid-template-columns:1fr 1fr 1fr;gap:7px}\
.cnki-util button{padding:9px 4px;font-size:11.5px}\
#cnki-btn-min{align-self:center;width:auto;background:transparent;color:#6f7787;border-color:transparent;font-weight:500;font-size:11.5px;padding:5px 14px}\
#cnki-btn-min:hover{color:var(--ink)}\
#cnki-captcha{display:none;margin:0 14px;background:rgba(245,165,36,.1);border:1px solid rgba(245,165,36,.45);border-radius:10px;padding:10px 11px;font-size:12px;line-height:1.6;color:#ffd479;text-align:center}\
#cnki-mode-area{background:var(--bg2);border:1px solid var(--line);border-radius:10px;padding:9px 10px}\
.cnki-cap{font:600 10px/1 var(--mono);letter-spacing:.14em;color:var(--dim);margin-bottom:8px}\
#cnki-mode-area label{display:flex;align-items:center;gap:8px;padding:6px 8px;border:1px solid transparent;border-radius:7px;cursor:pointer;font-size:12.5px;color:var(--dim);transition:background .15s,color .15s,border-color .15s}\
#cnki-mode-area label:hover{background:#1e222b;color:var(--ink)}\
#cnki-mode-area label:has(input:checked){border-color:rgba(245,165,36,.4);background:rgba(245,165,36,.08);color:var(--ink)}\
#cnki-mode-area input[type=radio]{margin:0;accent-color:var(--amber)}\
#cnki-settings{display:none;background:var(--bg2);border:1px solid var(--line);border-radius:10px;padding:11px 11px 3px}\
#cnki-settings .setting-group{margin-bottom:11px}\
#cnki-settings .setting-label{font:600 10px/1 var(--mono);letter-spacing:.12em;color:var(--amber);margin-bottom:6px}\
#cnki-settings .setting-desc{font-size:11px;line-height:1.55;color:var(--dim);margin-bottom:8px}\
#cnki-settings .input-row{display:flex;align-items:center;flex-wrap:wrap;gap:5px;font-size:12px;color:var(--dim)}\
#cnki-settings .input-row input[type=number]{width:48px;padding:5px 4px;background:#0d0f14;border:1px solid var(--line);border-radius:6px;color:var(--ink);text-align:center;font:600 12px/1 var(--mono)}\
#cnki-settings .input-row input[type=number]:focus{outline:none;border-color:var(--amber)}\
#cnki-settings>div>label{display:flex;align-items:center;gap:7px;cursor:pointer;font-size:12px;color:var(--dim)}\
#cnki-settings input[type=checkbox]{margin:0;accent-color:var(--amber)}\
.cnki-select-all-bar{display:none;align-items:center;background:rgba(90,169,247,.1);border:1px solid rgba(90,169,247,.3);border-radius:9px;padding:8px 10px;font-size:12px;line-height:1.5;color:#9ccbfb}\
#cnki-log{max-height:158px;overflow-y:auto;background:#0c0e13;border:1px solid var(--line);border-radius:10px;padding:7px 9px;font:11px/1.7 var(--mono);color:var(--dim)}\
#cnki-log>div{padding:2px 0 2px 7px;border-left:2px solid currentColor;border-bottom:1px solid #16181e;word-break:break-all}\
#cnki-log>div:last-child{border-bottom:none}\
.cnki-ts{color:#575e6c;margin-right:6px}\
@keyframes cnkiPulse{0%,100%{opacity:1}50%{opacity:.35}}\
@keyframes cnkiSheen{0%{background-position:-110% 0}100%{background-position:210% 0}}\
';
document.head.appendChild(style);
// ── 面板 ──
var panel = document.createElement('div');
panel.id = 'cnki-panel';
panel.innerHTML = '\
\
\
知网资源批量下载\
已记录 0\
v1.1.0\
\
\
\u26a0 \u6d4f\u89c8\u5668\u53ef\u80fd\u5f39\u51fa\u9a8c\u8bc1\u7801\uff0c\u8bf7\u5728\u5f39\u51fa\u7684\u7a97\u53e3\u4e2d\u5b8c\u6210\u9a8c\u8bc1
\
\
\
\
\u8bf7\u5728\u5de6\u4fa7\u5217\u8868\u4e2d\u52fe\u9009\u8981\u4e0b\u8f7d\u7684\u6587\u732e\
\
\
\
\
\
\ud83d\udcc4 下载格式
\
默认只下 PDF(从详情页解析 PDF 直链);勾选后在找不到 PDF 时退回 CAJ
\
\
\
\
\
\ud83d\udce6 单批上限
\
一批最多触发多少篇,达到后自动结束本批(自动翻页也按整批累计)
\
\
\
\u7bc7\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
';
document.body.appendChild(panel);
// ── 拖动(鼠标 + 触屏);位置存 localStorage,自动翻页重载后不会跳回右下角 ──
var POS_KEY = 'cnki_panel_pos';
var isDragging = false, dragX = 0, dragY = 0;
function clamp(v, min, max) { return Math.min(Math.max(v, min), max); }
function isInteractiveEl(t) {
return !!t && /^(BUTTON|INPUT|LABEL|SELECT|TEXTAREA|A)$/.test(t.tagName);
}
function applyPanelPos(left, top) {
var maxL = Math.max(0, window.innerWidth - panel.offsetWidth);
var maxT = Math.max(0, window.innerHeight - 44);
panel.style.left = clamp(left, 0, maxL) + 'px';
panel.style.top = clamp(top, 0, maxT) + 'px';
panel.style.right = 'auto';
panel.style.bottom = 'auto';
}
function startDrag(x, y) {
isDragging = true;
var rect = panel.getBoundingClientRect();
dragX = x - rect.left;
dragY = y - rect.top;
}
function moveDrag(x, y) {
if (!isDragging) return;
applyPanelPos(x - dragX, y - dragY);
}
function endDrag() {
if (!isDragging) return;
isDragging = false;
var rect = panel.getBoundingClientRect();
localStorage.setItem(POS_KEY, JSON.stringify({
left: Math.round(rect.left), top: Math.round(rect.top)
}));
}
try {
var savedPos = JSON.parse(localStorage.getItem(POS_KEY) || 'null');
if (savedPos && typeof savedPos.left === 'number' && typeof savedPos.top === 'number') {
applyPanelPos(savedPos.left, savedPos.top);
}
} catch (e) {}
panel.addEventListener('mousedown', function (e) {
if (isInteractiveEl(e.target)) return;
startDrag(e.clientX, e.clientY);
});
document.addEventListener('mousemove', function (e) { moveDrag(e.clientX, e.clientY); });
document.addEventListener('mouseup', endDrag);
panel.addEventListener('touchstart', function (e) {
if (isInteractiveEl(e.target) || !e.touches.length) return;
startDrag(e.touches[0].clientX, e.touches[0].clientY);
}, { passive: true });
document.addEventListener('touchmove', function (e) {
if (!isDragging || !e.touches.length) return;
e.preventDefault();
moveDrag(e.touches[0].clientX, e.touches[0].clientY);
}, { passive: false });
document.addEventListener('touchend', endDrag);
document.addEventListener('touchcancel', endDrag);
// ── DOM ──
var logBox = document.getElementById('cnki-log');
var progressBox = document.getElementById('cnki-progress');
var countdownBox = document.getElementById('cnki-countdown');
var captchaBox = document.getElementById('cnki-captcha');
var btnStart = document.getElementById('cnki-btn-start');
var btnPause = document.getElementById('cnki-btn-pause');
var btnStop = document.getElementById('cnki-btn-stop');
var btnMin = document.getElementById('cnki-btn-min');
var btnSettings = document.getElementById('cnki-btn-settings');
var settingsPanel = document.getElementById('cnki-settings');
var selectBar = document.getElementById('cnki-select-bar');
var btnSkipRest = document.getElementById('cnki-btn-skip-rest');
var cajFallbackEl = document.getElementById('cnki-caj-fallback');
var autoCloseEl = document.getElementById('cnki-auto-close');
var maxSessionEl = document.getElementById('cnki-max-session');
var running = false, paused = false, skipRest = false;
var downloadMode = 'sequential';
btnSkipRest.addEventListener('click', function() { skipRest = true; });
btnSettings.addEventListener('click', function() {
var show = settingsPanel.style.display === 'none' || !settingsPanel.style.display;
settingsPanel.style.display = show ? 'block' : 'none';
btnSettings.textContent = show ? '\u2699 \u6536\u8d77\u8bbe\u7f6e' : '\u2699 \u8bbe\u7f6e';
});
['cnki-rest-every','cnki-rest-min','cnki-rest-max','cnki-max-session'].forEach(function(id) {
var el = document.getElementById(id);
if (el) el.addEventListener('change', function() {
var map = {
'cnki-rest-every': 'restEvery', 'cnki-rest-min': 'restMin',
'cnki-rest-max': 'restMax', 'cnki-max-session': 'maxPerSession'
};
CONFIG[map[id]] = parseInt(el.value) || 1;
if (CONFIG.restMax < CONFIG.restMin) CONFIG.restMax = CONFIG.restMin;
saveSettings();
});
});
if (cajFallbackEl) cajFallbackEl.addEventListener('change', function() {
CONFIG.cajFallback = cajFallbackEl.checked;
saveSettings();
});
if (autoCloseEl) autoCloseEl.addEventListener('change', function() {
CONFIG.autoCloseTab = autoCloseEl.checked;
saveSettings();
});
document.querySelectorAll('input[name="cnki-mode"]').forEach(function(r) {
r.addEventListener('change', function() {
downloadMode = this.value;
selectBar.style.display = downloadMode === 'selected' ? 'flex' : 'none';
});
});
// 日志配色:调用处沿用的旧亮色统一映射到暗色面板上可读的色值
var LOG_COLORS = {
'#1a73e8': '#5aa9f7', '#0d904f': '#35c07a', '#e67e22': '#f5a524',
'#ea4335': '#f2545b', '#8e24aa': '#b48ef0', '#5c6bc0': '#8ab4f8'
};
function log(text, color) {
var item = document.createElement('div');
item.style.color = LOG_COLORS[color] || color || 'var(--blue)';
var now = new Date();
var ts = [now.getHours(), now.getMinutes(), now.getSeconds()]
.map(function(n){ return n.toString().padStart(2,'0'); }).join(':');
var stamp = document.createElement('span');
stamp.className = 'cnki-ts';
stamp.textContent = ts;
item.appendChild(stamp);
item.appendChild(document.createTextNode(text));
logBox.insertBefore(item, logBox.firstChild);
// 长批次下日志节点会一直堆积,只保留最近 200 条
while (logBox.childNodes.length > 200) logBox.removeChild(logBox.lastChild);
}
// 顶部读数 + 进度条(--p 驱动 #cnki-rail i 的宽度)
function setProgress(text, pct) {
progressBox.textContent = text;
if (typeof pct === 'number') {
panel.style.setProperty('--p', Math.max(0, Math.min(100, pct)) + '%');
}
}
// 面板状态:'' / running / resting / paused,驱动头部指示灯
function setState(state) {
panel.classList.remove('is-running', 'is-resting', 'is-paused');
if (state) panel.classList.add('is-' + state);
}
function sleep(ms) { return new Promise(function(r){ setTimeout(r, ms); }); }
async function waitIfPaused() {
while (paused && running) {
countdownBox.textContent = '\u5df2\u6682\u505c\uff0c\u70b9\u51fb\u201c\u7ee7\u7eed\u201d\u6062\u590d';
await sleep(500);
}
}
async function restDelay() {
skipRest = false;
var totalSec = Math.floor(Math.random() * (CONFIG.restMax - CONFIG.restMin)) + CONFIG.restMin;
log('\u4e2d\u573a\u4f11\u606f ' + totalSec + ' \u79d2\uff08\u53ef\u8df3\u8fc7\uff09', '#e67e22');
countdownBox.style.cssText = 'margin-top:6px;font:600 12px/1.5 var(--mono);color:#ffd479;background:rgba(245,165,36,.1);border:1px solid rgba(245,165,36,.4);border-radius:8px;padding:7px 9px;text-align:center';
setProgress('\u4e2d\u573a\u4f11\u606f\u4e2d\u2026');
setState('resting');
btnSkipRest.style.display = 'block';
for (var s = totalSec; s > 0; s--) {
if (!running || skipRest) break;
await waitIfPaused();
var m = Math.floor(s / 60), sec = s % 60;
countdownBox.textContent = '\u4f11\u606f\u4e2d ' + m + ':' + sec.toString().padStart(2,'0') + ' \uff08\u70b9\u4e0b\u65b9\u8df3\u8fc7\uff09';
await sleep(1000);
}
btnSkipRest.style.display = 'none';
if (skipRest) log('\u5df2\u8df3\u8fc7\u4f11\u606f', '#1a73e8');
countdownBox.textContent = '';
countdownBox.style.cssText = '';
if (running) setState(paused ? 'paused' : 'running');
}
// 每次重新获取表格(翻页后 DOM 会变)
function getTableBody() {
return document.querySelector('#gridTable table.result-table-list tbody');
}
// 从操作列中找下载入口:真正的 PDF 链接优先,其次才是通用下载链接
// 返回 { href, kind },kind 为 'pdf' | 'caj';找不到返回 null
// 注意:新版知网列表页只给一个「下载」入口(默认格式 CAJ),PDF 入口只在详情页
function findDownloadEntry(operatCell) {
var allLinks = operatCell.querySelectorAll('a');
var cajHref = '';
for (var i = 0; i < allLinks.length; i++) {
var a = allLinks[i];
var href = a.href || a.getAttribute('href') || '';
var text = (a.textContent || '').trim();
var cls = a.className || '';
var onclick = a.getAttribute('onclick') || '';
// 跳过空链接和 javascript: 伪链接(含 javascript:; 这类写法)
if (!href || href.indexOf('javascript:') === 0) continue;
// 明确的 PDF 链接
if (cls.indexOf('pdf') >= 0 || text.indexOf('PDF') >= 0 ||
href.indexOf('pdfdown') >= 0 || /\/PDFDownload\//i.test(href) ||
onclick.indexOf('pdf') >= 0 || onclick.indexOf('PDF') >= 0) {
return { href: href, kind: 'pdf' };
}
// 记录通用下载链接作为 CAJ 兜底候选
if (!cajHref && (cls.indexOf('downloadlink') >= 0 || cls.indexOf('download') >= 0)) {
cajHref = href;
}
}
return cajHref ? { href: cajHref, kind: 'caj' } : null;
}
// 详情页被验证/安全拦截时的特征串。只看页面头部,避免命中正文里讨论「验证码」的论文
var BLOCK_MARKERS = /验证码|安全验证|滑动验证|请完成验证|完成验证后|人机验证|verifyCode|captcha/i;
var _activeAbort = null;
function sameOrigin(url) {
try { return new URL(url, location.href).origin === location.origin; } catch (e) { return false; }
}
// 单次请求详情页:15 秒超时,且可被「终止」按钮打断
async function fetchPdfOnce(detailHref) {
var ctrl = (typeof AbortController !== 'undefined') ? new AbortController() : null;
var timer = null;
_activeAbort = ctrl;
if (ctrl) timer = setTimeout(function () { try { ctrl.abort(); } catch (e) {} }, 15000);
try {
var res = await fetch(detailHref, {
credentials: 'include',
signal: ctrl ? ctrl.signal : undefined
});
if (!res.ok) return { href: '', blocked: false };
var html = await res.text();
var doc = new DOMParser().parseFromString(html, 'text/html');
var links = doc.querySelectorAll('a');
for (var i = 0; i < links.length; i++) {
var a = links[i];
var href = a.getAttribute('href') || '';
if (!href || href.indexOf('javascript:') === 0) continue;
if (href.indexOf('//') === 0) href = location.protocol + href;
else if (href.charAt(0) === '/') href = location.origin + href;
if (!/^https?:/i.test(href)) continue;
if (href.indexOf('/download/order') < 0 && href.indexOf('pdfdown') < 0) continue;
if (/pdf/i.test((a.textContent || '') + (a.className || ''))) {
return { href: href, blocked: false };
}
}
// 没拿到下载链接:若页面头部带验证特征,判定为被拦下
return { href: '', blocked: BLOCK_MARKERS.test(html.slice(0, 4000)) };
} catch (e) {
return { href: '', blocked: false };
} finally {
if (timer) clearTimeout(timer);
_activeAbort = null;
}
}
// 从文献详情页解析 PDF 下载直链,失败重试一次(网络抖动 / 知网偶发 5xx)
// 直链形如 https://bar.cnki.net/bar/download/order?id=<密文>,格式编码在密文里,
// 所以只能从详情页重新取,改列表页那个链接的 URL 参数是无效的
async function fetchPdfHref(detailHref) {
for (var attempt = 0; attempt < 2; attempt++) {
var r = await fetchPdfOnce(detailHref);
if (r.href || r.blocked || !running) return r;
if (attempt === 0) await sleep(700 + Math.random() * 600);
}
return { href: '', blocked: false };
}
// ── 从表格行中收集下载信息 ──
function collectDownloadItems(mode) {
var items = [];
var tb = getTableBody();
if (!tb) return items;
var rows = tb.querySelectorAll('tr');
rows.forEach(function(row) {
if (mode === 'selected') {
var cb = row.querySelector('input[type="checkbox"]');
if (!cb || !cb.checked) return;
}
var titleLink = row.querySelector('td.name a.fz14') || row.querySelector('a.fz14');
var title = titleLink ? titleLink.textContent.trim() : '';
var operatCell = row.querySelector('td.operat');
if (!operatCell) return;
var dl = findDownloadEntry(operatCell);
if (title && dl) {
items.push({
title: title,
dl: dl,
detailHref: titleLink ? titleLink.href : '',
row: row,
titleLink: titleLink
});
}
});
return items;
}
function markDownloaded() {
var tb = getTableBody();
if (!tb) return;
var rows = tb.querySelectorAll('tr');
rows.forEach(function(row) {
var titleLink = row.querySelector('td.name a.fz14') || row.querySelector('a.fz14');
if (titleLink && isDownloaded(titleLink.textContent.trim())) {
titleLink.style.backgroundColor = '#d4edda';
}
});
}
// ── 最小化 ──
btnMin.addEventListener('click', function() {
panel.style.display = 'none';
var mb = document.createElement('button');
mb.style.cssText = 'position:fixed;top:80px;right:22px;z-index:99999;display:flex;align-items:center;justify-content:center;width:46px;height:46px;background:linear-gradient(180deg,#f7b53c,#e8960f);color:#241a00;border:none;border-radius:14px;font-size:18px;line-height:1;cursor:pointer;box-shadow:0 10px 26px rgba(0,0,0,.45)';
mb.title = '\u5c55\u5f00\u9762\u677f';
mb.textContent = '\u25bc';
mb.addEventListener('click', function(){ panel.style.display='block'; mb.remove(); });
document.body.appendChild(mb);
});
// ── 记录的导出 / 导入 ──
var btnImport = document.getElementById('cnki-btn-import');
var btnExport = document.getElementById('cnki-btn-export');
var countEl = document.getElementById('cnki-count');
function updateRecordCount() {
if (countEl) countEl.textContent = '\u5df2\u8bb0\u5f55 ' + getDownloadedCount();
}
function addRecord(raw) {
var key = normTitle(raw);
if (!key) return 'bad';
if (getDownloaded().indexOf(key) !== -1) return 'dup';
saveDownloaded(key);
return 'new';
}
function timeStamp() {
var d = new Date();
var p = function (n) { return String(n).padStart(2, '0'); };
return String(d.getFullYear()) + p(d.getMonth() + 1) + p(d.getDate()) +
'-' + p(d.getHours()) + p(d.getMinutes());
}
function downloadText(text, filename, mime) {
var blob = new Blob([text], { type: (mime || 'text/plain') + ';charset=utf-8' });
var url = URL.createObjectURL(blob);
var a = document.createElement('a');
a.href = url;
a.download = filename;
document.body.appendChild(a);
a.click();
a.remove();
setTimeout(function () { URL.revokeObjectURL(url); }, 5000);
}
// ── 批次日志(CSV) ──
// 一批 = 一次「开始批量下载」到结束;自动翻页续跑的后续页面算同一批
var BATCH_LOG_KEY = 'cnki_batch_log';
var BATCH_STAT_KEY = 'cnki_batch_stat';
var BATCH_CONTINUE_KEY = 'cnki_batch_continue';
var _batchLog = null;
var _batchStat = null;
function getBatchLog() {
if (!_batchLog) {
try { _batchLog = JSON.parse(localStorage.getItem(BATCH_LOG_KEY) || '[]'); } catch (e) { _batchLog = []; }
if (!Array.isArray(_batchLog)) _batchLog = [];
}
return _batchLog;
}
function batchAppend(title, status) {
var list = getBatchLog();
list.push({ t: String(title == null ? '' : title), s: status });
localStorage.setItem(BATCH_LOG_KEY, JSON.stringify(list));
}
function batchBegin() {
_batchLog = [];
_batchStat = null;
localStorage.removeItem(BATCH_LOG_KEY);
localStorage.removeItem(BATCH_STAT_KEY);
}
// 批次计数器(跨页持久化):单批上限和中场休息节奏必须按整批累计,
// 放在页面级变量里的话,每次自动翻页都会归零
function getBatchStat() {
if (!_batchStat) {
var s = null;
try { s = JSON.parse(localStorage.getItem(BATCH_STAT_KEY) || 'null'); } catch (e) { s = null; }
_batchStat = (s && typeof s === 'object') ? s : {};
['ok', 'failed', 'skipped', 'processed'].forEach(function (k) {
if (typeof _batchStat[k] !== 'number') _batchStat[k] = 0;
});
}
return _batchStat;
}
function bumpBatchStat(field, n) {
var st = getBatchStat();
st[field] = (st[field] || 0) + (n == null ? 1 : n);
localStorage.setItem(BATCH_STAT_KEY, JSON.stringify(st));
return st[field];
}
function csvCell(v) {
var s = String(v == null ? '' : v);
return /[",\r\n]/.test(s) ? '"' + s.replace(/"/g, '""') + '"' : s;
}
// 批次结束:写出 CSV(带 BOM,Excel 才认 UTF-8 中文)并清掉累计
function batchEnd() {
var list = getBatchLog();
_batchLog = null;
_batchStat = null;
localStorage.removeItem(BATCH_LOG_KEY);
localStorage.removeItem(BATCH_STAT_KEY);
if (!list.length) return;
var lines = ['\u5e8f\u53f7,\u9898\u76ee,\u72b6\u6001'];
list.forEach(function (r, i) {
lines.push((i + 1) + ',' + csvCell(r.t) + ',' + csvCell(r.s));
});
var name = 'cnki-batch-' + timeStamp() + '.csv';
downloadText('\ufeff' + lines.join('\r\n') + '\r\n', name, 'text/csv');
log('\u672c\u6279\u8bb0\u5f55\u5df2\u5bfc\u51fa\uff1a' + name + '\uff08' + list.length + ' \u884c\uff09', '#8e24aa');
}
// 导出为 JSON:{type,version,exportedAt,count,titles:[...]}
function exportRecords() {
var list = getDownloaded();
if (!list.length) { log('\u8fd8\u6ca1\u6709\u8bb0\u5f55\u53ef\u5bfc\u51fa', '#e67e22'); return; }
var payload = {
type: 'cnki-auto-download-records',
version: 1,
exportedAt: new Date().toISOString(),
count: list.length,
titles: list
};
downloadText(JSON.stringify(payload, null, 2), 'cnki-downloaded-' + timeStamp() + '.json', 'application/json');
log('\u5df2\u5bfc\u51fa ' + list.length + ' \u6761\u8bb0\u5f55', '#8e24aa');
}
// 兼容本脚本导出的 JSON(也接受纯标题数组)与一行一个标题的 txt
function parseRecordFile(text, isJson) {
if (isJson) {
try {
var data = JSON.parse(text);
if (Array.isArray(data)) return data;
if (data && Array.isArray(data.titles)) return data.titles;
} catch (e) {}
return [];
}
return text.split(/\r?\n/);
}
// 一次导入多个来源:导出的 json、一行一标题的 txt、以及已下载的 pdf/caj
function importFiles(files) {
var stat = { scanned: files.length, added: 0, dup: 0, bad: 0 };
var readers = [];
Array.prototype.slice.call(files).forEach(function (f) {
var name = f.name || '';
if (/\.(json|txt)$/i.test(name)) {
var isJson = /\.json$/i.test(name);
readers.push(new Promise(function (resolve) {
var fr = new FileReader();
fr.onload = function () { resolve(parseRecordFile(String(fr.result || ''), isJson)); };
fr.onerror = function () { resolve([]); };
fr.readAsText(f);
}));
return;
}
if (!/\.(pdf|caj|nh|kdh)$/i.test(name)) { stat.bad++; return; }
var r = addRecord(name);
if (r === 'new') stat.added++;
else if (r === 'dup') stat.dup++;
else stat.bad++;
});
Promise.all(readers).then(function (lists) {
lists.forEach(function (titles) {
titles.forEach(function (t) {
var r = addRecord(t);
if (r === 'new') stat.added++;
else if (r === 'dup') stat.dup++;
else stat.bad++;
});
});
log('\u5bfc\u5165\u5b8c\u6210\uff1a\u626b\u63cf ' + stat.scanned + ' \u4e2a\u6587\u4ef6\uff0c\u65b0\u589e ' +
stat.added + ' \u6761\uff0c\u91cd\u590d ' + stat.dup + ' \u6761' +
(stat.bad ? '\uff0c\u5ffd\u7565 ' + stat.bad + ' \u9879' : ''), '#8e24aa');
updateRecordCount();
markDownloaded();
});
}
btnExport.addEventListener('click', exportRecords);
btnImport.addEventListener('click', function () {
var picker = document.createElement('input');
picker.type = 'file';
picker.multiple = true;
picker.accept = '.json,.txt,.pdf,.caj,.nh,.kdh';
picker.style.display = 'none';
document.body.appendChild(picker);
picker.addEventListener('change', function () {
var files = picker.files;
picker.remove();
if (!files || !files.length) return;
importFiles(files);
});
picker.click();
});
document.getElementById('cnki-btn-clear').addEventListener('click', function() {
if (confirm('\u786e\u5b9a\u6e05\u7a7a\u6240\u6709\u4e0b\u8f7d\u8bb0\u5f55\uff1f\uff08\u5efa\u8bae\u5148\u5bfc\u51fa\uff09')) {
clearDownloaded();
updateRecordCount();
log('\u5df2\u6e05\u7a7a', '#ea4335');
var tb = getTableBody();
if (!tb) return;
var rows = tb.querySelectorAll('tr');
rows.forEach(function(row) {
var t = row.querySelector('td.name a.fz14') || row.querySelector('a.fz14');
if (t) t.style.backgroundColor = '';
});
}
});
btnPause.addEventListener('click', function() {
paused = !paused;
btnPause.textContent = paused ? '\u7ee7\u7eed' : '\u6682\u505c';
setState(paused ? 'paused' : 'running');
log(paused ? '\u5df2\u6682\u505c' : '\u5df2\u6062\u590d', paused ? '#e67e22' : '#0d904f');
});
btnStop.addEventListener('click', function() {
running = false; paused = false;
// 立刻掐掉正在进行的详情页请求,不用等它自己超时
if (_activeAbort) { try { _activeAbort.abort(); } catch (e) {} }
btnStop.style.display = 'none'; btnPause.style.display = 'none';
btnStart.style.display = 'block'; captchaBox.style.display = 'none';
btnPause.textContent = '\u6682\u505c';
countdownBox.textContent = ''; countdownBox.style.cssText = '';
setState('');
log('\u5df2\u7ec8\u6b62', '#ea4335');
setProgress('\u5df2\u7ec8\u6b62', 0);
});
// ── 开始下载 ──
btnStart.addEventListener('click', async function() {
var items = collectDownloadItems(downloadMode);
if (items.length === 0) {
if (downloadMode === 'selected') {
log('\u8bf7\u5148\u52fe\u9009\u8981\u4e0b\u8f7d\u7684\u6587\u732e\uff01', '#ea4335');
} else {
log('\u672a\u627e\u5230\u6709\u6548\u7684\u4e0b\u8f7d\u94fe\u63a5\uff01', '#ea4335');
}
// 自动翻页续跑到一个没有可下载项的新页面,视为这批结束
if (localStorage.getItem(BATCH_CONTINUE_KEY) === 'true') {
localStorage.removeItem(BATCH_CONTINUE_KEY);
batchEnd();
}
return;
}
CONFIG.restEvery = parseInt(document.getElementById('cnki-rest-every').value) || 50;
CONFIG.restMin = parseInt(document.getElementById('cnki-rest-min').value) || 15;
CONFIG.restMax = parseInt(document.getElementById('cnki-rest-max').value) || 30;
if (CONFIG.restMax < CONFIG.restMin) CONFIG.restMax = CONFIG.restMin;
CONFIG.cajFallback = !!(cajFallbackEl && cajFallbackEl.checked);
CONFIG.autoCloseTab = !!(autoCloseEl && autoCloseEl.checked);
CONFIG.maxPerSession = parseInt(maxSessionEl && maxSessionEl.value) || CONFIG.maxPerSession || 200;
saveSettings();
settingsPanel.style.display = 'none';
btnSettings.textContent = '\u2699 \u8bbe\u7f6e';
running = true; paused = false; skipRest = false;
btnStart.style.display = 'none'; btnPause.style.display = 'block'; btnStop.style.display = 'block';
btnPause.textContent = '\u6682\u505c';
logBox.innerHTML = '';
setState('running');
setProgress('\u51c6\u5907\u4e2d\u2026', 0);
// 自动翻页续跑的页面沿用同一批记录,手动点开始则新开一批
if (localStorage.getItem(BATCH_CONTINUE_KEY) === 'true') {
localStorage.removeItem(BATCH_CONTINUE_KEY);
} else {
batchBegin();
}
var modeLabel = downloadMode === 'selected' ? '\u52fe\u9009' : '\u987a\u5e8f';
log(modeLabel + '\u6a21\u5f0f\uff0c\u5171 ' + items.length + ' \u7bc7\uff0c\u7d2f\u8ba1\u5df2\u4e0b\u8f7d ' + getDownloadedCount(), '#1a73e8');
log('\u8bf7\u786e\u4fdd\u6d4f\u89c8\u5668\u5141\u8bb8\u5f39\u51fa\u7a97\u53e3\uff01', '#e67e22');
// 计数放进批次统计(跨页持久化);页面级只留「本页成功数」用于判断要不要翻页
var stat = getBatchStat();
var pageOk = 0;
var popupBlocked = false;
var captchaStreak = 0;
var crossOriginWarned = false;
for (var i = 0; i < items.length; i++) {
if (!running) break;
await waitIfPaused();
if (!running) break;
if (stat.ok >= CONFIG.maxPerSession) {
log('\u672c\u6279\u5df2\u8fbe\u4e0a\u9650 ' + stat.ok + ' \u7bc7\uff0c\u7ed3\u675f\u672c\u6279', '#e67e22');
running = false; break;
}
if (stat.processed > 0 && stat.processed % CONFIG.restEvery === 0) {
await restDelay();
if (!running) break;
}
var item = items[i];
if (isDownloaded(item.title)) {
if (item.titleLink) item.titleLink.style.backgroundColor = '#d4edda';
batchAppend(item.title, '跳过');
bumpBatchStat('skipped');
continue;
}
setProgress('[' + (i+1) + '/' + items.length + '] ' + item.title.substring(0, 22) + '...', (i / items.length) * 100);
log('[' + (i+1) + '] ' + item.title, '#1a73e8');
if (item.titleLink) {
item.titleLink.style.backgroundColor = '#fff3cd';
item.titleLink.scrollIntoView({behavior:'smooth', block:'center'});
}
// 解析真正的 PDF 直链:列表页直接给 PDF 就用,否则去详情页取
var dlHref = '', fmt = '';
if (item.dl && item.dl.kind === 'pdf') {
dlHref = item.dl.href; fmt = 'PDF';
}
// 页面不在知网域下(校内代理、镜像站)时浏览器不允许读详情页,只能降级走列表入口
var crossOrigin = !!item.detailHref && !sameOrigin(item.detailHref);
if (crossOrigin && !crossOriginWarned) {
crossOriginWarned = true;
log('当前页面与知网不同域,无法解析详情页,本批改用列表页入口(多为 CAJ)', '#e67e22');
}
if (!dlHref && item.detailHref && !crossOrigin) {
setProgress('[' + (i+1) + '/' + items.length + '] 解析 PDF 直链...', (i / items.length) * 100);
var r = await fetchPdfHref(item.detailHref);
if (!running) break;
if (r.href) {
dlHref = r.href; fmt = 'PDF'; captchaStreak = 0;
} else {
captchaStreak = r.blocked ? captchaStreak + 1 : 0;
if (captchaStreak >= 2) {
log('连续 2 篇被验证页拦下,已暂停本批:请在页面完成验证后重新开始', '#ea4335');
captchaBox.style.display = 'block';
batchAppend(item.title, '失败');
bumpBatchStat('failed'); bumpBatchStat('processed');
running = false; break;
}
}
}
if (!dlHref && (CONFIG.cajFallback || crossOrigin) && item.dl && item.dl.kind === 'caj') {
dlHref = item.dl.href; fmt = 'CAJ';
}
if (!dlHref) {
log(' 未取到 PDF 直链,已跳过(可在设置里开启 CAJ 兜底)', '#ea4335');
if (item.titleLink) item.titleLink.style.backgroundColor = '';
batchAppend(item.title, '失败');
bumpBatchStat('failed'); bumpBatchStat('processed');
continue;
}
// 打开下载链接(不操作子窗口 DOM)
var newWin = window.open(dlHref, '_blank');
if (!newWin) {
log('\u5f39\u7a97\u88ab\u62e6\u622a\uff01\u8bf7\u5141\u8bb8\u5f39\u7a97\u540e\u91cd\u8bd5', '#ea4335');
captchaBox.style.display = 'block';
batchAppend(item.title, '失败');
bumpBatchStat('failed'); bumpBatchStat('processed');
popupBlocked = true;
running = false; break;
}
// 可选:下载已交给浏览器,顺手把新标签页收掉(用闭包捕获,避免被后续循环改写)
if (CONFIG.autoCloseTab) {
(function (w) {
setTimeout(function () { try { w.close(); } catch (e) {} }, 1500);
})(newWin);
}
// 记录为已下载
if (item.titleLink) item.titleLink.style.backgroundColor = '#d4edda';
saveDownloaded(item.title);
updateRecordCount();
batchAppend(item.title, '已触发');
bumpBatchStat('ok'); bumpBatchStat('processed');
pageOk++;
log(' \u2713 \u5df2\u89e6\u53d1\u4e0b\u8f7d [' + fmt + '] [\u7d2f\u8ba1' + getDownloadedCount() + ']', '#0d904f');
// 篇间等待(给浏览器处理下载的时间,也防反爬)
if (i < items.length - 1 && running) {
var waitSec = 3 + Math.floor(Math.random() * 3);
for (var s = waitSec; s > 0; s--) {
if (!running) break;
await waitIfPaused();
countdownBox.textContent = s + '\u79d2\u540e\u4e0b\u4e00\u7bc7...';
await sleep(1000);
}
countdownBox.textContent = '';
}
}
// ── 翻页(顺序模式自动翻,勾选模式如果本页有下载也翻到下一页继续) ──
if (running) {
var shouldTurnPage = downloadMode === 'sequential' || pageOk > 0;
if (shouldTurnPage) {
var nextBtn = document.querySelector('a#PageNext') || document.querySelector('a.next');
if (!nextBtn) {
document.querySelectorAll('a').forEach(function(a) {
if (!nextBtn && (a.textContent.trim() === '\u4e0b\u4e00\u9875' || a.textContent.trim() === '>'))
nextBtn = a;
});
}
if (nextBtn) {
log('\u672c\u9875\u5b8c\u6210\uff01' + pageOk + '\u7bc7 \u7d2f\u8ba1' + getDownloadedCount(), '#0d904f');
log('\u7ffb\u9875\u7ee7\u7eed...', '#1a73e8');
await sleep(1000);
if (running) {
localStorage.setItem('cnki_auto_continue', 'true');
localStorage.setItem('cnki_dl_mode', downloadMode);
localStorage.setItem(BATCH_CONTINUE_KEY, 'true');
nextBtn.click(); return;
}
} else {
log('\u6ca1\u6709\u4e0b\u4e00\u9875\u4e86\uff0c\u5168\u90e8\u5b8c\u6210\uff01', '#0d904f');
}
}
}
running = false; paused = false;
btnStop.style.display = 'none'; btnPause.style.display = 'none'; btnStart.style.display = 'block';
btnPause.textContent = '\u6682\u505c';
countdownBox.textContent = ''; countdownBox.style.cssText = '';
if (!popupBlocked) captchaBox.style.display = 'none';
setState('');
// 这批到此为止(继续翻页的分支已经 return):先取批次计数,再输出本批 CSV
var batchTotal = { ok: stat.ok, failed: stat.failed, skipped: stat.skipped };
batchEnd();
var doneQuotes = [
'\u5168\u90e8\u641e\u5b9a\u5566\uff5e\u7d2f\u6b7b\u6211\u4e86\uff0c\u8001\u677f\u52a0\u9e21\u817f\uff01\ud83c\udf57',
'\u6d3b\u513f\u5e72\u5b8c\u4e86\uff01\u6211\u5148\u8e7a\u4e00\u4f1a\u513f\u2026\ud83d\ude34',
'\u53ee\uff5e\u60a8\u7684\u6587\u732e\u5957\u9910\u5df2\u9001\u8fbe\uff0c\u597d\u8bc4\u8bb0\u5f97\u7ed9\u4e94\u661f\u54e6\uff01\u2b50',
'\u5458\u5de5\u5df2\u7b4b\u75b2\u529b\u5c3d\uff0c\u8bf7\u6295\u5582\u5496\u5561\u2615\u540e\u518d\u8bd5',
'\u77e5\u7f51\u8d44\u6e90\u6279\u91cf\u4e0b\u8f7d\u5927\u529f\u544a\u6210\uff01\u53c8\u662f\u5145\u5b9e\u7684\u4e00\u5929\uff01\ud83c\udf1f',
'\u4e0b\u8f7d\u5b8c\u6bd5\uff01\u6211\u5148\u53bb\u505a\u4e2aSPA\u653e\u677e\u4e00\u4e0b\u2026\ud83d\udec0',
'\u53ee\u549a\uff01\u6587\u732e\u5df2\u5168\u90e8\u5230\u8d26\uff0c\u8bf7\u67e5\u6536\uff01\ud83d\udce6',
'\u6211\u5df2\u7ecf\u5c3d\u529b\u4e86\uff0c\u5269\u4e0b\u7684\u4ea4\u7ed9\u4f60\u7684\u8111\u5b50\u5566\uff01\ud83e\udde0'
];
var quote = doneQuotes[Math.floor(Math.random() * doneQuotes.length)];
setProgress(quote, 100);
log('\u2705 \u672c\u6279\u7ed3\u675f\uff1a\u5df2\u89e6\u53d1 ' + batchTotal.ok + ' \u7bc7\uff0c\u8df3\u8fc7 ' + batchTotal.skipped + '\uff0c\u5931\u8d25 ' + batchTotal.failed + '\uff0c\u7d2f\u8ba1\u8bb0\u5f55 ' + getDownloadedCount() + ' \u6761', '#0d904f');
log(quote, '#8e24aa');
});
// 尝试自动将每页显示数量调到50
try {
var pageSizeEl = document.querySelector('#perPageDiv .sort-default span, .countPageMark');
var curSize = pageSizeEl ? parseInt(pageSizeEl.textContent) : 0;
if (curSize && curSize < 50) {
var perPageDiv = document.querySelector('#perPageDiv, .countPageDiv');
if (perPageDiv) {
// 先触发展开下拉
var trigger = perPageDiv.querySelector('.sort-default') || perPageDiv;
if (trigger) trigger.click();
setTimeout(function() {
var opts = perPageDiv.querySelectorAll('li a, ul a');
opts.forEach(function(opt) {
if (opt.textContent.trim() === '50') {
log('\u81ea\u52a8\u5207\u6362\u6bcf\u987550\u7bc7', '#5c6bc0');
opt.click();
}
});
}, 300);
}
}
} catch(e) {}
markDownloaded();
updateRecordCount();
var initCount = 0;
var tb = getTableBody();
if (tb) {
tb.querySelectorAll('tr').forEach(function(row) {
var t = row.querySelector('td.name a.fz14') || row.querySelector('a.fz14');
if (t && isDownloaded(t.textContent.trim())) initCount++;
});
}
log('v1.1.0 \u5df2\u52a0\u8f7d\uff0c\u7d2f\u8ba1 ' + getDownloadedCount() + ' \u7bc7', '#0d904f');
if (initCount > 0) log('\u672c\u9875 ' + initCount + ' \u7bc7\u5df2\u4e0b\u8f7d\uff08\u7eff\u8272\uff09', '#0d904f');
if (localStorage.getItem('cnki_auto_continue') === 'true') {
localStorage.removeItem('cnki_auto_continue');
var savedMode = localStorage.getItem('cnki_dl_mode');
if (savedMode) {
downloadMode = savedMode;
localStorage.removeItem('cnki_dl_mode');
var radio = document.querySelector('input[name="cnki-mode"][value="' + savedMode + '"]');
if (radio) radio.checked = true;
if (savedMode === 'selected') selectBar.style.display = 'flex';
}
log('\u7ffb\u9875\u540e\u81ea\u52a8\u7ee7\u7eed...', '#1a73e8');
setTimeout(function(){ btnStart.click(); }, 2000);
}
}
})();