自动无缝翻页

自动无缝翻页,目前支持:423Down、Apphot、不死鸟、小众软件、异次元软件、三国杀论坛、PubMed

当前为 2021-03-31 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name 自动无缝翻页
  3. // @version 1.1.6
  4. // @author X.I.U
  5. // @description 自动无缝翻页,目前支持:423Down、Apphot、不死鸟、小众软件、异次元软件、三国杀论坛、PubMed
  6. // @match *://www.423down.com/*
  7. // @exclude *://www.423down.com/*.html
  8. // @match *://apphot.cc/*
  9. // @exclude *://apphot.cc/*.html
  10. // @match *://www.appinn.com/
  11. // @match *://www.appinn.com/*/*/
  12. // @match *://www.appinn.com/?s=*
  13. // @match *://pubmed.ncbi.nlm.nih.gov/?term=*
  14. // @match *://club.sanguosha.com/*
  15. // @match *://www.iplaysoft.com/*
  16. // @match *://iao.su/*
  17. // @icon https://i.loli.net/2021/03/07/rdijeYm83pznxWq.png
  18. // @grant GM_xmlhttpRequest
  19. // @grant GM_registerMenuCommand
  20. // @grant GM_openInTab
  21. // @license GPL-3.0 License
  22. // @run-at document-end
  23. // @namespace https://github.com/XIU2/UserScript
  24. // ==/UserScript==
  25.  
  26. (function() {
  27. // 注册脚本菜单
  28. GM_registerMenuCommand('反馈 & 欢迎申请支持', function () {window.GM_openInTab('https://github.com/XIU2/UserScript#xiu2userscript', {active: true,insert: true,setParent: true});window.GM_openInTab('https://greasyfork.org/zh-CN/scripts/419215/feedback', {active: true,insert: true,setParent: true});});
  29.  
  30. // 默认 ID 为 0
  31. var curSite = {SiteTypeID: 0};
  32.  
  33. // 自动翻页规则
  34. // type:1 = 脚本实现自动无缝翻页,2 = 网站自带了自动无缝翻页功能,只需要点击下一页按钮即可,这时 nextText 为按钮文本,避免一瞬间加载太多次下一页
  35. // HT_insert:1 = 插入该元素本身的前面;2 = 插入该元素当中,第一个子元素前面;3 = 插入该元素当中,最后一个子元素后面;4 = 插入该元素本身的后面;
  36. // scrollDelta:数值越大,滚动条触发点越靠上(越早开始翻页)
  37. // beforeFunction = 插入前执行函数;afterFunction = 插入后执行函数
  38. let DBSite = {
  39. _423down_postslist: {
  40. SiteTypeID: 1,
  41. pager: {
  42. type: 1,
  43. nextLink: '//div[@class="paging"]//a[contains(text(),"下一页")][@href]',
  44. pageElement: 'css;div.content-wrap ul.excerpt > li',
  45. HT_insert: ['css;div.content-wrap ul.excerpt', 3],
  46. replaceE: 'css;div.paging',
  47. scrollDelta: 1500
  48. }
  49. },
  50. apphot_postslist: {
  51. SiteTypeID: 2,
  52. pager: {
  53. type: 1,
  54. nextLink: '//div[@class="pagination"]//a[contains(text(),"下一页")][@href]',
  55. pageElement: 'css;div.content > article.excerpt',
  56. HT_insert: ['css;div.pagination', 1],
  57. replaceE: 'css;div.pagination',
  58. scrollDelta: 1500
  59. }
  60. },
  61. iao_su_postslist: {
  62. SiteTypeID: 3,
  63. pager: {
  64. type: 1,
  65. nextLink: '//li[@class="btn btn-primary next"]//a[@href]',
  66. pageElement: 'css;#index > article, #archive > article',
  67. HT_insert: ['css;ol.page-navigator', 1],
  68. replaceE: 'css;ol.page-navigator',
  69. scrollDelta: 800,
  70. beforeFunction: iao_su_postslist_beforeFunction
  71. }
  72. },
  73. appinn_postslist: {
  74. SiteTypeID: 4,
  75. pager: {
  76. type: 1,
  77. nextLink: '//a[@class="next page-numbers"][@href]',
  78. pageElement: 'css;section#latest-posts > article',
  79. HT_insert: ['css;nav.navigation.pagination', 1],
  80. replaceE: 'css;div.nav-links',
  81. scrollDelta: 1500
  82. }
  83. },
  84. iplaysoft_postslist: {
  85. SiteTypeID: 5,
  86. pager: {
  87. type: 1,
  88. nextLink: '//div[@class="pagenavi"]//a[@title="下一页"][@href]',
  89. pageElement: 'css;#postlist > div.entry',
  90. HT_insert: ['css;#postlist > .pagenavi-button', 1],
  91. replaceE: 'css;.pagenavi-button, .pagenavi',
  92. scrollDelta: 1200,
  93. beforeFunction: iplaysoft_postslist_beforeFunction
  94. }
  95. },
  96. iplaysoft_postcomments: {
  97. SiteTypeID: 6,
  98. pager: {
  99. type: 2,
  100. nextLink: '#loadHistoryComments',
  101. nextText: '展开后面',
  102. scrollDelta: 1200
  103. }
  104. },
  105. sanguosha_forum: {
  106. SiteTypeID: 7,
  107. pager: {
  108. type: 2,
  109. nextLink: '#autopbn',
  110. nextText: '下一页 »',
  111. scrollDelta: 800
  112. }
  113. },
  114. sanguosha_thread: {
  115. SiteTypeID: 8,
  116. pager: {
  117. type: 1,
  118. nextLink: '//a[@class="nxt"][@href]',
  119. pageElement: 'css;div#postlist > div[id^="post_"]',
  120. HT_insert: ['css;div#postlist', 3],
  121. replaceE: 'css;div.pg',
  122. scrollDelta: 800
  123. }
  124. },
  125. sanguosha_search: {
  126. SiteTypeID: 9,
  127. pager: {
  128. type: 1,
  129. nextLink: '//a[@class="nxt"][@href]',
  130. pageElement: 'css;div#threadlist > ul',
  131. HT_insert: ['css;div#threadlist', 3],
  132. replaceE: 'css;div.pg',
  133. scrollDelta: 800
  134. }
  135. },
  136. pubmed_postslist: {
  137. SiteTypeID: 10,
  138. pager: {
  139. type: 2,
  140. nextLink: 'button.load-button.next-page',
  141. nextText: 'Show more',
  142. scrollDelta: 1500
  143. }
  144. }
  145. };
  146.  
  147.  
  148. switch (location.host) {
  149. case "www.423down.com":
  150. curSite = DBSite._423down_postslist;
  151. break;
  152. case "apphot.cc":
  153. curSite = DBSite.apphot_postslist;
  154. break;
  155. case "www.appinn.com":
  156. curSite = DBSite.appinn_postslist;
  157. break;
  158. case "pubmed.ncbi.nlm.nih.gov":
  159. curSite = DBSite.pubmed_postslist;
  160. break;
  161. case "club.sanguosha.com":
  162. if(location.pathname.indexOf("forum") > -1){ // 各版块帖子列表
  163. curSite = DBSite.sanguosha_forum;
  164. }else if(location.pathname.indexOf("thread") > -1){ // 帖子内
  165. curSite = DBSite.sanguosha_thread;
  166. hidePgbtn(); // 隐藏帖子内的 [下一页] 按钮
  167. }else if(location.pathname.indexOf("search") > -1){ // 搜索结果
  168. curSite = DBSite.sanguosha_search;
  169. }
  170. break;
  171. case "www.iplaysoft.com":
  172. if(location.pathname.indexOf(".html") > -1 || location.pathname.indexOf("/p/") > -1){ // 文章内
  173. curSite = DBSite.iplaysoft_postcomments;
  174. }else{ // 其他页面
  175. curSite = DBSite.iplaysoft_postslist;
  176. }
  177. break;
  178. case "iao.su":
  179. curSite = DBSite.iao_su_postslist;
  180. break;
  181. }
  182. curSite.pageUrl = ""; // 下一页URL
  183. pageLoading(); // 自动无缝翻页
  184.  
  185.  
  186. // 自动无缝翻页
  187. function pageLoading() {
  188. if (curSite.SiteTypeID > 0){
  189. windowScroll(function (direction, e) {
  190. if (direction === "down") { // 下滑才准备翻页
  191. let scrollTop = document.documentElement.scrollTop || window.pageYOffset || document.body.scrollTop;
  192. let scrollDelta = curSite.pager.scrollDelta;
  193. if (document.documentElement.scrollHeight <= document.documentElement.clientHeight + scrollTop + scrollDelta) {
  194. if (curSite.pager.type === 1) {
  195. ShowPager.loadMorePage();
  196. }else{
  197. let autopbn = document.querySelector(curSite.pager.nextLink);
  198. if (autopbn){ // 如果正在加载,就不再点击
  199. if (!curSite.pager.nextText){ // 如果没有指定 nextText 就直接点击
  200. autopbn.click();
  201. }else if (autopbn.innerText.indexOf(curSite.pager.nextText) > -1){ // 如果指定了 nextText 就需要判断后再点击(避免已经在加载了,还重复点击)
  202. autopbn.click();
  203. }
  204. }
  205. }
  206. }
  207. }
  208. });
  209. }
  210. }
  211.  
  212.  
  213. // 隐藏帖子内的 [下一页] 按钮
  214. function hidePgbtn(){
  215. let style_hidePgbtn = document.createElement('style');
  216. style_hidePgbtn.innerHTML = `.pgbtn {display: none;}`;
  217. document.head.appendChild(style_hidePgbtn);
  218. }
  219.  
  220.  
  221. // iplaysoft 的插入前函数
  222. function iplaysoft_postslist_beforeFunction(pageElems) {
  223. pageElems.forEach(function (one) {
  224. let now = one.querySelector("img.lazyload")
  225. if (now && !now.getAttribute('src')) {
  226. now.setAttribute("src",now.getAttribute('data-src'))
  227. now.setAttribute("srcset",now.getAttribute('data-src'))
  228. now.setAttribute("class","lazyloaded")
  229. }
  230. });
  231. return pageElems
  232. }
  233.  
  234.  
  235. // iao.su 的插入前函数
  236. function iao_su_postslist_beforeFunction(pageElems) {
  237. pageElems.forEach(function (one) {
  238. let now = one.getElementsByClassName("post-card")[0]
  239. if (now) {
  240. now.getElementsByClassName("blog-background")[0].style.backgroundImage = 'url("' + RegExp("(?<=loadBannerDirect\\(').*(?=', '',)").exec(now.getElementsByTagName("script")[0].innerText)[0]; + '")';
  241. }
  242. });
  243. return pageElems
  244. }
  245.  
  246.  
  247. // 滚动条事件
  248. function windowScroll(fn1) {
  249. var beforeScrollTop = document.documentElement.scrollTop,
  250. fn = fn1 || function () {};
  251. setTimeout(function () { // 延时执行,避免刚载入到页面就触发翻页事件
  252. window.addEventListener("scroll", function (e) {
  253. var afterScrollTop = document.documentElement.scrollTop,
  254. delta = afterScrollTop - beforeScrollTop;
  255. if (delta == 0) return false;
  256. fn(delta > 0 ? "down" : "up", e);
  257. beforeScrollTop = afterScrollTop;
  258. }, false);
  259. }, 1000)
  260. }
  261.  
  262.  
  263. var ShowPager = { // 修改自 https://greasyfork.org/scripts/14178
  264. getFullHref: function (e) {
  265. if(e == null) return '';
  266. "string" != typeof e && (e = e.getAttribute("href"));
  267. var t = this.getFullHref.a;
  268. return t || (this.getFullHref.a = t = document.createElement("a")), t.href = e, t.href;
  269. },
  270. createDocumentByString: function (e) {
  271. if (e) {
  272. if ("HTML" !== document.documentElement.nodeName) return (new DOMParser).parseFromString(e, "application/xhtml+xml");
  273. var t;
  274. try {
  275. t = (new DOMParser).parseFromString(e, "text/html");
  276. } catch (e) {
  277. }
  278. if (t) return t;
  279. if (document.implementation.createHTMLDocument) t = document.implementation.createHTMLDocument("ADocument"); else try {
  280. (t = document.cloneNode(!1)).appendChild(t.importNode(document.documentElement, !1)),
  281. t.documentElement.appendChild(t.createElement("head")), t.documentElement.appendChild(t.createElement("body"));
  282. } catch (e) {
  283. }
  284. if (t) {
  285. var r = document.createRange();
  286. r.selectNodeContents(document.body);
  287. var n = r.createContextualFragment(e);
  288. t.body.appendChild(n);
  289. for (var a, o = {
  290. TITLE: !0,
  291. META: !0,
  292. LINK: !0,
  293. STYLE: !0,
  294. BASE: !0
  295. }, i = t.body, s = i.childNodes, c = s.length - 1; c >= 0; c--) o[(a = s[c]).nodeName] && i.removeChild(a);
  296. return t;
  297. }
  298. } else console.error("没有找到要转成DOM的字符串");
  299. },
  300. loadMorePage: function () {
  301. if (curSite.pager) {
  302. let curPageEle = getElementByXpath(curSite.pager.nextLink);
  303. var url = this.getFullHref(curPageEle);
  304. console.log(`${url} ${curPageEle} ${curSite.pageUrl}`);
  305. if(url === '') return;
  306. if(curSite.pageUrl === url) return;// 避免重复加载相同的页面
  307. curSite.pageUrl = url;
  308. // 读取下一页的数据
  309. curSite.pager.startFilter && curSite.pager.startFilter();
  310. GM_xmlhttpRequest({
  311. url: url,
  312. method: "GET",
  313. timeout: 5000,
  314. onload: function (response) {
  315. try {
  316. //console.log(`${response.responseText}`)
  317. var newBody = ShowPager.createDocumentByString(response.responseText);
  318. let pageElems = getAllElements(curSite.pager.pageElement, newBody, newBody);
  319. let toElement = getAllElements(curSite.pager.HT_insert[0])[0];
  320. if (pageElems.length >= 0) {
  321. // 如果有插入前函数就执行函数
  322. if (curSite.pager.beforeFunction)pageElems = curSite.pager.beforeFunction(pageElems);
  323. // 插入位置
  324. let addTo;
  325. switch (curSite.pager.HT_insert[1]) {
  326. case 1:
  327. addTo = "beforebegin"
  328. break;
  329. case 2:
  330. addTo = "afterbegin"
  331. break;
  332. case 3:
  333. addTo = "beforeend"
  334. break;
  335. case 4:
  336. addTo = "afterend"
  337. break;
  338. }
  339. // 插入新页面元素
  340. pageElems.forEach(function (one) {
  341. toElement.insertAdjacentElement(addTo, one);
  342. });
  343. // 替换待替换元素
  344. try {
  345. let oriE = getAllElements(curSite.pager.replaceE);
  346. let repE = getAllElements(curSite.pager.replaceE, newBody, newBody);
  347. if (oriE.length === repE.length) {
  348. for (var i = 0; i < oriE.length; i++) {
  349. oriE[i].outerHTML = repE[i].outerHTML;
  350. }
  351. }
  352. } catch (e) {
  353. console.log(e);
  354. }
  355. // 如果有插入后函数就执行函数
  356. if (curSite.pager.afterFunction)curSite.pager.afterFunction();
  357. }
  358. } catch (e) {
  359. console.log(e);
  360. }
  361. }
  362. });
  363. }
  364. },
  365. };
  366.  
  367.  
  368. function getElementByXpath(e, t, r) {
  369. r = r || document, t = t || r;
  370. try {
  371. return r.evaluate(e, t, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
  372. } catch (t) {
  373. return void console.error("无效的xpath");
  374. }
  375. }
  376.  
  377.  
  378. function getAllElements(e, t, r, n, o) {
  379. let getAllElementsByXpath = function(e, t, r) {
  380. return r = r || document, t = t || r, r.evaluate(e, t, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
  381. }
  382.  
  383. var i, s = [];
  384. if (!e) return s;
  385. if (r = r || document, n = n || window, o = o || void 0, t = t || r, "string" == typeof e) i = 0 === e.search(/^css;/i) ? function getAllElementsByCSS(e, t) {
  386. return (t || document).querySelectorAll(e);
  387. }(e.slice(4), t) : getAllElementsByXpath(e, t, r); else {
  388. if (!(i = e(r, n, o))) return s;
  389. if (i.nodeType) return s[0] = i, s;
  390. }
  391. return function makeArray(e) {
  392. var t, r, n, o = [];
  393. if (e.pop) {
  394. for (t = 0, r = e.length; t < r; t++) (n = e[t]) && (n.nodeType ? o.push(n) : o = o.concat(makeArray(n)));
  395. return a()(o);
  396. }
  397. if (e.item) {
  398. for (t = e.length; t;) o[--t] = e[t];
  399. return o;
  400. }
  401. if (e.iterateNext) {
  402. for (t = e.snapshotLength; t;) o[--t] = e.snapshotItem(t);
  403. return o;
  404. }
  405. }(i);
  406. }
  407. })();