起点小说优化|AI续写追更|VIP章节免费阅读|支持本章说显示|全本TXT一键下载|游客书架

提供多功能强大的起点小说网站优化插件,极大的增强起点中文网的使用体验:,支持免费阅读VIP付费章节,解锁本章说,保存阅读进度,还可以使用AI续写追更....

< 脚本起点小说优化|AI续写追更|VIP章节免费阅读|支持本章说显示|全本TXT一键下载|游客书架的反馈

提问/评论

§
发表于:2023-11-20

总结后的代码,仅读书阁书源可用,简单解决了空白段有评论的bug,解决了小说名带有括号附加内容时搜索不到的bug

// ==UserScript==
// @name         起点小说解锁|VIP章节免费阅读|极速章节识别
// @version      1.3.4
// @description  可解锁起点小说VIP付费章节。基本还原付费效果,无需设置即可阅读。
// @author       JiGuang
// @namespace    www.xyde.net.cn
// @homepageURL  http://www.xyde.net.cn
// @match        https://read.qidian.com/chapter/*
// @match        *://book.zongheng.com/chapter/*/*.html
// @require https://cdn.jsdelivr.net/npm/sweetalert2@11
// @require https://cdn.staticfile.org/jquery/2.0.3/jquery.min.js
// @grant GM_setValue
// @grant GM_getValue
// @grant GM_xmlhttpRequest
// @grant unsafeWindow
// @grant GM_registerMenuCommand
// @connect
// @license MIT
// ==/UserScript==

(function () {
    'use strict';
    //全局配置
    //获取cookie值
    var index = getCookie("choice");
    var times = getCookie("times");
    if (index == null) { index = 0 }
    if (times == null) { times = 0 }
    var config = {
        //配置版本号
        version: 1,
        //支持的书源地址:
        //步骤1
        webSites:
            ["http://www.biquge5200.cc/modules/article/search.php?searchkey=",
                "http://www.dushuge.com/hsdgiohsdigohsog.php?ie=gbk&q=",
                "https://www.disixs.com/search.php?keyword=",
                "https://so.biqusoso.com/s2.php?ie=utf-8&siteid=qu-la.com&q=",
                "http://www.b5200.net/modules/article/search.php?searchkey=",
                "http://www.siluke.com/search.php"
            ],
        //跳转网址:用于修正脚本读取章节地址自动把起点前缀拼接起来
        //步骤1
        webGo: ['http://www.biquge5200.cc/', 'http://www.dushuge.com', 'https://www.disixs.com', 'https://www.qu-la.com', 'http://www.b5200.net/', 'http://www.siluke.com'],
        //网页内容:F12查看页面元素 找到章节文字所在的标签id
        webContent: ['#content', '#content', '#content', '#txt', '#content', '#content'],
        //书源描述
        webDesc: ["笔趣阁", "读书阁", "58小说网", "官术网", "书趣阁", "思路客"],
        //正在使用的书源
        webSiteIndex: index,
        //搜索前缀:
        //步骤2:查看书源网站搜索关键字后跳转地址 并替换
        webSearch: ['', '', '', '', '', ''],
        //搜索方法 : 目前没有特别大的作用
        webMethod: ["GET", "GET", "GET", "POST", "POST", "GET"],
        //使用序列: 不同书源的获取章节目录的标签选择不同
        //步骤5:0 代表第一个字符串
        webReturn: [2, 2, 0, 1, 0, 0],
        //书源类型:0代表网页书源,1代表api请求书源
        webType: [0, 0, 0, 0, 0, 0],
        //具体章节网址替换
        webHref: [0, 0, 0, 0, 1, 0],
        //book:不同书源的获取作品名的标签选择不同
        //步骤3:去书源网站搜索页面查找标签并替换
        webBook: [".grid > a",
            "h4.bookname > a",
            "a.result-game-item-title-link",
            "a",
            ".odd > a",
            ".s2 > a"],
        //author
        //步骤3:去书源网站搜索页面查找标签并替换
        webAuthor: [".odd",
            "div.author",
            "div.result-game-item-info > p:nth-child(1) > span:nth-child(2)",
            "span.s4",
            ".odd",
            ".s4 > a"]
    }
    //注册的菜单和对应执行的函数
    var menus = [
        {
            name: '打开设置',
            event: openSetting
        },
    ]

    //增加cookie缓存
    function setCookie(cName, value, datetime) {
        var oDate = new Date();
        if (datetime == 0) { datetime = 1 * 24 * 60 * 60 * 1000 }
        oDate.setTime(oDate.getTime() + datetime);//设置过期时间
        var cookieString = cName + value + ";expires='" + oDate.toGMTString() + ";path=/";
        document.cookie = cookieString;//存cookie
    }

    //获取指定名称的cookie的值
    function getCookie(cName) {
        var arrStr = document.cookie.split("; ");
        for (var i = 0; i < arrStr.length; i++) {
            var temp = arrStr[i].split("=");
            if (temp[0] == cName) {
                return decodeURI(temp[1]);
            }
        }
    }

    //增加菜单
    function addMenu() {
        for (var menu of menus) {
            GM_registerMenuCommand(menu.name, menu.event)
        }
    }

    //添加新书源
    function openSetting() {
        try {
            document.querySelector("#j_navSettingBtn > a").click()
        } catch (e) {
            notify('打开设置失败', 'warning')
        }
    }


    //把更换书源增加到设置菜单
    function hookSetting() {
        let bookhtml = ``
        for (var di in config.webDesc) {
            bookhtml += `<option value="${di}">${config.webDesc[di]}</option>`
        }
        if (!document.querySelector(".setting-list-wrap")) {
            setTimeout(hookSetting, 1000)
            return
        }
        let e = document.createElement("div")
        e.innerHTML = `<li class="remind" style="margin-top:10px;">
<i>书源切换</i>
<select id="select" style="position:relative;top:5px">
<option value="#">请选择要切换的书源</option>
${bookhtml}
</select>
</li>`
        document.querySelector(".setting-list-wrap").firstElementChild.appendChild(e)
        document.querySelector("#select").onchange = function () {
            var index = document.querySelector("#select").value
            setCookie("choice=", index, 0)
            location.reload()
        }
        //打开评论
        document.querySelector("#j-sectionCommentBtn").onclick = function () {
            var state = document.querySelector("#j-sectionCommentSwitch").innerHTML
            if (state == "关闭") {
                // $("body").addClass("section-comment-open")
                $("html").addClass = ("j-sectionCommentLimit")
                $("#j_chapterBox > div > div").removeClass("j-sectionCommentLimit")
            }
            else {
                //  $("body").removeClass("section-comment-open")
                $("#j-readPage").removeClass("j-sectionCommentLimit")
                $("#j_chapterBox > div > div").removeClass("j-sectionCommentLimit")
                // $("#paragraph-review-app").css("display","none")
            }
        }
    }
    //自动加载本章说
    async function comment() {
        $("#j-readPage").removeClass("j-sectionCommentLimit")
        $("#j_chapterBox > div > div").removeClass("j-sectionCommentLimit")
    }

    //提示用户
    function notify(title = '操作成功', type = 'success', show = true) {
        console.log(title)
        const Toast = Swal.mixin({
            toast: true,
            position: 'top-end',
            showConfirmButton: false,
            timer: 2000,
            timerProgressBar: true,
            didOpen: (toast) => {
                toast.addEventListener('mouseenter', Swal.stopTimer)
                toast.addEventListener('mouseleave', Swal.resumeTimer)
            }
        })
        if (show)
            Toast.fire({
                icon: type,
                title: title
            })
        return Toast
    }

    //获取章节名
    function QDgetBookChapter() {
        if (document.querySelector("div > div.text-head > h3 > span.content-wrap")) {
            let res = '' + document.querySelector("div > div.text-head > h3 > span.content-wrap").innerText
            res = res.replace(' ', '')
            return res
        }
        return undefined
    }

    // 获取小说名字,去掉括号内的内容
    function QDgetBookName() {
        const bookNameElement = document.querySelector("#bookImg");
        if (bookNameElement) {
            // 使用正则表达式去掉括号内的内容
            const rawName = bookNameElement.innerText;
            const cleanedName = rawName.replace(/\([^)]*\)/g, '').trim();
            return cleanedName;
        } else {
            return null; // 或者返回一个默认的名称,或者抛出错误,具体根据需求来定
        }
    }


    //本章是否已被购买
    function QDgetChapterOrder() {
        // @ts-ignore
        return document.querySelector("a.admire.lang.j_admireBtn")
    }

    //设置页面阅读内容
    async function QDsetContent(content) {
        // 使用正则表达式进行替换
        content = content.replace(/(<br\s*\/?>\s*){2,}/g, '<br><br>');
        const regs = /<br>[\s]{0,1}<br>/g
        content = content.replace(regs, "<br><br>  ")
        let int = 1;
        while (true) {
            let key = `<span class="review-count" data-segid="${int++}" style=" cursor: pointer; "></span>`;
            content = content.replace("<br><br>", key);
            content = content.replace("</p><p>", key);
            if (content.indexOf("<br><br>") == "-1" && content.indexOf("</p><p>") == "-1") break;
        }
        var fir = '<p data-type="2">'
        content = fir + content
        var reg = RegExp("  ", "g");
        content = content.replace(reg, '</p><p data-type="2">');

        document.querySelector("div > div.read-content.j_readContent ").innerHTML = content
        let readQrcodeMobile = document.querySelector("#readQrcodeMobile")
        let cid = readQrcodeMobile.dataset.cid
        let bid = readQrcodeMobile.dataset.bid

        // 获取特定名称的 Cookie
        function getCookieByName(cookieName) {
            var name = cookieName + "=";
            var decodedCookie = decodeURIComponent(document.cookie);
            var cookieArray = decodedCookie.split(';');
            for (var i = 0; i < cookieArray.length; i++) {
                var cookie = cookieArray[i].trim();
                if (cookie.indexOf(name) === 0) {
                    return cookie.substring(name.length, cookie.length);
                }
            }
            return null;
        }

        // 获取当前 _csrfToken
        var _csrfToken = getCookieByName("_csrfToken");

        // 构建请求 URL,并替换 _csrfToken
        var requestURL = `https://vipreader.qidian.com/ajax/chapterReview/reviewSummary?_csrfToken=${_csrfToken}&bookId=${bid}&&chapterId=${cid}`;

        // 发送 AJAX 请求
        const res = await parseDocFromAjax("GET", requestURL, true);

        console.log(res)
        res.list.map(item => {
            const span = document.querySelector(`span[data-segid="${item.segmentId}"]`)
            span ? span.innerHTML = item.reviewNum + "<i><cite></cite></i>" : ""
        })
        document.getElementsByClassName('read-content')[0].setAttribute('style', `line-height: 1.5;letter-spacing: 1px`)
        const removeSpan = document.querySelectorAll("span[data-segid")
        removeSpan.forEach(item => {
            if (item.innerText == '' || item.innerText == 0) item.remove()
        })
        console.log("移除成功")
    }


    //将请求的url的html内容转化成document对象
    async function parseDocFromAjax(method, url, flag) {
        return new Promise((resolve, reject) => {
            GM_xmlhttpRequest({
                method,
                url: url,
                onload: (res) => {
                    if (config.webType[config.webSiteIndex] == 1 || flag) {
                        let str = res.response
                        // console.log(str)
                        str = str.replace(/\\r\\n  \\r\\n  /g, "<br><br>  &nbsp;&nbsp;&nbsp;&nbsp;")
                        str = str.replace(/\\r\\n  /g, "<br><br>  &nbsp;&nbsp;&nbsp;&nbsp;")
                        let arr = eval('(' + str + ')')
                        const { data } = arr
                        console.log(data)
                        return resolve(data)
                    }
                    let htmldoc = document.createElement('html')
                    let htmlstr = res.responseText
                    htmlstr = htmlstr.replace(/http /g, "https")
                    htmlstr = htmlstr.replace(/img src/g, "a url")
                    htmlstr = htmlstr.replace(/onerror/g, "class")
                    //  console.log(htmlstr)
                    htmldoc.innerHTML = htmlstr
                    console.log(url)
                    resolve(htmldoc)
                },
                onerror: (err) => {
                    reject(err)
                }
            })
        })
    }



    //搜索小说并返回结果
    async function searchBook(keywords) {
        const r = await parseDocFromAjax(config.webMethod[config.webSiteIndex], config.webSites[config.webSiteIndex] + keywords + config.webSearch[config.webSiteIndex])
        let resList = []
        if (config.webType[config.webSiteIndex] == 1) {
            r.map(item => {
                resList.push({ id: item.Id, bookName: item.Name, author: item.Author, url: config.webGo[config.webSiteIndex] + item.Id + "/" })
                //console.log(item)
            })
            //console.log(resList[0])
            return resList
        }
        var bookList = r.querySelectorAll(config.webBook[config.webSiteIndex])
        const authorList = r.querySelectorAll(config.webAuthor[config.webSiteIndex])
        for (let i in bookList) {
            if (bookList[i].title) {
                resList.push({ bookName: bookList[i].title, author: authorList[i].innerText, url: config.webGo[config.webSiteIndex] + bookList[i].pathname })
            }
            resList.push({ bookName: bookList[i].innerText, author: authorList[i].innerText, url: config.webGo[config.webSiteIndex] + bookList[i].pathname })
        }
        // console.log(resList)
        return resList
    }

    //获取小说目录
    async function getChapterList(book) {
        let resList = []
        let bookUrl = book.url.replace('https://vipreader.qidian.com/', config.webGo[config.webSiteIndex])
        const r = await parseDocFromAjax('GET', bookUrl)
        if (config.webType[config.webSiteIndex] == 1) {
            // console.log(r)
            r.list.map(item => {
                item.list.map(i => {
                    resList.push({ title: i.name, url: config.webGo[config.webSiteIndex] + book.id + "/" + i.id + ".html" })
                })
            })
            // console.log(resList);
            return resList
        }
        let s = ["#list > dl > dd > a", "ul.cf > li > a", "div.listmain > dl > dd > a"]
        //步骤4:如书源目录标签不相同 此处添加后再在webReturn修改对应数字
        const cateList = r.querySelectorAll(s[config.webReturn[config.webSiteIndex]])
        console.log("cateList:", cateList)
        for (let i of cateList) {
            // console.log( i)
            let url = i.getAttribute("href")
            if (config.webHref[config.webSiteIndex] == 1) {
                // console.log("Ok")
                //  bookUrl = bookUrl.substring(0, bookUrl.lastIndexOf("/")+1)
                config.webGo[config.webSiteIndex] = ''
            }
            url = config.webGo[config.webSiteIndex] + url
            resList.push({ title: i.innerText, url: url })
        }
        return resList
    }

    //获取章节内容
    async function getContent(pageUrl) {
        const res = await parseDocFromAjax('GET', pageUrl)
        if (config.webType[config.webSiteIndex] == 1) {
            let title = res.cname.replace(" ", '')
            if (res.content.indexOf(title) == -1) return res.content
            title = title + '<br>  '
            res.content = res.content.replace(title, '')
            //console.log('getContent:',res.content)
            return res.content
        }
        return res.querySelector(config.webContent[config.webSiteIndex]).innerHTML
    }


    //解析书源函数
    async function parseMain() {

        //搜索小说名字
        var r = await searchBook(QDgetBookName())
        var a = g_data.bookInfo.authorName
        let ii = 0
        //优先匹配名字相同的
        for (let suoyin in r) {
            if (r[suoyin].bookName == QDgetBookName() || r[suoyin].author == a) {
                ii = suoyin
                break;
            }
        }
        if (r[ii] == undefined) {
            console.log("搜索作者")
            r = await searchBook(a)
            for (let suoyin in r) {
                if (r[suoyin].bookName == QDgetBookName()) {
                    ii = suoyin
                    break;
                }
            }
        }
        //获取第一项结果章节目录
        if (r[ii] == undefined) {
            console.log('该小说暂无资源')
        }
        //  console.log(r[ii])
        const clist = await getChapterList(r[ii])
        if (QDgetBookChapter() == undefined || clist.length == 0) {
            console.log('抓取目录失败')
        }
        console.log('抓取目录成功')
        // console.log(clist)
        //获取章节名
        for (let i in clist) {
            let tit = '' + clist[i].title
            let str = tit
            tit = tit.replace(' ', '')
            //console.log('匹配',tit,QDgetBookChapter())
            var patt1 = /[a-zA-Z\u4e00-\u9fa5]+/g
            var patt2 = /[0-9]+/g
            str = QDgetBookChapter()
            var flag = false
            //排除纯数字章节的影响
            if (tit.match(patt1) == null) {
                tit = tit.match(patt2)
                str = str.match(patt2)
            }
            else if (str.match(patt1) == null) {
                str = str.match(patt2)
                tit = tit.match(patt2) == null ? tit.match(patt1) : tit.match(patt2)
            }
            else {
                str = str.match(patt1)
                tit = tit.match(patt1)
                //有些作者喜欢加第几卷第几章 但是书源网站没有卷名
                var str2 = str.join("").split(/卷|章/)
                var tit2 = tit.join("").split(/卷|章/)
                //模糊读取,若无法精准匹配 尝试模糊名匹配 并设置缓存默认以此方法匹配,默认是2分钟
                console.log(times)
                if (times >= 4 & times < 11) {
                    //自带数字章节名 首个字符串与书源匹配
                    if (str[0] == tit[0]) {
                        flag = true
                        setCookie("times=", times, 1 * 1000 * 60 * 2)//这里修改2可以改缓存时间
                    }
                }
                else if (times >= 11 & times < 17) {
                    //末尾名匹配
                    if (str2[str2.length - 1] == tit2[tit2.length - 1]) {
                        flag = true
                        setCookie("times=", times, 1 * 1000 * 60 * 2)
                    }
                }
                else if (times >= 17) {
                    //中间名匹配
                    if (str2[str2.length - 2] == tit2[tit2.length - 2]) {
                        flag = true
                        setCookie("times=", times, 1 * 1000 * 60 * 2)
                    }
                }
            }
            //  console.log(str[0],tit[0])
            if (str.join("") == tit.join("") || flag == true) {
                console.log('检查到结果')
                const content = await getContent(clist[i].url)
                QDsetContent(content)
                console.log('写入成功')
                notify('小说读取成功')
                return
            }
        }
        times++
        setCookie("times=", times, 1 * 1000 * 60 * 2)
        console.log('目录匹配失败')
        notify('未查询到该小说内容', 'warning')
        throw new Error('该书源解析失败')
    }


    //递归更换书源
    async function mergeOne(index) {
        try {
            if (index) {
                config.webSiteIndex = index
                console.log(index)
            }
            notify(`正在切换到书源${config.webDesc[config.webSiteIndex]}...`, 'info')
            await parseMain()
        } catch (e) {
            console.log(e)
            config.webSiteIndex = (config.webSiteIndex + 1) % 6
            mergeOne()
        }
    }
    //MAIN-BEFORE 主程序预备函数
    if (QDgetChapterOrder() != null) {
        notify(`已订阅章节`)
    } else {
        addMenu()
        //MAIN 主程序
        notify(`您正在阅读${QDgetBookName()}的${QDgetBookChapter()}`)
        mergeOne()
        comment()
        hookSetting()
    }

    // Your code here...
})();
§
发表于:2023-11-20

http://www.siluke.com/search.php 这样写是不对的,直接后缀加上书名都搜不出来网页,最起码应该https://www.siluke.com/search.php?keyword= 但该网站好像还有什么检测的样子导致这方法也不行。只能用http://www.isiluke.info/search.html?name= 代替了,但这个网址会导致本章说错位一行,且有些网站错别字与排版混乱,很影响阅读体验。不像www.siluke.com这个网址几乎没有错别字

§
发表于:2023-11-20

http://www.siluke.com/search.php 这样写是不对的,直接后缀加上书名都搜不出来网页,最起码应该https://www.siluke.com/search.php?keyword= 但该网站好像还有什么检测的样子导致这方法也不行。只能用http://www.isiluke.info/search.html?name= 代替了,但这个网址会导致本章说错位一行,且有些网站错别字与排版混乱,很影响阅读体验。不像www.siluke.com这个网址几乎没有错别字

这个书源我不太会改就放弃了,读书网那个可以用

§
发表于:2023-11-20

http://www.siluke.com/search.php 这样写是不对的,直接后缀加上书名都搜不出来网页,最起码应该https://www.siluke.com/search.php?keyword= 但该网站好像还有什么检测的样子导致这方法也不行。只能用http://www.isiluke.info/search.html?name= 代替了,但这个网址会导致本章说错位一行,且有些网站错别字与排版混乱,很影响阅读体验。不像www.siluke.com这个网址几乎没有错别字

这个书源我不太会改就放弃了,读书网那个可以用

读书阁的本章说也会错位,我简单改了改,好像可以正常用了

§
发表于:2023-11-20

我最近看的《国民法医》,读书阁错误简直惨不忍睹,错别字,排版混乱层出不穷,有的干脆变成陌生小说乱入,根本没法看。思路客也不行但会好很多,最后我只能正常小说看几段,然后回头专门看本章说几处

§
发表于:2023-11-21

我最近看的《国民法医》,读书阁错误简直惨不忍睹,错别字,排版混乱层出不穷,有的干脆变成陌生小说乱入,根本没法看。思路客也不行但会好很多,最后我只能正常小说看几段,然后回头专门看本章说几处

思路客有cloudflare的验证机制,请求会被拒绝

§
发表于:2023-11-21

我最近看的《国民法医》,读书阁错误简直惨不忍睹,错别字,排版混乱层出不穷,有的干脆变成陌生小说乱入,根本没法看。思路客也不行但会好很多,最后我只能正常小说看几段,然后回头专门看本章说几处

我新加了一个笔趣阁的书源,国民法医这本小说可以看到780章

§
发表于:2023-11-21

求新书源

§
发表于:2023-11-21

求新书源

给个联系方式我把代码发给你

§
发表于:2023-11-21

447536703@qq.com

§
发表于:2023-11-21

额,笔趣阁缺少文字啊

§
发表于:2023-11-21

额,笔趣阁缺少文字啊




新的发给你了,本章说可能有错位,我也不会解决,笔趣阁少字,读书阁排版有问题,58乱加内容,思路客我也不知道本章说对不对,这四个起码可以用了

§
发表于:2023-11-22

谢谢!!!好像差不多,思路客你发的还是之前有cloudflare验证的网址,我的打不开,不过凑合看了,就我这个小说比较特殊一点。另外,是否介绍下步骤五里webReturn,webType,webHref里面0,1,2如何选择的问题。content与#txt怎么选择。我之前有过一次偶然间在手机端的书源里找了一些书源出来替换试试,结果发现有的书源在第6个位置能正常使用,在其它位置则不能。我是编程小白,不清楚应该如何选择,就想着尝试自制下书源,特别是从手机端的书源里找些网址替换下。

§
发表于:2023-11-22

还有,以读书阁为例,搜索《国民法医》后出现2个内容,虽然两个都是同样的小说,但第一个名字都写错了且内容错误率非常高但脚本选的却是第一个。有没有办法让脚本选择第二个符合的,我看第二个错误率就很少了

§
发表于:2023-11-22

还有,以读书阁为例,搜索《国民法医》后出现2个内容,虽然两个都是同样的小说,但第一个名字都写错了且内容错误率非常高但脚本选的却是第一个。有没有办法让脚本选择第二个符合的,我看第二个错误率就很少了



你说的这种情况应该不会出现,脚本会获取起点的小说名和作者名,然后在书源获取搜索到的小说列表和作者列表,返回小说列表中找到名字相同的,当小说名多个相同时会比较作者名,你图片中第一个小说名和作者名都不匹配,应该不会选择第一个结果而是返回第二个,根据控制台结果来看返回的确实是第二个http://www.dushuge.com/html/57/57628/38321180.html

§
发表于:2023-11-22

不知是否脚本会错乱的缘故,我明明确定用的是读书阁的书源,然后内容却与文字不相符,然后我单独打开读书阁的网页时发现第一项符合我脚本展示的内容,由不得我不信。不过刚刚我想复现的时候发现突然又正常了。图片内容都是读书阁的同一个章节内容,我之前经常出现的是错误的一页

§
发表于:2023-11-22

之前我习惯用的是读书阁,但很多时候就会出现类似情况,好奇的我打开了读书阁的网页查看,发现没问题啊,甚至都很少错别字。然后偶然间我打开了不符合条件的第一项,发现一切都能对得上,错别字,排版混乱什么的一大堆

§
发表于:2023-11-22

之前我习惯用的是读书阁,但很多时候就会出现类似情况,好奇的我打开了读书阁的网页查看,发现没问题啊,甚至都很少错别字。然后偶然间我打开了不符合条件的第一项,发现一切都能对得上,错别字,排版混乱什么的一大堆

可能是因为读书阁的网站不稳定,有时会出现打不开网站的原因,导致选择了第一个?我也不太清楚原因,代码里做了书名和作者匹配,按道理应该不会选择第一个

§
发表于:2023-11-24

谢谢!思路客可以了,有时候新浏览器没打开过思路客的网站用脚本选择思路客可能通不过,所以最稳妥的就是先打开思路客网站,然后再用脚本选择思路客,这样就不受cloudflare验证影响了,一般就能正常使用了。思路客虽然载入慢了一点,但几乎没错别字与排版混乱,非常好

§
发表于:2023-12-07

你好,请问是失效了吗?进入vip章节没有任何按钮

§
发表于:2023-12-07

你好,请问是失效了吗?进入vip章节没有任何按钮

要切换到旧版

§
发表于:2023-12-08

谢谢解决了

§
发表于:2023-12-09

你好,手机浏览器用脚本怎么无法显示本章说的数量。例如用X浏览器,这浏览器支持脚本且不会像某些浏览器一样强制手机版看小说,但用了脚本后发现不显示本章说数量了,每段结尾都有一个空白框框,要一个个点过去,如果真实存在的话本章说会显示出来。

§
发表于:2023-12-09

你好,手机浏览器用脚本怎么无法显示本章说的数量。例如用X浏览器,这浏览器支持脚本且不会像某些浏览器一样强制手机版看小说,但用了脚本后发现不显示本章说数量了,每段结尾都有一个空白框框,要一个个点过去,如果真实存在的话本章说会显示出来。

这个可能是获取cookie的方式不同的原因吧,起点需要获取存储在浏览器cookie中_csrfToken的值,才能获取本章说的数量,可能是因为cookie获取方式不同

§
发表于:2023-12-09

你好,手机浏览器用脚本怎么无法显示本章说的数量。例如用X浏览器,这浏览器支持脚本且不会像某些浏览器一样强制手机版看小说,但用了脚本后发现不显示本章说数量了,每段结尾都有一个空白框框,要一个个点过去,如果真实存在的话本章说会显示出来。

你把源代码var _csrfToken = getCookieByName("_csrfToken");
改成
function getCookieValue(cookieName) {
var cookies = document.cookie.split(';');
for (var i = 0; i < cookies.length; i++) {
var cookie = cookies[i].trim();
if (cookie.indexOf(cookieName + '=') === 0) {
return cookie.substring(cookieName.length + 1);
}
}
return null;
}

var csrfTokenValue = getCookieValue('_csrfToken');

§
发表于:2023-12-10

我想要这个网站的书源https://www.69shuba.com

§
发表于:2023-12-18

请问有没有办法联系读书阁或者添加别的书源,我想看玄鉴仙族看不了

§
发表于:2023-12-19

要怎么用啊,我怎么用电脑用不了?

§
发表于:2023-12-20

总结后的代码,仅读书阁书源可用,简单解决了空白段有评论的bug,解决了小说名带有括号附加内容时搜索不到的bug

// ==UserScript==
// @name         起点小说解锁|VIP章节免费阅读|极速章节识别
// @version      1.3.4
// @description  可解锁起点小说VIP付费章节。基本还原付费效果,无需设置即可阅读。
// @author       JiGuang
// @namespace    www.xyde.net.cn
// @homepageURL  http://www.xyde.net.cn
// @match        https://read.qidian.com/chapter/*
// @match        *://book.zongheng.com/chapter/*/*.html
// @require https://cdn.jsdelivr.net/npm/sweetalert2@11
// @require https://cdn.staticfile.org/jquery/2.0.3/jquery.min.js
// @grant GM_setValue
// @grant GM_getValue
// @grant GM_xmlhttpRequest
// @grant unsafeWindow
// @grant GM_registerMenuCommand
// @connect
// @license MIT
// ==/UserScript==

(function () {
    'use strict';
    //全局配置
    //获取cookie值
    var index = getCookie("choice");
    var times = getCookie("times");
    if (index == null) { index = 0 }
    if (times == null) { times = 0 }
    var config = {
        //配置版本号
        version: 1,
        //支持的书源地址:
        //步骤1
        webSites:
            ["http://www.biquge5200.cc/modules/article/search.php?searchkey=",
                "http://www.dushuge.com/hsdgiohsdigohsog.php?ie=gbk&q=",
                "https://www.disixs.com/search.php?keyword=",
                "https://so.biqusoso.com/s2.php?ie=utf-8&siteid=qu-la.com&q=",
                "http://www.b5200.net/modules/article/search.php?searchkey=",
                "http://www.siluke.com/search.php"
            ],
        //跳转网址:用于修正脚本读取章节地址自动把起点前缀拼接起来
        //步骤1
        webGo: ['http://www.biquge5200.cc/', 'http://www.dushuge.com', 'https://www.disixs.com', 'https://www.qu-la.com', 'http://www.b5200.net/', 'http://www.siluke.com'],
        //网页内容:F12查看页面元素 找到章节文字所在的标签id
        webContent: ['#content', '#content', '#content', '#txt', '#content', '#content'],
        //书源描述
        webDesc: ["笔趣阁", "读书阁", "58小说网", "官术网", "书趣阁", "思路客"],
        //正在使用的书源
        webSiteIndex: index,
        //搜索前缀:
        //步骤2:查看书源网站搜索关键字后跳转地址 并替换
        webSearch: ['', '', '', '', '', ''],
        //搜索方法 : 目前没有特别大的作用
        webMethod: ["GET", "GET", "GET", "POST", "POST", "GET"],
        //使用序列: 不同书源的获取章节目录的标签选择不同
        //步骤5:0 代表第一个字符串
        webReturn: [2, 2, 0, 1, 0, 0],
        //书源类型:0代表网页书源,1代表api请求书源
        webType: [0, 0, 0, 0, 0, 0],
        //具体章节网址替换
        webHref: [0, 0, 0, 0, 1, 0],
        //book:不同书源的获取作品名的标签选择不同
        //步骤3:去书源网站搜索页面查找标签并替换
        webBook: [".grid > a",
            "h4.bookname > a",
            "a.result-game-item-title-link",
            "a",
            ".odd > a",
            ".s2 > a"],
        //author
        //步骤3:去书源网站搜索页面查找标签并替换
        webAuthor: [".odd",
            "div.author",
            "div.result-game-item-info > p:nth-child(1) > span:nth-child(2)",
            "span.s4",
            ".odd",
            ".s4 > a"]
    }
    //注册的菜单和对应执行的函数
    var menus = [
        {
            name: '打开设置',
            event: openSetting
        },
    ]

    //增加cookie缓存
    function setCookie(cName, value, datetime) {
        var oDate = new Date();
        if (datetime == 0) { datetime = 1 * 24 * 60 * 60 * 1000 }
        oDate.setTime(oDate.getTime() + datetime);//设置过期时间
        var cookieString = cName + value + ";expires='" + oDate.toGMTString() + ";path=/";
        document.cookie = cookieString;//存cookie
    }

    //获取指定名称的cookie的值
    function getCookie(cName) {
        var arrStr = document.cookie.split("; ");
        for (var i = 0; i < arrStr.length; i++) {
            var temp = arrStr[i].split("=");
            if (temp[0] == cName) {
                return decodeURI(temp[1]);
            }
        }
    }

    //增加菜单
    function addMenu() {
        for (var menu of menus) {
            GM_registerMenuCommand(menu.name, menu.event)
        }
    }

    //添加新书源
    function openSetting() {
        try {
            document.querySelector("#j_navSettingBtn > a").click()
        } catch (e) {
            notify('打开设置失败', 'warning')
        }
    }


    //把更换书源增加到设置菜单
    function hookSetting() {
        let bookhtml = ``
        for (var di in config.webDesc) {
            bookhtml += `<option value="${di}">${config.webDesc[di]}</option>`
        }
        if (!document.querySelector(".setting-list-wrap")) {
            setTimeout(hookSetting, 1000)
            return
        }
        let e = document.createElement("div")
        e.innerHTML = `<li class="remind" style="margin-top:10px;">
<i>书源切换</i>
<select id="select" style="position:relative;top:5px">
<option value="#">请选择要切换的书源</option>
${bookhtml}
</select>
</li>`
        document.querySelector(".setting-list-wrap").firstElementChild.appendChild(e)
        document.querySelector("#select").onchange = function () {
            var index = document.querySelector("#select").value
            setCookie("choice=", index, 0)
            location.reload()
        }
        //打开评论
        document.querySelector("#j-sectionCommentBtn").onclick = function () {
            var state = document.querySelector("#j-sectionCommentSwitch").innerHTML
            if (state == "关闭") {
                // $("body").addClass("section-comment-open")
                $("html").addClass = ("j-sectionCommentLimit")
                $("#j_chapterBox > div > div").removeClass("j-sectionCommentLimit")
            }
            else {
                //  $("body").removeClass("section-comment-open")
                $("#j-readPage").removeClass("j-sectionCommentLimit")
                $("#j_chapterBox > div > div").removeClass("j-sectionCommentLimit")
                // $("#paragraph-review-app").css("display","none")
            }
        }
    }
    //自动加载本章说
    async function comment() {
        $("#j-readPage").removeClass("j-sectionCommentLimit")
        $("#j_chapterBox > div > div").removeClass("j-sectionCommentLimit")
    }

    //提示用户
    function notify(title = '操作成功', type = 'success', show = true) {
        console.log(title)
        const Toast = Swal.mixin({
            toast: true,
            position: 'top-end',
            showConfirmButton: false,
            timer: 2000,
            timerProgressBar: true,
            didOpen: (toast) => {
                toast.addEventListener('mouseenter', Swal.stopTimer)
                toast.addEventListener('mouseleave', Swal.resumeTimer)
            }
        })
        if (show)
            Toast.fire({
                icon: type,
                title: title
            })
        return Toast
    }

    //获取章节名
    function QDgetBookChapter() {
        if (document.querySelector("div > div.text-head > h3 > span.content-wrap")) {
            let res = '' + document.querySelector("div > div.text-head > h3 > span.content-wrap").innerText
            res = res.replace(' ', '')
            return res
        }
        return undefined
    }

    // 获取小说名字,去掉括号内的内容
    function QDgetBookName() {
        const bookNameElement = document.querySelector("#bookImg");
        if (bookNameElement) {
            // 使用正则表达式去掉括号内的内容
            const rawName = bookNameElement.innerText;
            const cleanedName = rawName.replace(/\([^)]*\)/g, '').trim();
            return cleanedName;
        } else {
            return null; // 或者返回一个默认的名称,或者抛出错误,具体根据需求来定
        }
    }


    //本章是否已被购买
    function QDgetChapterOrder() {
        // @ts-ignore
        return document.querySelector("a.admire.lang.j_admireBtn")
    }

    //设置页面阅读内容
    async function QDsetContent(content) {
        // 使用正则表达式进行替换
        content = content.replace(/(<br\s*\/?>\s*){2,}/g, '<br><br>');
        const regs = /<br>[\s]{0,1}<br>/g
        content = content.replace(regs, "<br><br>  ")
        let int = 1;
        while (true) {
            let key = `<span class="review-count" data-segid="${int++}" style=" cursor: pointer; "></span>`;
            content = content.replace("<br><br>", key);
            content = content.replace("</p><p>", key);
            if (content.indexOf("<br><br>") == "-1" && content.indexOf("</p><p>") == "-1") break;
        }
        var fir = '<p data-type="2">'
        content = fir + content
        var reg = RegExp("  ", "g");
        content = content.replace(reg, '</p><p data-type="2">');

        document.querySelector("div > div.read-content.j_readContent ").innerHTML = content
        let readQrcodeMobile = document.querySelector("#readQrcodeMobile")
        let cid = readQrcodeMobile.dataset.cid
        let bid = readQrcodeMobile.dataset.bid

        // 获取特定名称的 Cookie
        function getCookieByName(cookieName) {
            var name = cookieName + "=";
            var decodedCookie = decodeURIComponent(document.cookie);
            var cookieArray = decodedCookie.split(';');
            for (var i = 0; i < cookieArray.length; i++) {
                var cookie = cookieArray[i].trim();
                if (cookie.indexOf(name) === 0) {
                    return cookie.substring(name.length, cookie.length);
                }
            }
            return null;
        }

        // 获取当前 _csrfToken
        var _csrfToken = getCookieByName("_csrfToken");

        // 构建请求 URL,并替换 _csrfToken
        var requestURL = `https://vipreader.qidian.com/ajax/chapterReview/reviewSummary?_csrfToken=${_csrfToken}&bookId=${bid}&&chapterId=${cid}`;

        // 发送 AJAX 请求
        const res = await parseDocFromAjax("GET", requestURL, true);

        console.log(res)
        res.list.map(item => {
            const span = document.querySelector(`span[data-segid="${item.segmentId}"]`)
            span ? span.innerHTML = item.reviewNum + "<i><cite></cite></i>" : ""
        })
        document.getElementsByClassName('read-content')[0].setAttribute('style', `line-height: 1.5;letter-spacing: 1px`)
        const removeSpan = document.querySelectorAll("span[data-segid")
        removeSpan.forEach(item => {
            if (item.innerText == '' || item.innerText == 0) item.remove()
        })
        console.log("移除成功")
    }


    //将请求的url的html内容转化成document对象
    async function parseDocFromAjax(method, url, flag) {
        return new Promise((resolve, reject) => {
            GM_xmlhttpRequest({
                method,
                url: url,
                onload: (res) => {
                    if (config.webType[config.webSiteIndex] == 1 || flag) {
                        let str = res.response
                        // console.log(str)
                        str = str.replace(/\\r\\n  \\r\\n  /g, "<br><br>  &nbsp;&nbsp;&nbsp;&nbsp;")
                        str = str.replace(/\\r\\n  /g, "<br><br>  &nbsp;&nbsp;&nbsp;&nbsp;")
                        let arr = eval('(' + str + ')')
                        const { data } = arr
                        console.log(data)
                        return resolve(data)
                    }
                    let htmldoc = document.createElement('html')
                    let htmlstr = res.responseText
                    htmlstr = htmlstr.replace(/http /g, "https")
                    htmlstr = htmlstr.replace(/img src/g, "a url")
                    htmlstr = htmlstr.replace(/onerror/g, "class")
                    //  console.log(htmlstr)
                    htmldoc.innerHTML = htmlstr
                    console.log(url)
                    resolve(htmldoc)
                },
                onerror: (err) => {
                    reject(err)
                }
            })
        })
    }



    //搜索小说并返回结果
    async function searchBook(keywords) {
        const r = await parseDocFromAjax(config.webMethod[config.webSiteIndex], config.webSites[config.webSiteIndex] + keywords + config.webSearch[config.webSiteIndex])
        let resList = []
        if (config.webType[config.webSiteIndex] == 1) {
            r.map(item => {
                resList.push({ id: item.Id, bookName: item.Name, author: item.Author, url: config.webGo[config.webSiteIndex] + item.Id + "/" })
                //console.log(item)
            })
            //console.log(resList[0])
            return resList
        }
        var bookList = r.querySelectorAll(config.webBook[config.webSiteIndex])
        const authorList = r.querySelectorAll(config.webAuthor[config.webSiteIndex])
        for (let i in bookList) {
            if (bookList[i].title) {
                resList.push({ bookName: bookList[i].title, author: authorList[i].innerText, url: config.webGo[config.webSiteIndex] + bookList[i].pathname })
            }
            resList.push({ bookName: bookList[i].innerText, author: authorList[i].innerText, url: config.webGo[config.webSiteIndex] + bookList[i].pathname })
        }
        // console.log(resList)
        return resList
    }

    //获取小说目录
    async function getChapterList(book) {
        let resList = []
        let bookUrl = book.url.replace('https://vipreader.qidian.com/', config.webGo[config.webSiteIndex])
        const r = await parseDocFromAjax('GET', bookUrl)
        if (config.webType[config.webSiteIndex] == 1) {
            // console.log(r)
            r.list.map(item => {
                item.list.map(i => {
                    resList.push({ title: i.name, url: config.webGo[config.webSiteIndex] + book.id + "/" + i.id + ".html" })
                })
            })
            // console.log(resList);
            return resList
        }
        let s = ["#list > dl > dd > a", "ul.cf > li > a", "div.listmain > dl > dd > a"]
        //步骤4:如书源目录标签不相同 此处添加后再在webReturn修改对应数字
        const cateList = r.querySelectorAll(s[config.webReturn[config.webSiteIndex]])
        console.log("cateList:", cateList)
        for (let i of cateList) {
            // console.log( i)
            let url = i.getAttribute("href")
            if (config.webHref[config.webSiteIndex] == 1) {
                // console.log("Ok")
                //  bookUrl = bookUrl.substring(0, bookUrl.lastIndexOf("/")+1)
                config.webGo[config.webSiteIndex] = ''
            }
            url = config.webGo[config.webSiteIndex] + url
            resList.push({ title: i.innerText, url: url })
        }
        return resList
    }

    //获取章节内容
    async function getContent(pageUrl) {
        const res = await parseDocFromAjax('GET', pageUrl)
        if (config.webType[config.webSiteIndex] == 1) {
            let title = res.cname.replace(" ", '')
            if (res.content.indexOf(title) == -1) return res.content
            title = title + '<br>  '
            res.content = res.content.replace(title, '')
            //console.log('getContent:',res.content)
            return res.content
        }
        return res.querySelector(config.webContent[config.webSiteIndex]).innerHTML
    }


    //解析书源函数
    async function parseMain() {

        //搜索小说名字
        var r = await searchBook(QDgetBookName())
        var a = g_data.bookInfo.authorName
        let ii = 0
        //优先匹配名字相同的
        for (let suoyin in r) {
            if (r[suoyin].bookName == QDgetBookName() || r[suoyin].author == a) {
                ii = suoyin
                break;
            }
        }
        if (r[ii] == undefined) {
            console.log("搜索作者")
            r = await searchBook(a)
            for (let suoyin in r) {
                if (r[suoyin].bookName == QDgetBookName()) {
                    ii = suoyin
                    break;
                }
            }
        }
        //获取第一项结果章节目录
        if (r[ii] == undefined) {
            console.log('该小说暂无资源')
        }
        //  console.log(r[ii])
        const clist = await getChapterList(r[ii])
        if (QDgetBookChapter() == undefined || clist.length == 0) {
            console.log('抓取目录失败')
        }
        console.log('抓取目录成功')
        // console.log(clist)
        //获取章节名
        for (let i in clist) {
            let tit = '' + clist[i].title
            let str = tit
            tit = tit.replace(' ', '')
            //console.log('匹配',tit,QDgetBookChapter())
            var patt1 = /[a-zA-Z\u4e00-\u9fa5]+/g
            var patt2 = /[0-9]+/g
            str = QDgetBookChapter()
            var flag = false
            //排除纯数字章节的影响
            if (tit.match(patt1) == null) {
                tit = tit.match(patt2)
                str = str.match(patt2)
            }
            else if (str.match(patt1) == null) {
                str = str.match(patt2)
                tit = tit.match(patt2) == null ? tit.match(patt1) : tit.match(patt2)
            }
            else {
                str = str.match(patt1)
                tit = tit.match(patt1)
                //有些作者喜欢加第几卷第几章 但是书源网站没有卷名
                var str2 = str.join("").split(/卷|章/)
                var tit2 = tit.join("").split(/卷|章/)
                //模糊读取,若无法精准匹配 尝试模糊名匹配 并设置缓存默认以此方法匹配,默认是2分钟
                console.log(times)
                if (times >= 4 & times < 11) {
                    //自带数字章节名 首个字符串与书源匹配
                    if (str[0] == tit[0]) {
                        flag = true
                        setCookie("times=", times, 1 * 1000 * 60 * 2)//这里修改2可以改缓存时间
                    }
                }
                else if (times >= 11 & times < 17) {
                    //末尾名匹配
                    if (str2[str2.length - 1] == tit2[tit2.length - 1]) {
                        flag = true
                        setCookie("times=", times, 1 * 1000 * 60 * 2)
                    }
                }
                else if (times >= 17) {
                    //中间名匹配
                    if (str2[str2.length - 2] == tit2[tit2.length - 2]) {
                        flag = true
                        setCookie("times=", times, 1 * 1000 * 60 * 2)
                    }
                }
            }
            //  console.log(str[0],tit[0])
            if (str.join("") == tit.join("") || flag == true) {
                console.log('检查到结果')
                const content = await getContent(clist[i].url)
                QDsetContent(content)
                console.log('写入成功')
                notify('小说读取成功')
                return
            }
        }
        times++
        setCookie("times=", times, 1 * 1000 * 60 * 2)
        console.log('目录匹配失败')
        notify('未查询到该小说内容', 'warning')
        throw new Error('该书源解析失败')
    }


    //递归更换书源
    async function mergeOne(index) {
        try {
            if (index) {
                config.webSiteIndex = index
                console.log(index)
            }
            notify(`正在切换到书源${config.webDesc[config.webSiteIndex]}...`, 'info')
            await parseMain()
        } catch (e) {
            console.log(e)
            config.webSiteIndex = (config.webSiteIndex + 1) % 6
            mergeOne()
        }
    }
    //MAIN-BEFORE 主程序预备函数
    if (QDgetChapterOrder() != null) {
        notify(`已订阅章节`)
    } else {
        addMenu()
        //MAIN 主程序
        notify(`您正在阅读${QDgetBookName()}的${QDgetBookChapter()}`)
        mergeOne()
        comment()
        hookSetting()
    }

    // Your code here...
})();

能给个最新的么 我的邮箱是1160727134@qq.com

§
发表于:2023-12-28

大佬,能给个新的吗,邮箱xvttgrer@163.com

§
发表于:2023-12-30

请问手机x浏览器本章说空白的问题该怎么解决,已经照评论区的解决办法把源代码var _csrfToken = getCookieByName("_csrfToken");
改成
function getCookieValue(cookieName) {
var cookies = document.cookie.split(';');
for (var i = 0; i < cookies.length; i++) {
var cookie = cookies[i].trim();
if (cookie.indexOf(cookieName + '=') === 0) {
return cookie.substring(cookieName.length + 1);
}
}
return null;
}

var csrfTokenValue = getCookieValue('_csrfToken');
但依旧没有解决。

§
发表于:2024-01-13

请问手机x浏览器本章说空白的问题该怎么解决,已经照评论区的解决办法把源代码var _csrfToken = getCookieByName("_csrfToken");
改成
function getCookieValue(cookieName) {
var cookies = document.cookie.split(';');
for (var i = 0; i < cookies.length; i++) {
var cookie = cookies[i].trim();
if (cookie.indexOf(cookieName + '=') === 0) {
return cookie.substring(cookieName.length + 1);
}
}
return null;
}

var csrfTokenValue = getCookieValue('_csrfToken');
但依旧没有解决。

这个要看浏览器的。X浏览器不支持,VIA浏览器也不行,我换到狐猴浏览器才可以。

§
发表于:2024-01-14

请问手机x浏览器本章说空白的问题该怎么解决,已经照评论区的解决办法把源代码var _csrfToken = getCookieByName("_csrfToken");
改成
function getCookieValue(cookieName) {
var cookies = document.cookie.split(';');
for (var i = 0; i < cookies.length; i++) {
var cookie = cookies[i].trim();
if (cookie.indexOf(cookieName + '=') === 0) {
return cookie.substring(cookieName.length + 1);
}
}
return null;
}

var csrfTokenValue = getCookieValue('_csrfToken');
但依旧没有解决。

这个要看浏览器的。X浏览器不支持,VIA浏览器也不行,我换到狐猴浏览器才可以。

请问是哪个狐猴浏览器?
有好多不一样的。

§
发表于:2024-01-14

请问手机x浏览器本章说空白的问题该怎么解决,已经照评论区的解决办法把源代码var _csrfToken = getCookieByName("_csrfToken");
改成
function getCookieValue(cookieName) {
var cookies = document.cookie.split(';');
for (var i = 0; i < cookies.length; i++) {
var cookie = cookies[i].trim();
if (cookie.indexOf(cookieName + '=') === 0) {
return cookie.substring(cookieName.length + 1);
}
}
return null;
}

var csrfTokenValue = getCookieValue('_csrfToken');
但依旧没有解决。

这个要看浏览器的。X浏览器不支持,VIA浏览器也不行,我换到狐猴浏览器才可以。

请问是哪个狐猴浏览器?
有好多不一样的。

不是只有一种吗?还有多种?我不清楚,我随便搜索下载的,版本是2.5.3.001

§
发表于:2024-01-22

如果只是本章说错位还好 主要是错位导致后面的本章说直接不显示了 除了换源没别的办法吗

发表回复

登录以发表回复。