🏠 Home 

Oxford Academic导出PDF

自动获取并合并PDF


安装此脚本?
  1. // ==UserScript==
  2. // @name Oxford Academic导出PDF
  3. // @namespace https://qinlili.bid
  4. // @version 1.0.0
  5. // @description 自动获取并合并PDF
  6. // @author 琴梨梨
  7. // @license WTFPL
  8. // @match https://academic.oup.com/edited-volume/*
  9. // @icon https://oup.silverchair-cdn.com/UI/app/img/v-638282418223920402/apple-touch-icon.png
  10. // @run-at document-idle
  11. // @require https://lib.baomitu.com/pdf-lib/1.17.1/pdf-lib.min.js#sha512-z8IYLHO8bTgFqj+yrPyIJnzBDf7DDhWwiEsk4sY+Oe6J2M+WQequeGS7qioI5vT6rXgVRb4K1UVQC5ER7MKzKQ==
  12. // @grant GM_xmlhttpRequest
  13. // @grant GM_registerMenuCommand
  14. // ==/UserScript==
  15. (async function() {
  16. //CODENAME:Carboxylate
  17. 'use strict';
  18. const config={
  19. //带目录导出,需要依赖闭源的PSPDFKIT,目前暂不可用,有待进一步开发
  20. writeOutline:false,
  21. //去除账号标识,开发中
  22. removeAccountInfo:false
  23. };
  24. GM.registerMenuCommand("原神,启动!", () => {
  25. location.href="https://ys.mihoyo.com/";
  26. });
  27. const dlFile = (link, name) => {
  28. let eleLink = document.createElement('a');
  29. eleLink.download = name;
  30. eleLink.style.display = 'none';
  31. eleLink.href = link;
  32. document.body.appendChild(eleLink);
  33. eleLink.click();
  34. document.body.removeChild(eleLink);
  35. };
  36. const loadScriptAsync = link => {
  37. return new Promise(resolve => {
  38. let script = document.createElement("script");
  39. script.src = link;
  40. script.onload = resolve;
  41. document.body.appendChild(script);
  42. });
  43. };
  44. switch(location.host){
  45. case("academic.oup.com"):{
  46. //文档详情页
  47. console.log("关注笙歌喵~关注帅比笙歌超可#OvO谢谢喵~");
  48. console.log("Carboxylate 1.0.0");
  49. const titleDiv=document.getElementsByClassName("book-bottom-section__title-wrap")[0];
  50. let dlBtn=document.createElement("button");
  51. dlBtn.innerText="下载全部PDF";
  52. titleDiv.appendChild(dlBtn);
  53. dlBtn.onclick=async ()=>{
  54. dlBtn.onclick=null;
  55. //阶段1:建立目录结构
  56. dlBtn.innerText="0% 分析目录结构";
  57. const catagory=document.getElementsByClassName("bookToc")[0];
  58. let parsedCatagory={
  59. title:document.getElementsByClassName("book-info__title")[0].innerText.trim(),
  60. author:document.getElementsByClassName("book-info__author-link")[0].innerText.trim(),
  61. chapters:[]
  62. };
  63. const parseChapter=(ele,depth)=>{
  64. if(ele.getElementsByClassName("collections-child-list").length==0||ele.getElementsByClassName("collections-child-list")[0].children.length==0){
  65. //顶级章节,直接处理
  66. [].forEach.call(ele.children,sub=>{
  67. if(sub.className=="tocLink"&&sub.tagName=="A"){
  68. //获取标题和链接
  69. parsedCatagory.chapters.push({
  70. title:(sub.getElementsByClassName("tocLink-label")[0]?sub.getElementsByClassName("tocLink-label")[0].innerText:"")+" "+sub.getElementsByClassName("tocLink-title")[0].innerText,
  71. link:sub.href,
  72. depth:depth,
  73. child:false,
  74. debug:sub
  75. });
  76. }
  77. })
  78. }else{
  79. //读取子章节
  80. parsedCatagory.chapters.push({
  81. title:(ele.getElementsByClassName("tocLink-label")[0]?ele.getElementsByClassName("tocLink-label")[0].innerText:"")+" "+ele.getElementsByClassName("tocLink-title")[0].innerText,
  82. link:null,
  83. depth:depth,
  84. child:true,
  85. debug:ele
  86. });
  87. //递归遍历
  88. [].forEach.call(ele.getElementsByTagName("ul")[0].getElementsByTagName("li"),ele=>{
  89. parseChapter(ele,depth+1);
  90. });
  91. }
  92. }
  93. [].forEach.call(catagory.children,ele=>{
  94. if(ele.tagName=="LI"){
  95. parseChapter(ele,0);
  96. }
  97. });
  98. console.log(parsedCatagory);
  99. //阶段2:解析PDF地址
  100. dlBtn.innerText="5% 解析PDF地址";
  101. let count=0;
  102. for(let chapter of parsedCatagory.chapters){
  103. if(chapter.child==false){
  104. //可下载的章节,需要解析地址
  105. let chapterRequest=await fetch(chapter.link, {
  106. "referrer": location.href,
  107. "method": "GET",
  108. "mode": "cors",
  109. "credentials": "include"
  110. });
  111. let chapterPage=await chapterRequest.text();
  112. const parser = new DOMParser();
  113. const chapterDoc = parser.parseFromString(chapterPage, "text/html");
  114. chapter.pdflink=chapterDoc.getElementsByClassName("at-pdfLink")[0].href;
  115. };
  116. count++;
  117. dlBtn.innerText=(5+25*count/parsedCatagory.chapters.length).toFixed(2)+"% 解析PDF地址["+count+"/"+parsedCatagory.chapters.length+"]";
  118. };
  119. console.log(parsedCatagory);
  120. //阶段3:分段下载PDF
  121. dlBtn.innerText="30% 下载PDF";
  122. count=0;
  123. for(let chapter of parsedCatagory.chapters){
  124. if(chapter.child==false){
  125. //异步跨域下载PDF
  126. function asyncFetch(){
  127. return new Promise(resolve => {
  128. let pic=GM_xmlhttpRequest({
  129. method: "GET", url: chapter.pdflink, responseType: "blob", onload: (res) => {
  130. console.log(res.response);
  131. chapter.pdffile=res.response;
  132. resolve();
  133. }
  134. })
  135. });
  136. }
  137. await asyncFetch();
  138. };
  139. count++;
  140. dlBtn.innerText=(30+60*count/parsedCatagory.chapters.length).toFixed(2)+"% 下载PDF["+count+"/"+parsedCatagory.chapters.length+"]";
  141. };
  142. console.log(parsedCatagory);
  143. //阶段4:合并PDF导出
  144. dlBtn.innerText="90% 合并PDF";
  145. const pdfDoc = await PDFLib.PDFDocument.create();
  146. count=0;
  147. pdfDoc.setTitle(parsedCatagory.title);
  148. pdfDoc.setAuthor(parsedCatagory.author);
  149. pdfDoc.setCreator("Carboxylate By QINLILI");
  150. pdfDoc.setCreationDate(new Date());
  151. pdfDoc.setModificationDate(new Date());
  152. //合并PDF并记录页码
  153. for(let chapter of parsedCatagory.chapters){
  154. if(chapter.child==false){
  155. //合并PDF
  156. const pdfObj=await PDFLib.PDFDocument.load(await chapter.pdffile.arrayBuffer());
  157. const copiedPages = await pdfDoc.copyPages(pdfObj, pdfObj.getPageIndices());
  158. copiedPages.forEach((page) => pdfDoc.addPage(page));
  159. chapter.pages=copiedPages.length;
  160. };
  161. count++;
  162. dlBtn.innerText=(90+6*count/parsedCatagory.chapters.length).toFixed(2)+"% 合并PDF["+count+"/"+parsedCatagory.chapters.length+"]";
  163. };
  164. console.log(parsedCatagory);
  165. //导出文件
  166. dlBtn.innerText="96% 写入目录";
  167. const mergedPdfFile = await pdfDoc.save();
  168. if(config.writeOutline){
  169. await loadScriptAsync("https://cdn.jsdelivr.net/npm/pspdfkit@2023.4.0/dist/pspdfkit.min.js");
  170. let foo=document.createElement("div");
  171. foo.className="foo";
  172. foo.style.width="100vw";
  173. foo.style.height="100vh";
  174. foo.style.display="none";
  175. document.body.appendChild(foo);
  176. let instance=await PSPDFKit.load({
  177. baseUrl:"https://cdn.jsdelivr.net/npm/pspdfkit@2023.4.0/dist/",
  178. document: mergedPdfFile.buffer,
  179. container:'.foo'
  180. });
  181. for(let chapter of parsedCatagory.chapters){
  182. const bookmark = new PSPDFKit.Bookmark({
  183. name: chapter.title,
  184. action: new PSPDFKit.Actions.GoToAction({ pageIndex: count })
  185. });
  186. await instance.create(bookmark);
  187. if(chapter.child==false){
  188. //小标题累计页码
  189. count+=chapter.pages;
  190. };
  191. };
  192. dlBtn.innerText="98% 导出文件 带目录导出耗时极长,请保持耐心";
  193. const documentBuffer = await instance.exportPDF();
  194. const pdfFile=new Blob([documentBuffer]);
  195. dlFile(URL.createObjectURL(pdfFile),parsedCatagory.title+".pdf")
  196. }else{
  197. dlBtn.innerText="98% 导出文件";
  198. const pdfFile=new Blob([mergedPdfFile]);
  199. dlFile(URL.createObjectURL(pdfFile),parsedCatagory.title+".pdf")
  200. };
  201. dlBtn.innerText="100% 下载成功";
  202. };
  203. break;
  204. };
  205. default:{
  206. break;
  207. }
  208. }
  209. })();