Wenku Doc Downloader

下载“百度文库”“豆丁网”文档

当前为 2021-11-30 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name Wenku Doc Downloader
  3. // @namespace http://tampermonkey.net/
  4. // @version 0.6
  5. // @description 下载“百度文库”“豆丁网”文档
  6. // @author allenlv2690@gmail.com
  7. // @match https://wenku.baidu.com/view/*
  8. // @match https://www.docin.com/p-*
  9. // @icon https://www.google.com/s2/favicons?domain=limestart.cn
  10. // @grant none
  11. // @license GPL-3.0-only
  12. // @create 2021-11-22
  13. // @note 1. 现在支持导出pdf(保留排版与图片),使用【HTML转PDF】程序即可。
  14. // @note 2. 但pdf中的图片无法直接复制:以Acrobat为例,先选中图片,然后右键另存出去,然后单独保存的图片可以正常使用,如果直接复制pdf中的图片粘贴到word中很可能失败。
  15. // @note 3. 【HTML转PDF】程序的链接在百度网盘(详见 https://greasyfork.org/zh-CN/scripts/435884-wenku-doc-downloader)。
  16. // ==/UserScript==
  17.  
  18. /*
  19. * 附属功能函数部分
  20. */
  21.  
  22. function createAndDownloadFile(fileName, content) {
  23. // 创建并下载文件
  24. var aTag = document.createElement('a');
  25. var blob = new Blob([content]);
  26. aTag.download = fileName;
  27. aTag.href = URL.createObjectURL(blob);
  28. aTag.click();
  29. URL.revokeObjectURL(blob);
  30. }
  31.  
  32. function formatText(text){
  33. // 用于纯文本文档的文本美化
  34. var reg_exp_1 = new RegExp(" [(]?=[\u4e00-\u9fa5] [)]");
  35. var reg_exp_2 = new RegExp("(?<=TEMP[\u4e00-\u9fa5]) ");
  36. var reg_exp_3 = new RegExp("(?<=[\u4e00-\u9fa5]) (?=[\u4e00-\u9fa5])");
  37.  
  38. var text_1 = text.replace(reg_exp_1, "TEMP");
  39. var text_2 = text_1.replace(reg_exp_2, "");
  40. var text_3 = text_2.replace("TEMP", "");
  41. var text_final = text_3.replace(/ /g, " ");
  42. return text_final;
  43. }
  44.  
  45. function formatText2(text) {
  46. // 用于图形文字混合型文档的文本美化
  47. var reg_exp = new RegExp("[  ]{2,}");
  48. var content_1 = text.replace(reg_exp, "\n");
  49.  
  50. var content_2 = content_1.replace(/[  ]\n/g, "\n");
  51.  
  52. var reg_exp_2 = new RegExp("\n[   ]*\n*\n");
  53. var content_3 = content_2.replace(reg_exp_2, "\n");
  54.  
  55. var reg_exp_3 = new RegExp(" *\n * ");
  56. var content_4 = content_3.replace(reg_exp_3, "\n");
  57.  
  58. var content_5 = content_4.replace(/[  ]/g, " ");
  59. var final_content = content_5.replace(/[ \n]精选文档[ \n]/g).replace(/\n{2,}/g, "\n");
  60.  
  61. return final_content;
  62. }
  63.  
  64. function detectType() {
  65. // 获取文档类型名称
  66. var doc_title_wrap = document.getElementsByClassName("doc-title-wrap")[0];
  67. var file_type = doc_title_wrap.children[0].className;
  68. var pdf, doc, ppt, excel, type;
  69. // 判断文档类型
  70. if (file_type.search("word") !== -1) {
  71. type = "word";
  72. }
  73. else if (file_type.search("ppt") !== -1) {
  74. type = "ppt";
  75. }
  76. else if (file_type.search("excel") !== -1) {
  77. type = "excel";
  78. }
  79. else if (file_type.search("pdf") !== -1) {
  80. type = "pdf";
  81. }
  82. else if (file_type.search("txt" !== -1)) {
  83. type = "txt";
  84. }
  85. else {
  86. type = file_type;
  87. }
  88. // 分别尝试获取相应元素列表,若列表长度为0则不存在相应元素,否则存在
  89. var pic_nums = document.getElementsByClassName("reader-pic-item").length;
  90. var word_nums = document.getElementsByClassName("reader-word-layer").length;
  91. var ppt_img_nums = document.getElementsByClassName("ppt-image-wrap").length;
  92.  
  93. // 判断文档类型、文字和图片的数量状况
  94. if (type === "word" && !word_nums && pic_nums) {
  95. // doc: 纯图片
  96. return "doc-only-pic";
  97. }
  98. else if (type === "word" && word_nums > 2 && pic_nums <= 1) {
  99. // doc: 纯文字
  100. return "doc-only-word";
  101. }
  102. else if (type === "word" && pic_nums > 2 && word_nums > 2) {
  103. // doc: 图形、文字混合
  104. return "doc-pic-word";
  105. }
  106. else if (type === "pdf" && pic_nums > 2 && word_nums === 1) {
  107. // pdf: 带有一行文字标题,之后都是图形
  108. return "pdf-pic-title";
  109. }
  110. else if (type === "pdf" && !word_nums && pic_nums) {
  111. // pdf: 纯图形
  112. return "pdf-only-pic";
  113. }
  114. else if (type === "pdf" && !pic_nums && word_nums > 1) {
  115. // pdf: 纯文字
  116. return "pdf-only-word";
  117. }
  118. else if (type === "pdf" && word_nums > 2 && pic_nums > 1) {
  119. // pdf: 图形、文字混合
  120. return "pdf-pic-word";
  121. }
  122. else if ((type === "ppt" && ppt_img_nums > 2) || (type === "pdf" && !word_nums && !pic_nums && ppt_img_nums)) {
  123. // ppt: 包含至少3页内容 / 纯ppt图形页面构成
  124. return "ppt";
  125. }
  126. else if (type === "excel" && pic_nums && word_nums > 2) {
  127. // excel: 包含可选中文字
  128. return "excel-only-word";
  129. }
  130. else if (type === "excel" && pic_nums && !word_nums) {
  131. // excel: 纯图形
  132. return "excel-only-pic";
  133. }
  134. else if (type === "txt") {
  135. // txt: 纯文字
  136. return "txt";
  137. }
  138. else {
  139. return {"源文档类型": type,
  140. "图形数量": pic_nums,
  141. "文字块数量": word_nums,
  142. "ppt纯图形页面数量": ppt_img_nums};
  143. }
  144. }
  145.  
  146. /*
  147. * 主要功能函数部分
  148. */
  149.  
  150. function saveHtml() {
  151. // 提示用户保存完整html页面
  152. var hint1 = "请按下Ctrl+S以保存页面\n";
  153. var hint2 = "请保存【网页,全部】或【网页,完成】而非仅HTML或单个文件\n";
  154. var hint3 = "保存后应当有1个【xxx.html】文件和1个【xxx_files】文件夹\n";
  155. var hint4 = "请复制这两个文件到【HTML转PDF】程序所在的文件夹"
  156. alert(hint1 + hint2 + hint3 + hint4);
  157. }
  158.  
  159. function createSaveHtmlBtn() {
  160. // 创建 下载html 按钮
  161. var btn_3 = document.createElement("button");
  162. // 样式设定
  163. btn_3.setAttribute("class", "save-html-btn");
  164. btn_3.style.height = "25px";
  165. btn_3.style.width = "15%";
  166. btn_3.style.marginLeft = "0.2%";
  167. btn_3.style.backgroundColor = "orange";
  168. btn_3.style.border = "none";
  169. btn_3.textContent = "导出pdf(实验性)";
  170. btn_3.style.fontWeight = "bold";
  171. btn_3.style.borderRadius = "10%";
  172. // 绑定事件,添加到页面上
  173. btn_3.onclick = saveHtml;
  174. var section = document.getElementsByClassName("btns_section")[0];
  175. section.appendChild(btn_3);
  176. }
  177.  
  178. function readAll() {
  179. var read_all_btn = document.getElementsByClassName("read-all")[0];
  180. // 如果存在“继续阅读”的按钮
  181. if (read_all_btn) {
  182. // 点击“继续阅读”按钮
  183. read_all_btn.click();
  184. }
  185. else{
  186. var hint = "文档已经完全展开,可以导出";
  187. alert(hint);
  188. try {
  189. // 判断文档类型
  190. var category = detectType();
  191. }
  192. catch {
  193. alert("未知/特殊文档类型,例如学术文献,暂不支持下载\n也可与作者反馈或联系:\nallenlv2690@gmail.com");
  194. return undefined;
  195. }
  196. // 准备调整按钮,先获取按钮
  197. var init_btn = document.getElementsByClassName("init-btn")[0];
  198. var save_doc_btn = document.getElementsByClassName("save-doc-btn")[0];
  199.  
  200. // 根据文档类型判断是否要增加“导出pdf”橙色按钮
  201. if (category === "doc-only-word" ||
  202. category === "doc-pic-word" ||
  203. category === "pdf-only-word" ||
  204. category === "pdf-pic-word" ||
  205. category === "excel-only-word") {
  206. // 非纯图片文档可以使用html转pdf的功能(excel不行)
  207. save_doc_btn.style.width = "34.8%";
  208. createSaveHtmlBtn();
  209. }
  210. // 根据文档类型判断是否要更换绿色按钮的文字
  211. else if (category === "doc-pic-only" ||
  212. category === "pdf-pic-title" ||
  213. category === "ppt" ||
  214. category === "pdf-only-pic" ||
  215. category === "excel-only-pic"){
  216. save_doc_btn.textContent = "导出全部图片链接";
  217. }
  218. // 调整按钮显示状况
  219. save_doc_btn.style.removeProperty("display");
  220. init_btn.style.display = "none";
  221. }
  222. }
  223.  
  224. function savePDFData() {
  225. // 存储pdf型data(假定是内容是pic)
  226. // alert("Function savePDFData was called.");
  227. var pic_urls = document.getElementsByClassName("reader-pic-item");
  228. var text_list = [];
  229. // 去掉前缀
  230. var reg_exp_1 = new RegExp(": ?url[(]");
  231. // 去掉后缀
  232. var reg_exp_2 = new RegExp("[)]; ?background-position");
  233.  
  234. for (var i = 0; i < pic_urls.length; i++){
  235. var whole_text = pic_urls[i].getAttribute("style");
  236. var de_pretext = whole_text.split(reg_exp_1)[1];
  237. var url = de_pretext.split(reg_exp_2)[0];
  238. text_list.push(url);
  239. }
  240.  
  241. text_list[0] = text_list[0].replace(/"/g, "");
  242. var content = text_list.join("\n");
  243. // 启动下载
  244. createAndDownloadFile("urls.csv", content);
  245. }
  246.  
  247. function saveDocData() {
  248. // 存储doc型data(内容是text)
  249. // alert("Function saveDocData was called.");
  250. // 获取文本
  251. var text_elements = document.getElementsByClassName("reader-word-layer");
  252. var texts = [];
  253. for (var elem of text_elements){
  254. texts.push(elem.textContent);
  255. }
  256. // 美化后导出文本
  257. var origin_content = texts.join("");
  258. var content = formatText(origin_content);
  259. createAndDownloadFile("纯文本文档.txt", content);
  260. }
  261.  
  262. function savePPTData() {
  263. // 存储ppt型data(内容是pic)
  264. // alert("Function savePPTData was called.");
  265. var pic_elements = document.getElementsByClassName("ppt-image-wrap");
  266. var pic_urls = [];
  267.  
  268. for (var elem of pic_elements) {
  269. var pic_obj = elem.children[0];
  270. var url = pic_obj.src;
  271. pic_urls.push(url);
  272. }
  273. var content = pic_urls.join("\n");
  274. // 启动下载
  275. createAndDownloadFile("urls.csv", content);
  276. }
  277.  
  278. function saveExcelData() {
  279. // 1. 拿到表格
  280. var table_pic = document.getElementsByClassName("reader-pic-item")[0];
  281. var url = table_pic.style.getPropertyValue("background-image");
  282. // 获取图片地址
  283. var pure_url = url.slice(5, -2);
  284.  
  285. // 2. 拿到表格内文字信息
  286. var text_elems = document.getElementsByClassName("reader-word-layer");
  287. var text_list = [];
  288. for (var elem of text_elems) {
  289. text_list.push(elem.textContent);
  290. }
  291. var _text = text_list.join("\n");
  292. // 替换奇怪的空格
  293. var text = _text.replace(/ /g, " ");
  294.  
  295. // 3. 合并至一个字符串,然后导出
  296. var head = "表格图形链接如下(复制到浏览器中打开):";
  297. var content = head + "\n\n" + pure_url + "\n\n" + text;
  298. createAndDownloadFile("图片地址和表格内容.txt", content);
  299. }
  300.  
  301. function saveDocAndPicData() {
  302. // 对于文字和图形混合型的data只能存储其中的纯文字
  303. // alert("Function saveDocAndPicData was called.");
  304. // 获取文本
  305. var text_elements = document.getElementsByClassName("reader-word-layer");
  306. var texts = [];
  307. for (var elem of text_elements){
  308. texts.push(elem.textContent);
  309. }
  310. var origin_content = texts.join("");
  311. // 美化后导出文本
  312. var content = formatText2(origin_content);
  313. createAndDownloadFile("纯文本文档.txt", content);
  314. }
  315.  
  316. function saveTxtData() {
  317. // 存储纯文本到本地
  318. var text_elements = document.getElementsByClassName("p-txt");
  319. var texts = [];
  320. for (var elem of text_elements){
  321. texts.push(elem.textContent);
  322. }
  323. var content = texts.join("");
  324. createAndDownloadFile("纯文本文档.txt", content);
  325. }
  326.  
  327. function saveData() {
  328. // 存储文档数据到本地
  329. var category = detectType();
  330. if (category === "doc-pic-only" ||
  331. category === "pdf-pic-title" ||
  332. category === "pdf-only-pic" ||
  333. category === "excel-only-pic"){
  334. // 对于纯图形文档,都用【图片下载合并器】来处理
  335. savePDFData();
  336. }
  337. else if (category === "doc-only-word" ||
  338. category === "doc-pic-word" ||
  339. category === "pdf-only-word" ||
  340. category === "pdf-pic-word") {
  341. // 对于包含大量文字、且非表格的文档,直接提出纯文本
  342. saveDocData();
  343. }
  344. else if (category === "ppt") {
  345. // ppt按类似于纯图文档的方法处理
  346. savePPTData();
  347. }
  348. else if (category === "excel-only-word") {
  349. // excel仅保存其中的纯文字
  350. saveExcelData();
  351. }
  352. else if (category === "txt") {
  353. // txt直接保存
  354. saveTxtData();
  355. }
  356. else {
  357. var info = [];
  358. for (var key in category){
  359. info.push(key + " : " + category[key]);
  360. }
  361. alert("未知处理类型,请反馈或联系作者:\nallenlv2690@gmail.com\n" + info.join("\n"));
  362. }
  363. }
  364.  
  365. /*
  366. * 主函数部分
  367. */
  368.  
  369. function baiduWenku() {
  370. // 创建脚本启动按钮1、2、3
  371. var btn_1 = document.createElement("button");
  372. var btn_2 = document.createElement("button");
  373.  
  374. // 设定按钮1、2、3样式
  375. btn_1.setAttribute("class", "init-btn");
  376. btn_1.style.height = "25px";
  377. btn_1.style.width = "50%";
  378. btn_1.style.marginLeft = "25%";
  379. btn_1.style.border = "none";
  380. btn_1.style.backgroundColor = "blue";
  381.  
  382. btn_2.setAttribute("class", "save-doc-btn");
  383. btn_2.style.height = "25px";
  384. btn_2.style.width = "50%";
  385. btn_2.style.marginLeft = "25%";
  386. btn_2.style.backgroundColor = "green";
  387. btn_2.style.border = "none";
  388. btn_2.style.display = "none";
  389. btn_2.textContent = "导出纯文本";
  390. btn_2.style.color = "white";
  391. btn_2.style.fontWeight = "bold";
  392.  
  393. // 绑定主函数
  394. btn_1.onclick = readAll;
  395. btn_2.onclick = saveData;
  396.  
  397. // 添加按钮元素到页面
  398. var section = document.createElement("section");
  399. section.setAttribute("class", "btns_section");
  400. section.appendChild(btn_1);
  401. section.appendChild(btn_2);
  402. document.body.appendChild(section);
  403. // 确认主程序加载完毕
  404. console.log("Program Loaded");
  405. }
  406.  
  407. function docin() {
  408. // 创建脚本启动按钮
  409. var btn = document.createElement("button");
  410. // 设定按钮1、2样式
  411. btn.style.height = "25px";
  412. btn.style.width = "50%";
  413. btn.style.marginLeft = "25%";
  414. btn.style.backgroundColor = "green";
  415.  
  416. // 绑定主函数
  417. var printPage = function() {window.print();};
  418. btn.addEventListener("click", printPage);
  419. // 添加按钮元素到页面
  420. document.body.appendChild(btn);
  421. // 确认主程序加载完毕
  422. console.log("Program Loaded");
  423. }
  424.  
  425. function main() {
  426. var host = window.location.host;
  427. if (host === "wenku.baidu.com") {
  428. baiduWenku();
  429. }
  430. else if (host === "www.docin.com") {
  431. docin();
  432. }
  433. else {
  434. console.log("匹配到了无效网页");
  435. }
  436. }
  437.  
  438. main();