八一中文

https://www.blxs.info

zpccool (13551) 16小时前 下载:380

小说 免费 小说 八一中文
优化正文提取,过滤导航/推荐等非正文内容;修复章节目录排序(去重+正序)
二维码导入(APP尚未完成该功能)
// @name 八一中文
// @uuid bayizhongwen
// @version 1.0.0
// @author AI
// @url https://www.blxs.info
// @logo https://www.blxs.info/favicon.ico
// @enabled true
// @type novel
// @tags 免费,小说,八一中文
// @description 优化正文提取,过滤导航/推荐等非正文内容;修复章节目录排序(去重+正序)

async function TEST(type) {
    if (type === '__list__') return ['search', 'explore', 'bookInfo', 'chapterList', 'chapterContent'];
    if (type === 'search') {
        var r = await search('斗破苍穹', 1);
        return { passed: r && r.length > 0, message: '搜索结果数: ' + (r ? r.length : 0) };
    }
    if (type === 'explore') {
        var books = await explore(1, '玄幻小说');
        return { passed: books && books.length > 0, message: '发现页 [玄幻小说]: ' + (books ? books.length : 0) + ' 条结果' };
    }
    if (type === 'bookInfo') {
        var r = await bookInfo('https://www.blxs.info/2/2680/');
        return { passed: !!r.name, message: '书名: ' + r.name + ' 作者: ' + r.author };
    }
    if (type === 'chapterList') {
        var r = await chapterList('https://www.blxs.info/2/2680/');
        return { passed: r.length > 0, message: '章节数: ' + r.length };
    }
    if (type === 'chapterContent') {
        var chapters = await chapterList('https://www.blxs.info/2/2680/');
        if (!chapters || chapters.length === 0) return { passed: false, message: '无法获取章节列表' };
        var r = await chapterContent(chapters[0].url);
        return { passed: r.length > 100, message: '正文长度: ' + r.length };
    }
}

function extractCover(el, selector) {
    return legado.dom.selectAttr(el, selector, 'data-src') ||
           legado.dom.selectAttr(el, selector, 'data-original') ||
           legado.dom.selectAttr(el, selector, 'src') ||
           '';
}

var BASE = 'https://www.blxs.info';
var UA = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36';

function toAbs(href) {
    if (!href) return '';
    if (href.indexOf('http') === 0) return href;
    return BASE + (href.charAt(0) === '/' ? href : '/' + href);
}

function cleanText(text) {
    if (!text) return '';
    return text.replace(/ /g, ' ').replace(/\s+/g, ' ').trim();
}

async function httpGet(url, referer) {
    var headers = {
        'User-Agent': UA,
        'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
        'Accept-Language': 'zh-CN,zh;q=0.9',
        'Referer': referer || BASE
    };
    return await legado.http.get(url, headers);
}

async function search(keyword, page) {
    var url = BASE + '/modules/article/search.php?searchkey=' + encodeURIComponent(keyword);
    var html = await httpGet(url);
    if (!html) return [];
    var doc = legado.dom.parse(html);
    var books = [];
    var rows = legado.dom.selectAll(doc, '.grid tr, #main table tr, table tr');
    for (var i = 0; i < rows.length; i++) {
        var row = rows[i];
        var link = legado.dom.select(row, 'a');
        if (!link) continue;
        var name = cleanText(legado.dom.text(link));
        if (!name || name === '文章名称' || name === '小说名称') continue;
        var bookUrl = toAbs(legado.dom.attr(link, 'href'));
        var author = '';
        var tds = legado.dom.selectAll(row, 'td');
        if (tds && tds.length >= 3) author = cleanText(legado.dom.text(tds[2]));
        var coverUrl = extractCover(row, 'img');
        if (coverUrl) coverUrl = toAbs(coverUrl);
        books.push({ name: name, author: author, bookUrl: bookUrl, coverUrl: coverUrl, lastChapter: '', kind: '' });
    }
    legado.dom.free(doc);
    return books;
}

async function bookInfo(bookUrl) {
    var html = await httpGet(bookUrl);
    if (!html) return { name: '', author: '', coverUrl: '', intro: '', kind: '', tocUrl: bookUrl, lastChapter: '' };
    var doc = legado.dom.parse(html);
    var name = legado.dom.selectText(doc, '#maininfo h1') || legado.dom.selectText(doc, '#info h1') || legado.dom.selectText(doc, 'h1') || '';
    if (!name) {
        var title = legado.dom.selectText(doc, 'title') || '';
        name = title.split(' - ')[0].trim();
    }
    var coverUrl = extractCover(doc, '#fmimg img');
    coverUrl = toAbs(coverUrl);
    var intro = legado.dom.selectText(doc, '#intro') || legado.dom.selectText(doc, '.intro') || '';
    var author = '', kind = '', lastChapter = '';
    var infoPs = legado.dom.selectAll(doc, '#info p');
    if (infoPs.length === 0) infoPs = legado.dom.selectAll(doc, '#maininfo p');
    for (var i = 0; i < infoPs.length; i++) {
        var txt = legado.dom.text(infoPs[i]);
        if (txt.indexOf('作者') !== -1) author = txt.replace(/.*作者[::]\s*/, '').trim();
        if (txt.indexOf('分类') !== -1) kind = txt.replace(/.*(?:分类|类别)[::]\s*/, '').trim();
        if (txt.indexOf('最新章节') !== -1) lastChapter = txt.replace(/.*(?:最新章节|更新)[::]\s*/, '').trim();
    }
    if (!author) {
        var fullText = legado.dom.selectText(doc, 'body') || '';
        var m = fullText.match(/作者[::]\s*([^\s<]+)/);
        if (m) author = m[1];
    }
    legado.dom.free(doc);
    return {
        name: cleanText(name), author: cleanText(author), coverUrl: coverUrl,
        intro: cleanText(intro), kind: cleanText(kind), lastChapter: cleanText(lastChapter),
        tocUrl: bookUrl
    };
}

async function chapterList(tocUrl) {
    var html = await httpGet(tocUrl);
    if (!html) return [];
    var doc = legado.dom.parse(html);
    var links = legado.dom.selectAll(doc, '#list dd a');
    if (links.length === 0) links = legado.dom.selectAll(doc, '.listmain dd a, #list a');
    
    var chapters = [];
    for (var i = 0; i < links.length; i++) {
        var href = legado.dom.attr(links[i], 'href');
        if (!href || href.indexOf('javascript:') === 0) continue;
        var name = cleanText(legado.dom.text(links[i]));
        if (name) chapters.push({ name: name, url: toAbs(href) });
    }
    
    // 修复章节排序:反向去重,得到正确从第一章开始的顺序
    var seen = new Set();
    var unique = [];
    for (var j = chapters.length - 1; j >= 0; j--) {
        var key = chapters[j].url;
        if (!seen.has(key)) {
            seen.add(key);
            unique.push(chapters[j]);
        }
    }
    unique.reverse();
    legado.dom.free(doc);
    return unique;
}

async function chapterContent(chapterUrl) {
    var html = await httpGet(chapterUrl, BASE + '/');
    if (!html) return '';

    var doc = legado.dom.parse(html);
    var content = legado.dom.selectText(doc, '#content') || '';

    if (!content) {
        legado.dom.free(doc);
        return '';
    }

    content = content.replace(/&nbsp;/g, ' ').replace(/&lt;/g, '<').replace(/&gt;/g, '>').replace(/&amp;/g, '&').replace(/&quot;/g, '"');

    var lines = content.split('\n');
    var out = [];
    var inContent = false;
    var chapterTitleSeen = false;

    for (var i = 0; i < lines.length; i++) {
        var line = lines[i].trim();
        if (line.length === 0) continue;

        // === 一、跳过网站导航/分类列表 ===
        if (/^(八一中文|八一中文网|blxs\.info|www\.blxs\.info)[网\s]*$/.test(line)) continue;
        if (/^(玄幻小说|修真小说|都市小说|穿越小说|网游小说|科幻小说|悬疑小说|历史小说|军事小说|言情小说|其他小说|全本小说|排行榜单?)$/.test(line)) continue;

        // === 二、跳过面包屑导航(含 > 分隔符)===
        if (line.indexOf(' > ') !== -1 && line.indexOf('第') !== -1) continue;
        if (/^[\s\S]*>[>\s]*第\d+章/.test(line)) continue;

        // === 三、跳过章节操作栏(投票、书签、目录等)===
        if (/^(投推荐票|上一章|下一章|加入书签|章节报错|本章未完|返回书页|手机版|繁體|简体)/.test(line)) continue;
        if (/^(推荐票|月票|打赏|目录|首页|我的书架|阅读记录|报错|最新网址)/.test(line)) continue;
        if (/(&larr;|&rarr;|←|→)/.test(line) && line.length < 30) continue;

        // === 四、跳过"热门推荐"区块 ===
        if (/^热门推荐/.test(line)) { inContent = false; continue; }
        if (!inContent && /^(黄金瞳|沈知心|凡人修仙|农女殊色|天道图书馆|轮回乐园|诡秘之主|牧神记|雪中悍刀行|大奉打更人|深空彼岸|择日飞升|星门|问道红尘|长生界|完美世界|遮天|斗破苍穹|斗罗大陆|武动乾坤|吞噬星空|莽荒纪|求魔|我欲封天|仙逆|盘龙|寸芒|星辰变|九鼎记|傲世九重天|武神|绝世武神|武炼巅峰|斗战神|元尊|太古神王|万古神帝|圣墟|源天机|混沌剑神|逆天邪神|龙符|帝霸|无敌天下|择天|将夜|庆余年|琅琊榜|赘婿|雪中|大道朝天|剑来|牧狐记|择天记|全职高手|暗黑破坏神|魔兽世界|英雄联盟|王者荣耀|原神|崩坏|明日方舟|阴阳师|FGO|碧蓝航线|少女前线|公主连结|原神启动|崩坏三|明日方舟舟|方舟指令|蔚蓝档案|鸣潮|绝区零|燕云十六声|第五人格|光遇|我的世界|Minecraft|泰拉瑞亚|饥荒|星露谷物语|动物森友会|塞尔达|艾尔登法环|只狼|黑暗之魂|血源诅咒|仁王|怪猎|塞尔达传说|旷野之息|王国之泪|博德之门| Baldur|Divinity|神界原罪|极乐迪斯科| Disco|Elysium|赛博朋克|Cyberpunk|巫师|The Witcher|GTA|侠盗猎车手|Grand Theft Auto|RDR|荒野大镖客|Red Dead|生化危机|Resident Evil|RE|恶灵附身|Dead Space|死亡空间|死亡搁浅|Stray|空洞骑士|Hollow Knight|奥日|茶杯头|Cuphead|蔚蓝|Celeste|哈迪斯|Hades|杀戮尖塔|Slay the Spire|以撒的结合|Binding of Isaac|挺进地牢|Enter the Gungeon|雨中冒险|Rain World|空洞骑士|丝之歌|Hollow Knight: Silksong|银河战士|Metroid|密特罗德|Metroid Dread|恐惧饥荒|Don't Starve|缺氧|Oxygen Not Included|环世界|RimWorld|异化之地|Terraria|星露谷|Stardew Valley|动物森友会|Animal Crossing|集合啦|New Horizons|健身环大冒险|Ring Fit Adventure|马力欧卡|Mario Kart|任天堂|Nintendo|Switch|PS5|PlayStation|Xbox|Steam|EPIC|Ubisoft|EA|Activision|Blizzard|Bungie|FromSoftware|宫崎英高|Miyazaki|小岛秀夫|Kojima|Todd Howard|Bethesda|CDPR|CD Projekt Red|Rockstar|R星)$/.test(line)) { inContent = false; continue; }

        // === 五、检测正文开始(第一个看起来像正文的段落)===
        // 正文段落通常:长度 > 15 字,不含特殊标记,不是纯标题
        if (!inContent && line.length > 20 && !/^第\d+章/.test(line) && !/^[{}()\[\]\/\\<>]+$/.test(line) && !/^[\d\s\-:]+$/.test(line)) {
            inContent = true;
        }

        // === 六、处理章节标题(只保留一次)===
        if (/^第\d+章\s+.+/.test(line)) {
            if (!chapterTitleSeen) {
                chapterTitleSeen = true;
                inContent = true;
                continue; // 标题不加入正文
            } else {
                continue; // 重复标题跳过
            }
        }

        // === 七、收集正文行 ===
        if (inContent && line.length > 0) {
            // 最后防线:排除明显非正文的内容
            if (line.length < 5) continue;
            if (/^[\d\s\-:.]+$/.test(line)) continue; // 纯数字/符号
            if (/^(http|www\.|https?:)/.test(line)) continue; // URL
            if (line.indexOf('javascript:') !== -1) continue;
            if (/^[{}()\[\]\/\\<>]{3,}$/.test(line)) continue; // 纯括号
            out.push(line);
        }
    }

    legado.dom.free(doc);
    var result = out.join('\n\n');
    
    // 后处理:清理首尾可能的残留
    result = result.replace(/^[\s\n]+/, '').replace(/[\s\n]+$/, '');
    
    return result;
}

async function explore(page, category) {
    legado.log('[EXPLORE] page=' + page + ', category=' + category);
    if (category === 'GETALL') {
        return ['玄幻小说', '修真小说', '都市小说', '穿越小说', '网游小说', '科幻小说', '悬疑小说', '历史小说', '军事小说', '言情小说', '其他小说', '全本小说', '排行榜'];
    }
    var catMap = {
        '玄幻小说': '/xuanhuanxiaoshuo/', '修真小说': '/xiuzhenxiaoshuo/', '都市小说': '/dushixiaoshuo/',
        '穿越小说': '/chuanyuexiaoshuo/', '网游小说': '/wangyouxiaoshuo/', '科幻小说': '/kehuanxiaoshuo/',
        '悬疑小说': '/xuanyixiaoshuo/', '历史小说': '/lishixiaoshuo/', '军事小说': '/junshixiaoshuo/',
        '言情小说': '/yanqingxiaoshuo/', '其他小说': '/qitaxiaoshuo/', '全本小说': '/quanben/', '排行榜': '/paihangbang/'
    };
    var path = catMap[category];
    if (!path) return [];
    var url = BASE + path;
    if (page > 1) url = url + page + '/';
    legado.log('[EXPLORE] 请求 URL: ' + url);
    var headers = { 'User-Agent': UA, 'Referer': BASE + '/' };
    var html = await legado.http.get(url, headers);
    if (!html) return [];
    var doc = legado.dom.parse(html);
    var items = legado.dom.selectAll(doc, '.novelslist2 li');
    if (items.length === 0) items = legado.dom.selectAll(doc, '.novelslist li');
    if (items.length === 0) items = legado.dom.selectAll(doc, '#main li');
    
    var books = [];
    for (var i = 0; i < items.length; i++) {
        var item = items[i];
        var link = legado.dom.select(item, 'a');
        if (!link) continue;
        var name = legado.dom.text(link).trim();
        if (!name || name === '文章名称') continue;
        var bookUrl = legado.dom.attr(link, 'href');
        if (!bookUrl) continue;
        bookUrl = toAbs(bookUrl);
        var author = '';
        var authorEl = legado.dom.select(item, '.s2, .author, span');
        if (authorEl) author = legado.dom.text(authorEl).replace(/作者[::]\s*/, '').trim();
        
        books.push({
            name: name,
            bookUrl: bookUrl,
            author: author,
            coverUrl: '',
            kind: category,
            intro: ''
        });
    }
    legado.dom.free(doc);
    return books;
}
广告