35小说

http://www.35ge.info

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

小说 免费 小说 免费小说
35中文网(原 swsk.org),免费网文小说站。
二维码导入(APP尚未完成该功能)
// @uuid        019e120a-185b-7567-9c3d-3cb5e291e768
// @name        35小说
// @version     1.0.0
// @author      Ethereal
// @url         http://www.35ge.info
// @type        novel
// @logo        http://www.35ge.info/favicon.ico
// @enabled     true
// @tags        免费,小说,免费小说
// @description 35中文网(原 swsk.org),免费网文小说站。

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

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.35ge.info/102/102234/');
    return { passed: !!r.name, message: 'bookInfo name=' + r.name };
  }

  if (type === 'chapterList') {
    var r = await chapterList('http://www.35ge.info/102/102234/');
    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.35ge.info/xs/102/102234/17644533.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.35ge.info';

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

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

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

/** 简介:去掉 meta/转义里混入的 \n 字面量与折行,避免阅读器里出现 “\n” 乱码 */
function normalizeBookIntro(s) {
  if (!s) return '';
  s = s.replace(/\\n/g, '');
  s = s.replace(/\\r/g, '');
  s = s.replace(/[\r\n\u2028\u2029]+/g, ' ');
  s = s.replace(/[ \u00A0\u3000]+/g, ' ').trim();
  return s;
}

/** 与 bookInfo 一致,从详情页 HTML 取 og:image */
function coverUrlFromBookPageHtml(html) {
  var doc = legado.dom.parse(html);
  var u = legado.dom.selectAttr(doc, '[property="og:image"]', 'content') || '';
  legado.dom.free(doc);
  if (!u) return '';
  return toAbs(u);
}

/** 分类/搜索列表无可靠封面,对每本书请求详情页取封面 */
async function fillCoversFromBookPages(books) {
  if (!books.length) return books;
  var urls = [];
  for (var i = 0; i < books.length; i++) {
    urls.push(books[i].bookUrl);
  }
  legado.log('[cover] batchGet book pages n=' + urls.length);
  var results = await legado.http.batchGet(urls, null, 8);
  for (var j = 0; j < results.length; j++) {
    if (results[j].ok && books[j]) {
      books[j].coverUrl = coverUrlFromBookPageHtml(results[j].data);
    }
  }
  return books;
}

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

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

  var url = BASE + '/modules/article/search.php?searchkey=' + encodeURIComponent(keyword);
  var html = await legado.http.get(url);

  var doc = legado.dom.parse(html);
  var books = [];

  // 搜索结果在 .novelslist2 表格中,每行一本书
  // 表头 li[0] 需跳过,数据行从 li[1] 开始
  var items = legado.dom.selectAll(doc, '.novelslist2 ul li');
  for (var i = 0; i < items.length; i++) {
    var el = items[i];

    // .s2 内含书名链接
    var bookUrl = legado.dom.selectAttr(el, '.s2 a', 'href');
    if (!bookUrl) continue;
    bookUrl = toAbs(bookUrl);

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

    var author = (legado.dom.selectText(el, '.s4') || '').trim();
    var kind = (legado.dom.selectText(el, '.s1') || '').trim();
    var lastChapter = (legado.dom.selectText(el, '.s3 a') || '').trim();

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

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

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

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

  var intro = legado.dom.selectText(doc, '#intro') || '';
  if (!intro) {
    intro = legado.dom.selectAttr(doc, '[property="og:description"]', 'content') || '';
  }
  intro = normalizeBookIntro(intro);

  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:       intro,
    lastChapter: legado.dom.selectAttr(doc, '[property="og:novel:latest_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 = {};

  var html = await legado.http.get(tocUrl);
  var doc = legado.dom.parse(html);

  // 仅「正文」块中的章节(与「最新章节」块分离,避免乱序与重复)
  var links = legado.dom.selectAll(doc, '#list dl dt:nth-of-type(2) ~ 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);
  legado.log('[chapterList] total=' + chapters.length);
  return chapters;
}

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

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

  var html = await legado.http.get(chapterUrl);
  var doc = legado.dom.parse(html);

  var contentEl = legado.dom.select(doc, '#content');
  if (!contentEl) {
    legado.dom.free(doc);
    legado.log('[content] #content not found');
    return '';
  }

  // 鸿蒙等环境上 legado.dom.remove 全页再 parse 可能取不到 #content,故不用 remove+二次 parse
  var contentHtml = legado.dom.html(contentEl) || '';
  legado.dom.free(doc);

  contentHtml = contentHtml.replace(/<script[\s\S]*?<\/script>/gi, '');

  var parts = contentHtml.split(/<br\s*\/?>|<\/?p[^>]*>/i);
  var paragraphs = [];
  for (var k = 0; k < parts.length; k++) {
    var line = parts[k].replace(/<[^>]+>/g, '').replace(/&nbsp;/g, ' ').replace(/\s+/g, ' ').trim();
    if (line) {
      paragraphs.push(line);
    }
  }
  var text = paragraphs.join('\n\n');

  // 插入的广告括号:(飞速小说网 www/feisuxs.com) 等
  text = text.replace(/[\((][^))]*飞速小说网[^))]*[\))]/g, '');
  text = text.replace(/飞速小说网[\s\u00A0\u3000]*www[\s\/\uFF0F/..]*feisuxs\.com/gi, '');
  text = text.replace(/chaptererror\s*\(\s*\)\s*;?/gi, '');

  text = text.replace(/本章未完|加入书签|章节报错|请收藏|最快更新|天才一秒记住|35中文网|35ge\.info/g, '');
  return text.replace(/\s*\n\s*/g, '\n\n').trim();
}

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

var EXPLORE_CATEGORIES = [
  { name: '玄幻魔法', path: '/xs/1-default-0-0-0-0-0-0-1.html' },
  { name: '武侠修真', path: '/xs/2-default-0-0-0-0-0-0-1.html' },
  { name: '都市言情', path: '/xs/3-default-0-0-0-0-0-0-1.html' },
  { name: '历史军事', path: '/xs/4-default-0-0-0-0-0-0-1.html' },
  { name: '游戏竞技', path: '/xs/5-default-0-0-0-0-0-0-1.html' },
  { name: '科幻恐怖', path: '/xs/6-default-0-0-0-0-0-0-1.html' },
  { name: '其他类型', path: '/xs/7-default-0-0-0-0-0-0-1.html' },
  { name: '完本小说', path: '/xs/0-default-0-0-0-0-2-0-1.html' },
];

async function explore(page, category) {
  legado.log('[explore] category=' + category + ' page=' + page);
  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 path = cat.path.replace(/-\d+\.html$/, '-' + page + '.html');
  var url = BASE + path;
  var html = await legado.http.get(url);
  var doc = legado.dom.parse(html);
  var books = [];

  if (cat.name === '完本小说') {
    var itemsW = legado.dom.selectAll(doc, '#main div.topbooks ul li');
    for (var w = 0; w < itemsW.length; w++) {
      var ew = itemsW[w];
      var bookUrlW = legado.dom.selectAttr(ew, 'a[href*="/xs/"]', 'href');
      if (!bookUrlW) continue;
      bookUrlW = toAbs(bookUrlW);
      var nameW = legado.dom.selectText(ew, 'a[href*="/xs/"]');
      if (!nameW) continue;
      books.push({
        name: nameW,
        author: '',
        bookUrl: bookUrlW,
        coverUrl: '',
      });
    }
  } else {
    var items = legado.dom.selectAll(doc, '#newscontent .l ul li');
    for (var i = 0; i < items.length; i++) {
      var el = items[i];
      var bookUrl = legado.dom.selectAttr(el, '.s2 a', 'href');
      if (!bookUrl) continue;
      bookUrl = toAbs(bookUrl);

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

      var author = (legado.dom.selectText(el, '.s4') || '').trim();

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

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