Wenku Doc Downloader

下载“百度文库”文档,导出txt或pdf。支持①豆丁网②爱问共享资料(新浪文档)③得力文库④道客巴巴,文档导出pdf。在文档页面最最底部有蓝/绿色长方形按钮,说明脚本生效了。2021/12/17百度文档更新了技术,【脚本对大部分百度文档无效了】,请等待以后更新,抱歉。

目前为 2021-12-26 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name Wenku Doc Downloader
  3. // @namespace http://tampermonkey.net/
  4. // @version 1.3.7
  5. // @description 下载“百度文库”文档,导出txt或pdf。支持①豆丁网②爱问共享资料(新浪文档)③得力文库④道客巴巴,文档导出pdf。在文档页面最最底部有蓝/绿色长方形按钮,说明脚本生效了。2021/12/17百度文档更新了技术,【脚本对大部分百度文档无效了】,请等待以后更新,抱歉。
  6. // @author allenlv2690@gmail.com
  7. // @note 紧急修复了部分豆丁网文档无效的bug。
  8. // @match https://wenku.baidu.com/view/*
  9. // @match https://*.docin.com/p-*
  10. // @match https://ishare.iask.sina.com.cn/f/*
  11. // @match https://www.deliwenku.com/p-*
  12. // @match *://www.doc88.com/p-*
  13. // @match https://wk.baidu.com/view/*
  14. // @require https://cdn.bootcdn.net/ajax/libs/FileSaver.js/2.0.5/FileSaver.js
  15. // @require https://cdn.bootcdn.net/ajax/libs/jszip/3.6.0/jszip.js
  16. // @require https://cdn.bootcdn.net/ajax/libs/jspdf/2.3.1/jspdf.umd.min.js
  17. // @icon https://wenku.baidu.com/favicon.ico
  18. // @grant none
  19. // @license GPL-3.0-only
  20. // @create 2021-11-22
  21. // ==/UserScript==
  22.  
  23. "use strict";
  24.  
  25. let options = {
  26. "fast_mode": false
  27. }
  28.  
  29. const jsPDF = jspdf.jsPDF;
  30.  
  31. /*
  32. * 附属功能函数部分
  33. */
  34.  
  35. /**
  36. * 创建并下载文件
  37. * @param {string} fileName
  38. * @param {string} content
  39. */
  40. function createAndDownloadFile(fileName, content) {
  41. let aTag = document.createElement('a');
  42. let blob = new Blob([content]);
  43. aTag.download = fileName;
  44. aTag.href = URL.createObjectURL(blob);
  45. aTag.click();
  46. URL.revokeObjectURL(blob);
  47. }
  48.  
  49. /**
  50. * 允许打印页面
  51. */
  52. function allowPrint() {
  53. let style = document.createElement("style");
  54. style.innerHTML = `@media print {
  55. body{
  56. display:block;
  57. }
  58. }`;
  59. document.getElementsByTagName("head")[0].appendChild(style);
  60. }
  61.  
  62. /**
  63. * 隐藏按钮,打印页面,显示按钮
  64. */
  65. function hideBtnThenPrint() {
  66. // 隐藏按钮,然后打印页面
  67. let section = document.getElementsByClassName("btns_section")[0];
  68. section.style.display = "none";
  69. window.print();
  70. // 打印结束,显示按钮
  71. section.style.removeProperty("display");
  72. }
  73.  
  74. /**
  75. * 增强按钮(默认为蓝色按钮:展开文档)的点击效果
  76. * @param {String} custom_btn 按钮变量名
  77. */
  78. function enhanceBtnClickReaction(custom_btn = null) {
  79. let aim_btn;
  80. // 如果不使用自定义按钮元素,则默认为使用蓝色展开文档按钮
  81. if (!custom_btn || custom_btn === "btn_1") {
  82. aim_btn = document.getElementsByClassName("init-btn")[0];
  83. } else if (custom_btn === "btn_2") {
  84. aim_btn = document.querySelector(".save-doc-btn");
  85. } else if (custom_btn === "btn_3") {
  86. aim_btn = document.querySelector(".save-html-btn");
  87. console.log(aim_btn);
  88. }
  89.  
  90. let old_color = aim_btn.style.color; // 保存旧的颜色
  91. let old_text = aim_btn.textContent; // 保存旧的文字内容
  92. // 暗红缩小
  93. aim_btn.style.color = "#c90000";
  94. aim_btn.style.fontWeight = "normal";
  95. aim_btn.textContent = `——>[ ${old_text} ]<——`;
  96. // 复原加粗
  97. let changeColor = function() {
  98. aim_btn.style.color = old_color;
  99. aim_btn.style.fontWeight = "bold";
  100. aim_btn.textContent = old_text;
  101. };
  102. setTimeout(changeColor, 1500);
  103. }
  104.  
  105. /**
  106. * 用input框跳转到对应页码
  107. * @param {Element} cur_page 当前页码
  108. * @param {string} aim_page 目标页码
  109. * @param {string} event_type 键盘事件类型:"keyup" | "keypress" | "keydown"
  110. */
  111. function jump2pageNo(cur_page, aim_page, event_type) {
  112. // 设置跳转页码为目标页码
  113. cur_page.value = aim_page;
  114. // 模拟回车事件来跳转
  115. let keyboard_event_enter = new KeyboardEvent(event_type, {
  116. bubbles: true,
  117. cancelable: true,
  118. keyCode: 13
  119. });
  120. cur_page.dispatchEvent(keyboard_event_enter);
  121. }
  122.  
  123. /**
  124. * 滚动到页面底部
  125. */
  126. function scrollToBottom() {
  127. window.scrollTo({
  128. top: document.body.scrollHeight,
  129. behavior: "smooth"
  130. });
  131. }
  132.  
  133. /**
  134. * 用于纯文本文档的文本美化,适用性:百度文库
  135. * @param {string} text
  136. * @returns 美化后的文本
  137. */
  138. function formatText(text) {
  139. let reg_exp_1 = new RegExp(" ?[(]?=[\u4e00-\u9fa5] ?[)]");
  140. let reg_exp_2 = new RegExp("(?<=TEMP[\u4e00-\u9fa5]) ?");
  141.  
  142. let text_1 = text.replace(reg_exp_1, "TEMP");
  143. let text_2 = text_1.replace(reg_exp_2, "");
  144. let text_3 = text_2.replace("TEMP", "");
  145. let text_final = text_3.replace(/ /g, " ");
  146. return text_final;
  147. }
  148.  
  149. /**
  150. * 用于图形文字混合型文档的文本美化,适用性:百度文库
  151. * @param {string} text
  152. * @returns 美化后的文本
  153. */
  154. function formatText2(text) {
  155. let reg_exp = new RegExp("[  ]{2,}");
  156. let content_1 = text.replace(reg_exp, "\n");
  157.  
  158. let content_2 = content_1.replace(/[  ]\n/g, "\n");
  159.  
  160. let reg_exp_2 = new RegExp("\n[   ]*\n*\n");
  161. let content_3 = content_2.replace(reg_exp_2, "\n");
  162.  
  163. let reg_exp_3 = new RegExp(" *\n * ");
  164. let content_4 = content_3.replace(reg_exp_3, "\n");
  165.  
  166. let content_5 = content_4.replace(/[  ]/g, " ");
  167. let final_content = content_5.replace(/[ \n]精选文档[ \n]/g).replace(/\n{2,}/g, "\n");
  168.  
  169. return final_content;
  170. }
  171.  
  172. /**
  173. * 判断文档的详细类型,适用性:百度文库
  174. * @returns 源文档类型-实际内容类型,如: doc-pic-word
  175. */
  176. function detectType() {
  177. // 获取文档类型名称
  178. let file_type, type;
  179. try {
  180. let doc_title_wrap = document.getElementsByClassName("doc-title-wrap")[0];
  181. file_type = doc_title_wrap.children[0].className;
  182. } catch (e) {
  183. alert("请刷新页面以激活该按钮。\n先点击【导出pdf】橙色按钮后该按钮将无法使用。");
  184. return "safe_quit";
  185. }
  186. // 判断文档类型
  187. if (file_type.search("word") !== -1) {
  188. type = "word";
  189. } else if (file_type.search("ppt") !== -1) {
  190. type = "ppt";
  191. } else if (file_type.search("excel") !== -1) {
  192. type = "excel";
  193. } else if (file_type.search("pdf") !== -1) {
  194. type = "pdf";
  195. } else if (file_type.search("txt" !== -1)) {
  196. type = "txt";
  197. } else {
  198. type = file_type;
  199. }
  200. // 分别尝试获取相应元素列表,若列表长度为0则不存在相应元素,否则存在
  201. let pic_nums = document.getElementsByClassName("reader-pic-item").length;
  202. let word_nums = document.getElementsByClassName("reader-word-layer").length;
  203. let ppt_img_nums = document.getElementsByClassName("ppt-image-wrap").length;
  204.  
  205. // 判断文档类型、文字和图片的数量状况
  206. if ((type === "ppt" && ppt_img_nums > 1) || (type === "pdf" && !word_nums && !pic_nums && ppt_img_nums)) {
  207. // ppt: 包含至少2页内容 / 纯ppt图形页面构成
  208. return "ppt";
  209. } else {
  210. return type;
  211. }
  212. }
  213.  
  214. /**
  215. * 用try移除元素
  216. * @param {Element} element 要移除的元素
  217. */
  218. function tryToRemoveElement(element) {
  219. try {
  220. element.remove();
  221. } catch (e) {
  222. console.log();
  223. }
  224. }
  225.  
  226. /**
  227. * 用try移除 [元素列表1, 元素列表2, ...] 中的元素
  228. * @param {Array} elem_list_box 要移除的元素列表构成的列表
  229. */
  230. function tryToRemoveSameElem(elem_list_box) {
  231. for (let elem_list of elem_list_box) {
  232. if (!elem_list) {
  233. continue;
  234. }
  235. for (let elem of elem_list) {
  236. try {
  237. elem.remove();
  238. } catch (e) {
  239. console.log();
  240. }
  241. }
  242. }
  243. }
  244.  
  245. /**
  246. * 使文档在页面上居中
  247. * @param {String} class_name 文档元素的class
  248. * @param {String} default_offset 文档部分向右偏移的百分比(0-59)
  249. * @returns 偏移值是否合法
  250. */
  251. function centerDoc(class_name, default_offset) {
  252. let doc_main = document.getElementsByClassName(class_name)[0];
  253. let offset = window.prompt("请输入偏移百分位:", default_offset);
  254. // 如果输入的数字不在 0-59 内,提醒用户重新设置
  255. if (offset.length === 1 && offset.search(/[0-9]/) !== -1) {
  256. doc_main.style.marginLeft = offset + "%";
  257. return true;
  258. } else if (offset.length === 2 && offset.search(/[1-5][0-9]/) !== -1) {
  259. doc_main.style.marginLeft = offset + "%";
  260. return true
  261. } else {
  262. alert("请输入一个正整数,范围在0至59之间,用来使文档居中\n(不同文档偏移量不同,所以需要手动调整)");
  263. return false;
  264. }
  265. }
  266.  
  267. /**
  268. * 提示文档已经展开,改变按钮形态:隐去蓝色按钮,显示绿色按钮
  269. * @param {Boolean} shrink_btn2 是否缩小绿色按钮
  270. * @param {Boolean} recommend_btn2 是否推荐绿色按钮
  271. * @param {String} btn2_new_text (如果需要)替换绿色按钮的文本为新文本
  272. * @param {Boolean} use_hint 是否提示文档已经展开
  273. */
  274. function transformButtons(shrink_btn2 = false, recommend_btn2 = false, btn2_new_text = "", use_hint = true) {
  275. // 提示文档已经展开
  276. if (use_hint) {
  277. let hint = "文档已经完全展开,可以导出";
  278. alert(hint);
  279. }
  280. // 准备调整按钮,先获取按钮
  281. let btn_1 = document.getElementsByClassName("init-btn")[0];
  282. let btn_2 = document.getElementsByClassName("save-doc-btn")[0];
  283. // 调整按钮显示状况
  284. btn_1.style.display = "none";
  285. btn_2.style.removeProperty("display");
  286. if (shrink_btn2) {
  287. btn_2.style.width = "34.8%";
  288. }
  289. if (btn2_new_text) {
  290. btn_2.textContent = btn2_new_text;
  291. }
  292. if (recommend_btn2) {
  293. btn_2.textContent += "(推荐)";
  294. }
  295. }
  296.  
  297. /*
  298. * 主要功能函数部分
  299. */
  300.  
  301. /**
  302. * 清理并打印得力文库的文档页
  303. */
  304. function printPageDeliwenku() {
  305. // 移除页面上的无关元素
  306. let selector = ".hr-wrap, #readshop, .nav_uis, .bookdesc, #boxright, .QQ_S1, .QQ_S, #outer_page_more, .works-manage-box.shenshu, .works-intro, .mt10.related-pic-box, .mt10.works-comment, .foot_nav, .siteInner";
  307. let elem_list = document.querySelectorAll(selector);
  308. for (let elem of elem_list) {
  309. tryToRemoveElement(elem);
  310. }
  311. // 修改页间距
  312. let outer_pages = document.getElementsByClassName("outer_page");
  313. for (let page of outer_pages) {
  314. page.style.marginBottom = "20px";
  315. }
  316. // 使文档居中
  317. alert("建议使用:\n偏移量: 3\n缩放: 112\n请上下滚动页面,确保每页内容都加载完成以避免空白页\n如果预览时有空白页或文末有绿色按钮,请取消打印重试");
  318. if (!centerDoc("boxleft", "3")) {
  319. return; // 如果输入非法,终止函数调用
  320. }
  321. // 打印文档
  322. hideBtnThenPrint();
  323. }
  324.  
  325. /**
  326. * 清理并打印爱问共享资料的文档页
  327. * @returns 如果输入偏移量非法,返回空值以终止函数
  328. */
  329. function printPageiShare() {
  330. // # 清理并打印爱问共享资料的文档页
  331. // ## 移除页面上无关的元素
  332. // ### 移除单个元素
  333. let topbanner = document.getElementsByClassName("detail-topbanner")[0];
  334. let header = document.getElementsByClassName("new-detail-header")[0];
  335. let fixright = document.getElementById("fix-right");
  336. let redpacket = document.getElementsByClassName("loginRedPacket-dialog")[0];
  337. let fixedrightfull = document.getElementsByClassName("fixed-right-full")[0];
  338. let footer = document.getElementsByClassName("website-footer")[0];
  339. let guess = document.getElementsByClassName("guess-you-like-warpper")[0];
  340. let detailtopbox = document.getElementsByClassName("detail-top-box")[0];
  341. let fullscreen = document.getElementsByClassName("reader-fullScreen")[0];
  342. let endhint = document.getElementsByClassName("endof-trial-reading")[0];
  343. let crumb_arrow;
  344. try { crumb_arrow = document.getElementsByClassName("crumb-arrow")[0].parentElement; } catch (e) { console.log(); }
  345. let copyright = document.getElementsByClassName("copyright-container")[0];
  346. let state_btn = document.getElementsByClassName("state-bottom")[0];
  347. let comments = document.getElementsByClassName("user-comments-wrapper")[0];
  348. // ### 执行移除
  349. let elem_list = [
  350. topbanner,
  351. header,
  352. fixright,
  353. redpacket,
  354. fixedrightfull,
  355. footer,
  356. guess,
  357. detailtopbox,
  358. fullscreen,
  359. endhint,
  360. crumb_arrow,
  361. copyright,
  362. state_btn,
  363. comments
  364. ];
  365. for (let elem of elem_list) {
  366. tryToRemoveElement(elem);
  367. }
  368. // ### 移除全部同类元素
  369. let elem_list_2 = document.querySelectorAll(".tui-detail, .adv-container")
  370. for (let elem_2 of elem_list_2) {
  371. tryToRemoveElement(elem_2);
  372. }
  373. // 使文档居中
  374. alert("建议使用:\n偏移量: 18\n缩放: 默认\n如果预览中有广告,就取消打印\n再点一次按钮,预览中应该就没有广告了");
  375. if (!centerDoc("doc-main", "18")) {
  376. return; // 如果输入非法,终止函数调用
  377. }
  378. // 隐藏按钮,然后打印页面
  379. hideBtnThenPrint();
  380. }
  381.  
  382. /**
  383. * 清理并打百度文库的文档页
  384. * @returns 如果输入偏移量非法,返回空值以终止函数
  385. */
  386. function printPageBaidu() {
  387. // # 清理并打百度文库的文档页
  388. // ## 移除无关页面元素
  389. // ### 要移除的单个元素
  390. let header_wrapper = document.getElementsByClassName("header-wrapper")[0];
  391. let right_wrapper = document.getElementById("right-wrapper-id");
  392. let reader_topbar = document.getElementsByClassName("reader-topbar")[0];
  393. let end_fold_page = document.getElementsByClassName("try-end-fold-page")[0];
  394. let catalog = document.querySelector(".catalog");
  395. let bottom = document.querySelector(".journal");
  396.  
  397. let elem_list = [
  398. header_wrapper,
  399. right_wrapper,
  400. reader_topbar,
  401. end_fold_page,
  402. catalog,
  403. bottom
  404. ]
  405. for (let elem of elem_list) {
  406. tryToRemoveElement(elem);
  407. }
  408. // ### 移除全部同类元素
  409. let lazy_load_list = document.getElementsByClassName("lazy-load");
  410. let no_full_screen_list = document.getElementsByClassName("no-full-screen");
  411. let ads = document.getElementsByClassName("hx-warp");
  412.  
  413. tryToRemoveSameElem([lazy_load_list, ads, no_full_screen_list]);
  414.  
  415. // ## 文档类型测试
  416. // ### 常规文档测试
  417. let normal = Boolean(document.getElementsByClassName("left-wrapper")[0]);
  418. // ### 文献/期刊文档测试
  419. let journal = Boolean(document.getElementById("#journal-view"))
  420. let class_name;
  421. if (normal) {
  422. class_name = "left-wrapper";
  423. } else if (journal) {
  424. class_name = "right-wrapper";
  425. } else {
  426. alert("文档部分元素class不为left-wrapper,且id不为journal-view\n是未知文档类型,无法处理,请联系作者\nQQ: 2690874578\nEmail: allenlv2690@gmail.com");
  427. }
  428.  
  429. // 使文档居中
  430. alert("建议使用:\n偏移量: 0\n缩放: 118%\n请上下滚动页面,确保每页内容都加载完成以避免空白页\n如果预览时有空白页或文末有按钮,请取消打印重试");
  431. if (!centerDoc(class_name, "0")) {
  432. return; // 如果输入非法,退出函数调用
  433. }
  434. // 隐藏按钮,然后打印页面
  435. hideBtnThenPrint();
  436. }
  437.  
  438. function printPageWK() {
  439. // 去水印
  440. document.querySelectorAll("[class*=__wm]").forEach((elem) => {
  441. elem.className += " __web-inspector-hide-shortcut__";
  442. });
  443. // 调整页面边距、圆角、背景色
  444. let pages = document.querySelectorAll(".retype-page");
  445. pages.forEach((page) => {
  446. page.style.borderRadius = "0";
  447. page.style.background = "";
  448. page.style.padding = "0 0 0 0";
  449. });
  450. // 页面间隔调为0
  451. document.querySelectorAll(".gap").forEach((gap) => {
  452. gap.style.height = "0";
  453. });
  454. hideBtnThenPrint();
  455. }
  456.  
  457. /**
  458. * 清理WK页面的无效元素
  459. */
  460. function clearPageWK() {
  461. // 去除元素
  462. let selector = ".vip-cashier-dialog-wrap, .top-card, .college-strong-guide-contain, .reader-pop-manager-view-containter, .middle-box-root";
  463. document.querySelectorAll(selector).forEach((elem) => {
  464. tryToRemoveElement(elem);
  465. });
  466. // 去除父级元素
  467. let child_selector = ".bartop, .barbottom";
  468. document.querySelectorAll(child_selector).forEach((elem) => {
  469. tryToRemoveElement(elem.parentElement);
  470. });
  471. // 前项元素去除
  472. let previous_selector = ".reader-pop-manager-view-containter";
  473. document.querySelectorAll(previous_selector).forEach((elem) => {
  474. tryToRemoveElement(elem.previousElementSibling);
  475. });
  476. }
  477.  
  478. /**
  479. * 创建“打印页面到PDF”按钮
  480. * @param {Function} printPageWebsite
  481. * @returns btn_3元素引用
  482. */
  483. function createPrintPageBtn(printPageWebsite) {
  484. let btn_3 = document.createElement("button");
  485. // 样式设定
  486. btn_3.setAttribute("class", "save-html-btn");
  487. btn_3.style.height = "25px";
  488. btn_3.style.width = "15%";
  489. btn_3.style.marginLeft = "0.2%";
  490. btn_3.style.backgroundColor = "orange";
  491. btn_3.style.border = "none";
  492. btn_3.textContent = "导出pdf";
  493. btn_3.style.color = "black";
  494. btn_3.style.fontWeight = "bold";
  495. btn_3.style.borderRadius = "10%";
  496. btn_3.style.zIndex = "99999";
  497. // 绑定事件,添加到页面上
  498. btn_3.onclick = () => {
  499. enhanceBtnClickReaction("btn_3");
  500. printPageWebsite();
  501. };
  502. let section = document.getElementsByClassName("btns_section")[0];
  503. section.appendChild(btn_3);
  504. return btn_3;
  505. }
  506.  
  507. /**
  508. * 点击“继续阅读”,适用性:得力文库
  509. */
  510. function readAllDeliwenku() {
  511. // 点击“同意并开始预览全文”
  512. let start_btn = document.getElementsByClassName("pre_button")[0];
  513. let display = start_btn.parentElement.parentElement.style.display;
  514. // 如果该按钮显示着,则点击,然后滚动至页面底部,最后终止函数
  515. if (!display) {
  516. start_btn.children[0].click();
  517. setTimeout("scroll(0, document.body.scrollHeight)", 200);
  518. return;
  519. }
  520. // 增强按钮点击效果
  521. enhanceBtnClickReaction();
  522.  
  523. let read_all_btn = document.getElementsByClassName("fc2e")[0];
  524. let display2 = read_all_btn.parentElement.parentElement.style.display
  525. // 继续阅读
  526. if (display2 !== "none") {
  527. // 获取input元素
  528. let cur_page = document.querySelector("#pageNumInput");
  529. let page_old = cur_page.value;
  530. let page_max = cur_page.parentElement.nextElementSibling.textContent.replace(" / ", "");
  531. // 跳转到尾页
  532. jump2pageNo(cur_page, page_max, "keydown");
  533. // 跳转回来
  534. jump2pageNo(cur_page, page_old, "keydown");
  535.  
  536. // 切换按钮准备导出
  537. } else {
  538. // 推荐导出全部图片链接
  539. transformButtons(true, true);
  540. // btn_3 橙色按钮
  541. createPrintPageBtn(printPageDeliwenku);
  542. }
  543. }
  544.  
  545. /**
  546. * 点击“展开继续阅读”,适用性:爱尚共享资料
  547. */
  548. function readAlliShare() {
  549. // 获取“继续阅读”元素
  550. let red_btn = document.getElementsByClassName("red-color")[0];
  551. let red_text = red_btn.textContent;
  552. // 增强按钮点击效果
  553. enhanceBtnClickReaction();
  554. // 如果可以展开,则展开
  555. if (red_text.search("点击可继续阅读") !== -1) {
  556. red_btn.click();
  557. setTimeout(readAlliShare, 1000);
  558. }
  559. // 否则启动按钮2,准备清理页面然后打印为PDF
  560. else {
  561. // 平滑往返页面顶部和底部
  562. window.scrollTo({
  563. top: 0,
  564. behavior: "smooth"
  565. });
  566.  
  567. // 推荐导出全部图片链接
  568. transformButtons(true, true);
  569. // btn_3,橙色按钮
  570. createPrintPageBtn(printPageiShare);
  571.  
  572. // 显示svg图片的链接
  573. let page1 = document.querySelector('[data-num="1"] .data-detail embed');
  574. if (!page1) {
  575. // 如果不存在svg图形,终止后续代码
  576. return;
  577. }
  578. let page2 = document.querySelector('[data-num="2"] .data-detail embed');
  579. let [svg1_src_div, svg2_src_div] = [document.createElement("div"), document.createElement("div")];
  580. svg1_src_div.innerHTML = `<div id="src-1"
  581. style="font-weight: bold;font-size: 20px; height: 100px; width: 100%">
  582. 访问以下链接以复制文字:<br>${page1.src}
  583. </div>`;
  584. svg2_src_div.innerHTML = `<div id="src-1"
  585. style="font-weight: bold;font-size: 20px; height: 100px; width: 100%">
  586. 访问以下链接以复制文字:<br>${page2.src}
  587. </div>`;
  588. // 添加到页面上
  589. page1.parentElement.parentElement.parentElement.append(svg1_src_div);
  590. page2.parentElement.parentElement.parentElement.append(svg2_src_div);
  591. }
  592. }
  593.  
  594.  
  595. /**
  596. * 点击“继续阅读”,适用性:百度文库
  597. * @returns 特殊文档类型不予下载,返回null
  598. */
  599. function readAll() {
  600. // 获取“继续阅读”按钮
  601. let read_all_btn = document.getElementsByClassName("read-all")[0];
  602. // 如果存在“继续阅读”的按钮
  603. if (read_all_btn) {
  604. // 跳转到文末(等同于展开全文)
  605. let cur_page = document.getElementsByClassName("cur-page")[0];
  606. // 取得最大页码
  607. let page_max = cur_page.parentElement.children[2];
  608. // 设置跳转页码为最大页码
  609. cur_page.value = page_max;
  610. // 跳转到尾页
  611. jump2pageNo(cur_page, page_max, "keyup");
  612.  
  613. // 否则认为已经展开了文档,判断文档类型,然后显示对应的按钮
  614. } else {
  615. let type;
  616. try {
  617. // 判断文档类型
  618. type = detectType();
  619. console.log(type);
  620. } catch (e) {
  621. alert("未知/特殊文档类型,例如学术文献,暂不支持下载\n也可与作者反馈或联系:\nallenlv2690@gmail.com");
  622. return null;
  623. }
  624. // 添加 btn_3
  625. createPrintPageBtn(printPageBaidu);
  626. // ppt、pdf有部分文档是纯图片的
  627. if (type === "ppt") {
  628. transformButtons(true, true, "导出全部图片链接");
  629. }
  630. // 其他均为canvas图形
  631. else {
  632. transformButtons(true);
  633. // 禁用按钮
  634. document.querySelector(".save-doc-btn").disabled = true;
  635. document.querySelector(".save-html-btn").disabled = true;
  636. // 监听scroll,捕获canvas
  637. let storeCanvases_Baidu = () => {
  638. let selector = ".creader-canvas";
  639. let id_format = "creader-canvas-@";
  640. storeCanvases(selector, id_format, max_id);
  641. }
  642. window.onscroll = storeCanvases_Baidu;
  643. }
  644. }
  645. }
  646.  
  647. function readAllDoc88() {
  648. // 获取“继续阅读”按钮
  649. let continue_btn = document.querySelector("#continueButton");
  650. // 增强按钮点击效果
  651. enhanceBtnClickReaction();
  652. // 如果存在“继续阅读”按钮
  653. if (continue_btn) {
  654. // 跳转到文末(等同于展开全文)
  655. let cur_page = document.querySelector("#pageNumInput");
  656. // 取得最大页码
  657. let page_max = cur_page.parentElement.textContent.replace(" / ", "");
  658. // 跳转到尾页
  659. jump2pageNo(cur_page, page_max, "keypress");
  660. // 返回顶部
  661. setTimeout(jump2pageNo(cur_page, "1", "keypress"), 1000);
  662. }
  663. // 否则启动按钮2
  664. else {
  665. transformButtons(true, false, "", true);
  666. // 显示btn_3
  667. document.querySelector(".save-html-btn").style.removeProperty("display");
  668. }
  669. }
  670.  
  671. function readAllWK() {
  672. enhanceBtnClickReaction();
  673. let first_open = document.querySelector(".open-arrow");
  674. // 第一次展开文档
  675. if (first_open) {
  676. first_open.click();
  677. setTimeout(readAllWK, 1000);
  678. }
  679. // 非第一次展开文档
  680. else {
  681. let read_all = document.querySelector(".pagerwg-button");
  682. // 如果“继续阅读”按钮不存在或已经被隐藏
  683. if (!read_all || read_all.style.display === "none") {
  684. // 尝试关闭弹窗(如果存在)
  685. try {
  686. functiondocument.querySelector(".btn-cancel[id*=wui]").click();
  687. } catch (e) { console.log("继续阅读按钮引起的弹窗不存在,无需关闭"); }
  688. // 尝试移除底部无关元素
  689. let bottom_part = document.querySelector(".live-broadcast-pop-wrap");
  690. tryToRemoveElement(bottom_part.previousElementSibling);
  691. // 换为按钮2
  692. transformButtons(false, false, "", true);
  693. }
  694. // 否则文档尚未完全展开,继续点击按钮展开文档
  695. else {
  696. setTimeout(() => {
  697. read_all.click();
  698. clearPageWK();
  699. readAllWK();
  700. }, 1000);
  701. }
  702. }
  703. }
  704.  
  705. /**
  706. * 存储非PPT文档的png图形链接,适用性:百度文库
  707. */
  708. function savePDFData() {
  709. let pic_urls = document.getElementsByClassName("reader-pic-item");
  710. let text_list = [];
  711. // 去掉前缀
  712. let reg_exp_1 = new RegExp(": ?url[(]");
  713. // 去掉后缀
  714. let reg_exp_2 = new RegExp("[)]; ?background-position");
  715.  
  716. for (let i = 0; i < pic_urls.length; i++) {
  717. let whole_text = pic_urls[i].getAttribute("style");
  718. let de_pretext = whole_text.split(reg_exp_1)[1];
  719. let url = de_pretext.split(reg_exp_2)[0];
  720. text_list.push(url);
  721. }
  722.  
  723. text_list[0] = text_list[0].replace(/"/g, "");
  724. let content = text_list.join("\n");
  725. // 启动下载
  726. createAndDownloadFile("urls.csv", content);
  727. }
  728.  
  729. /**
  730. * 存储纯文本,适用性:百度文库
  731. */
  732. function saveDocData() {
  733. // 获取文本
  734. let text_elements = document.getElementsByClassName("reader-word-layer");
  735. let texts = [];
  736. for (let elem of text_elements) {
  737. texts.push(elem.textContent);
  738. }
  739. // 美化后导出文本
  740. let origin_content = texts.join("");
  741. let content = formatText(origin_content);
  742. createAndDownloadFile("纯文本文档.txt", content);
  743. }
  744.  
  745. /**
  746. * 存储PPT图像链接,适用性:百度文库
  747. */
  748. function savePPTData() {
  749. let pic_elements = document.getElementsByClassName("ppt-image-wrap");
  750. let pic_urls = [];
  751.  
  752. for (let elem of pic_elements) {
  753. let pic_obj = elem.children[0];
  754. let url = pic_obj.src;
  755. pic_urls.push(url);
  756. }
  757. let content = pic_urls.join("\n");
  758. // 启动下载
  759. createAndDownloadFile("urls.csv", content);
  760. }
  761.  
  762. /**
  763. * 存储文字型表格,适用性:百度文库
  764. */
  765. function saveExcelData() {
  766. // 1. 拿到表格
  767. let table_pic = document.getElementsByClassName("reader-pic-item")[0];
  768. let url = table_pic.style.getPropertyValue("background-image");
  769. // 获取图片地址
  770. let pure_url = url.slice(5, -2);
  771.  
  772. // 2. 拿到表格内文字信息
  773. let text_elems = document.getElementsByClassName("reader-word-layer");
  774. let text_list = [];
  775. for (let elem of text_elems) {
  776. text_list.push(elem.textContent);
  777. }
  778. let _text = text_list.join("\n");
  779. // 替换奇怪的空格
  780. let text = _text.replace(/ /g, " ");
  781.  
  782. // 3. 合并至一个字符串,然后导出
  783. let head = "表格图形链接如下(复制到浏览器中打开):";
  784. let content = head + "\n\n" + pure_url + "\n\n" + text;
  785. createAndDownloadFile("图片地址和表格内容.txt", content);
  786. }
  787.  
  788. /**
  789. * 对于文字和图形混合型的data只能存储其中的纯文字\
  790. * 适用性:百度文库
  791. */
  792. function saveDocAndPicData() {
  793. // 获取文本
  794. let text_elements = document.getElementsByClassName("reader-word-layer");
  795. let texts = [];
  796. for (let elem of text_elements) {
  797. texts.push(elem.textContent);
  798. }
  799. let origin_content = texts.join("");
  800. // 美化后导出文本
  801. let content = formatText2(origin_content);
  802. createAndDownloadFile("纯文本文档.txt", content);
  803. }
  804.  
  805. /**
  806. * 存储纯文本到本地,适用性:百度文库
  807. */
  808. function saveTxtData() {
  809. let text_elements = document.getElementsByClassName("p-txt");
  810. let texts = [];
  811. for (let elem of text_elements) {
  812. texts.push(elem.textContent);
  813. }
  814. let content = texts.join("");
  815. createAndDownloadFile("纯文本文档.txt", content);
  816. }
  817.  
  818. /**
  819. * 按文档类型,用对应方法储存数据到本地,适用性:百度文库
  820. * @returns 特殊情况下返回null,表示安全退出
  821. */
  822. function saveData() {
  823. let type = detectType();
  824. if (type === "ppt") {
  825. // ppt按类似于纯图文档的方法处理
  826. savePPTData();
  827. } else if (type === "safe_quit") {
  828. // 安全退出
  829. return null;
  830. } else {
  831. let data = {};
  832. }
  833. }
  834.  
  835. /**
  836. * 下载全部图片链接,适用性:爱问共享资料、得力文库
  837. * @param {string} selector 图形元素的父级元素
  838. */
  839. function savePicUrls(selector) {
  840. let pages = document.querySelectorAll(selector);
  841. let pic_urls = [];
  842.  
  843. for (let elem of pages) {
  844. let pic_obj = elem.children[0];
  845. let url = pic_obj.src;
  846. pic_urls.push(url);
  847. }
  848. let content = pic_urls.join("\n");
  849. // 启动下载
  850. createAndDownloadFile("urls.csv", content);
  851. }
  852.  
  853. /**
  854. * 存储所有canvas图形为png到一个压缩包
  855. * @param {Array} node_list canvas元素列表
  856. * @param {String} title 文档标题
  857. */
  858. function saveCanvasesToZip(node_list, title) {
  859. // canvas元素转为png图像
  860. // 所有png合并为一个zip压缩包
  861. let zip = new JSZip();
  862. let n = node_list.length;
  863.  
  864. for (let i = 0; i < n; i++) {
  865. let canvas = node_list[i];
  866. let data_base64 = canvas.toDataURL();
  867. let blob = atob(data_base64.split(",")[1]);
  868. zip.file(`page-${i+1}.png`, blob, { binary: true });
  869. }
  870.  
  871. // 导出zip
  872. // promise.then(onCompleted, onRejected);
  873. zip.generateAsync({ type: "blob" }).then(function(content) {
  874. // see filesaver.js
  875. console.log(content);
  876. saveAs(content, `${title}.zip`);
  877. });
  878. }
  879.  
  880. /**
  881. * 将canvas转为jpeg,然后导出pdf
  882. * @param {Array} node_list canvas元素列表
  883. * @param {String} title 文档标题
  884. * @param {Number} quality 图片质量,浮点数,范围 (0,1],默认值0.92
  885. */
  886. function saveCanvasesToPDF(node_list, title, quality = 0.92) {
  887. let first_canvas = node_list[0];
  888. // 如果style的长宽不存在,则直接用canvas的元素长宽
  889. let width_str, height_str;
  890. if (!first_canvas.style.width) {
  891. [width_str, height_str] = [first_canvas.style.width.replace(/(px)|(rem)|(em)/, ""), first_canvas.style.height.replace(/(px)|(rem)|(em)/, "")];
  892. } else {
  893. [width_str, height_str] = [first_canvas.width, first_canvas.height];
  894. }
  895. // jsPDF的第三个参数为format,当自定义时,参数为数字数组。
  896. let [width, height] = [parseFloat(width_str), parseFloat(height_str)];
  897. // 如果文档第一页的宽比长更大,则landscape,否则portrait
  898. let orientation = width > height ? 'l' : 'p';
  899. let pdf = new jsPDF(orientation, 'px', [height, width]);
  900.  
  901. // 保存每一页文档到每一页pdf
  902. node_list.forEach(function(canvas, index) {
  903. pdf.addImage(canvas.toDataURL("image/jpeg", quality), 'JPEG', 0, 0, width, height);
  904. // 如果当前不是文档最后一页,则需要添加下一个空白页
  905. if (index !== node_list.length - 1) {
  906. pdf.addPage();
  907. }
  908. });
  909.  
  910. // 导出文件
  911. pdf.save(`${title}.pdf`);
  912. }
  913.  
  914.  
  915. /**
  916. * 创建两个初始按钮:展开文档、存储文档
  917. * @returns
  918. */
  919. function create2btns() {
  920. // 创建脚本启动按钮1、2
  921. let btn_1 = document.createElement("button");
  922. let btn_2 = document.createElement("button");
  923.  
  924. // 设定按钮1、2样式
  925. btn_1.setAttribute("class", "init-btn");
  926. btn_1.style.height = "25px";
  927. btn_1.style.width = "50%";
  928. btn_1.style.marginLeft = "25%";
  929. btn_1.style.border = "none";
  930. btn_1.style.backgroundColor = "blue";
  931. btn_1.style.color = "white";
  932. btn_1.style.fontWeight = "bold";
  933. btn_1.textContent = "展开文档";
  934. btn_1.style.zIndex = "99999";
  935.  
  936. btn_2.setAttribute("class", "save-doc-btn");
  937. btn_2.style.height = "25px";
  938. btn_2.style.width = "50%";
  939. btn_2.style.marginLeft = "25%";
  940. btn_2.style.backgroundColor = "green";
  941. btn_2.style.border = "none";
  942. btn_2.style.display = "none";
  943. btn_2.style.color = "white";
  944. btn_2.style.fontWeight = "bold";
  945. btn_2.style.zIndex = "99999";
  946.  
  947. // 添加按钮元素到页面
  948. let section = document.createElement("section");
  949. section.setAttribute("class", "btns_section");
  950. section.appendChild(btn_1);
  951. section.appendChild(btn_2);
  952. document.body.appendChild(section);
  953. // 返回元素引用
  954. return [btn_1, btn_2]
  955. }
  956.  
  957. /*
  958. * 主函数部分
  959. */
  960.  
  961. /**
  962. * 百度文库文档下载策略
  963. */
  964. function baiduWenku() {
  965. // 创建脚本启动按钮1、2
  966. let [btn_1, btn_2] = create2btns();
  967. btn_2.textContent = "导出全部图片";
  968.  
  969. // 绑定主函数
  970. btn_1.onclick = () => {
  971. enhanceBtnClickReaction();
  972. readAll();
  973. };
  974. btn_2.onclick = () => {
  975. enhanceBtnClickReaction("btn_2");
  976. saveData();
  977. };
  978.  
  979. // 解除打印限制
  980. allowPrint();
  981. }
  982.  
  983. /**
  984. * 豆丁文档下载策略
  985. */
  986. function docin() {
  987. // 创建脚本启动按钮
  988. let [btn_1, btn_2] = create2btns();
  989. btn_2.textContent = "导出全部图片";
  990.  
  991. // 隐藏底部工具栏
  992. document.querySelector("#j_select").click(); // 选择指针
  993. let tool_bar = document.querySelector(".reader_tools_bar_wrap.tools_bar_small.clear");
  994. tool_bar.style.display = "none";
  995.  
  996. // 绑定主函数
  997. let getCanvasList = function() {
  998. // 获取全部canvas元素,用于传递canvas元素列表给 btn_2 和 btn_3
  999. let parent_node_list = document.querySelectorAll(".hkswf-content");
  1000. let node_list = [];
  1001. for (let node of parent_node_list) {
  1002. node_list.push(node.firstElementChild);
  1003. }
  1004. return node_list;
  1005. };
  1006.  
  1007. let prepare = function() {
  1008. // 获取canvas元素列表
  1009. let node_list = getCanvasList();
  1010. // 获取文档标题
  1011. let title;
  1012. if (document.querySelector("h1 [title=doc]")) {
  1013. title = document.querySelector("h1 [title=doc]").nextElementSibling.textContent;
  1014. } else if (document.querySelector(".doc_title")) {
  1015. title = document.querySelector(".doc_title").textContent;
  1016. } else {
  1017. title = "文档";
  1018. }
  1019. // 根据页数决定图形质量
  1020. let quality, page_num_str, page_num;
  1021. try {
  1022. page_num_str = document.querySelectorAll(".info_txt")[1].children[0].textContent;}
  1023. catch {page_num_str = "26";}
  1024. page_num = parseInt(page_num_str);
  1025.  
  1026. if (page_num <= 25) {
  1027. quality = 1.0;
  1028. } else if (25 < page_num <= 50) {
  1029. quality = 0.85;
  1030. } else {
  1031. quality = 0.7;
  1032. }
  1033.  
  1034. return [node_list, title, quality];
  1035. }
  1036.  
  1037. // 判断是否有canvas元素
  1038. let detectCanvas = function() {
  1039. let btn_2 = document.querySelector(".save-doc-btn");
  1040. let haveCanvas = getCanvasList().length === 0 ? false : true;
  1041. // 如果没有canvas元素,则认为文档页面由外链图片构成
  1042. if (!haveCanvas) {
  1043. // 调整按钮显示
  1044. transformButtons(false, false, "导出全部图片链接", false);
  1045. // 绑定主函数
  1046. btn_2.onclick = function() {
  1047. enhanceBtnClickReaction("btn_2");
  1048. if (confirm("确定每页内容都加载完成了吗?")) {
  1049. savePicUrls("[id*=img_]");
  1050. }
  1051. }
  1052. } else {
  1053. // 调整按钮显示
  1054. transformButtons(true, false, "", false);
  1055. // btn_2: 导出zip
  1056. btn_2.onclick = function() {
  1057. enhanceBtnClickReaction("btn_2");
  1058. if (confirm("确定每页内容都加载完成了吗?")) {
  1059. saveCanvasesToZip(...prepare());
  1060. }
  1061. };
  1062. // btn_3: 导出pdf
  1063. createPrintPageBtn(function() {
  1064. if (confirm("确定每页内容都加载完成了吗?")) {
  1065. saveCanvasesToPDF(...prepare());
  1066. }
  1067. });
  1068. }
  1069. }
  1070. btn_1.textContent = "判断文档类型";
  1071. btn_1.onclick = detectCanvas;
  1072. }
  1073.  
  1074. /**
  1075. * 爱问共享资料文档下载策略
  1076. */
  1077. function ishare() {
  1078. // 创建脚本启动按钮1、2
  1079. let [btn_1, btn_2] = create2btns();
  1080. btn_2.textContent = "导出全部图片链接";
  1081.  
  1082. // 绑定主函数
  1083. btn_1.onclick = readAlliShare;
  1084. btn_2.onclick = function() { savePicUrls(".data-detail"); };
  1085.  
  1086. // 移除底部下载条
  1087. let detailfixed = document.getElementsByClassName("detail-fixed")[0];
  1088. detailfixed.remove();
  1089. }
  1090.  
  1091. /**
  1092. * 得力文库文档下载策略
  1093. */
  1094. function deliwenku() {
  1095. // 创建脚本启动按钮1、2
  1096. let [btn_1, btn_2] = create2btns();
  1097. btn_2.textContent = "导出全部图片链接";
  1098.  
  1099. // 绑定主函数
  1100. btn_1.onclick = readAllDeliwenku;
  1101. btn_2.onclick = function() { savePicUrls('.inner_page div'); };
  1102.  
  1103. // 尝试关闭页面弹窗
  1104. try { document.querySelector("div[title=点击关闭]").click(); } catch (e) { console.log(0); }
  1105. // 解除打印限制
  1106. allowPrint();
  1107. }
  1108.  
  1109. /**
  1110. * 道客巴巴文档下载策略
  1111. */
  1112. function doc88() {
  1113. // 创建脚本启动按钮1、2
  1114. let [btn_1, btn_2] = create2btns();
  1115. btn_2.textContent = "导出全部图片";
  1116.  
  1117. // 绑定主函数
  1118. let prepare = function() {
  1119. // 获取canvas元素列表
  1120. let node_list = document.querySelectorAll(".inner_page");
  1121. // 获取文档标题
  1122. let title;
  1123. if (document.querySelector(".doctopic h1")) {
  1124. title = document.querySelector(".doctopic h1").title;
  1125. } else {
  1126. title = "文档";
  1127. }
  1128. // 根据页数决定图形质量
  1129. let quality, page_num_str, page_num;
  1130. page_num_str = document.querySelector("#pageNumInput").parentElement.textContent.replace(" / ", "");
  1131. page_num = parseInt(page_num_str);
  1132.  
  1133. if (page_num <= 25) {
  1134. quality = 1.0;
  1135. } else if (25 < page_num <= 50) {
  1136. quality = 0.85;
  1137. } else {
  1138. quality = 0.7;
  1139. }
  1140.  
  1141. return [node_list, title, quality];
  1142. }
  1143.  
  1144. // btn_1: 展开文档
  1145. btn_1.onclick = readAllDoc88;
  1146. // btn_2: 导出zip
  1147. btn_2.onclick = function() {
  1148. enhanceBtnClickReaction("btn_2");
  1149. if (confirm("确定每页内容都加载完成了吗?")) {
  1150. saveCanvasesToZip(...prepare());
  1151. }
  1152. };
  1153. // btn_3: 导出pdf
  1154. let btn_3 = createPrintPageBtn(function() {
  1155. if (confirm("确定每页内容都加载完成了吗?")) {
  1156. saveCanvasesToPDF(...prepare());
  1157. }
  1158. });
  1159. btn_3.style.display = "none";
  1160. }
  1161.  
  1162. /**
  1163. * wk文档下载策略
  1164. */
  1165. function baiduWenkuMobile() {
  1166. // 创建初始按钮
  1167. clearPageWK();
  1168. let [btn_1, btn_2] = create2btns();
  1169. btn_2.textContent = "打印页面到PDF";
  1170. // 绑定主函数
  1171. btn_1.onclick = readAllWK;
  1172. btn_2.onclick = () => {
  1173. enhanceBtnClickReaction("btn_2");
  1174. printPageWK();
  1175. }
  1176. }
  1177.  
  1178. /**
  1179. * 主函数:识别网站,执行对应文档下载策略
  1180. */
  1181. function main() {
  1182. let host = window.location.host;
  1183. if (host === "wenku.baidu.com") {
  1184. baiduWenku();
  1185. } else if (host === "wk.baidu.com") {
  1186. baiduWenkuMobile();
  1187. } else if (host.includes("docin.com")) {
  1188. docin();
  1189. } else if (host === "ishare.iask.sina.com.cn") {
  1190. ishare();
  1191. } else if (host === "www.deliwenku.com") {
  1192. deliwenku();
  1193. } else if (host === "www.doc88.com") {
  1194. doc88();
  1195. } else {
  1196. console.log("匹配到了无效网页");
  1197. }
  1198. }
  1199.  
  1200. if (options["fast_mode"]) {
  1201. main();
  1202. } else {
  1203. window.onload = main;
  1204. }