// ==UserScript== // @name 起点小说vip自动换源 // @namespace http://tampermonkey.net/ // @version 2.3.0 // @description 对起点的vip小说自动搜索笔趣阁,要设置滚动模式,自动加载形式,支持手机和网页 // @author JucyZhu // @match https://m.qidian.com/book/* // @match https://read.qidian.com/chapter/* // @match https://vipreader.qidian.com/chapter/* // @require https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.min.js // @connect www.xbiquge.la // @connect www.42zw.com // @connect www.shuquge.com // @connect www.20xs.cc // @connect www.bqg999.cc // @run-at document-end // @grant GM_xmlhttpRequest // @downloadURL none // ==/UserScript== (function() { 'use strict'; // Your code here... // 声明全局参数 // ===== 自定义站点规则 ===== const sites = [ // 详细版规则示例。注:该网站已无法访问。 { //基本信息 siteName: "999笔趣阁", URL: "https://www.bqg999.cc/", urlUpdata:false, searchURL: "https://www.bqg999.cc/s.php?q=", method: "GET", formData: "q=", needName: true, bookName: ".bookname a", authorName: ".author", chapters: ".listmain dd a", contentSplit: "4space", //章节分段方式 content: "#content", replace: "", slice:[0,-1] }, { //基本信息 siteName: "书趣阁", URL: "http://www.shuquge.com/", urlUpdata:true, searchURL: "http://www.shuquge.com/search.php", method: "POST", formData: "s=6445266503022880974&searchkey=", needName: false, bookName: ".bookname a", authorName: ".author", chapters: ".listmain dl a:gt(11)", contentSplit: "\n\n", //章节分段方式 content: "#content", replace: "", slice:[0,-1] }, { //基本信息 siteName: "新笔趣云", URL: "https://www.20xs.cc", urlUpdata:false, searchURL: "https://www.znlzd.com/ar.php?keyWord=", method: "GET", formData: "keyWord=", needName: true, bookName: "strong a", authorName: "#li1 .name span:odd", chapters: "#list dl a", contentSplit: "node", //章节分段方式 content: "#content p", replace: "", slice:[] }, { //基本信息 siteName: "新笔趣阁", // 站点名字... (可选) URL: "http://www.xbiquge.la", urlUpdata:false, searchURL: "http://www.xbiquge.la/modules/article/waps.php", //搜索相关 method: "POST", //请求方式 formData: "searchkey=", //请求数据 needName: false, bookName: "tbody tr .even a", //解析搜索结果相关 authorName: "tbody tr .even:odd", chapters: "#list dl a", //解析目录列表相关 contentSplit: "\n\n", //章节分段方式 content: "#content", //解析章节内容 replace: "#content p", slice:[0,-1] } ]; var data = { book: {}, currentChapter: {}, chapters: [], isApp: false }, chapterNum = 0, first_time = true, lock = true, siteId = 0, show = false; //脚本更新data获取书籍信息 getBookContents(); //滚动事件 $(window).scroll(function() { if (data.isApp) { show = ($(".read-article .read-section").last().offset().top - $(document).scrollTop() < $( window).height()); } else { show = ($("#j_chapterBox .text-wrap:last").offset().top - $(document).scrollTop() < $(window) .height()); } if (($(window).scrollTop() * 1.05 > $(document).height() - $(window).height()) && show && lock) { lock = false; loadbook(); } }); function loadbook() { updata(); //更新data if (chapterNum > 3) { if (data.isApp) { $(".read-section").first().remove() chapterNum = $(".read-section").length } else { $(".text-wrap").first().remove() chapterNum = $(".text-wrap").length } } if (data.currentChapter.vipState == 1 && chapterNum <= 3) { updataCurrentChapter(data.currentChapter.id + 1) //console.log(data.currentChapter) insertCurrentVipContents(); } }; //根据当前章节名称,如果是vip则从笔趣阁获取内容资源并插入 function insertCurrentVipContents() { //根据找到的当前章节url获取说内容并解析 console.log("------正在请求《" + data.currentChapter.chapterName + "》的内容------") var href = data.currentChapter.href; GM_xmlhttpRequest({ method: "GET", url: href, headers: { "Content-Type": "application/x-www-form-urlencoded;charset=utf-8" }, data: "", onload: function(response) { //console.log("请求成功"); insertChapter(parseContent(response.responseText)) }, onerror: function(response) { console.log("请求失败: href: " + href); } }) }; //更新vip状态和显示章节数 function updata() { //有没有到vipstate==0 &&(登录按钮,订阅按钮长度大于0(有.btn-primary) //或者.j_subscribeBtn vip章节全部订阅和订阅本章按钮长度大于0 //或者发现.w-all前往vip章节长度大于0) if (data.currentChapter.vipState == 0) { if ($(".btn-primary").length > 0) { //手机版 data.currentChapter.vipState = 1; $(".btn-primary").remove(); findLastChapter() } else if ($(".j_subscribeBtn").length > 0 || $(".w-all").length > 0) { //网页版 data.currentChapter.vipState = 1; findLastChapter() //updataCurrentChapter(data.currentChapter.id + 1) $(".w-all").remove(); } //findLastChapter() } else if (data.currentChapter.vipState == 1) { if (data.isApp) { chapterNum = $(".read-section").length //findLastChapter() } else { chapterNum = $(".text-wrap").length //findLastChapter() } } }; function getBookChapterInfor() { if (typeof(g_data.book) == "undefined") { //说明是电脑网页端 data.book.bookName = g_data.bookInfo.bookName; data.book.authorName = g_data.bookInfo.authorName; data.currentChapter.chapterName = $(".j_chapterName:last span:first").text(); data.currentChapter.vipState = g_data.chapter.vipStatus; data.currentChapter.contentList = []; data.currentChapter.id = 0; data.isApp = false; } else { //手机网页端 data.book.bookName = g_data.book.bookName; data.book.authorName = g_data.book.authorName; data.currentChapter.chapterName = g_data.chapter.chapterName; data.currentChapter.vipState = g_data.chapter.vipStatus; data.currentChapter.contentList = []; data.currentChapter.id = 0; data.isApp = true; } return data; }; //寻找匹配目录 function findLastChapter() { //遍历目录找到当前目录的url var chapter = ""; if (data.isApp) { data.currentChapter.chapterName = $(".read-section h3").last().text() } else { data.currentChapter.chapterName = $(".text-wrap:last h3 span:first").text() } var current_chapter = data.currentChapter.chapterName; if (data.chapters.length > 0) { console.log("------正在查找阅读到的最新章节------") for (var i = 0, len = data.chapters.length; i < len; i++) { chapter = data.chapters[i].name; var c = chapter.replace(/\s/g, "").replace(/[::()()]/, "") var cu = current_chapter.replace(/\s/g, "").replace(/[::()()]/, "") if (cu === c || cu.indexOf(c) != -1 || c.indexOf(cu) != -1) { updataCurrentChapter(i); break; } else { updataCurrentChapter(-1); } } } else { updataCurrentChapter(-1); console.log("章节目录没有解析到"); } //return data.currentChapter.id }; function updataCurrentChapter(id) { if (id > -1) { data.currentChapter.id = id; data.currentChapter.chapterName = data.chapters[id].name; data.currentChapter.href = data.chapters[id].href; } else { data.currentChapter.id = id; data.currentChapter.chapterName = "ERROR"; data.currentChapter.href = "www.baidu.com"; } } //根据章节名获取笔趣阁的目录列表,并赋值给data.chapters function getBookContents() { //从当前页面获取小说名称和当前章节 var config = sites[siteId] getBookChapterInfor(); //仅第一次运行 //console.log(data.book.booName); console.log("------开始从 " + config.siteName + " 搜索《 " + data.book.bookName + " 》 ------"); if (config.needName) { config.searchURL = config.searchURL + data.book.bookName } runAsync(config.searchURL, config.method, config.formData + data.book.bookName).then(( result) => { //bookname,authorName ==>搜索===>解析==>href==>请求==>response function getTitleAuthorList(result) { //console.log(result.responseText); var $html = $('
').html(result.responseText); var $table = $($html); var nameList = $(config.bookName, $table), authorList = $(config.authorName, $table), href = ""; //console.log("nameList", nameList); //console.log("authorList", authorList); ///console.log($(nameList[0]).text() + "-----" + $(authorList[0]).text()) for (var i = 0, len = nameList.length; i < len; i++) { if (data.book.bookName == $.trim($(nameList[i]).text()) &&( data.book.authorName == $.trim($(authorList[i]).text()) || $.trim($(authorList[i]).text()).indexOf(data.book.authorName) != -1)) { //console.log($(nameList[i]).text() + "-----" + $(authorList[i]).text()) //console.log(nameList[i]) href = $(nameList[i]).attr("href"); //href = $(nameList[i]).href; //没有http 说明url要拼接 if (href.indexOf("http") == -1) { href = config.URL + href }; //console.log("href", href) break; } } if (nameList.length == 0 || href == "") { console.log("------从 " + config.siteName + " 搜索《 " + data.book.bookName + " 》 失败------"); siteId = siteId + 1; if (siteId < sites.length) { getBookContents() } else { console.log("------未能找到小说,请继续完善地址池------"); } } if(config.urlUpdata){ config.URL = href.replace("index.html","") } return runAsync(href); } return getTitleAuthorList(result); }).then((response) => { //response==>解析==>目录列表==>保存到data==>寻找匹配的章节 console.log("------开始从response解析目录-------- "); function getChapterNameHref(response) { var $html = $('
').html(response.responseText); var $table = $($html); var chapters = $(config.chapters, $table); //console.log(chapters); for (var i = 0, len = chapters.length; i < len; i++) { var chapterTmp = { name: "", href: "" }; chapterTmp.name = $(chapters[i]).text(); chapterTmp.href = config.URL + $(chapters[i]).attr("href"); //chapterTmp.href = $(chapters[i]).href; //console.log(chapterTmp); data.chapters.push(chapterTmp); } //寻找匹配的章节 //console.log(data.chapters) findLastChapter(); if (data.currentChapter.id != -1) { //console.log(data.chapters[data.currentChapter.id].name) return runAsync(data.chapters[data.currentChapter.id].href); } else { console.log("目录没有匹配") return runAsync(data.chapters[0].href); } } return getChapterNameHref(response); }).then((response) => { //response==>解析==>章节内容==>保存到data.currentChapter.contentList==>寻找匹配的章节 //console.log(response.responseText) var c = parseContent(response.responseText) if (($(".j_subscribeBtn").length > 0 || $(".btn-primary").length > 0) && c.length > 0) { insertChapter(c); } }) }; function parseContent(text) { var config = sites[siteId] var $html = $('
').html(text); var $content = $(config.content, $html); //根据config.contentSplit进行不同的读取方式 var contents = [] switch (config.contentSplit) { case "\n\n": // contents = $content.text().replace($(config.relace, $html).text(), "").split( // "\n\n"); contents = $content.text().split("\n\n"); //console.log(contents) break; case "node": for (var i = 0, len = $content.length; i < len; i++) { //contents.push($($content[i]).text()); contents.push($content[i].innerText); } break; case "4space": //console.log("4space",$content.text()) contents = $content.text().split("  "); //console.log("4space",contents) break; default: console.log("未设置 " + config.contentSplit + " 的解析方法") } if (contents.length == 0) { console.log("未解析到章节内容,自动换源!!!!!!!") siteId = siteId + 1; getBookContents() } //console.log("contents:",contents) var s = sites[siteId].slice; if(s.length == 1){ contents = contents.slice(s[0]) }else{ contents = contents.slice(s[0],s[1]) } data.currentChapter.contentList = contents //console.log("4space",data.currentChapter.contentList) return contents } // 插入文章 function insertChapter(contents) { var section, paragraph, p = "", i = 0, mlen = -1; if (data.isApp) { //手机中插入 chapterNum = $(".read-section").length section = $(".read-article .read-section").last(); $(".read-article").append(section.clone()); section = $(".read-article .read-section").last(); paragraph = $("p", section).first(); $(".btn-primary", section).remove(); $(".read-rss-auto", section).remove(); $("p", section).remove(); $("h3", section).text(data.currentChapter.chapterName); for (i = 0, mlen = contents.length; i < mlen; i++) { p = contents[i]; paragraph.text(p); section.append(paragraph.clone()); } } else { chapterNum = $(".text-wrap").length //web中插入 section = $("#j_chapterBox .text-wrap:last"); $("#j_chapterBox").append(section.clone()); section = $("#j_chapterBox .text-wrap:last"); paragraph = $(".read-content p:first", section); $(".vip-limit-wrap", section).remove(); $(".admire-wrap", section).remove(); $(".read-content p", section).remove(); $("h3:first span:first", section).text(data.currentChapter.chapterName); for (i = 0, mlen = contents.length; i < mlen; i++) { p = contents[i]; paragraph.text(p); $(".read-content", section).append(paragraph.clone()); } } console.log("------已经插入<<" + data.chapters[data.currentChapter.id].name + ">>------") lock = true; }; function runAsync(url, send_type = "GET", data_ry = "") { console.log("请求数据: url: " + url + " method: " + send_type + " data: " + data_ry); var p = new Promise((resolve, reject) => { GM_xmlhttpRequest({ method: send_type, url: url, headers: { "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36 Edg/89.0.774.57", "Content-Type": "application/x-www-form-urlencoded" }, data: data_ry, onload: function(response) { resolve(response); }, onerror: function(response) { console.log("请求失败: url: " + url + " method: " + send_type + " data: " + data_ry); reject("请求失败: url = " + url); } }); }) return p; }; })();