365小说网

http://www.shukuge.com

ethereal-essence (13554) 16小时前 下载:305

小说 免费 小说 免费小说
365小说网(shukuge.com),免费全本小说在线阅读站。
二维码导入(APP尚未完成该功能)
// @uuid        019e120e-21b4-7377-8ec3-4d067a4350ac
// @name        365小说网
// @version     1.0.1
// @author      Ethereal
// @url         http://www.shukuge.com
// @type        novel
// @logo        http://www.shukuge.com/favicon.ico
// @enabled     true
// @tags        免费,小说,免费小说
// @description 365小说网(shukuge.com),免费全本小说在线阅读站。

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

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

  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 || typeof books[0] === 'string') return { passed: false, message: '发现页 [玄幻] 返回为空或格式错误' };
    return { passed: true, message: '发现页 [玄幻]: ' + books.length + ' 条结果 ✓' };
  }

  if (type === 'bookInfo') {
    var r = await bookInfo('http://www.shukuge.com/book/166751/');
    return { passed: !!r.name, message: 'bookInfo name=' + r.name };
  }

  if (type === 'chapterList') {
    var r = await chapterList('http://www.shukuge.com/book/166751/index.html');
    return { passed: r.length > 0, message: 'chapterList cnt=' + r.length + ' first=' + (r[0] ? r[0].name : 'N/A') };
  }

  if (type === 'chapterContent') {
    var r = await chapterContent('http://www.shukuge.com/book/166751/53009545.html');
    return { passed: r.length > 100, message: 'chapterContent len=' + r.length + ' first=' + r.substring(0, 40) };
  }

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

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

var BASE = 'http://www.shukuge.com';

/** 章节 URL 正则:匹配 /book/数字/数字.html */
var CHAPTER_URL_PATTERN = /\/book\/\d+\/\d+\.html/;

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

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

/** 分类/发现列表页:封面在封面链内 img,站点使用 src */
function listItemCoverUrl(el) {
  return legado.dom.selectAttr(el, 'a.cover img', 'src') || '';
}

function parseListItemMeta(el) {
  var author = '';
  var kind = '';
  var spans = legado.dom.selectAll(el, '.bookdesc .sp span');
  if (spans && spans.length > 0) {
    author = (legado.dom.text(spans[0]) || '').replace(/作者[::]\s*/, '').trim();
  }
  if (spans && spans.length > 1) {
    kind = (legado.dom.text(spans[1]) || '').replace(/分类[::]\s*/, '').trim();
  }
  var lastChapter = legado.dom.selectText(el, '.bookdesc .desc a') || '';
  return { author: author, kind: kind, lastChapter: lastChapter };
}

/** 分类页 URL:第 1 页为 /path/,第 n 页为 /path/n */
function categoryPageUrl(path, page) {
  var p = Number(page) || 1;
  if (p <= 1) {
    return BASE + path;
  }
  var base = path.replace(/\/+$/, '');
  return BASE + base + '/' + p;
}

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

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

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

  // 搜索结果在 .listitem 中
  var items = legado.dom.selectAll(doc, '.listitem');
  for (var i = 0; i < items.length; i++) {
    var el = items[i];

    var bookUrl = legado.dom.selectAttr(el, '.bookdesc > a', 'href');
    if (!bookUrl) continue;
    bookUrl = toAbs(bookUrl);

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

    var coverUrl = listItemCoverUrl(el);
    var meta = parseListItemMeta(el);

    books.push({
      name: name,
      author: meta.author,
      bookUrl: bookUrl,
      coverUrl: coverUrl,
      lastChapter: meta.lastChapter,
      kind: meta.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 name = legado.dom.selectAttr(doc, '.bookdcover img', 'alt') || '';
  var coverSrc = legado.dom.selectAttr(doc, '.bookdcover img', 'src') || '';
  var coverUrl = coverSrc ? toAbs(coverSrc) : '';
  var author = legado.dom.selectText(doc, '.bookdmore p:nth-child(3) a') || '';
  var intro = legado.dom.selectText(doc, '.bookdtext p') || '';
  intro = intro.replace(/下载后请在24小时之内删除[\s\S]*/i, '').trim();

  var lastChapter = legado.dom.selectText(doc, '.bookdmore p:nth-child(7) a') || '';
  var kind = legado.dom.selectText(doc, '.bookdmore p:nth-child(1) a') || '';

  var tocHref = legado.dom.selectAttr(doc, '.bookdtext a.btn-primary[href*="/index.html"]', 'href');
  if (!tocHref) {
    throw new Error('[bookInfo] missing 在线阅读 toc link');
  }
  var tocUrl = toAbs(tocHref);

  var result = {
    name: name,
    author: author,
    coverUrl: coverUrl,
    intro: intro,
    lastChapter: lastChapter,
    kind: kind,
    tocUrl: tocUrl,
  };

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

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

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

  // 365小说网目录可能分页,通过 <option> 枚举所有分页
  var html0 = await legado.http.get(tocUrl);
  var doc0 = legado.dom.parse(html0);
  var optVals = legado.dom.selectAllAttrs(doc0, 'option', 'value');
  legado.dom.free(doc0);

  if (optVals && optVals.length > 1) {
    // 分页模式
    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]));
    }
    var batchResults = await legado.http.batchGet(pageUrls);
    for (var p = 0; p < batchResults.length; p++) {
      var res = batchResults[p];
      if (!res.ok) {
        throw new Error('[chapterList] batch page failed: ' + res.url);
      }
      var doc = legado.dom.parse(res.data);
      var links = legado.dom.selectAll(doc, 'dl dd a');
      for (var i = 0; i < links.length; i++) {
        var href = legado.dom.attr(links[i], 'href') || '';
        if (!CHAPTER_URL_PATTERN.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] single-page toc');
    var html1 = await legado.http.get(tocUrl);
    var doc1 = legado.dom.parse(html1);
    var links1 = legado.dom.selectAll(doc1, 'dl dd a');
    for (var j = 0; j < links1.length; j++) {
      var href1 = legado.dom.attr(links1[j], 'href') || '';
      if (!CHAPTER_URL_PATTERN.test(href1)) continue;
      var chUrl1 = toAbs(href1);
      var chName1 = (legado.dom.text(links1[j]) || '').trim();
      if (chName1 && chUrl1 && !seenUrls[chUrl1]) {
        seenUrls[chUrl1] = 1;
        chapters.push({ name: chName1, url: chUrl1 });
      }
    }
    legado.dom.free(doc1);
  }

  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, '#content #content');
    if (!contentEl) {
      legado.dom.free(doc);
      legado.log('[content] #content #content not found at page ' + (p + 1));
      break;
    }

    var nextLink = legado.dom.selectByText(doc, '下一页');
    var nextHref = nextLink ? (legado.dom.attr(nextLink, 'href') || '') : '';

    var contentHtml = legado.dom.html(contentEl) || '';
    legado.dom.free(doc);

    var lines = contentHtml.split(/<br\s*\/?>|<\/?p[^>]*>/i);
    for (var i = 0; i < lines.length; i++) {
      var text = lines[i].replace(/<[^>]+>/g, '').replace(/&nbsp;/g, ' ').replace(/\s+/g, ' ').trim();
      if (text && !/本章未完|加入书签|章节报错|365小说网|shukuge\.com/.test(text)) {
        paragraphs.push(text);
      }
    }

    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: '/i-xuanhuan/' },
  { name: '言情',     path: '/i-yanqing/' },
  { name: '穿越',     path: '/i-chuanyue/' },
  { name: '重生',     path: '/i-chongsheng/' },
  { name: '架空',     path: '/i-jiakong/' },
  { name: '总裁',     path: '/i-zongcai/' },
  { name: '武侠',     path: '/i-wuxia/' },
  { name: '仙侠',     path: '/i-xianxia/' },
  { name: '耽美',     path: '/i-danmei/' },
  { name: '都市',     path: '/i-dushi/' },
  { name: '军事',     path: '/i-junshi/' },
  { name: '网游',     path: '/i-wangyou/' },
  { name: '悬疑',     path: '/i-xuanyi/' },
  { name: '文学',     path: '/i-wenxue/' },
  { name: '科幻',     path: '/i-kehuan/' },
  { name: '修真',     path: '/i-xiuzhen/' },
  { name: '历史',     path: '/i-lishi/' },
  { name: '其他',     path: '/i-qita/' },
  { name: '最新小说', path: '/new/' },
  { name: '排行榜',   path: '/top/' },
];

async function explore(page, category) {
  legado.log('[explore] page=' + page + ' category=' + category);
  var cat = null;
  for (var i = 0; i < EXPLORE_CATEGORIES.length; i++) {
    if (EXPLORE_CATEGORIES[i].name === category) {
      cat = EXPLORE_CATEGORIES[i];
      break;
    }
  }
  if (!cat) return EXPLORE_CATEGORIES.map(function(c) { return c.name; });

  var url = categoryPageUrl(cat.path, page);
  var html = await legado.http.get(url);
  var doc = legado.dom.parse(html);
  var books = [];

  var items = legado.dom.selectAll(doc, '.listitem');
  for (var i = 0; i < items.length; i++) {
    var el = items[i];
    var bookUrl = legado.dom.selectAttr(el, '.bookdesc > a', 'href');
    if (!bookUrl) continue;
    bookUrl = toAbs(bookUrl);

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

    var coverUrl = listItemCoverUrl(el);
    var meta = parseListItemMeta(el);
    books.push({
      name: name,
      author: meta.author,
      bookUrl: bookUrl,
      coverUrl: coverUrl,
      kind: meta.kind,
      lastChapter: meta.lastChapter,
    });
  }

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