知网文#、硕博论文PDF批量下载,下载硕博论文章节目录
// ==UserScript== // @name ##知网CNKI硕博论文PDF下载 // @version 3.2.1 // @namespace https://greasyfork.org/users/244539 // @icon https://www.cnki.net/favicon.ico // @description 知网文#、硕博论文PDF批量下载,下载硕博论文章节目录 // @author @爱与自由、@TheOneAdonis // @match *://*.cnki.net/* // @match 需要匹配的域名 // @run-at document-idle // @grant unsafeWindow // @grant GM_getValue // @grant GM_setValue // @grant GM_xmlhttpRequest // @grant GM_download // ==/UserScript== // 如果没有显示批量下载的按钮,大概率是因为你的知网地址没有匹配到。 // 解决办法:将你的域名按照上面的 @match 的格式,添加到后面,保存脚本,刷新网页,应该就可以了。(最后要加个*) // 例:// @match https://web###.zjnu.edu.cn/* (function() { 'use strict'; let useWeb### = GM_getValue('useWeb###', false); // 默认不使用Web### const $ = unsafeWindow.jQuery; function loadCss(code) { const style = document.createElement('style'); style.textContent = code; document.head.appendChild(style); } function createLoading(text, duration) { const loadingContent = document.createElement('div'); loadingContent.style.cssText = 'position:fixed;top:50%;left:50%;transform:translate(-50%, -50%);text-align:center;color:#333;font-size:16px;width:400px;height:40px;line-height:40px;z-index:9999;border:1px solid #0f5de5;padding:5px;background-color:#fff'; loadingContent.textContent = text; document.body.appendChild(loadingContent); setTimeout(() => document.body.removeChild(loadingContent), duration); return loadingContent; } function createPopupButton() { const popupButton = document.createElement('button'); popupButton.textContent = '批量下载PDF'; popupButton.id = 'super-pdf' popupButton.style.cssText = 'position:fixed;right:50px;top:50%;transform:translateY(-50%);z-index:9999;padding:10px;background-color:#0f5de5;color:#fff;border:none;cursor:pointer;border-radius:5px;'; popupButton.addEventListener('click', () => { if (!localStorage.getItem('cnkiFirstTimePopupShown')) { showFirstTimePopup(); } else { createPopup(); loadSavedData(); } }); document.body.appendChild(popupButton); } function showFirstTimePopup() { const popup = document.createElement('div'); popup.style.cssText = 'position:fixed;top:0;left:0;width:100%;height:100%;background-color:rgba(0,0,0,0.5);z-index:10000;display:flex;justify-content:center;align-items:center;'; const content = document.createElement('div'); content.style.cssText = 'background-color:#fff;padding:20px;border-radius:10px;max-width:80%;max-height:80%;overflow-y:auto;'; content.innerHTML = ` <h2>使用说明 v3.2 </h2> <p> </p> <ul class="first_show_li"> <li><b>首先请确保脚本为最新版本,如果不是,请<a target="_blank" style="color:#df1919" href="https://greasyfork.org/zh-CN/scripts/389343">点击这里</a>进行更新。</b></li> <li>1.脚本只能获取当前页的文#,知网默认每页20篇,如果想更多,可将每页数量设置为50。</li> <li>2.如需清除已获取的数据 / 获取新数据,请先点击"<b>清除数据</b>"按钮。</li> <li>3.<b>如果只能下载一个,可能是浏览器拦截,允许弹出多窗口即可。</b></li> <li>4.脚本只支持<b>新版知网</b>,不能在<b>隐私模式、无痕窗口</b>运行。</li> <li>5.增加了批量下载延迟(2-5秒),增加了关键词筛选功能(文#关键词keywords)</li> <li>6.如果有问题,或者脚本报错,请在脚本网站留言。</li> <li><b>注意:请不要频繁获取数据,容易提示频繁操作。严重有封ip风险。</b></li> </ul> <button id="closeFirstTimePopup" style="padding:10px;background-color:#0f5de5;color:#fff;border:none;cursor:pointer;border-radius:5px;float:right">我知道了</button>`; popup.appendChild(content); document.body.appendChild(popup); document.getElementById('closeFirstTimePopup').addEventListener('click', () => { document.body.removeChild(popup); localStorage.setItem('cnkiFirstTimePopupShown', 'true'); createPopup(); loadSavedData(); }); } function createPopup() { if (document.getElementById('popup')) return; const popup = document.createElement('div'); popup.id = 'popup'; popup.style.cssText = 'position:fixed;top:0;left:0;width:100%;height:100%;background-color:rgba(0,0,0,0.5);z-index:9999;'; const content = document.createElement('div'); content.style.cssText = 'position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);width:80%;max-height:80%;overflow-y:auto;background-color:#fff;padding:20px;border-radius:10px;box-shadow:0 0 10px rgba(0,0,0,0.5);min-height:200px'; const closeButton = createButton('关闭', () => document.body.removeChild(popup),'red'); closeButton.style.float = 'right'; const linkButton = createButton('获取链接', getLinks); const selectAllButton = createButton('全选', selectAll); const deselectAllButton = createButton('取消全选', deselectAll); const downloadButton = createButton('下载', downloadSelected,'#008000'); downloadButton.title = "点击此处下载选中的文件"; downloadButton.id = "dl_sel"; const clearDataButton = createButton('清除数据', clearData,'#ff0000'); const web###Toggle = createButton(useWeb### ? '禁用Web###' : '启用Web###', toggleWeb###); web###Toggle.id = 'web###-toggle'; const tips = createTips('关闭窗口数据不会消失,如需获取新数据,先点击清除数据,重新获取链接。 | 如果是远程登录的web###,获取出错可以尝试把web###开关打开。', '#0b1f64'); const tips2 = createTips('如果报错,请在脚本网站留言反馈。提供地址、控制台的截图(右键检查或审查元素,转到控制台)。', '#0b1f64'); const tips3 = document.createElement('div'); tips3.id = 'script_author'; tips3.innerHTML = '更新不易,希望大家关注我的公众号和视频频道,感谢支持~ 公众号:木星宝库 YouTube:<a target="_blank" href="https://www.youtube.com/channel/UCC1ExQh99BVTaPbGbGTcUzg/">点击这里</a> B站:<a target="_blank" href="https://space.bilibili.com/7241318">点击这里</a> '; // 新增关键词筛选功能 const keywordFilterContainer = document.createElement('div'); keywordFilterContainer.style.cssText = 'display:flex;margin-bottom:10px;margin-top:5px;'; const keywordInput = document.createElement('input'); keywordInput.type = 'text'; keywordInput.placeholder = '输入关键词筛选(多个关键词用逗号分隔)'; keywordInput.style.cssText = 'flex-grow:1;padding:5px 5px 5px 8px;margin-right:10px;border:1px solid #eee;border-radius:3px;'; const filterButton = createButton('筛选', filterByKeywords, '#ff6600'); const resetFilterButton = createButton('重置', resetFilter, '#888'); keywordFilterContainer.append(keywordInput, filterButton, resetFilterButton); content.append(closeButton, linkButton, selectAllButton, deselectAllButton, downloadButton, clearDataButton, web###Toggle,tips, tips2, tips3, keywordFilterContainer); const table = document.createElement('table'); table.id = 'my-table'; table.style.cssText = 'width:100%;margin-top:10px;border-collapse:collapse;'; table.innerHTML = '<thead><tr><th style="width:3%">多选</th><th style="width:3%">序号</th><th style="width:60%">名称</th><th style="width:7%">链接</th><th>关键词</th></tr></thead><tbody></tbody>'; content.appendChild(table); popup.appendChild(content); document.body.appendChild(popup); // 加载保存的数据 loadSavedData(); // 获取关键词筛选输入框和按钮的引用 window.keywordInput = keywordInput; } function filterByKeywords() { const keywords = window.keywordInput.value.split(/[,,]/).map(k => k.trim()).filter(Boolean); if (keywords.length === 0) return; const table = document.querySelector('#my-table tbody'); const rows = table.querySelectorAll('tr'); rows.forEach(row => { const keywordsCell = row.querySelector('td:last-child'); const keywordText = keywordsCell.textContent.toLowerCase(); const shouldShow = keywords.some(keyword => keywordText.includes(keyword.toLowerCase()) ); row.style.display = shouldShow ? '' : 'none'; }); createLoading(`已筛选出包含关键词的论文`, 2000); } // 重置筛选 function resetFilter() { const table = document.querySelector('#my-table tbody'); const rows = table.querySelectorAll('tr'); rows.forEach(row => { row.style.display = ''; }); window.keywordInput.value = ''; createLoading(`已重置筛选`, 2000); } // 清除数据函数 function clearData() { localStorage.removeItem('cnkiTableData'); const table = document.querySelector('#my-table tbody'); table.innerHTML = ''; updateSelectCount(); createLoading('数据已清除', 2000); } function createButton(text, onClick, color = '#0f5de5') { const button = document.createElement('button'); button.textContent = text; button.className = 'diy-btn'; button.style.color = color; button.addEventListener('click', onClick); return button; } function createTips(text, color) { const tips = document.createElement('span'); tips.textContent = text; tips.className = 'diy-span'; tips.style.color = color; return tips; } function selectAll() { document.querySelectorAll('.selectItem').forEach(item => item.checked = true); updateSelectCount(); } function deselectAll() { document.querySelectorAll('.selectItem').forEach(item => item.checked = false); updateSelectCount(); } function delay(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } async function downloadSelected() { const selectedItems = document.querySelectorAll('.selectItem:checked'); if (selectedItems.length === 0) { createLoading('请选择要下载的项目!', 2000); return; } for (const item of selectedItems) { const link = item.closest('tr').querySelector('a[href^="http"]'); if (link) { // 打开链接 window.open(link.href, '_blank'); // 生成随机延迟时间:2-5秒(步长0.5秒) const randomDelay = Math.floor(Math.random() * 7) * 500 + 2000; console.log(`延迟 ${randomDelay / 1000} 秒后继续下载...`); // 延迟下一次下载 await delay(randomDelay); } } } function updateSelectCount() { const count = document.querySelectorAll('.selectItem:checked').length; document.querySelector('#dl_sel').textContent = `下载 (${count})`; } // 查找包含“PDF下载”文字内容的链接并获取其链接地址 function findPDFDownloadLink(element, textContent) { // 获取所有包含链接的 li 元素 var listItems = element.querySelectorAll('li'); // 遍历所有 li 元素,查找包含指定文本内容的链接 for (var i = 0; i < listItems.length; i++) { var link = listItems[i].querySelector('a'); if (link && link.innerText.includes(textContent)) { // 获取链接地址 var href = link.href; console.log('链接地址:', href); return href; // 返回找到的链接地址 } } console.log('未找到包含指定文字内容的链接'); return null; // 如果未找到匹配的链接,返回null } // 切换Web###的函数 function toggleWeb###() { useWeb### = !useWeb###; GM_setValue('useWeb###', useWeb###); const toggleButton = document.getElementById('web###-toggle'); if (toggleButton) { toggleButton.textContent = useWeb### ? '禁用Web###' : '启用Web###'; } createLoading(`Web### ${useWeb### ? '已启用' : '已禁用'}`, 2000); } function convertToWeb###Link(originalLink) { if (!useWeb###) { return originalLink; } const web###Domain = window.location.origin; const path = originalLink.replace(/^(https?:\/\/)?(www\.)?[^\/]+/, ''); return `${web###Domain}${path}`; } async function getLinks() { const table = document.querySelector('#my-table tbody'); if (table.children.length > 0) { createLoading('已有数据!请先清除数据后再获取新链接。', 2000); return; } const links = Array.from(document.querySelectorAll('.fz14')).map(link => { const originalHref = link.href; const web###Href = convertToWeb###Link(originalHref); console.log('Original link:', originalHref); console.log('Web### link:', web###Href); return web###Href; }); const loading = createLoading('获取链接中(请耐心等待)……', 60000); try { console.log(links); const batchSize = 5; // 每批处理的链接数 for (let i = 0; i < links.length; i += batchSize) { const batch = links.slice(i, i + batchSize); const responses = await Promise.all(batch.map(link => fetch(link, { method: 'GET', mode: 'same-origin', referrerPolicy: 'unsafe-url', }).then(response => response.text()) )); responses.forEach((html, index) => { const doc = new DOMParser().parseFromString(html, 'text/html'); const h1Element = doc.querySelector('.wx-tit h1'); h1Element.querySelectorAll('span').forEach(element => element.remove()); const title = h1Element?.textContent.trim() || '无标题'; const author = Array.from(doc.querySelectorAll('.author')).map(a => a.textContent).join(' '); const pdfLink = findPDFDownloadLink(doc.querySelector('.operate-btn'),'PDF下载'); const keywords = Array.from(doc.querySelectorAll('.keywords a')).map(keywords => `<span class="diy-btn" style="font-size:12px">${keywords.textContent.replace(";","")}</span>`).join(''); const row = table.insertRow(); row.innerHTML = ` <td style="text-align:center"><input type="checkbox" class="selectItem"></td> <td style="text-align:center">${i + index + 1}</td> <td><span class="diy_title">${title}</span><span class="diy_author">${author}</span></td> <td style="text-align:center">${pdfLink !== null ? `<a href="${pdfLink}" target="_blank" class="diy-btn">PDF下载</a>` : '获取的链接为空,请检查!'}</td> <td>${keywords}</td> `; row.querySelector('input[type="checkbox"]').addEventListener('change', updateSelectCount); }); // 更新加载提示 loading.textContent = `正在获取链接...(${Math.min((i + batchSize) / links.length * 100, 100).toFixed(0)}%)`; } document.body.removeChild(loading); createLoading('获取完毕!', 2000); // 在获取完链接后保存数据 saveData(); } catch (error) { console.error('Error fetching links:', error); document.body.removeChild(loading); createLoading(`获取链接出错:${error.message}\n links: ${links}`, 3000); } } // 新增:保存数据到localStorage function saveData() { const tableRows = Array.from(document.querySelectorAll('#my-table tbody tr')).map(row => ({ title: row.querySelector('.diy_title').textContent, author: row.querySelector('.diy_author').textContent, pdfLink: row.querySelector('a[href^="http"]')?.href || '', keywords: row.querySelector('td:last-child').innerHTML })); localStorage.setItem('cnkiTableData', JSON.stringify(tableRows)); } // 新增:从localStorage加载数据 function loadSavedData() { const savedData = localStorage.getItem('cnkiTableData'); if (savedData) { const tableData = JSON.parse(savedData); const table = document.querySelector('#my-table tbody'); table.innerHTML = ''; // 清空现有内容 tableData.forEach((item, index) => { const row = table.insertRow(); row.innerHTML = ` <td style="text-align:center"><input type="checkbox" class="selectItem"></td> <td style="text-align:center">${index + 1}</td> <td><span class="diy_title">${item.title}</span><span class="diy_author">${item.author}</span></td> <td style="text-align:center">${item.pdfLink ? `<a href="${item.pdfLink}" target="_blank" class="diy-btn">PDF下载</a>` : '获取链接失败!'}</td> <td>${item.keywords}</td> `; row.querySelector('input[type="checkbox"]').addEventListener('change', updateSelectCount); }); updateSelectCount(); } } // 目录下载功能 function addCategoryDownloadButton() { const otherBtns = document.querySelector('.other-btns'); if (!otherBtns) return; const li = document.createElement('li'); li.className = 'btn-diy'; li.style.cssText = 'width:auto;height:23px;line-height:22px;background-color:#3f8af0;border-radius:3px;'; const a = document.createElement('a'); a.textContent = '目录下载'; a.className = 'a-diy'; a.style.cssText = 'color:#ffffff;padding:2px 10px;'; a.href = 'javascript:void(0)'; a.addEventListener('click', downloadCategory); li.appendChild(a); otherBtns.appendChild(li); } async function downloadCategory() { const hrefLink = findPDFDownloadLink(document.querySelector('.operate-btn'),'章节下载'); console.log("目录链接:" + hrefLink); if (!hrefLink) { createLoading('无法获取目录链接', 2000); return; } try { const response = await fetch(hrefLink); const html = await response.text(); const doc = new DOMParser().parseFromString(html, 'text/html'); const title = document.querySelector('.wx-tit h1')?.textContent.trim() || '未知标题'; const chapters = Array.from(doc.querySelectorAll('.ls-chapters li')) .map(chapter => chapter.textContent.trim()) .filter(Boolean) .map(text => { const parts = text.split('-'); return `${parts[0].replace(/\n/g, '\t')} \n`; }) .join(''); console.log(chapters); saveFile(title, chapters); } catch (error) { console.error('Error downloading category:', error); createLoading('下载目录失败', 2000); } } function saveFile(name, data) { const blob = new Blob([data], { type: 'text/plain' }); const link = document.createElement('a'); link.href = URL.createObjectURL(blob); link.download = `${name}.txt`; link.click(); URL.revokeObjectURL(link.href); } // 初始化函数 function init() { const url = window.location.href.toLowerCase(); console.log(url); // 在所有页面上添加批量下载按钮 // createPopupButton(); // 如果不显示按钮,将下面三行注释,取消上一行的代码注释。 if (url.includes('defaultr###lt') || url.includes('advsearch') || url.includes('search')) { createPopupButton(); } // 在文#详情页面添加目录下载按钮 if (url.includes('abstract')) { addCategoryDownloadButton(); } // 加载自定义CSS loadCss(` .diy-btn { display: inline-block; vertical-align: middle; padding: 2px 8px; line-height: 18px; color: #0f5de5; font-size: 14px; text-align: center; background-color: #e3ecfd; border: 1px solid #fff; cursor: pointer; } .diy-span { display: inline-block; vertical-align: middle; padding: 2px 8px; line-height: 18px; color: #0f5de5; font-size: 12px; text-align: center; } #popup table tr td, #popup table tr th { line-height: 20px; height: 20px; padding: 5px; border: 1px solid #eee; } .diy_title { display: block; color: #524d4d; font-size: 14px; font-weight: bold; } .diy_author { color: #666; } .font-size-button { font-size: 14px; display: block; line-height: 18px; border: 1px solid #e2e2e2; border-radius: 2px; background-color: #f5f5f5; color: #504f4f; float: left; padding: 3px; position: absolute; right: 0; width: 28px; cursor: pointer; } #script_author { color: rgb(14 93 228); display: block; background-color: #e3ecfd; margin-top: 5px; text-align: center; padding: 4px; } .first_show_li li{ line-height:30px; } `); } // 在页面加载完成后运行初始化函数 // 在页面加载完成后运行初始化函数 window.addEventListener('load', init); })();