Wenku Doc Downloader

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

当前为 2021-12-21 提交的版本,查看 最新版本

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

QingJ © 2025

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