几周前突然被问到下载某个期刊某个关键词的40篇文献,本以为是件轻松的事,直到打开官网才发现Wiley甚至都没有导出doi和下载按钮只能导出引用格式 ^-^ 每次下载要一个一个点进去作为一个合格的mjj怎么可以被这种事难住于是便写了个脚本给他加上了这俩功能,分享下给同样需要的友友
准备工作
一个可以登陆Wiley官网的账号或者其他方式(包括某宝),后续要在登录状态下进行
添加油猴脚本
具体油猴脚本如下
// ==UserScript==
// @name Wiley Selected DOI Exporter and PDF Downloader
// @namespace https://onlinelibrary.wiley.com/
// @version 2.3.0
// @description Add Wiley-style checkbox and Copy DOI button; export selected DOIs and download selected PDFs.
// @author xhh
// @match https://onlinelibrary.wiley.com/action/doSearch*
// @match https://onlinelibrary.wiley.com/search/advanced*
// @grant GM_openInTab
// @grant GM_setClipboard
// @run-at document-idle
// ==/UserScript==
(function () {
"use strict";
const DELAY = 8000;
const STORE = "wiley_selected_dois_v2";
const DOI_RE = /10\.\d{4,9}\/[-._;()/:A-Z0-9]+/ig;
const selected = new Map();
let downloading = false;
let stopDownload = false;
let injectTimer = null;
const $ = (s, r = document) => r.querySelector(s);
const $$ = (s, r = document) => Array.from(r.querySelectorAll(s));
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));
function decodeSafe(s) {
try {
return decodeURIComponent(s);
} catch {
return s;
}
}
function norm(doi) {
return decodeSafe(String(doi || ""))
.replace(/^doi:/i, "")
.replace(/^https?:\/\/doi\.org\//i, "")
.replace(/[?#].*$/g, "")
.replace(/[.,;)\]\s]+$/g, "")
.trim();
}
function key(doi) {
return norm(doi).toLowerCase();
}
function uniqDois(list) {
const seen = new Set();
const out = [];
for (const x of list) {
const doi = norm(x);
const k = key(doi);
if (!/^10\.\d{4,9}\//i.test(doi) || seen.has(k)) continue;
seen.add(k);
out.push(doi);
}
return out;
}
function findDois(text) {
return uniqDois((decodeSafe(String(text || "")).match(DOI_RE) || []).map(norm));
}
function pdfUrl(doi) {
const path = norm(doi)
.split("/")
.map(part => encodeURIComponent(part))
.join("/");
return `https://onlinelibrary.wiley.com/doi/pdfdirect/${path}?download=true`;
}
function loadSelected() {
try {
const data = JSON.parse(sessionStorage.getItem(STORE) || "[]");
data.forEach(r => {
const doi = norm(r.doi);
if (doi) selected.set(key(doi), { doi, hasPdf: r.hasPdf !== false });
});
} catch {}
}
function saveSelected() {
sessionStorage.setItem(STORE, JSON.stringify([...selected.values()]));
}
function selectedList() {
return [...selected.values()].filter(r => /^10\.\d{4,9}\//i.test(norm(r.doi)));
}
async function copyText(text) {
try {
await navigator.clipboard.writeText(text);
return true;
} catch {}
try {
if (typeof GM_setClipboard === "function") {
GM_setClipboard(text, "text");
return true;
}
} catch {}
try {
const ta = document.createElement("textarea");
ta.value = text;
ta.readOnly = true;
ta.style.cssText = "position:fixed;left:-99999px;top:-99999px;opacity:0;";
document.body.appendChild(ta);
ta.select();
const ok = document.execCommand("copy");
ta.remove();
return ok;
} catch {
return false;
}
}
function getRecord(item) {
const input = $(".bulkDownloadInput input[value], input[data-has-pdf][value]", item);
const raw = [input?.value, input?.title];
$$('a[href*="/doi/"], a[href*="doi="], [data-ajax-content*="doi="]', item)
.forEach(el => raw.push(el.getAttribute("href"), el.getAttribute("data-ajax-content")));
const doi = findDois(raw.join(" "))[0];
if (!doi) return null;
const hasPdf = String(input?.getAttribute("data-has-pdf") ?? "true").toLowerCase() !== "false";
return { doi, hasPdf, key: key(doi) };
}
function getItems() {
const set = new Set();
$$("li.search__item.bulkDownloadWrapper, li.search__item, li.bulkDownloadWrapper")
.forEach(li => set.add(li));
$$(".bulkDownloadInput input[value], input[data-has-pdf][value]")
.forEach(input => {
const li = input.closest("li");
if (li) set.add(li);
});
return [...set].filter(item => getRecord(item));
}
function setSelected(record, checked) {
if (checked) {
selected.set(record.key, { doi: record.doi, hasPdf: record.hasPdf });
} else {
selected.delete(record.key);
}
saveSelected();
updateUI();
}
function addItemTool(item, record) {
const old = $(".wiley-doi-item-tool", item);
if (old?.dataset.key === record.key) return;
if (old) old.remove();
const tool = document.createElement("div");
tool.className = "wiley-doi-item-tool";
tool.dataset.key = record.key;
tool.title = record.doi;
tool.innerHTML = `
<label class="wiley-doi-select-label" title="选择该文献">
<input type="checkbox" class="wiley-select-doi-checkbox">
<span>Select</span>
</label>
<button type="button" class="wiley-doi-copy-btn">Copy DOI</button>
${record.hasPdf ? "" : '<span class="wiley-doi-no-pdf">No PDF</span>'}
`;
const checkbox = $(".wiley-select-doi-checkbox", tool);
checkbox.dataset.doiKey = record.key;
checkbox.checked = selected.has(record.key);
checkbox.addEventListener("change", () => setSelected(record, checkbox.checked));
const copyBtn = $(".wiley-doi-copy-btn", tool);
copyBtn.title = record.doi;
copyBtn.addEventListener("click", async e => {
e.preventDefault();
e.stopPropagation();
const ok = await copyText(record.doi);
console.log(`[Wiley DOI Tool] ${ok ? "Copied" : "Failed to copy"} DOI: ${record.doi}`);
});
const body = $(".item__body", item) || item;
const header = $(".meta__header", body);
if (header) {
header.classList.add("wiley-doi-header-with-tool");
header.appendChild(tool);
} else {
tool.classList.add("wiley-doi-item-tool-fallback");
body.insertBefore(tool, body.firstChild);
}
}
function enhanceItems() {
getItems().forEach(item => {
const record = getRecord(item);
if (record) addItemTool(item, record);
});
}
function saveTxt(filename, content) {
const blob = new Blob([content], { type: "text/plain;charset=utf-8" });
const url = URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = url;
a.download = filename;
a.style.display = "none";
document.body.appendChild(a);
a.click();
setTimeout(() => {
URL.revokeObjectURL(url);
a.remove();
}, 1000);
}
function stamp() {
const d = new Date();
const p = n => String(n).padStart(2, "0");
return `${d.getFullYear()}-${p(d.getMonth() + 1)}-${p(d.getDate())}_${p(d.getHours())}-${p(d.getMinutes())}-${p(d.getSeconds())}`;
}
function exportDois() {
const list = selectedList();
if (!list.length) {
alert("请先勾选至少一篇文献。");
return;
}
const dois = uniqDois(list.map(r => r.doi));
saveTxt(`wiley-selected-dois-${stamp()}.txt`, dois.join("\n") + "\n");
console.log(`[Wiley DOI Tool] Exported ${dois.length} DOI(s).`);
}
function openPdf(url) {
try {
if (typeof GM_openInTab === "function") {
GM_openInTab(url, { active: false, insert: true, setParent: true });
} else {
window.open(url, "_blank", "noopener,noreferrer");
}
} catch {
window.open(url, "_blank", "noopener,noreferrer");
}
}
async function downloadPdfs() {
if (downloading) {
stopDownload = true;
updateUI();
return;
}
const all = selectedList();
if (!all.length) {
alert("请先勾选至少一篇文献。");
return;
}
const pdfs = all.filter(r => r.hasPdf !== false);
const skipped = all.length - pdfs.length;
if (!pdfs.length) {
alert("选中的文献中没有可下载 PDF 的条目。");
return;
}
const ok = confirm(
`将依次打开 ${pdfs.length} 个 Wiley PDF 下载链接,每篇间隔 ${DELAY / 1000} 秒。` +
(skipped ? `\n\n其中 ${skipped} 篇标记为无 PDF,将会跳过。` : "") +
`\n\n请确认你对这些文献有合法访问权限,并且浏览器允许 Wiley 弹出新标签页。`
);
if (!ok) return;
downloading = true;
stopDownload = false;
updateUI();
let done = 0;
for (const r of pdfs) {
if (stopDownload) break;
done += 1;
console.log(`[Wiley DOI Tool] Opening ${done}/${pdfs.length}: ${r.doi}`);
openPdf(pdfUrl(r.doi));
if (done < pdfs.length) await sleep(DELAY);
}
downloading = false;
stopDownload = false;
updateUI();
console.log(`[Wiley DOI Tool] Finished. Processed ${done}/${pdfs.length}.`);
}
function selectPage() {
let count = 0;
getItems().forEach(item => {
const r = getRecord(item);
if (!r) return;
selected.set(r.key, { doi: r.doi, hasPdf: r.hasPdf });
count += 1;
});
saveSelected();
updateUI();
console.log(`[Wiley DOI Tool] Selected ${count} item(s) on current page.`);
}
function clearSelected() {
selected.clear();
saveSelected();
updateUI();
console.log("[Wiley DOI Tool] Selection cleared.");
}
function updateUI() {
const count = selected.size;
$$(".wiley-select-doi-checkbox").forEach(cb => {
cb.checked = selected.has(cb.dataset.doiKey);
});
const countEl = $("#wiley-selected-count");
const exportBtn = $("#wiley-export-selected-doi-btn");
const downloadBtn = $("#wiley-download-selected-pdf-btn");
const clearBtn = $("#wiley-clear-selected-btn");
if (countEl) countEl.textContent = `Selected ${count}`;
if (exportBtn) exportBtn.disabled = !count;
if (clearBtn) clearBtn.disabled = !count && !downloading;
if (downloadBtn) {
downloadBtn.disabled = !count && !downloading;
downloadBtn.textContent = downloading ? "Stop" : "Download PDFs";
}
}
function addStyle() {
if ($("#wiley-doi-tool-style")) return;
const style = document.createElement("style");
style.id = "wiley-doi-tool-style";
style.textContent = `
.meta__header.wiley-doi-header-with-tool {
display: flex !important;
align-items: center !important;
flex-wrap: wrap !important;
gap: 8px 14px !important;
}
.wiley-doi-item-tool {
margin-left: auto !important;
display: inline-flex !important;
align-items: center !important;
justify-content: flex-end !important;
gap: 8px !important;
font-size: 13px !important;
line-height: 1.2 !important;
white-space: nowrap !important;
}
.wiley-doi-item-tool-fallback {
float: right !important;
margin: 0 0 8px 12px !important;
}
.wiley-doi-select-label {
display: inline-flex !important;
align-items: center !important;
gap: 5px !important;
cursor: pointer !important;
user-select: none !important;
font-weight: 600 !important;
color: #666 !important;
margin: 0 !important;
}
.wiley-select-doi-checkbox {
width: 14px !important;
height: 14px !important;
margin: 0 !important;
cursor: pointer !important;
accent-color: #005274 !important;
}
.wiley-doi-copy-btn,
.wiley-doi-bulk-btn {
border: 1px solid #005274 !important;
border-radius: 3px !important;
background: #fff !important;
color: #005274 !important;
padding: 4px 9px !important;
font-size: 12px !important;
font-weight: 700 !important;
line-height: 1.25 !important;
cursor: pointer !important;
box-shadow: none !important;
font-family: inherit !important;
}
.wiley-doi-copy-btn:hover,
.wiley-doi-bulk-btn:hover {
background: #edf6f8 !important;
border-color: #003f5c !important;
color: #003f5c !important;
}
.wiley-doi-copy-btn:disabled,
.wiley-doi-bulk-btn:disabled {
opacity: 0.45 !important;
cursor: not-allowed !important;
background: #f7f7f7 !important;
color: #777 !important;
border-color: #c9c9c9 !important;
}
.wiley-doi-copy-btn:focus,
.wiley-doi-bulk-btn:focus,
.wiley-select-doi-checkbox:focus {
outline: 2px solid #8ec3d5 !important;
outline-offset: 2px !important;
}
.wiley-doi-no-pdf {
font-size: 12px !important;
color: #b7317d !important;
font-weight: 700 !important;
}
.wiley-doi-bulk-actions {
float: right !important;
display: flex !important;
align-items: center !important;
justify-content: flex-end !important;
flex-wrap: wrap !important;
gap: 8px !important;
margin: 0 0 10px 16px !important;
font-size: 13px !important;
}
.wiley-selected-count {
font-weight: 700 !important;
color: #666 !important;
white-space: nowrap !important;
}
.wiley-fallback-bulk-download {
padding: 10px 0 !important;
margin: 10px 0 !important;
border-bottom: 1px solid #ddd !important;
}
@media (max-width: 900px) {
.wiley-doi-item-tool {
margin-left: 0 !important;
width: 100% !important;
justify-content: flex-start !important;
margin-top: 4px !important;
}
.wiley-doi-bulk-actions {
float: none !important;
justify-content: flex-start !important;
margin-left: 0 !important;
}
}
`;
document.head.appendChild(style);
}
function addBar() {
if ($("#wiley-doi-bulk-actions")) return;
let host = $('div.bulk-download[data-content-type="articlesAndChapters"], div.bulk-download');
if (!host) {
host = document.createElement("div");
host.className = "bulk-download wiley-fallback-bulk-download";
const anchor = $("ol.search-result__body, .search-result__body, #search-result, .search__results");
(anchor?.parentNode || document.body).insertBefore(host, anchor || document.body.firstChild);
}
const bar = document.createElement("div");
bar.id = "wiley-doi-bulk-actions";
bar.className = "wiley-doi-bulk-actions";
bar.innerHTML = `
<span id="wiley-selected-count" class="wiley-selected-count">Selected 0</span>
<button type="button" id="wiley-select-current-page-btn" class="wiley-doi-bulk-btn">Select Page</button>
<button type="button" id="wiley-export-selected-doi-btn" class="wiley-doi-bulk-btn">Export DOI</button>
<button type="button" id="wiley-download-selected-pdf-btn" class="wiley-doi-bulk-btn">Download PDFs</button>
<button type="button" id="wiley-clear-selected-btn" class="wiley-doi-bulk-btn">Clear</button>
`;
const clearfix = $(".clearfix", host);
host.insertBefore(bar, clearfix || null);
$("#wiley-select-current-page-btn").addEventListener("click", selectPage);
$("#wiley-export-selected-doi-btn").addEventListener("click", exportDois);
$("#wiley-download-selected-pdf-btn").addEventListener("click", downloadPdfs);
$("#wiley-clear-selected-btn").addEventListener("click", clearSelected);
}
function inject() {
addStyle();
addBar();
enhanceItems();
updateUI();
}
function watch() {
new MutationObserver(() => {
clearTimeout(injectTimer);
injectTimer = setTimeout(inject, 150);
}).observe(document.body, { childList: true, subtree: true });
}
loadSelected();
inject();
watch();
})();
复制这里的内容在Tampermonkey插件中添加该脚本即可,目前的两篇下载间隔是8s但是可以通过DELAY = 8000调整一下
完成效果
打开插件后进入Wiley官网的效果如图

基本的选中导出和下载功能都在啦 ^-^