奇书网
https://www.qishu33.com
zpccool (13551) 16小时前 下载:303
小说 小说 免费
奇书网 - 好看的小说大全免费在线阅读和txt下载。
// @name 奇书网
// @uuid qishu
// @version 1.0.0
// @author AI
// @url https://www.qishu33.com
// @type novel
// @enabled true
// @tags 小说,免费
// @description 奇书网 - 好看的小说大全免费在线阅读和txt下载。
var BASE = 'https://www.qishu33.com';
var CATEGORIES = [
{ title: '玄幻', id: '1' },
{ title: '仙侠', id: '2' },
{ title: '都市', id: '3' },
{ title: '职场', id: '4' },
{ title: '历史', id: '5' },
{ title: '军事', id: '6' },
{ title: '科幻', id: '7' },
{ title: '游戏', id: '8' },
{ title: '灵异', id: '9' },
{ title: '乡村', id: '10' },
{ title: '现言', id: '11' },
{ title: '古言', id: '12' },
{ title: '穿越', id: '13' },
{ title: '总裁', id: '14' },
{ title: '浪漫', id: '15' },
{ title: '耽美', id: '16' }
];
function absUrl(url, baseUrl) {
if (!url) return '';
if (url.indexOf('http') === 0) return url;
if (url.indexOf('//') === 0) return 'https:' + url;
if (url.indexOf('/') === 0) return BASE + url;
var base = baseUrl || BASE;
var lastSlash = base.lastIndexOf('/');
if (lastSlash <= 7) return base + '/' + url;
return base.substring(0, lastSlash + 1) + url;
}
var DEFAULT_HEADERS = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
'Accept': 'text/html,application/xhtml+xml',
'Accept-Language': 'zh-CN,zh;q=0.9',
'Referer': BASE
};
// ─── 解析书籍 ─────────────────────────────────────────────────────────
function parseBooks(html, defaultKind) {
var books = [];
var seen = {};
var items = html.match(/<div\s+class=["']item["'][^>]*>([\s\S]*?)<\/div>\s*<\/div>/gi);
if (items && items.length > 0) {
for (var i = 0; i < items.length; i++) {
var itemHtml = items[i];
var linkMatch = itemHtml.match(/href=["']([^"']*\/xiaoshuo\/\d+[^"']*)["'][^>]*>\s*<[^>]*>([^<]*)/i);
if (linkMatch) {
var bookUrl = absUrl(linkMatch[1]);
var name = linkMatch[2].replace(/^《|》$/g, '').trim();
if (!name) { var nm2 = itemHtml.match(/<h[23][^>]*>([^<]+)/); if (nm2) name = nm2[1].replace(/^《|》$/g, '').trim(); }
if (name && bookUrl && !seen[bookUrl]) {
seen[bookUrl] = true;
var am = itemHtml.match(/作者[::]\s*([^<\n]+)/);
books.push({ name: name, author: am ? am[1].trim() : '', bookUrl: bookUrl, coverUrl: '', kind: defaultKind || '', lastChapter: '' });
}
}
}
if (books.length > 0) return books;
}
var linkPattern = /href=["']([^"']*\/xiaoshuo\/(\d+)[^"']*)["'][^>]*>([^<]+)<\/a>/gi;
var match;
while ((match = linkPattern.exec(html)) !== null) {
var bookUrl2 = absUrl(match[1]);
var name2 = match[3].replace(/^《|》$/g, '').trim();
if (!name2 || name2.indexOf('章') !== -1 || name2.indexOf('节') !== -1) continue;
if (bookUrl2.indexOf('/xiaoshuo/') === -1) continue;
if (!seen[bookUrl2]) {
seen[bookUrl2] = true;
books.push({ name: name2, author: '', bookUrl: bookUrl2, coverUrl: '', kind: defaultKind || '', lastChapter: '' });
}
}
return books;
}
// ─── 解析书籍详情 ─────────────────────────────────────────────────────
function parseBookInfo(html) {
var result = { name: '', author: '', coverUrl: '', intro: '', kind: '', lastChapter: '' };
var metas = [
{ key: 'name', pat: /<meta\s+property=["']og:novel:book_name["']\s+content=["']([^"']+)["']/i },
{ key: 'name', pat: /<meta\s+property=["']og:title["']\s+content=["']([^"']+)["']/i },
{ key: 'author', pat: /<meta\s+property=["']og:novel:author["']\s+content=["']([^"']+)["']/i },
{ key: 'coverUrl', pat: /<meta\s+property=["']og:image["']\s+content=["']([^"']+)["']/i },
{ key: 'intro', pat: /<meta\s+name=["']description["']\s+content=["']([^"']+)["']/i },
{ key: 'kind', pat: /<meta\s+property=["']og:novel:category["']\s+content=["']([^"']+)["']/i },
{ key: 'lastChapter', pat: /<meta\s+property=["']og:novel:latest_chapter_name["']\s+content=["']([^"']+)["']/i }
];
for (var i = 0; i < metas.length; i++) {
if (result[metas[i].key]) continue;
var m = html.match(metas[i].pat);
if (m && m[1]) result[metas[i].key] = m[1];
}
if (!result.author) { var am = html.match(/作者[::]\s*([^<"\n]+)/); if (am) result.author = am[1].trim(); }
if (!result.intro) { var im = html.match(/简介[::]\s*([^\n<]+)/); if (im) result.intro = im[1].trim(); }
if (!result.lastChapter) { var cm = html.match(/最新章节[::]?\s*\[([^\]]+)\]/); if (cm) result.lastChapter = cm[1]; }
if (!result.name) result.name = '未知书名';
if (!result.author) result.author = '未知作者';
return result;
}
// ─── 搜索 ─────────────────────────────────────────────────────────────
async function search(keyword, page) {
try {
if (!keyword) return [];
legado.log('[search] keyword=' + keyword);
var encoded = await legado.urlEncodeCharset(keyword, 'utf-8');
var searchUrl = BASE + '/search.html?searchkey=' + encoded;
if (page && page > 1) searchUrl += '&page=' + page;
var html = await legado.http.get(searchUrl, DEFAULT_HEADERS);
var books = parseBooks(html, '');
legado.log('[search] found ' + books.length);
return books;
} catch (e) { legado.log('[search] error: ' + e); return []; }
}
// ─── 发现页 ────────────────────────────────────────────────────────────
async function explore(page, category) {
try {
if (category === 'GETALL' || !category) {
var names = [];
for (var i = 0; i < CATEGORIES.length; i++) names.push(CATEGORIES[i].title);
return names;
}
var catId = null;
for (var j = 0; j < CATEGORIES.length; j++) {
if (CATEGORIES[j].title === category) { catId = CATEGORIES[j].id; break; }
}
if (!catId) return [];
var url = BASE + '/type/' + catId + '_0_0_lastupdate_' + (page || 1) + '.html';
var html = await legado.http.get(url, DEFAULT_HEADERS);
return parseBooks(html, category);
} catch (e) { legado.log('[explore] error: ' + e); return []; }
}
// ─── 书籍详情 ─────────────────────────────────────────────────────────
async function bookInfo(bookUrl) {
try {
var url = absUrl(bookUrl);
var html = await legado.http.get(url, DEFAULT_HEADERS);
var info = parseBookInfo(html);
return {
name: info.name, author: info.author, bookUrl: url, tocUrl: url,
coverUrl: absUrl(info.coverUrl), intro: info.intro,
kind: info.kind, lastChapter: info.lastChapter
};
} catch (e) {
return { name: '未知', author: '未知', bookUrl: bookUrl, tocUrl: bookUrl,
coverUrl: '', intro: '', kind: '', lastChapter: '' };
}
}
// ─── 章节列表 ─────────────────────────────────────────────────────────
async function chapterList(tocUrl) {
try {
var url = absUrl(tocUrl);
legado.log('[chapterList] url=' + url);
var html = await legado.http.get(url, DEFAULT_HEADERS);
// bookId from URL
var bookId = '';
var idm = url.match(/\/xiaoshuo\/(\d+)/);
if (idm) bookId = idm[1];
// total chapters from paixu(ID, TOTAL)
var totalCh = '';
var pm = html.match(/paixu\(\d+,\s*(\d+)\)/);
if (pm) totalCh = pm[1];
legado.log('[chapterList] bookId=' + bookId + ' totalCh=' + totalCh);
if (!bookId || !totalCh) { legado.log('[chapterList] missing params'); return []; }
// 从书页 #toplist 解析前几章
var chapters = [];
var seen = {};
var rePage = /href=["']([^"']*read_\d+\.html)["'][^>]*>([^<]*)<\/a>/gi;
var mP;
while ((mP = rePage.exec(html)) !== null) {
var hrefP = mP[1];
var textP = mP[2].trim();
var ciP = textP.indexOf('第');
var cleanP = ciP >= 0 ? textP.substring(ciP) : textP;
cleanP = cleanP.replace(/^\s+/, '').replace(/\s+$/, '');
if (cleanP.indexOf('第') !== -1 && cleanP.indexOf('章') !== -1) {
var chUrlP = absUrl(hrefP, BASE);
if (!seen[chUrlP]) { seen[chUrlP] = true; chapters.push({ name: cleanP, url: chUrlP }); }
}
}
legado.log('[chapterList] from page: ' + chapters.length);
// 获取剩余章节: /novelsearch/novel/getdlist/?id=ID&num=TOTAL&order=asc
var api = BASE + '/novelsearch/novel/getdlist/?id=' + bookId + '&num=' + totalCh + '&order=asc';
legado.log('[chapterList] api=' + api);
var apiHtml = await legado.http.get(api, DEFAULT_HEADERS);
legado.log('[chapterList] api response=' + (apiHtml ? apiHtml.length : 0));
if (apiHtml && apiHtml.length >= 500) {
var reApi = /href=["']([^"']*read_\d+\.html)["'][^>]*>([^<]*)<\/a>/gi;
var mA;
while ((mA = reApi.exec(apiHtml)) !== null) {
var hrefA = mA[1];
var textA = mA[2].trim();
var ciA = textA.indexOf('第');
var cleanA = ciA >= 0 ? textA.substring(ciA) : textA;
cleanA = cleanA.replace(/^\s+/, '').replace(/\s+$/, '');
if (cleanA.indexOf('第') !== -1 && cleanA.indexOf('章') !== -1) {
var chUrlA = absUrl(hrefA, BASE);
if (!seen[chUrlA]) { seen[chUrlA] = true; chapters.push({ name: cleanA, url: chUrlA }); }
}
}
}
// 按 read_N 数字排序
chapters.sort(function(a, b) {
var na = parseInt((a.url.match(/read_(\d+)/) || [0,0])[1], 10);
var nb = parseInt((b.url.match(/read_(\d+)/) || [0,0])[1], 10);
return na - nb;
});
legado.log('[chapterList] total=' + chapters.length);
return chapters;
} catch (e) { legado.log('[chapterList] error: ' + e); return []; }
}
// ─── 章节正文 ─────────────────────────────────────────────────────────
async function chapterContent(chapterUrl) {
try {
var url = absUrl(chapterUrl);
var html = await legado.http.get(url, DEFAULT_HEADERS);
var doc = legado.dom.parse(html);
var contentEl = null;
var sels = ['#content', '.content', '.chapter-content', '.book-content', '.xscontent', '.read-content', '#J_content'];
for (var i = 0; i < sels.length; i++) {
var el = legado.dom.select(doc, sels[i]);
if (el && legado.dom.html(el).length > 200) { contentEl = el; break; }
}
if (!contentEl) contentEl = legado.dom.select(doc, 'body');
legado.dom.remove(contentEl, 'script, style, .ad, .ads, .share');
var hc = legado.dom.html(contentEl);
legado.dom.free(doc);
return processContent(hc);
} catch (e) { legado.log('[content] error: ' + e); return ''; }
}
function processContent(htmlContent) {
if (!htmlContent) return '';
var t = htmlContent
.replace(/<br\s*\/?>\s*<br\s*\/?>/gi, '{{P}}')
.replace(/<br\s*\/?>/gi, '\n')
.replace(/<[^>]+>/g, '')
.replace(/\{\{P\}\}/g, '\n\n');
t = t.replace(/ /g, ' ').replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"').replace(/'/g, "'");
t = t.replace(/上一章[\s\S]*?下一章/g, '').replace(/可以使用回车快捷键阅读[\s\S]*/g, '').replace(/记住网站域名[\s\S]*/g, '').replace(/天才一秒记住[\s\S]*/g, '').replace(/请收藏[\s\S]*/g, '').replace(/本章未完[\s\S]*/g, '').replace(/章节报错[\s\S]*/g, '');
return t.replace(/[ \t]+/g, ' ').replace(/\n{3,}/g, '\n\n').trim();
}
// ─── 测试 ────────────────────────────────────────────────────────────
async function TEST(type) {
if (type === '__list__') return ['search', 'explore', 'bookInfo', 'chapterList', 'chapterContent'];
if (type === 'search') { var r = await search('斗破苍穹', 1); if (!r || r.length < 1) return { passed: false, message: '无结果' }; return { passed: true, message: '搜索' + r.length + '条' }; }
if (type === 'explore') { var b = await explore(1, '都市'); if (!b || b.length < 1) return { passed: false, message: '空' }; return { passed: true, message: '发现' + b.length + '条' }; }
if (type === 'bookInfo') { var r = await bookInfo('https://www.qishu33.com/xiaoshuo/142580/'); if (!r || !r.name) return { passed: false, message: '空' }; return { passed: true, message: r.name + ' ' + r.author }; }
if (type === 'chapterList') { var r = await chapterList('https://www.qishu33.com/xiaoshuo/142580/'); if (!r || r.length < 1) return { passed: false, message: '空' }; return { passed: true, message: r.length + '章 ' + r[0].name + ' ~ ' + r[r.length-1].name }; }
if (type === 'chapterContent') { var r = await chapterContent('https://www.qishu33.com/xiaoshuo/wudaochangsheng0congliehukaishishuashulianduxiuxing/read_1.html'); if (!r || r.length < 100) return { passed: false, message: '短' }; return { passed: true, message: r.length + '字 ' + r.substring(0, 30) }; }
return { passed: false, message: '?' };
}