NSFC_conclusion_downloader

帮助你直接下载国自然结题报告

目前為 2023-12-16 提交的版本,檢視 最新版本

  1. // ==UserScript==
  2. // @name NSFC_conclusion_downloader
  3. // @namespace https://blog.rhilip.info/
  4. // @version 1.13
  5. // @description 帮助你直接下载国自然结题报告
  6. // @author Rhilip
  7. // @match https://kd.nsfc.gov.cn/finalDetails*
  8. // @match https://kd.nsfc.cn/finalDetails*
  9. // @require https://unpkg.com/jspdf@2.3.0/dist/jspdf.umd.min.js
  10. // @require https://unpkg.com/jquery@3.6.0/dist/jquery.js
  11. // ==/UserScript==
  12.  
  13. /* globals $, jspdf */
  14.  
  15. (async function() {
  16. 'use strict';
  17.  
  18. // 准备交互按钮
  19. const downloadBtn = $('<div class="el-button is-round">下载全文</div>');
  20.  
  21. // 点击交互按钮时需要开始下载操作
  22. downloadBtn.click(async () => {
  23. downloadBtn.prop('disabled', true).addClass('is-disabled');
  24. if (!/暂无结题报告全文/.test($('#related').text())) {
  25. // 获得项目信息: 编号(加密后)、批准号、项目名称
  26. const urlParams = new URLSearchParams(location.search);
  27. const dependUintID = urlParams.get('id');
  28. const projectID = $('.basic_info > div.el-row div:contains("项目批准号:") + div').text();
  29. const projectName = $('.basic_info > div.el-row div:contains("项目名称:") + div').text();
  30.  
  31. // 准备需要的PDF文件,并删除初始页
  32. const doc = new jspdf.jsPDF();
  33. doc.deletePage(1);
  34. doc.setDocumentProperties({
  35. title: `${projectID} ${projectName}`,
  36. subject: location.href,
  37. creator: 'NSFC_conclusion_downloader'
  38. });
  39.  
  40. let has_download_error = 0;
  41.  
  42. // 核心下载方法
  43. const image = new Image();
  44. for (let i=1;;i++) {
  45. downloadBtn.text(`正在下载第 ${i} 页`);
  46.  
  47. // 随机等待一段时间(2-3秒)防止国自然服务器压力过大
  48. // await new Promise(resolve => setTimeout(resolve, Math.random() * 1e3 + 2e3));
  49.  
  50. try {
  51. // 获得图片链接
  52. const { data: requestData } = await $.post('/api/baseQuery/completeProjectReport', {id: dependUintID, index: i});
  53.  
  54. // 获得Blob形式的imageData,这样可以防止image.src和jsPDF.addImage会产生两次图片请求,浪费带宽
  55. // (实际变成了一次请求服务器和两次请求本地blob)
  56. const imageDataAsBlob = await $.ajax({
  57. url: requestData.url,
  58. method: 'GET',
  59. xhrFields: { responseType: 'blob'}
  60. });
  61. // 加载图片并获得图片的 width, height 属性
  62. image.src = URL.createObjectURL(imageDataAsBlob);
  63. await image.decode();
  64.  
  65. // 将图片添加进PDF中
  66. doc.addPage([image.width, image.height], image.width < image.height ? 'p' : 'l');
  67. doc.addImage(image, "PNG", 0, 0, image.width, image.height);
  68.  
  69. if (requestData.hasnext === false) break; // hasnext可能为null,此处应该明确为false才break
  70. } catch (e) {
  71. if (e.status === 404) { // 说明此时已经到了最后一页
  72. break;
  73. } else { // 其他错误
  74. doc.addPage('a4', 'p');
  75. doc.text(`Fail to download Page.${i}, Please check and re-download it.`, 10, 10);
  76.  
  77. // 对下载出错的重新启用下载按钮并计数
  78. has_download_error += 1;
  79. downloadBtn.prop('disabled', false).removeClass('is-disabled');
  80. }
  81.  
  82. // 连续5次出错,认为存在网络或者其他问题,需要用户检查
  83. if (has_download_error >= 5) {
  84. break;
  85. }
  86. }
  87. }
  88.  
  89. // 我们并没法 await 保存过程,所以直接显示下载完成就好,浏览器处理好会自动显示下载文件
  90. doc.save(`${projectID} ${projectName}.pdf`);
  91. downloadBtn.text(has_download_error > 0 ? '下载过程中可能出错,请检查下载文件或尝试重新下载': '下载完成');
  92. }
  93. });
  94.  
  95. // 将交互按钮插入到页面中
  96. const observer = new MutationObserver(mutations => {
  97. const conclusion_another = $('div.link_title:has(div:contains("结题报告"),div:contains("全文"))');
  98. if (conclusion_another.length > 0 && downloadBtn.is(':hidden')) {
  99. conclusion_another.find('div.verticalBar_T').after(downloadBtn);
  100. }
  101. });
  102.  
  103. observer.observe(document.body, {
  104. childList: true,
  105. subtree: true
  106. });
  107. })();

QingJ © 2025

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