Greasy Fork is available in English.
A powerful userscript that adds convenient document download functionality to ProQuest search results. Features include single document download and batch download capabilities, with custom file naming and download status indicators.
当前为
// ==UserScript==
// @name ProQuest Document Downloader
// @namespace http://tampermonkey.net/
// @version 0.2
// @description A powerful userscript that adds convenient document download functionality to ProQuest search results. Features include single document download and batch download capabilities, with custom file naming and download status indicators.
// @author powcai
// @match https://www.proquest.com/*
// @grant GM_xmlhttpRequest
// @grant GM_addStyle
// @run-at document-end
// @connect *
// @license MIT
// ==/UserScript==
(function() {
'use strict';
// 添加样式
GM_addStyle(`
.download-all-btn {
position: fixed;
top: 20px;
right: 20px;
background-color: #4CAF50;
color: white;
padding: 10px 20px;
border: none;
border-radius: 4px;
cursor: pointer;
z-index: 9999;
}
.single-download-btn {
background-color: #2196F3;
color: white;
padding: 5px 10px;
border: none;
border-radius: 4px;
cursor: pointer;
margin: 5px;
}
.download-status {
color: #666;
margin-left: 10px;
font-size: 12px;
}
`);
// 下载单个文档
async function downloadDocument(downloadUrl, statusElement, fileName) {
try {
if (statusElement) {
statusElement.textContent = '准备下载...';
}
return new Promise((resolve, reject) => {
GM_xmlhttpRequest({
method: 'GET',
url: downloadUrl,
responseType: 'blob',
onload: function(response) {
if (response.status === 200) {
const blob = new Blob([response.response], { type: 'application/pdf' });
const url = window.URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = fileName;
document.body.appendChild(a);
a.click();
window.URL.revokeObjectURL(url);
a.remove();
if (statusElement) {
statusElement.textContent = '下载完成';
statusElement.style.color = '#4CAF50';
}
resolve();
} else {
if (statusElement) {
statusElement.textContent = '下载失败: ' + response.status;
statusElement.style.color = '#f44336';
}
reject(new Error('下载失败: ' + response.status));
}
},
onerror: function(error) {
console.error('下载请求失败:', error);
if (statusElement) {
statusElement.textContent = '下载失败: 网络错误';
statusElement.style.color = '#f44336';
}
reject(error);
}
});
});
} catch (error) {
console.error('下载过程出错:', error);
if (statusElement) {
statusElement.textContent = '下载失败: ' + error.message;
statusElement.style.color = '#f44336';
}
throw error;
}
}
// 添加下载按钮到每个搜索结果
function addDownloadButtons() {
const resultItems = document.querySelectorAll('li.resultItem');
resultItems.forEach(item => {
// 检查是否已经添加了下载按钮
if (item.querySelector('.single-download-btn')) {
return;
}
const pdfLink = item.querySelector('a[href*="fulltextPDF"]');
if (!pdfLink) return;
const titleElement = item.querySelector('.truncatedResultsTitle');
let fileName = 'document.pdf';
if (titleElement) {
// 获取标题文本,去除首尾空白,并替换不允许作为文件名的字符
fileName = titleElement.textContent.trim()
.replace(/[/\\?%*:|"<>]/g, '-') // 替换不允许的字符为连字符
.replace(/\s+/g, '_') // 将空白字符替换为下划线
+ '.pdf'; // 添加 .pdf 后缀
}
const buttonContainer = document.createElement('div');
buttonContainer.style.display = 'flex';
buttonContainer.style.alignItems = 'center';
const downloadBtn = document.createElement('button');
downloadBtn.className = 'single-download-btn';
downloadBtn.textContent = '下载文档';
const status = document.createElement('span');
status.className = 'download-status';
buttonContainer.appendChild(downloadBtn);
buttonContainer.appendChild(status);
item.appendChild(buttonContainer);
downloadBtn.onclick = async () => {
try {
const pdfPageUrl = pdfLink.href;
console.log("pdfPageUrl", pdfPageUrl)
const response = await fetch(pdfPageUrl);
const html = await response.text();
const parser = new DOMParser();
const doc = parser.parseFromString(html, 'text/html');
const downloadLink = doc.querySelector('a.pdf-download[download="ProQuestDocument.pdf"]');
if (downloadLink) {
await downloadDocument(downloadLink.href, status, fileName);
} else {
throw new Error('找不到PDF下载链接');
}
} catch (error) {
console.error('单个文档下载失败:', error);
status.textContent = '下载失败: ' + error.message;
status.style.color = '#f44336';
}
};
});
}
// 添加批量下载按钮
function addBatchDownloadButton() {
if (document.querySelector('.download-all-btn')) {
return;
}
const batchButton = document.createElement('button');
batchButton.className = 'download-all-btn';
batchButton.textContent = '批量下载全部';
document.body.appendChild(batchButton);
batchButton.onclick = async () => {
const resultItems = document.querySelectorAll('li.resultItem');
let delay = 0;
for (const item of resultItems) {
const pdfLink = item.querySelector('a[href*="fulltextPDF"]');
console.log(pdfLink)
if (!pdfLink) continue;
const status = item.querySelector('.download-status') || document.createElement('span');
status.className = 'download-status';
item.appendChild(status);
const titleElement = item.querySelector('.truncatedResultsTitle');
let fileName = 'document.pdf';
if (titleElement) {
// 获取标题文本,去除首尾空白,并替换不允许作为文件名的字符
fileName = titleElement.textContent.trim()
.replace(/[/\\?%*:|"<>]/g, '-') // 替换不允许的字符为连字符
.replace(/\s+/g, '_') // 将空白字符替换为下划线
+ '.pdf'; // 添加 .pdf 后缀
}
setTimeout(async () => {
try {
const pdfPageUrl = pdfLink.href;
const response = await fetch(pdfPageUrl);
const html = await response.text();
const parser = new DOMParser();
const doc = parser.parseFromString(html, 'text/html');
const downloadLink = doc.querySelector('a.pdf-download[download="ProQuestDocument.pdf"]');
if (downloadLink) {
await downloadDocument(downloadLink.href, status, fileName);
} else {
throw new Error('找不到PDF下载链接');
}
} catch (error) {
console.error('批量下载过程中出错:', error);
status.textContent = '下载失败: ' + error.message;
status.style.color = '#f44336';
}
}, delay);
delay += 2000; // 每个下载间隔2秒
}
};
}
// 监听页面变化
const observer = new MutationObserver((mutations) => {
for (const mutation of mutations) {
if (mutation.addedNodes.length) {
addDownloadButtons();
addBatchDownloadButton();
}
}
});
// 初始化
function initialize() {
addDownloadButtons();
addBatchDownloadButton();
observer.observe(document.body, {
childList: true,
subtree: true
});
}
// 页面加载完成后初始化
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initialize);
} else {
initialize();
}
})();