存书啦

https://www.cunshu.la

laizhiyu (6509)09/29 01:18

【技术实现】
搜索:HTML 搜索(GET /search.php?q=)
发现:CSS 选择器(library.php 分页)
详情:CSS 选择器(详情页与目录同页)
目录:CSS 选择器(chapter-chip)
正文:CSS@html + JS 解码,反爬字符自动还原
登录:POST 表单登录(loginUi + loginUrl,Cookie 持久化)
【局限性】
- 网站有 GOEDGE WAF 防护,必须使用移动端 UA 请求头
- 章节以"段"为单位(TXT 分段),非传统章节
- 列表页作者字段实为上传者昵称
- 作者规则 v4:作者只存在于正文预览文本/书名,五级兜底(行首「作者 X」全行扫描 > 预览行内「书名 作者:X」 > 书名内嵌 > 书名by作者 > 整页兜底),自动截断后缀、屏蔽免责话术,源站无作者则留空
- 登录需在书源设置中配置邮箱和密码(切勿在公开群组分享含账号的书源)
二维码导入
{
    "bookSourceComment": "【技术实现】\n搜索:HTML 搜索(GET \/search.php?q=)\n发现:CSS 选择器(library.php 分页)\n详情:CSS 选择器(详情页与目录同页)\n目录:CSS 选择器(chapter-chip)\n正文:CSS@html + JS 解码,反爬字符自动还原\n登录:POST 表单登录(loginUi + loginUrl,Cookie 持久化)\n【局限性】\n- 网站有 GOEDGE WAF 防护,必须使用移动端 UA 请求头\n- 章节以\"段\"为单位(TXT 分段),非传统章节\n- 列表页作者字段实为上传者昵称\n- 作者规则 v4:作者只存在于正文预览文本\/书名,五级兜底(行首「作者 X」全行扫描 > 预览行内「书名 作者:X」 > 书名内嵌 > 书名by作者 > 整页兜底),自动截断后缀、屏蔽免责话术,源站无作者则留空\n- 登录需在书源设置中配置邮箱和密码(切勿在公开群组分享含账号的书源)",
    "bookSourceGroup": "小说",
    "bookSourceName": "存书啦",
    "bookSourceType": 0,
    "bookSourceUrl": "https:\/\/www.cunshu.la",
    "bookUrlPattern": "https?:\/\/www\\.cunshu\\.la\/read\/[A-Za-z0-9+\/=]+",
    "customButton": false,
    "customOrder": 56,
    "enabled": true,
    "enabledCookieJar": true,
    "enabledExplore": true,
    "eventListener": false,
    "exploreUrl": "最新上传::\/library.php?sort=latest&page={{page}}\n最多阅读::\/library.php?sort=hot&page={{page}}",
    "header": "{\"User-Agent\":\"Mozilla\/5.0 (Linux; Android 10; SM-G975F) AppleWebKit\/537.36 (KHTML, like Gecko) Chrome\/120.0.0.0 Mobile Safari\/537.36\"}",
    "lastUpdateTime": "1790615938266",
    "loginUi": "[{\"name\": \"登录邮箱\", \"type\": \"text\", \"style\": {\"layout_flexGrow\": 1, \"layout_flexBasisPercent\": 1}}, {\"name\": \"登录密码\", \"type\": \"text\", \"style\": {\"layout_flexGrow\": 1, \"layout_flexBasisPercent\": 1}}]",
    "loginUrl": "function login() {\n    var info = source.getLoginInfoMap();\n    if (!info) throw new Error('请先输入邮箱和密码');\n    var email = info.get('登录邮箱');\n    var pwd = info.get('登录密码');\n    email = email == null ? '' : String(email).trim();\n    pwd = pwd == null ? '' : String(pwd);\n    if (!email || !pwd) throw new Error('请先输入邮箱和密码');\n    var url = 'https:\/\/www.cunshu.la\/login.php';\n    var body = 'email=' + encodeURIComponent(email) + '&password=' + encodeURIComponent(pwd);\n    var headers = {'Content-Type': 'application\/x-www-form-urlencoded'};\n    var resp = java.post(url, body, headers, 30000);\n    if (!resp) throw new Error('登录请求失败');\n    var respBody = resp.body();\n    if (!respBody) throw new Error('登录响应为空');\n    if (respBody.indexOf('邮箱或密码错误') >= 0) throw new Error('邮箱或密码错误');\n    if (respBody.indexOf('auth-form') >= 0) throw new Error('登录失败,请重试');\n}",
    "respondTime": 4302,
    "ruleBookInfo": {
        "author": "@js:\n\/\/ 作者规则 v4:规则作用域无 baseElement,一律用 result(整页HTML) 自解析 jsoup\n\/\/ 五级兜底:①行首「作者 X\/作者:X」全行扫描 ②预览行内「书名 作者:X」③书名内嵌 ④书名by作者 ⑤整页兜底\nvar NL = String.fromCharCode(10);\nvar stop = '写文|不易|回复|评论|声明|原创|地址|建议|支持|请到|的话|表示|群里|群号';\nvar CUT = \/[\\s((【\\[「《\\\/、,,]\/;\nfunction clean(s) {\n  s = String(s);\n  s = s.replace(\/^\\s+\/, '').replace(\/\\s+$\/, '');\n  s = s.replace(\/^[\\s::,,.。、;;~~\\-—]+\/, '');\n  s = s.replace(\/[\\s::,,.。、;;~~\\-—]+$\/, '');\n  return s;\n}\nfunction pick(t) {\n  t = String(t);\n  var i = t.search(CUT);\n  if (i > 0) t = t.substring(0, i);\n  t = clean(t);\n  if (!t) return '';\n  if (t.length > 18) return '';\n  if (new RegExp('^(' + stop + ')$').test(t)) return '';\n  if (new RegExp('(写文|不易|群号|原创地址|回复或评论|请到|的话|声明)').test(t)) return '';\n  return t;\n}\nvar html = '';\ntry { html = String(result == null ? '' : result); } catch (e) { html = ''; }\nvar lines = [];\nvar title = '';\ntry {\n  var d = Packages.org.jsoup.Jsoup.parse(html);\n  var pt = d.select('.preview-text');\n  if (pt.size() > 0) {\n    var h = String(pt.get(0).html()).replace(\/<br\\s*\\\/?>\/gi, NL);\n    h = String(Packages.org.jsoup.parser.Parser.unescapeEntities(h, true));\n    h = h.replace(\/<[^>]+>\/g, '');\n    lines = String(h).split(NL);\n  }\n  var nm = d.select('.ph-name');\n  if (nm.size() > 0) title = String(nm.get(0).text());\n} catch (e1) {}\nvar name = '';\nvar i;\nfor (i = 0; i < lines.length; i++) {\n  var m = clean(lines[i]).match(\/^作\\s*者\\s*[\\s::]+(.+)$\/);\n  if (m) { name = pick(m[1]); if (name) break; }\n}\nif (!name) {\n  for (i = 0; i < lines.length; i++) {\n    var m2 = clean(lines[i]).match(\/^.{0,40}?作\\s*者\\s*[\\s::]+(.+)$\/);\n    if (m2) { name = pick(m2[1]); if (name) break; }\n  }\n}\nif (!name) {\n  var m3 = clean(title).match(\/作\\s*者\\s*[\\s::]+(.+)$\/);\n  if (m3) name = pick(m3[1]);\n}\nif (!name) {\n  for (i = 0; i < lines.length && i < 6; i++) {\n    var m4 = clean(lines[i]).match(\/^.{2,40}?[bB][yY]([^bB\\s]{1,16})$\/);\n    if (m4) { name = pick(m4[1]); if (name) break; }\n  }\n}\nif (!name) {\n  var flat = String(html).replace(\/<[^>]+>\/g, ' ');\n  var m5 = flat.match(\/作\\s*者\\s*[\\s::]+\\s*([^\\s,。;、!?!?*()()【】\\[\\]{}《》\"']{1,18})\/);\n  if (m5) name = pick(m5[1]);\n}\nresult = name;",
        "intro": ".preview-text@text",
        "name": ".ph-name@text"
    },
    "ruleContent": {
        "content": ".chapter-text@html@js:\nvar html = result;\nhtml = html.replace(\/<span class=\"kw-protect\">([\\s\\S]*?)<\\\/span>\/g, function(m, inner) {\n    return inner.replace(\/<i class=\"cr\" data-cp=\"([0-9a-fA-F]+)\"><\\\/i>\/g, function(m2, cp) {\n        var code = parseInt(cp, 16);\n        if (code && !isNaN(code)) return String.fromCodePoint(code);\n        return '';\n    });\n});\nhtml = html.replace(\/<br\\s*\\\/?>\/gi, '\\n');\nhtml = html.replace(\/<[^>]+>\/g, '');\nhtml = html.replace(\/\\n{3,}\/g, '\\n\\n');\nresult = html;",
        "nextContentUrl": "text.下一段@href",
        "title": ".chapter-title@text"
    },
    "ruleExplore": {
        "bookList": ".resource-list .resource-card",
        "bookUrl": "@href",
        "name": ".rc-name@text"
    },
    "ruleSearch": {
        "bookList": ".resource-card-group .resource-card",
        "bookUrl": ".rc-name-link@href",
        "name": ".rc-name@text"
    },
    "ruleToc": {
        "chapterList": ".chapter-overlay-grid .chapter-chip",
        "chapterName": "@text",
        "chapterUrl": "@href"
    },
    "searchUrl": "\/search.php?q={{key}}",
    "weight": 0
}
广告