22笔趣阁

https://m.22biqu.net

zpccool (13551) 1天前 下载:344

小说 笔趣阁 小说 免费小说 域名多
22笔趣阁(22biqu.net),笔趣阁系列镜像站,海量免费小说。
二维码导入(APP尚未完成该功能)
// @name        22笔趣阁
// @version     2.0.0
// @uuid        22biquge
// @author      Ai
// @url         https://m.22biqu.net
// @logo        https://m.22biqu.net/favicon.ico
// @enabled false
// @tags        笔趣阁,小说,免费小说,域名多
// @description 22笔趣阁(22biqu.net),笔趣阁系列镜像站,海量免费小说。

// ─── 内置测试 ─────────────────────────────────────────────────────────────

async function TEST(type) {
  if (type === '__list__') return ['search', 'explore'];

  if (type === 'search') {
    var results = await search('斗破苍穹', 1);
    if (!results || results.length < 1) return { passed: false, message: '搜索结果为空' };
    var found = false;
    for (var i = 0; i < results.length; i++) {
      if (results[i].author && results[i].author.indexOf('天蚕土豆') !== -1) { found = true; break; }
    }
    if (!found) return { passed: false, message: '搜索结果中未找到作者包含"天蚕土豆"的条目' };
    return { passed: true, message: '搜索"斗破苍穹"返回 ' + results.length + ' 条结果 ✓' };
  }

  if (type === 'explore') {
    var books = await explore(1, '总人气排行');
    if (!books || books.length < 1) return { passed: false, message: '发现页 [总人气排行] 返回为空' };
    return { passed: true, message: '发现页 [总人气排行]: ' + books.length + ' 条结果 ✓' };
  }

  return { passed: false, message: '未知测试类型: ' + type };
}

// ─── 配置 ────────────────────────────────────────────────────────────────

var BASE = 'https://m.22biqu.net';

// ─── 工具 ────────────────────────────────────────────────────────────────

/**
 * 将相对 URL 补全为绝对 URL。
 */
function toAbs(href) {
  if (!href) return '';
  if (href.indexOf('http') === 0) return href;
  return BASE + (href.charAt(0) === '/' ? href : '/' + href);
}

// ─── 搜索 ─────────────────────────────────────────────────────────────────

async function search(keyword, page) {
  legado.log('[search] keyword=' + keyword);

  var url = BASE + '/ss/?searchkey=' + encodeURIComponent(keyword) + '&submit=';
  var html = await legado.http.get(url);
  var doc = legado.dom.parse(html);
  var books = [];

  var items = legado.dom.selectAll(doc, '.bookbox');
  for (var i = 0; i < items.length; i++) {
    var el = items[i];

    var bookUrl = legado.dom.selectAttr(el, 'a[href*="/biqu"]', 'href');
    if (!bookUrl) continue;
    bookUrl = toAbs(bookUrl);

    var name = legado.dom.selectText(el, '.bookname a');
    if (!name) continue;

    var coverUrl = legado.dom.selectAttr(el, 'img', 'data-src')
                || legado.dom.selectAttr(el, 'img', 'data-original')
                || legado.dom.selectAttr(el, 'img', 'src')
                || '';

    var authorBlock = legado.dom.selectText(el, '.author') || '';
    var authorM = authorBlock.match(/作者[::]\s*(\S+)/);
    var author = authorM ? authorM[1] : '';
    var kindM = authorBlock.match(/类型[::]\s*(\S+)/);
    var kind = kindM ? kindM[1] : '';

    var lastChapter = legado.dom.selectText(el, '.update a') || '';

    books.push({
      name: name,
      author: author,
      bookUrl: bookUrl,
      coverUrl: coverUrl,
      lastChapter: lastChapter,
      kind: kind,
    });
  }

  legado.dom.free(doc);
  legado.log('[search] found=' + books.length);
  return books;
}

// ─── 书籍详情 ─────────────────────────────────────────────────────────────

async function bookInfo(bookUrl) {
  legado.log('[bookInfo] url=' + bookUrl);
  var html = await legado.http.get(bookUrl);
  var doc = legado.dom.parse(html);

  var result = {
    name:        legado.dom.selectAttr(doc, '[property="og:novel:book_name"]', 'content') || '',
    author:      legado.dom.selectAttr(doc, '[property="og:novel:author"]', 'content') || '',
    coverUrl:    legado.dom.selectAttr(doc, '[property="og:image"]', 'content') || '',
    intro:       legado.dom.selectAttr(doc, '[property="og:description"]', 'content') || '',
    lastChapter: legado.dom.selectAttr(doc, '[property="og:novel:latest_chapter_name"]', 'content')
              || legado.dom.selectAttr(doc, '[property="og:novel:lastest_chapter_name"]', 'content')
              || '',
    kind:        legado.dom.selectAttr(doc, '[property="og:novel:category"]', 'content') || '',
    tocUrl:      bookUrl,
  };

  legado.dom.free(doc);
  return result;
}

// ─── 章节列表 ─────────────────────────────────────────────────────────────

async function chapterList(tocUrl) {
  legado.log('[chapterList] url=' + tocUrl);
  var chapters = [];
  var seenUrls = {};

  // ── 步骤1:从第一页获取所有分页 URL ─────────────────────────────────────
  // 22biqu.net 在目录底部有 <select id="indexselect">,每个 option value 是一个分页路径。
  // 直接枚举比跟随"下一页"更可靠,不受超时截断影响。
  var html0 = await legado.http.get(tocUrl);
  var doc0  = legado.dom.parse(html0);
  var optVals = legado.dom.selectAllAttrs(doc0, '#indexselect option', 'value');
  legado.dom.free(doc0);

  if (optVals && optVals.length > 0) {
    // ── 模式一:下拉枚举所有分页(推荐)───────────────────────────────────
    legado.log('[chapterList] select mode, pages=' + optVals.length);
    // ── 并发批量抓取所有分页 ────────────────────────────────────────────────
    var pageUrls = [];
    for (var p = 0; p < optVals.length && p < 50; p++) {
      pageUrls.push(toAbs(optVals[p]));
    }
    legado.log('[chapterList] batchGet ' + pageUrls.length + ' pages');
    var batchResults = await legado.http.batchGet(pageUrls);
    for (var p = 0; p < batchResults.length; p++) {
      var res = batchResults[p];
      if (!res.ok) { legado.log('[chapterList] page failed: ' + res.url); continue; }
      var doc  = legado.dom.parse(res.data);
      var links = legado.dom.selectAll(doc, '.directoryArea:not(#chapterlist) a');
      for (var i = 0; i < links.length; i++) {
        var href = legado.dom.attr(links[i], 'href') || '';
        if (!/\/biqu\d+\/\d+\.html/.test(href)) continue;
        var chUrl  = toAbs(href);
        var chName = (legado.dom.text(links[i]) || '').trim();
        if (chName && chUrl && !seenUrls[chUrl]) {
          seenUrls[chUrl] = 1;
          chapters.push({ name: chName, url: chUrl });
        }
      }
      legado.dom.free(doc);
    }
  } else {
    // ── 模式二:跟随"下一页"链接(备用)──────────────────────────────────
    legado.log('[chapterList] nextpage mode');
    var url = tocUrl;
    for (var p = 0; p < 50; p++) {
      var html = await legado.http.get(url);
      var doc  = legado.dom.parse(html);
      var links = legado.dom.selectAll(doc, '.directoryArea:not(#chapterlist) a');
      for (var i = 0; i < links.length; i++) {
        var href = legado.dom.attr(links[i], 'href') || '';
        if (!/\/biqu\d+\/\d+\.html/.test(href)) continue;
        var chUrl  = toAbs(href);
        var chName = (legado.dom.text(links[i]) || '').trim();
        if (chName && chUrl && !seenUrls[chUrl]) {
          seenUrls[chUrl] = 1;
          chapters.push({ name: chName, url: chUrl });
        }
      }
      var nextLink = legado.dom.selectByText(doc, '下一页');
      var nextHref = nextLink ? (legado.dom.attr(nextLink, 'href') || '') : '';
      legado.dom.free(doc);
      if (!nextHref || nextHref.indexOf('javascript') !== -1) break;
      var nextUrl = toAbs(nextHref);
      if (nextUrl === url) break;
      url = nextUrl;
    }
  }

  // 网站目录已为正序(第1章在前),直接返回
  legado.log('[chapterList] total=' + chapters.length);
  return chapters;
}

// ─── 正文 ─────────────────────────────────────────────────────────────────

async function chapterContent(chapterUrl) {
  legado.log('[content] url=' + chapterUrl);
  var paragraphs = [];
  var url = chapterUrl;
  var MAX_PAGES = 10;

  for (var p = 0; p < MAX_PAGES; p++) {
    var html = await legado.http.get(url);
    var doc = legado.dom.parse(html);

    var contentEl = legado.dom.select(doc, '#chaptercontent');
    if (!contentEl) {
      legado.dom.free(doc);
      legado.log('[content] chaptercontent not found at page ' + (p + 1));
      break;
    }

    // 提取每个 <p> 段落的文本
    var pTags = legado.dom.selectAll(contentEl, 'p');
    for (var i = 0; i < pTags.length; i++) {
      var text = (legado.dom.text(pTags[i]) || '').trim();
      if (text && !/本章未完|加入书签|章节报错|点击下一页|笔趣阁/.test(text)) {
        paragraphs.push(text);
      }
    }

    // 翻页:id="pt_next" 的链接
    var nextHref = legado.dom.selectAttr(doc, '#pt_next', 'href');
    legado.dom.free(doc);

    if (!nextHref || nextHref.indexOf('javascript') !== -1) break;
    var nextUrl = toAbs(nextHref);
    if (nextUrl === url) break;
    url = nextUrl;
  }

  return paragraphs.join('\n\n');
}

// ─── 发现页 ──────────────────────────────────────────────────────────────

var EXPLORE_CATEGORIES = [
  { name: '总人气排行', path: '/rank/allvisit/' },
  { name: '月排行榜',   path: '/rank/monthvisit/' },
  { name: '周排行榜',   path: '/rank/weekvisit/' },
  { name: '收藏榜',     path: '/rank/goodnum/' },
  { name: '玄幻魔法',   path: '/fenlei/1_1.html' },
  { name: '武侠修真',   path: '/fenlei/2_1.html' },
  { name: '都市言情',   path: '/fenlei/3_1.html' },
  { name: '历史军事',   path: '/fenlei/4_1.html' },
  { name: '游戏竞技',   path: '/fenlei/5_1.html' },
  { name: '科幻灵异',   path: '/fenlei/6_1.html' },
  { name: '女生耽美',   path: '/fenlei/7_1.html' },
];

/**
 * 从排行/分类页 HTML 中提取书籍列表。
 * 兼容 hot_sale(排行页)和 bookbox(分类页)两种卡片结构。
 */
function parseBooksFromHtml(html) {
  var doc = legado.dom.parse(html);
  var books = [];

  // 先尝试 hot_sale 结构(排行页)
  var items = legado.dom.selectAll(doc, '.hot_sale');
  for (var i = 0; i < items.length; i++) {
    var el = items[i];
    var bookUrl = legado.dom.selectAttr(el, 'a[href*="/biqu"]', 'href');
    if (!bookUrl) continue;
    bookUrl = toAbs(bookUrl);

    var name = legado.dom.selectText(el, '.title') || legado.dom.selectText(el, 'h4') || '';
    name = name.replace(/^\d+\./, '').trim();

    var coverUrl = legado.dom.selectAttr(el, 'img', 'data-src')
                || legado.dom.selectAttr(el, 'img', 'data-original')
                || legado.dom.selectAttr(el, 'img', 'src')
                || '';

    var authorText = legado.dom.selectText(el, '.author') || '';
    var author = authorText.replace(/作者[::]\s*/, '').replace(/\s*[((]\d{4}[\s\S]*$/, '').trim();

    if (name && bookUrl) {
      books.push({ name: name, author: author, bookUrl: bookUrl, coverUrl: coverUrl, kind: '' });
    }
  }

  // 如果 hot_sale 没结果,尝试 bookbox 结构(分类页)
  if (books.length === 0) {
    items = legado.dom.selectAll(doc, '.bookbox');
    for (var j = 0; j < items.length; j++) {
      var el2 = items[j];
      var bookUrl2 = legado.dom.selectAttr(el2, 'a[href*="/biqu"]', 'href');
      if (!bookUrl2) continue;
      bookUrl2 = toAbs(bookUrl2);

      var name2 = legado.dom.selectText(el2, '.bookname a') || '';
      var coverUrl2 = legado.dom.selectAttr(el2, 'img', 'data-src')
                   || legado.dom.selectAttr(el2, 'img', 'data-original')
                   || legado.dom.selectAttr(el2, 'img', 'src')
                   || '';

      var authorBlock2 = legado.dom.selectText(el2, '.author') || '';
      var authorM2 = authorBlock2.match(/作者[::]\s*(\S+)/);
      var author2 = authorM2 ? authorM2[1] : '';
      var kindM2 = authorBlock2.match(/类型[::]\s*(\S+)/);
      var kind2 = kindM2 ? kindM2[1] : '';
      var lastChapter2 = legado.dom.selectText(el2, '.update a') || '';

      if (name2 && bookUrl2) {
        books.push({ name: name2, author: author2, bookUrl: bookUrl2, coverUrl: coverUrl2, lastChapter: lastChapter2, kind: kind2 });
      }
    }
  }

  legado.dom.free(doc);
  return books;
}

async function explore(page, category) {
  legado.log('[explore] page=' + page + ' category=' + category);

  if (category === 'GETALL') {
    var names = [];
    for (var i = 0; i < EXPLORE_CATEGORIES.length; i++) {
      names.push(EXPLORE_CATEGORIES[i].name);
    }
    return names;
  }

  var cat = null;
  for (var j = 0; j < EXPLORE_CATEGORIES.length; j++) {
    if (EXPLORE_CATEGORIES[j].name === category) {
      cat = EXPLORE_CATEGORIES[j];
      break;
    }
  }

  if (!cat) {
    legado.log('[explore] 未知分类: ' + category);
    return [];
  }

  var url = BASE + cat.path;
  legado.log('[explore] url=' + url);
  var html = await legado.http.get(url);
  return parseBooksFromHtml(html);
}

// 设计目标是  发现功能 高级的发现功能 可以返回具体的html 代码让直接在界面内进行渲染,  然后内部也包含执行一些js 功能 ,js 功能  触发后也能转发回来
广告