中国知网CNKI硕博论文PDF下载

知网文献、硕博论文PDF批量下载,下载硕博论文章节目录

  1. // ==UserScript==
  2. // @name 中国知网CNKI硕博论文PDF下载
  3. // @version 3.2.1
  4. // @namespace https://gf.qytechs.cn/users/244539
  5. // @icon https://www.cnki.net/favicon.ico
  6. // @description 知网文献、硕博论文PDF批量下载,下载硕博论文章节目录
  7. // @author @爱与自由、@TheOneAdonis
  8. // @match *://*.cnki.net/*
  9. // @match 需要匹配的域名
  10. // @run-at document-idle
  11. // @grant unsafeWindow
  12. // @grant GM_getValue
  13. // @grant GM_setValue
  14. // @grant GM_xmlhttpRequest
  15. // @grant GM_download
  16. // ==/UserScript==
  17.  
  18. // 如果没有显示批量下载的按钮,大概率是因为你的知网地址没有匹配到。
  19. // 解决办法:将你的域名按照上面的 @match 的格式,添加到后面,保存脚本,刷新网页,应该就可以了。(最后要加个*)
  20. // 例:// @match https://webvpn.zjnu.edu.cn/*
  21.  
  22. (function() {
  23. 'use strict';
  24.  
  25.  
  26. let useWebVPN = GM_getValue('useWebVPN', false); // 默认不使用WebVPN
  27.  
  28. const $ = unsafeWindow.jQuery;
  29.  
  30. function loadCss(code) {
  31. const style = document.createElement('style');
  32. style.textContent = code;
  33. document.head.appendChild(style);
  34. }
  35.  
  36. function createLoading(text, duration) {
  37. const loadingContent = document.createElement('div');
  38. 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';
  39. loadingContent.textContent = text;
  40. document.body.appendChild(loadingContent);
  41. setTimeout(() => document.body.removeChild(loadingContent), duration);
  42. return loadingContent;
  43. }
  44.  
  45. function createPopupButton() {
  46. const popupButton = document.createElement('button');
  47. popupButton.textContent = '批量下载PDF';
  48. popupButton.id = 'super-pdf'
  49. 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;';
  50. popupButton.addEventListener('click', () => {
  51. if (!localStorage.getItem('cnkiFirstTimePopupShown')) {
  52. showFirstTimePopup();
  53. } else {
  54. createPopup();
  55. loadSavedData();
  56. }
  57. });
  58. document.body.appendChild(popupButton);
  59. }
  60. function showFirstTimePopup() {
  61. const popup = document.createElement('div');
  62. 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;';
  63.  
  64. const content = document.createElement('div');
  65. content.style.cssText = 'background-color:#fff;padding:20px;border-radius:10px;max-width:80%;max-height:80%;overflow-y:auto;';
  66. content.innerHTML = `
  67. <h2>使用说明 v3.2 </h2>
  68. <p>&nbsp;</p>
  69. <ul class="first_show_li">
  70. <li><b>首先请确保脚本为最新版本,如果不是,请<a target="_blank" style="color:#df1919" href="https://gf.qytechs.cn/zh-CN/scripts/389343">点击这里</a>进行更新。</b></li>
  71. <li>1.脚本只能获取当前页的文献,知网默认每页20篇,如果想更多,可将每页数量设置为50。</li>
  72. <li>2.如需清除已获取的数据 / 获取新数据,请先点击"<b>清除数据</b>"按钮。</li>
  73. <li>3.<b>如果只能下载一个,可能是浏览器拦截,允许弹出多窗口即可。</b></li>
  74. <li>4.脚本只支持<b>新版知网</b>,不能在<b>隐私模式、无痕窗口</b>运行。</li>
  75. <li>5.增加了批量下载延迟(2-5秒),增加了关键词筛选功能(文献关键词keywords)</li>
  76. <li>6.如果有问题,或者脚本报错,请在脚本网站留言。</li>
  77. <li><b>注意:请不要频繁获取数据,容易提示频繁操作。严重有封ip风险。</b></li>
  78. </ul>
  79. <button id="closeFirstTimePopup" style="padding:10px;background-color:#0f5de5;color:#fff;border:none;cursor:pointer;border-radius:5px;float:right">我知道了</button>`;
  80.  
  81. popup.appendChild(content);
  82. document.body.appendChild(popup);
  83.  
  84. document.getElementById('closeFirstTimePopup').addEventListener('click', () => {
  85. document.body.removeChild(popup);
  86. localStorage.setItem('cnkiFirstTimePopupShown', 'true');
  87. createPopup();
  88. loadSavedData();
  89. });
  90. }
  91. function createPopup() {
  92. if (document.getElementById('popup')) return;
  93.  
  94. const popup = document.createElement('div');
  95. popup.id = 'popup';
  96. popup.style.cssText = 'position:fixed;top:0;left:0;width:100%;height:100%;background-color:rgba(0,0,0,0.5);z-index:9999;';
  97. const content = document.createElement('div');
  98. 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';
  99.  
  100. const closeButton = createButton('关闭', () => document.body.removeChild(popup),'red');
  101. closeButton.style.float = 'right';
  102. const linkButton = createButton('获取链接', getLinks);
  103. const selectAllButton = createButton('全选', selectAll);
  104. const deselectAllButton = createButton('取消全选', deselectAll);
  105. const downloadButton = createButton('下载', downloadSelected,'#008000');
  106. downloadButton.title = "点击此处下载选中的文件";
  107. downloadButton.id = "dl_sel";
  108. const clearDataButton = createButton('清除数据', clearData,'#ff0000');
  109. const webVPNToggle = createButton(useWebVPN ? '禁用WebVPN' : '启用WebVPN', toggleWebVPN);
  110. webVPNToggle.id = 'webvpn-toggle';
  111. const tips = createTips('关闭窗口数据不会消失,如需获取新数据,先点击清除数据,重新获取链接。 | 如果是远程登录(不可用)的webvpn,获取出错可以尝试把webvpn开关打开。', '#0b1f64');
  112. const tips2 = createTips('如果报错,请在脚本网站留言反馈。提供地址、控制台的截图(右键检查或审查元素,转到控制台)。', '#0b1f64');
  113. const tips3 = document.createElement('div');
  114. tips3.id = 'script_author';
  115. tips3.innerHTML = '更新不易,希望大家关注我的公众号和视频频道,感谢支持~ &nbsp;&nbsp;&nbsp; 公众号:木星宝库&nbsp;&nbsp;&nbsp;YouTube:<a target="_blank" href="https://www.youtube.com/channel/UCC1ExQh99BVTaPbGbGTcUzg/">点击这里</a> &nbsp;&nbsp;&nbsp; B站:<a target="_blank" href="https://space.bilibili.com/7241318">点击这里</a> ';
  116.  
  117. // 新增关键词筛选功能
  118. const keywordFilterContainer = document.createElement('div');
  119. keywordFilterContainer.style.cssText = 'display:flex;margin-bottom:10px;margin-top:5px;';
  120. const keywordInput = document.createElement('input');
  121. keywordInput.type = 'text';
  122. keywordInput.placeholder = '输入关键词筛选(多个关键词用逗号分隔)';
  123. keywordInput.style.cssText = 'flex-grow:1;padding:5px 5px 5px 8px;margin-right:10px;border:1px solid #eee;border-radius:3px;';
  124. const filterButton = createButton('筛选', filterByKeywords, '#ff6600');
  125. const resetFilterButton = createButton('重置', resetFilter, '#888');
  126.  
  127. keywordFilterContainer.append(keywordInput, filterButton, resetFilterButton);
  128.  
  129. content.append(closeButton, linkButton, selectAllButton, deselectAllButton, downloadButton, clearDataButton, webVPNToggle,tips, tips2, tips3, keywordFilterContainer);
  130.  
  131. const table = document.createElement('table');
  132. table.id = 'my-table';
  133. table.style.cssText = 'width:100%;margin-top:10px;border-collapse:collapse;';
  134. 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>';
  135.  
  136. content.appendChild(table);
  137. popup.appendChild(content);
  138. document.body.appendChild(popup);
  139.  
  140. // 加载保存的数据
  141. loadSavedData();
  142. // 获取关键词筛选输入框和按钮的引用
  143. window.keywordInput = keywordInput;
  144. }
  145.  
  146. function filterByKeywords() {
  147. const keywords = window.keywordInput.value.split(/[,,]/).map(k => k.trim()).filter(Boolean);
  148. if (keywords.length === 0) return;
  149.  
  150. const table = document.querySelector('#my-table tbody');
  151. const rows = table.querySelectorAll('tr');
  152.  
  153. rows.forEach(row => {
  154. const keywordsCell = row.querySelector('td:last-child');
  155. const keywordText = keywordsCell.textContent.toLowerCase();
  156.  
  157. const shouldShow = keywords.some(keyword =>
  158. keywordText.includes(keyword.toLowerCase())
  159. );
  160.  
  161. row.style.display = shouldShow ? '' : 'none';
  162. });
  163.  
  164. createLoading(`已筛选出包含关键词的论文`, 2000);
  165. }
  166.  
  167. // 重置筛选
  168. function resetFilter() {
  169. const table = document.querySelector('#my-table tbody');
  170. const rows = table.querySelectorAll('tr');
  171.  
  172. rows.forEach(row => {
  173. row.style.display = '';
  174. });
  175.  
  176. window.keywordInput.value = '';
  177. createLoading(`已重置筛选`, 2000);
  178. }
  179. // 清除数据函数
  180. function clearData() {
  181. localStorage.removeItem('cnkiTableData');
  182. const table = document.querySelector('#my-table tbody');
  183. table.innerHTML = '';
  184. updateSelectCount();
  185. createLoading('数据已清除', 2000);
  186. }
  187. function createButton(text, onClick, color = '#0f5de5') {
  188. const button = document.createElement('button');
  189. button.textContent = text;
  190. button.className = 'diy-btn';
  191. button.style.color = color;
  192. button.addEventListener('click', onClick);
  193. return button;
  194. }
  195.  
  196. function createTips(text, color) {
  197. const tips = document.createElement('span');
  198. tips.textContent = text;
  199. tips.className = 'diy-span';
  200. tips.style.color = color;
  201. return tips;
  202. }
  203.  
  204. function selectAll() {
  205. document.querySelectorAll('.selectItem').forEach(item => item.checked = true);
  206. updateSelectCount();
  207. }
  208.  
  209. function deselectAll() {
  210. document.querySelectorAll('.selectItem').forEach(item => item.checked = false);
  211. updateSelectCount();
  212. }
  213.  
  214. function delay(ms) {
  215. return new Promise(resolve => setTimeout(resolve, ms));
  216. }
  217.  
  218. async function downloadSelected() {
  219. const selectedItems = document.querySelectorAll('.selectItem:checked');
  220. if (selectedItems.length === 0) {
  221. createLoading('请选择要下载的项目!', 2000);
  222. return;
  223. }
  224.  
  225. for (const item of selectedItems) {
  226. const link = item.closest('tr').querySelector('a[href^="http"]');
  227. if (link) {
  228. // 打开链接
  229. window.open(link.href, '_blank');
  230.  
  231. // 生成随机延迟时间:2-5秒(步长0.5秒)
  232. const randomDelay = Math.floor(Math.random() * 7) * 500 + 2000;
  233. console.log(`延迟 ${randomDelay / 1000} 秒后继续下载...`);
  234.  
  235. // 延迟下一次下载
  236. await delay(randomDelay);
  237. }
  238. }
  239.  
  240. }
  241.  
  242.  
  243. function updateSelectCount() {
  244. const count = document.querySelectorAll('.selectItem:checked').length;
  245. document.querySelector('#dl_sel').textContent = `下载 (${count})`;
  246. }
  247.  
  248. // 查找包含“PDF下载”文字内容的链接并获取其链接地址
  249. function findPDFDownloadLink(element, textContent) {
  250. // 获取所有包含链接的 li 元素
  251. var listItems = element.querySelectorAll('li');
  252. // 遍历所有 li 元素,查找包含指定文本内容的链接
  253. for (var i = 0; i < listItems.length; i++) {
  254. var link = listItems[i].querySelector('a');
  255. if (link && link.innerText.includes(textContent)) {
  256. // 获取链接地址
  257. var href = link.href;
  258. console.log('链接地址:', href);
  259. return href; // 返回找到的链接地址
  260. }
  261. }
  262. console.log('未找到包含指定文字内容的链接');
  263. return null; // 如果未找到匹配的链接,返回null
  264. }
  265.  
  266. // 切换WebVPN的函数
  267. function toggleWebVPN() {
  268. useWebVPN = !useWebVPN;
  269. GM_setValue('useWebVPN', useWebVPN);
  270. const toggleButton = document.getElementById('webvpn-toggle');
  271. if (toggleButton) {
  272. toggleButton.textContent = useWebVPN ? '禁用WebVPN' : '启用WebVPN';
  273. }
  274. createLoading(`WebVPN ${useWebVPN ? '已启用' : '已禁用'}`, 2000);
  275. }
  276. function convertToWebVPNLink(originalLink) {
  277. if (!useWebVPN) {
  278. return originalLink;
  279. }
  280. const webVPNDomain = window.location.origin;
  281. const path = originalLink.replace(/^(https?:\/\/)?(www\.)?[^\/]+/, '');
  282. return `${webVPNDomain}${path}`;
  283. }
  284. async function getLinks() {
  285. const table = document.querySelector('#my-table tbody');
  286. if (table.children.length > 0) {
  287. createLoading('已有数据!请先清除数据后再获取新链接。', 2000);
  288. return;
  289. }
  290.  
  291. const links = Array.from(document.querySelectorAll('.fz14')).map(link => {
  292. const originalHref = link.href;
  293. const webVPNHref = convertToWebVPNLink(originalHref);
  294. console.log('Original link:', originalHref);
  295. console.log('WebVPN link:', webVPNHref);
  296. return webVPNHref;
  297. });
  298. const loading = createLoading('获取链接中(请耐心等待)……', 60000);
  299.  
  300. try {
  301. console.log(links);
  302. const batchSize = 5; // 每批处理的链接数
  303. for (let i = 0; i < links.length; i += batchSize) {
  304. const batch = links.slice(i, i + batchSize);
  305. const responses = await Promise.all(batch.map(link =>
  306. fetch(link, {
  307. method: 'GET',
  308. mode: 'same-origin',
  309. referrerPolicy: 'unsafe-url',
  310. }).then(response => response.text())
  311. ));
  312.  
  313. responses.forEach((html, index) => {
  314. const doc = new DOMParser().parseFromString(html, 'text/html');
  315. const h1Element = doc.querySelector('.wx-tit h1');
  316. h1Element.querySelectorAll('span').forEach(element => element.remove());
  317. const title = h1Element?.textContent.trim() || '无标题';
  318. const author = Array.from(doc.querySelectorAll('.author')).map(a => a.textContent).join(' ');
  319. const pdfLink = findPDFDownloadLink(doc.querySelector('.operate-btn'),'PDF下载');
  320. const keywords = Array.from(doc.querySelectorAll('.keywords a')).map(keywords => `<span class="diy-btn" style="font-size:12px">${keywords.textContent.replace(";","")}</span>`).join('');
  321. const row = table.insertRow();
  322. row.innerHTML = `
  323. <td style="text-align:center"><input type="checkbox" class="selectItem"></td>
  324. <td style="text-align:center">${i + index + 1}</td>
  325. <td><span class="diy_title">${title}</span><span class="diy_author">${author}</span></td>
  326. <td style="text-align:center">${pdfLink !== null ? `<a href="${pdfLink}" target="_blank" class="diy-btn">PDF下载</a>` : '获取的链接为空,请检查!'}</td>
  327. <td>${keywords}</td>
  328. `;
  329.  
  330. row.querySelector('input[type="checkbox"]').addEventListener('change', updateSelectCount);
  331. });
  332.  
  333. // 更新加载提示
  334. loading.textContent = `正在获取链接...(${Math.min((i + batchSize) / links.length * 100, 100).toFixed(0)}%)`;
  335. }
  336.  
  337. document.body.removeChild(loading);
  338. createLoading('获取完毕!', 2000);
  339.  
  340. // 在获取完链接后保存数据
  341. saveData();
  342. } catch (error) {
  343. console.error('Error fetching links:', error);
  344. document.body.removeChild(loading);
  345. createLoading(`获取链接出错:${error.message}\n links: ${links}`, 3000);
  346. }
  347. }
  348.  
  349. // 新增:保存数据到localStorage
  350. function saveData() {
  351. const tableRows = Array.from(document.querySelectorAll('#my-table tbody tr')).map(row => ({
  352. title: row.querySelector('.diy_title').textContent,
  353. author: row.querySelector('.diy_author').textContent,
  354. pdfLink: row.querySelector('a[href^="http"]')?.href || '',
  355. keywords: row.querySelector('td:last-child').innerHTML
  356. }));
  357. localStorage.setItem('cnkiTableData', JSON.stringify(tableRows));
  358. }
  359.  
  360. // 新增:从localStorage加载数据
  361. function loadSavedData() {
  362. const savedData = localStorage.getItem('cnkiTableData');
  363. if (savedData) {
  364. const tableData = JSON.parse(savedData);
  365. const table = document.querySelector('#my-table tbody');
  366. table.innerHTML = ''; // 清空现有内容
  367. tableData.forEach((item, index) => {
  368. const row = table.insertRow();
  369. row.innerHTML = `
  370. <td style="text-align:center"><input type="checkbox" class="selectItem"></td>
  371. <td style="text-align:center">${index + 1}</td>
  372. <td><span class="diy_title">${item.title}</span><span class="diy_author">${item.author}</span></td>
  373. <td style="text-align:center">${item.pdfLink ? `<a href="${item.pdfLink}" target="_blank" class="diy-btn">PDF下载</a>` : '获取链接失败!'}</td>
  374. <td>${item.keywords}</td>
  375. `;
  376. row.querySelector('input[type="checkbox"]').addEventListener('change', updateSelectCount);
  377. });
  378. updateSelectCount();
  379. }
  380. }
  381.  
  382. // 目录下载功能
  383. function addCategoryDownloadButton() {
  384. const otherBtns = document.querySelector('.other-btns');
  385. if (!otherBtns) return;
  386.  
  387. const li = document.createElement('li');
  388. li.className = 'btn-diy';
  389. li.style.cssText = 'width:auto;height:23px;line-height:22px;background-color:#3f8af0;border-radius:3px;';
  390.  
  391. const a = document.createElement('a');
  392. a.textContent = '目录下载';
  393. a.className = 'a-diy';
  394. a.style.cssText = 'color:#ffffff;padding:2px 10px;';
  395. a.href = 'javascript:void(0)';
  396. a.addEventListener('click', downloadCategory);
  397.  
  398. li.appendChild(a);
  399. otherBtns.appendChild(li);
  400. }
  401.  
  402. async function downloadCategory() {
  403. const hrefLink = findPDFDownloadLink(document.querySelector('.operate-btn'),'章节下载');
  404. console.log("目录链接:" + hrefLink);
  405. if (!hrefLink) {
  406. createLoading('无法获取目录链接', 2000);
  407. return;
  408. }
  409.  
  410. try {
  411. const response = await fetch(hrefLink);
  412. const html = await response.text();
  413. const doc = new DOMParser().parseFromString(html, 'text/html');
  414. const title = document.querySelector('.wx-tit h1')?.textContent.trim() || '未知标题';
  415. const chapters = Array.from(doc.querySelectorAll('.ls-chapters li'))
  416. .map(chapter => chapter.textContent.trim())
  417. .filter(Boolean)
  418. .map(text => {
  419. const parts = text.split('-');
  420. return `${parts[0].replace(/\n/g, '\t')} \n`;
  421. })
  422. .join('');
  423. console.log(chapters);
  424. saveFile(title, chapters);
  425. } catch (error) {
  426. console.error('Error downloading category:', error);
  427. createLoading('下载目录失败', 2000);
  428. }
  429. }
  430.  
  431. function saveFile(name, data) {
  432. const blob = new Blob([data], { type: 'text/plain' });
  433. const link = document.createElement('a');
  434. link.href = URL.createObjectURL(blob);
  435. link.download = `${name}.txt`;
  436. link.click();
  437. URL.revokeObjectURL(link.href);
  438. }
  439.  
  440.  
  441. // 初始化函数
  442. function init() {
  443. const url = window.location.href.toLowerCase();
  444. console.log(url);
  445.  
  446. // 在所有页面上添加批量下载按钮
  447. // createPopupButton();
  448. // 如果不显示按钮,将下面三行注释,取消上一行的代码注释。
  449. if (url.includes('defaultresult') || url.includes('advsearch') || url.includes('search')) {
  450. createPopupButton();
  451. }
  452.  
  453. // 在文献详情页面添加目录下载按钮
  454. if (url.includes('abstract')) {
  455. addCategoryDownloadButton();
  456. }
  457.  
  458. // 加载自定义CSS
  459. loadCss(`
  460. .diy-btn {
  461. display: inline-block;
  462. vertical-align: middle;
  463. padding: 2px 8px;
  464. line-height: 18px;
  465. color: #0f5de5;
  466. font-size: 14px;
  467. text-align: center;
  468. background-color: #e3ecfd;
  469. border: 1px solid #fff;
  470. cursor: pointer;
  471. }
  472. .diy-span {
  473. display: inline-block;
  474. vertical-align: middle;
  475. padding: 2px 8px;
  476. line-height: 18px;
  477. color: #0f5de5;
  478. font-size: 12px;
  479. text-align: center;
  480. }
  481. #popup table tr td,
  482. #popup table tr th {
  483. line-height: 20px;
  484. height: 20px;
  485. padding: 5px;
  486. border: 1px solid #eee;
  487. }
  488. .diy_title {
  489. display: block;
  490. color: #524d4d;
  491. font-size: 14px;
  492. font-weight: bold;
  493. }
  494. .diy_author {
  495. color: #666;
  496. }
  497. .font-size-button {
  498. font-size: 14px;
  499. display: block;
  500. line-height: 18px;
  501. border: 1px solid #e2e2e2;
  502. border-radius: 2px;
  503. background-color: #f5f5f5;
  504. color: #504f4f;
  505. float: left;
  506. padding: 3px;
  507. position: absolute;
  508. right: 0;
  509. width: 28px;
  510. cursor: pointer;
  511. }
  512. #script_author {
  513. color: rgb(14 93 228);
  514. display: block;
  515. background-color: #e3ecfd;
  516. margin-top: 5px;
  517. text-align: center;
  518. padding: 4px;
  519. }
  520. .first_show_li li{
  521. line-height:30px;
  522. }
  523. `);
  524. }
  525.  
  526. // 在页面加载完成后运行初始化函数
  527.  
  528. // 在页面加载完成后运行初始化函数
  529. window.addEventListener('load', init);
  530.  
  531. })();
  532.  

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址