篱笆好文学
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.
// @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(/</g, '<')
.replace(/>/g, '>')
.replace(/"/g, '"')
.replace(/'/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);
}