篱笆好文学

https://m.libahao.com

zpccool (13551) 1天前 下载:509

小说 novel
Real libahao parser. Uses the site's own verification endpoint and never returns placeholder reading tips as content.
二维码导入(APP尚未完成该功能)
// @name        篱笆好文学
// @uuid        libahaowenxue
// @version     2.2.0
// @author      Ai
// @url         https://m.libahao.com
// @enabled     true
// @type        novel
// @tags        novel
// @description Real libahao parser. Uses the site's own verification endpoint and never returns placeholder reading tips as content.

async function TEST(type) {
  if (type === '__list__') return ['search', 'explore', 'bookInfo', 'chapterList', 'chapterContent'];
  if (type === 'search') {
    var r = await search('\u6597\u7834', 1);
    return { passed: r && r.length > 0, message: 'search results=' + (r ? r.length : 0) };
  }
  if (type === 'explore') {
    var e = await explore(1, '\u7384\u5e7b');
    return { passed: e && e.length > 0, message: 'explore results=' + (e ? e.length : 0) };
  }
  if (type === 'bookInfo') {
    var b = await bookInfo(BASE + '/book/10856992_4008/');
    return { passed: !!b.name, message: 'book=' + b.name + ' author=' + b.author };
  }
  if (type === 'chapterList') {
    var cs = await chapterList(BASE + '/book/10856992_4008/');
    return { passed: cs && cs.length > 20, message: 'chapters=' + (cs ? cs.length : 0) };
  }
  if (type === 'chapterContent') {
    var text = await chapterContent(BASE + '/book/10856992_4008/1.html');
    var ok = text && text.length > 200 && !hasBadText(text);
    return { passed: ok, message: 'content len=' + (text ? text.length : 0) };
  }
}

var BASE = 'https://m.libahao.com';
var UA = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36';
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,en;q=0.8',
  'Referer': BASE + '/'
};
var CATS = {
  '\u9996\u9875': '/',
  '\u7384\u5e7b': '/xuanhuan/',
  '\u4fee\u771f': '/xiuzhen/',
  '\u90fd\u5e02': '/dushi/',
  '\u5386\u53f2': '/lishi/',
  '\u7f51\u6e38': '/wangyou/',
  '\u79d1\u5e7b': '/kehuan/',
  '\u5973\u9891': '/nvpin/',
  '\u5176\u4ed6': '/qita/'
};
var SCAN_PATHS = ['/', '/xuanhuan/', '/xiuzhen/', '/dushi/', '/lishi/', '/wangyou/', '/kehuan/', '/nvpin/', '/qita/'];
var VERIFIED = false;
var BAD_TEXTS = [
  '\u7eaf\u51c0\u6a21\u5f0f',
  '\u9000\u51fa\u7eaf\u51c0\u6a21\u5f0f',
  '\u900f\u660e\u7262\u7b3c',
  '\u771f\u6b63\u7684\u4e16\u754c\u4e00\u76f4\u90fd\u5728\u90a3\u91cc',
  '\u8bf7\u5230\u7f51\u7ad9\u9605\u8bfb',
  '\u8acb\u5230\u7db2\u7ad9\u95b1\u8b80',
  '\u5730\u533a\u62e6\u622a',
  'Just a moment',
  'Enable JavaScript',
  'userverify'
];

function headers(extra) {
  var h = {};
  for (var k in HEADERS) h[k] = HEADERS[k];
  if (extra) for (var e in extra) h[e] = extra[e];
  return h;
}

function abs(href) {
  if (!href) return '';
  href = ('' + href).replace(/&/g, '&').trim();
  if (href.indexOf('//') === 0) return 'https:' + href;
  if (href.indexOf('http') === 0) {
    return href.replace(/^https?:\/\/(?:www\.|m\.)?libahao\.com/i, BASE);
  }
  return BASE + (href.charAt(0) === '/' ? href : '/' + href);
}

function decodeText(s) {
  return (s || '')
    .replace(/ | /gi, ' ')
    .replace(/&/g, '&')
    .replace(/&lt;/g, '<')
    .replace(/&gt;/g, '>')
    .replace(/&quot;/g, '"')
    .replace(/&#39;/g, "'");
}

function clean(s) {
  return decodeText(s || '').replace(/<[^>]+>/g, '').replace(/\s+/g, ' ').trim();
}

function hasBadText(text) {
  text = '' + (text || '');
  for (var i = 0; i < BAD_TEXTS.length; i++) {
    if (text.indexOf(BAD_TEXTS[i]) >= 0) return true;
  }
  return false;
}

async function ensureVerified(referer) {
  if (VERIFIED) return;
  try {
    await legado.http.post(
      BASE + '/v2',
      't=' + Date.now(),
      headers({
        'Content-Type': 'application/x-www-form-urlencoded',
        'Origin': BASE,
        'Referer': referer || BASE + '/'
      })
    );
  } catch (e) {}
  VERIFIED = true;
}

async function getHtml(url, referer, needVerify) {
  url = abs(url);
  if (needVerify) await ensureVerified(referer || url);
  return '' + await legado.http.get(url, headers({ 'Referer': referer || BASE + '/' }));
}

function parseBooks(html, kind) {
  html = '' + (html || '');
  if (!html || hasBadText(html)) return [];
  var out = [];
  var seen = {};

  function pushBook(href, name, author, intro, cover) {
    if (!/\/book\/\d+_\d+\/?$/.test(href || '')) return;
    var url = abs(href);
    if (seen[url]) return;
    name = clean(name);
    if (!name || name === '\u4e66\u67b6') return;
    seen[url] = true;
    out.push({
      name: name,
      author: clean(author).replace(/^\u4f5c\u8005[:\uff1a]\s*/, ''),
      bookUrl: url,
      tocUrl: url,
      coverUrl: abs(cover),
      intro: clean(intro),
      kind: kind || ''
    });
  }

  var re = /<a\b[^>]*href=["']([^"']*\/book\/\d+_\d+\/?)["'][^>]*>([\s\S]*?)<\/a>/gi;
  var m;
  while ((m = re.exec(html)) !== null) {
    var inner = m[2] || '';
    var titleMatch = inner.match(/class=["'][^"']*book-title[^"']*["'][^>]*>([\s\S]*?)<\/[^>]+>/i)
      || inner.match(/class=["'][^"']*title[^"']*["'][^>]*>([\s\S]*?)<\/[^>]+>/i)
      || inner.match(/alt=["']([^"']+)["']/i);
    var coverMatch = inner.match(/<img[^>]+(?:data-src|src)=["']([^"']+)["']/i);
    pushBook(m[1], titleMatch ? titleMatch[1] : inner, '', '', coverMatch ? coverMatch[1] : '');
  }

  var doc = legado.dom.parse(html);
  var items = legado.dom.selectAll(doc, '.book-item, .novel-item, .item, li, a[href*="/book/"]');
  for (var i = 0; i < items.length; i++) {
    var item = items[i];
    var a = legado.dom.select(item, 'a[href*="/book/"]') || item;
    var href = legado.dom.attr(a, 'href') || '';
    if (!/\/book\/\d+_\d+\/?$/.test(href)) continue;

    var name = legado.dom.selectText(item, '.book-title')
      || legado.dom.selectText(item, '.title')
      || legado.dom.attr(a, 'title')
      || legado.dom.text(a)
      || '';

    var img = legado.dom.select(item, 'img');
    var cover = img ? (legado.dom.attr(img, 'data-src') || legado.dom.attr(img, 'src') || '') : '';
    var author = legado.dom.selectText(item, '.book-author') || legado.dom.selectText(item, '.author') || '';
    var intro = legado.dom.selectText(item, '.book-description') || legado.dom.selectText(item, '.description') || legado.dom.selectText(item, 'p') || '';
    pushBook(href, name, author, intro, cover);
  }
  legado.dom.free(doc);
  return out;
}

function filterBooks(pool, keyword) {
  var lower = (keyword || '').toLowerCase();
  var out = [];
  var seen = {};
  for (var i = 0; i < pool.length; i++) {
    var b = pool[i];
    if (!b || seen[b.bookUrl]) continue;
    seen[b.bookUrl] = true;
    var hay = (b.name + ' ' + b.author + ' ' + b.intro).toLowerCase();
    if (hay.indexOf(lower) >= 0) out.push(b);
  }
  return out;
}

async function search(keyword, page) {
  if (page > 1 || !keyword) return [];
  var pool = [];
  try {
    pool = pool.concat(parseBooks(await getHtml(BASE + '/sou?wd=' + encodeURIComponent(keyword)), ''));
  } catch (e) {}
  for (var i = 0; i < SCAN_PATHS.length; i++) {
    try {
      pool = pool.concat(parseBooks(await getHtml(BASE + SCAN_PATHS[i]), ''));
    } catch (e2) {}
  }
  return filterBooks(pool, keyword);
}

async function bookInfo(bookUrl) {
  bookUrl = abs(bookUrl);
  var html = await getHtml(bookUrl);
  if (hasBadText(html)) return { name: '', bookUrl: bookUrl, tocUrl: bookUrl };
  var doc = legado.dom.parse(html);
  var name = legado.dom.selectAttr(doc, 'meta[property="og:novel:book_name"]', 'content')
    || legado.dom.selectText(doc, '.book-info-title')
    || legado.dom.selectText(doc, '.book-title')
    || legado.dom.selectText(doc, 'h1')
    || '';
  var author = legado.dom.selectAttr(doc, 'meta[property="og:novel:author"]', 'content') || legado.dom.selectText(doc, '.book-author') || '';
  var intro = legado.dom.selectAttr(doc, 'meta[property="og:description"]', 'content') || legado.dom.selectText(doc, '.book-desc') || legado.dom.selectText(doc, '.intro') || '';
  var kind = legado.dom.selectAttr(doc, 'meta[property="og:novel:category"]', 'content') || '';
  var latest = legado.dom.selectAttr(doc, 'meta[property="og:novel:latest_chapter_name"]', 'content')
    || legado.dom.selectAttr(doc, 'meta[property="og:novel:lastest_chapter_name"]', 'content')
    || '';
  var latestUrl = legado.dom.selectAttr(doc, 'meta[property="og:novel:latest_chapter_url"]', 'content')
    || legado.dom.selectAttr(doc, 'meta[property="og:novel:lastest_chapter_url"]', 'content')
    || '';
  var cover = legado.dom.selectAttr(doc, 'meta[property="og:image"]', 'content') || legado.dom.selectAttr(doc, '.book-cover img', 'src') || '';
  legado.dom.free(doc);
  return {
    name: clean(name),
    author: clean(author).replace(/^\u4f5c\u8005[:\uff1a]\s*/, ''),
    bookUrl: bookUrl,
    tocUrl: bookUrl,
    coverUrl: abs(cover),
    intro: clean(intro),
    kind: clean(kind),
    lastChapter: clean(latest),
    latestChapter: clean(latest),
    latestChapterUrl: abs(latestUrl)
  };
}

async function chapterList(tocUrl) {
  tocUrl = abs(tocUrl);
  var html = await getHtml(tocUrl);
  if (hasBadText(html)) return [];
  var doc = legado.dom.parse(html);
  var blocks = legado.dom.selectAll(doc, '.chapter-list');
  var root = doc;
  for (var i = 0; i < blocks.length; i++) {
    if ((legado.dom.text(blocks[i]) || '').indexOf('\u7ae0\u8282\u5217\u8868') >= 0) {
      root = blocks[i];
      break;
    }
  }
  var links = legado.dom.selectAll(root, 'a[href*="/book/"]');
  var out = [];
  var prefix = '';
  var seen = {};
  for (var j = 0; j < links.length; j++) {
    var firstHref = legado.dom.attr(links[j], 'href') || '';
    if (/\/book\/\d+_\d+\/\d+\.html/.test(firstHref)) {
      prefix = abs(firstHref).replace(/\/\d+\.html(?:[?#].*)?$/, '/');
      break;
    }
  }
  for (var k = 0; k < links.length; k++) {
    var href = legado.dom.attr(links[k], 'href') || '';
    if (!/\/book\/\d+_\d+\/\d+\.html/.test(href)) continue;
    var name = clean(legado.dom.text(links[k]));
    if (!name) continue;
    var url = prefix ? prefix + (out.length + 1) + '.html' : abs(href);
    var key = name + '@' + url;
    if (seen[key]) continue;
    seen[key] = true;
    out.push({ name: name, url: url });
  }
  legado.dom.free(doc);
  return out;
}

function cleanContentHtml(html) {
  var text = decodeText(html || '')
    .replace(/<script[\s\S]*?<\/script>/gi, '')
    .replace(/<style[\s\S]*?<\/style>/gi, '')
    .replace(/<!--[\s\S]*?-->/g, '')
    .replace(/<br\s*\/?>/gi, '\n')
    .replace(/<\/p>\s*<p[^>]*>/gi, '\n')
    .replace(/<p[^>]*>/gi, '')
    .replace(/<\/div>/gi, '\n')
    .replace(/<[^>]+>/g, '')
    .replace(/\u672c\u7ae0\u672a\u5b8c[\s\S]*$/g, '')
    .replace(/\u8bf7\u6536\u85cf[\s\S]*$/g, '')
    .replace(/[ \t]+\n/g, '\n')
    .replace(/\n[ \t]+/g, '\n')
    .replace(/\n\s*\n+/g, '\n')
    .replace(/[ \t]+/g, ' ')
    .trim();
  return hasBadText(text) ? '' : text;
}

function chapterNumber(url) {
  var m = (url || '').match(/\/(\d+)(?:_\d+)?\.html(?:[?#].*)?$/);
  return m ? m[1] : '';
}

function continueUrl(html, baseNum) {
  if (!baseNum) return '';
  var re = /href=["']([^"']+)["'][^>]*>[\s\S]{0,120}?\u7ee7\u7eed\u9605\u8bfb/i;
  var m = (html || '').match(re);
  if (!m) return '';
  var next = abs(m[1]);
  var ok = new RegExp('/' + baseNum + '_\\d+\\.html(?:[?#].*)?$').test(next);
  return ok ? next : '';
}

function extractChapterContent(html) {
  if (!html || hasBadText(html)) return '';
  var doc = legado.dom.parse('' + html);
  var el = legado.dom.select(doc, '#chapterContent')
    || legado.dom.select(doc, '.chapter-content')
    || legado.dom.select(doc, '#content');
  var content = el ? cleanContentHtml(legado.dom.html(el)) : '';
  legado.dom.free(doc);
  return content;
}

async function chapterContent(chapterUrl) {
  var start = abs(chapterUrl);
  var baseNum = chapterNumber(start);
  var url = start;
  var referer = start.replace(/\/\d+(?:_\d+)?\.html(?:[?#].*)?$/, '/');
  var pieces = [];
  var seen = {};

  await ensureVerified(referer);
  for (var i = 0; i < 6 && url && !seen[url]; i++) {
    seen[url] = true;
    var html = await getHtml(url, referer, true);
    var text = extractChapterContent(html);
    if (!text) {
      VERIFIED = false;
      await ensureVerified(referer);
      html = await getHtml(url, referer, true);
      text = extractChapterContent(html);
    }
    if (text) pieces.push(text);
    var next = continueUrl(html, baseNum);
    if (!next) break;
    referer = url;
    url = next;
  }

  var result = pieces.join('\n');
  if (!result || hasBadText(result)) return '';
  return result;
}

async function explore(page, category) {
  if (!category || category === 'GETALL') return Object.keys(CATS);
  if (page > 1) return [];
  var path = CATS[category] || CATS['\u9996\u9875'];
  return parseBooks(await getHtml(BASE + path), category);
}
广告