爱丽书屋
https://m.ailisw.com
laizhiyu (6509)5天前
更新了一下源注释提示。。
AI的又一力作,AI大王Orz。一个有四重反爬技术的网站。经过我和AI大王的通力合作,主要是AI大王写代码我调试,经过了一下午的折磨,AI写代码,我摸鱼的时候调试。终于弄出来了。即使如此,现在也有一点不太完美,有时候需要点登录按钮,弹出他的网页,然后点一下搜索那个框,回返回才能继续搜到书。就这样吧,将就用。虽然现在大把源,你们不一定会有人用这么垃圾的源。。但这是我刚开始学写源的时候的,遇到的第一个究极难的网站,记到了现在,终于完成了心中的坎。虽然是AI写的。AI大王Orz。
{
"bookSourceComment": "如果用不了,可以点一下登录,在下搜索框返回刷新。\n爱丽书屋:https:\/\/m.ailisw.com\n技术特点:\n1. 正文通过JS加载.dat文件,内容为控制字符压缩的HTML实体+自定义字体反爬\n2. 目录页WAF拦截,通过详情页信息生成章节列表\n3. 正文解码:反向替换控制字符→还原HTML实体→解码Unicode→字体替换\n4. 搜索WAF拦截,loginUrl+loginCheckJs手动验证WAF,cookie保存后POST搜索\n",
"bookSourceGroup": "AI,18加",
"bookSourceName": "爱丽书屋",
"bookSourceType": 0,
"bookSourceUrl": "https:\/\/m.ailisw.com",
"bookUrlPattern": "https?:\/\/m\\.ailisw\\.com\/[a-zA-Z0-9]+\\.html",
"concurrentRate": "1000",
"customButton": false,
"customOrder": 9,
"enabled": true,
"enabledCookieJar": true,
"enabledExplore": true,
"eventListener": false,
"exploreUrl": "[{\"title\": \"书库\", \"url\": \"\", \"style\": {\"layout_flexGrow\": 0, \"layout_flexBasisPercent\": 1}}, {\"title\": \"全部\", \"url\": \"\/shuku\/0_0_0_{{page}}_0_0_0.html\", \"style\": {\"layout_flexGrow\": 1, \"layout_flexBasisPercent\": 0.29}}, {\"title\": \"都市\", \"url\": \"\/shuku\/0_0_0_{{page}}_0_1_0.html\", \"style\": {\"layout_flexGrow\": 1, \"layout_flexBasisPercent\": 0.29}}, {\"title\": \"现言\", \"url\": \"\/shuku\/0_0_0_{{page}}_0_2_0.html\", \"style\": {\"layout_flexGrow\": 1, \"layout_flexBasisPercent\": 0.29}}, {\"title\": \"同人\", \"url\": \"\/shuku\/0_0_0_{{page}}_0_3_0.html\", \"style\": {\"layout_flexGrow\": 1, \"layout_flexBasisPercent\": 0.29}}, {\"title\": \"幻言\", \"url\": \"\/shuku\/0_0_0_{{page}}_0_4_0.html\", \"style\": {\"layout_flexGrow\": 1, \"layout_flexBasisPercent\": 0.29}}, {\"title\": \"玄幻\", \"url\": \"\/shuku\/0_0_0_{{page}}_0_5_0.html\", \"style\": {\"layout_flexGrow\": 1, \"layout_flexBasisPercent\": 0.29}}, {\"title\": \"古言\", \"url\": \"\/shuku\/0_0_0_{{page}}_0_6_0.html\", \"style\": {\"layout_flexGrow\": 1, \"layout_flexBasisPercent\": 0.29}}, {\"title\": \"网游\", \"url\": \"\/shuku\/0_0_0_{{page}}_0_7_0.html\", \"style\": {\"layout_flexGrow\": 1, \"layout_flexBasisPercent\": 0.29}}, {\"title\": \"男频\", \"url\": \"\/shuku\/0_0_0_{{page}}_0_0_1.html\", \"style\": {\"layout_flexGrow\": 1, \"layout_flexBasisPercent\": 0.29}}, {\"title\": \"女频\", \"url\": \"\/shuku\/0_0_0_{{page}}_0_0_2.html\", \"style\": {\"layout_flexGrow\": 1, \"layout_flexBasisPercent\": 0.29}}, {\"title\": \"排行\", \"url\": \"\/shuku\/rank_{{page}}.html\", \"style\": {\"layout_flexGrow\": 1, \"layout_flexBasisPercent\": 0.29}}, {\"title\": \"完本\", \"url\": \"\/shuku\/finish_{{page}}.html\", \"style\": {\"layout_flexGrow\": 1, \"layout_flexBasisPercent\": 0.29}}]",
"header": "{\"User-Agent\": \"Mozilla\/5.0 (Linux; Android 12) AppleWebKit\/537.36 Mobile Safari\/537.36\", \"Accept-Language\": \"zh-CN,zh;q=0.9\"}",
"lastUpdateTime": "1783679208431",
"loginCheckJs": "var r = source.getKey() + '\/';\nvar js = 'setTimeout(function(){window.legado.getHTML(document.documentElement.outerHTML);},5000);';\nvar html = String(java.webView('', r, js));\nif (html.indexOf('wafjs') !== -1 || html.indexOf('loading ailisw') !== -1) {\n java.startBrowserAwait(r, '请等待页面加载完成后点击返回');\n}\nresult;",
"loginUrl": "https:\/\/m.ailisw.com\/",
"respondTime": 180000,
"ruleBookInfo": {
"author": ".author a@text",
"coverUrl": ".BGsectionOne-top-left img@data-lazy",
"intro": ".sectionTwo-content p@text",
"kind": ".category a@text",
"lastChapter": ".chapter-table a.0@text",
"name": ".title@text"
},
"ruleContent": {
"content": "<js>\nvar html = String(result);\nvar d = html.match(\/initTxt\\(\"([^\"]+)\"\/);\nif (!d) '内容加载失败';\nelse {\n var u = d[1];\n if (u.indexOf('\/\/') === 0) u = 'https:' + u;\n var s = String(java.ajax(u));\n var j = s.match(\/_txt_call\\((\\{[\\s\\S]*\\})\\)\/);\n if (!j) '解析失败';\n else {\n var c = j[1].match(\/\"content\"\\s*:\\s*\"([\\s\\S]*?)\"\\s*,\\s*\"replace\"\/);\n if (!c) c = j[1].match(\/\"content\"\\s*:\\s*\"([\\s\\S]*?)\"\/);\n if (!c) '无content';\n else {\n var ct = c[1].replace(\/\\\\n\/g,'\\n').replace(\/\\\\r\/g,'\\r').replace(\/\\\\t\/g,'\\t').replace(\/\\\\\"\/g,'\"').replace(\/\\\\\\\\\/g,'\\\\');\n var r = \/\"([^\"]+)\"\\s*:\\s*\"((?:\\\\b|\\\\n|[\\s\\S]))\"\/g;\n var m;\n while ((m = r.exec(j[1])) !== null) {\n ct = ct.split(m[2]).join(m[1]);\n }\n ct = ct.replace(\/&#x([0-9a-fA-F]+);\/g, function(_, h) { return String.fromCharCode(parseInt(h, 16)) });\n ct = ct.replace(\/&#(\\d+);\/g, function(_, d) { return String.fromCharCode(parseInt(d, 10)) });\n var b = html.match(\/ttf;base64,([^\\)]+)\/);\n if (b) try { ct = String(java.replaceFont(ct, java.queryBase64TTF(b[1]), java.queryTTF(\"https:\/\/alanskycn.gitee.io\/teachme\/assets\/font\/Source Han Sans CN Regular.ttf\"), true)) } catch(e) {}\n ct = ct.replace(\/\\\\n\/g, '\\n');\n ct.split('\\n').filter(function(l) { return l.trim() && !\/AILISW|ailisw|温馨提示|加载中\/.test(l) }).join('\\n');\n }\n }\n}\n<\/js>",
"nextContentUrl": "a.next@href"
},
"ruleExplore": {
"author": "p.gray a@text",
"bookList": "li.list-item",
"bookUrl": "h3 a@href",
"lastChapter": "p.gray@text##.*·\\s*##",
"name": "h3 a@text"
},
"ruleSearch": {
"author": ".sc-title a.green@text",
"bookList": "<js>\nvar cf = \/wafjs|loading ailisw\/.test(src) || src.indexOf('search-card') === -1;\nif (cf) {\n cookie.removeCookie(source.getKey());\n var x = java.get('url');\n b = java.startBrowserAwait(x, '请等待页面加载完成后点击返回');\n result = b;\n}\nresult;\n<\/js>\n.search-card",
"bookUrl": ".sc-title a.0@href",
"lastChapter": ".sc-time span.0@text",
"name": ".sc-title a.0@text"
},
"ruleToc": {
"chapterList": "<js>\nvar html = String(result);\nvar doc = Packages.org.jsoup.Jsoup.parse(html);\n\n\/\/ Extract book URL prefix from baseUrl\n\/\/ baseUrl is like https:\/\/m.ailisw.com\/hqMm.html\n\/\/ prefix should be \/hqMm\/\nvar prefix = '';\nvar baseMatch = String(baseUrl).match(\/\\\/([a-zA-Z0-9]+)\\.html\/);\nif (baseMatch) {\n prefix = '\/' + baseMatch[1] + '\/';\n}\n\n\/\/ Extract chapter count from .chaptercount text\nvar count = 0;\nvar ccElem = doc.selectFirst('.chaptercount');\nif (ccElem) {\n var ccText = String(ccElem.text());\n var cm = ccText.match(\/共(\\d+)章\/);\n if (cm) count = parseInt(cm[1]);\n}\n\n\/\/ Fallback: try from og metadata\nif (count === 0) {\n var allMetas = doc.select('meta');\n for (var mi = 0; mi < allMetas.size(); mi++) {\n var meta = allMetas.get(mi);\n if (String(meta.attr('property')) === 'og:novel:latest_chapter_url') {\n var lcUrl = String(meta.attr('content'));\n var lcm = lcUrl.match(\/\\\/(\\d+)\\.html\/);\n if (lcm) count = parseInt(lcm[1]);\n break;\n }\n }\n}\n\n\/\/ Generate chapter list\nvar sb = '<html><body><ul>';\nfor (var i = 1; i <= count; i++) {\n sb += '<li><a href=\"' + prefix + i + '.html\">分节 ' + i + '<\/a><\/li>';\n}\nsb += '<\/ul><\/body><\/html>';\nsb;\n<\/js>\nul li",
"chapterName": "a@text",
"chapterUrl": "a@href"
},
"searchUrl": "@js:\nvar su = source.getKey();\nvar body = 'keyword=' + String(key) + '&submit=搜作品';\nvar postUrl = su + '\/search\/,' + JSON.stringify({'method': 'POST', 'body': String(body)});\njava.put('url', postUrl);\npostUrl;",
"weight": 0
}