diff --git a/parser/pom.xml b/parser/pom.xml index 8ca6064..0a22b0b 100644 --- a/parser/pom.xml +++ b/parser/pom.xml @@ -12,7 +12,7 @@ cn.qaiu parser - 10.2.3 + 10.2.5 jar cn.qaiu:parser diff --git a/parser/src/main/java/cn/qaiu/parser/PanDomainTemplate.java b/parser/src/main/java/cn/qaiu/parser/PanDomainTemplate.java index 80d2f00..c241d11 100644 --- a/parser/src/main/java/cn/qaiu/parser/PanDomainTemplate.java +++ b/parser/src/main/java/cn/qaiu/parser/PanDomainTemplate.java @@ -68,42 +68,41 @@ public enum PanDomainTemplate { t-is.cn */ LZ("蓝奏云", - compile("https://(?:[a-zA-Z\\d-]+\\.)?(" + - "lanzoul|" + - "lanzouh|" + - "lanosso|" + - "lanpv|" + - "bakstotre|" + - "lanzouo|" + - "lanzov|" + - "lanpw|" + - "ulanzou|" + - "lanzouf|" + - "lanzn|" + - "lanzouj|" + - "lanzouk|" + - "lanzouq|" + - "lanzouv|" + - "lanzoue|" + - "lanzouw|" + - "lanzoub|" + - "lanzouu|" + - "lanwp|" + - "lanzouy|" + - "lanzoup|" + - "woozooo|" + - "lanzv|" + - "dmpdmp|" + - "lanrar|" + - "webgetstore|" + - "lanzb|" + - "lanzoux|" + - "lanzout|" + - "lanzouc|" + - "lanzoui|" + - "lanzoug|" + - "lanzoum" + - ")\\.com/(.+/)?(?.+)"), + compile("https://(?:[a-zA-Z\\d-]+\\.)?(" + + "lanzoul|" + + "lanzouh|" + + "lanosso|" + + "lanpv|" + + "bakstotre|" + + "lanzouo|" + + "lanzov|" + + "lanpw|" + + "ulanzou|" + + "lanzouf|" + + "lanzn|" + + "lanzouj|" + + "lanzouk|" + + "lanzouq|" + + "lanzouv|" + + "lanzoue|" + + "lanzouw|" + + "lanzoub|" + + "lanzouu|" + + "lanwp|" + + "lanzouy|" + + "lanzoup|" + + "woozooo|" + + "lanzv|" + + "dmpdmp|" + + "lanrar|" + + "lanzb|" + + "lanzoux|" + + "lanzout|" + + "lanzouc|" + + "lanzoui|" + + "lanzoug|" + + "lanzoum" + + ")\\.com/(?.+)"), "https://w1.lanzn.com/{shareKey}", LzTool.class), diff --git a/parser/src/main/java/cn/qaiu/parser/impl/LzTool.java b/parser/src/main/java/cn/qaiu/parser/impl/LzTool.java index c09c31f..d50e920 100644 --- a/parser/src/main/java/cn/qaiu/parser/impl/LzTool.java +++ b/parser/src/main/java/cn/qaiu/parser/impl/LzTool.java @@ -64,7 +64,7 @@ public class LzTool extends PanBase { String html = asText(res); if (html.contains("var arg1='")) { webClientSession = WebClientSession.create(clientNoRedirects); - setCookie(html); + setCookie(html, sUrl); webClientSession.getAbs(sUrl) .putHeaders(headers0) .send().onSuccess(res2 -> { @@ -81,6 +81,29 @@ public class LzTool extends PanBase { } private void doParser(String html, String pwd, String sUrl) { + // 检测是否为目录分享链接 (含 /s/、/b/ 路径段或 b0 开头的路径段) + if (sUrl.matches(".*/(s|b)/[^/]+.*") || sUrl.matches(".*/b0[^/]+.*")) { + fail("该链接为蓝奏云目录分享,请使用目录解析接口"); + return; + } + // 若仍是校验页 (parse()中cookie域名与实际URL不匹配时会出现), 重试一次 + if (html.contains("var arg1='")) { + webClientSession = WebClientSession.create(clientNoRedirects); + setCookie(html, sUrl); + webClientSession.getAbs(sUrl).putHeaders(headers0).send().onSuccess(res -> { + String html2 = asText(res); + if (html2.contains("var arg1='")) { + fail("蓝奏云反爬校验失败,请稍后重试"); + return; + } + doParserInternal(html2, pwd, sUrl); + }).onFailure(handleFail(sUrl)); + return; + } + doParserInternal(html, pwd, sUrl); + } + + private void doParserInternal(String html, String pwd, String sUrl) { try { setFileInfo(html, shareLinkInfo); } catch (Exception e) { @@ -98,20 +121,18 @@ public class LzTool extends PanBase { } catch (Exception e) { fail(e, "js引擎执行失败"); } - } - else { + } else { // 没有密码 String iframePath = matcher.group(1); String absoluteURI = SHARE_URL_PREFIX + iframePath; webClientSession.getAbs(absoluteURI).putHeaders(headers0).send().onSuccess(res2 -> { - String html2= asText(res2); - // Matcher matcher2 = Pattern.compile("'sign'\s*:\s*'(\\w+)'").matcher(html2); + String html2 = asText(res2); String jsText = getJsText(html2); if (jsText == null) { headers0.add("Referer", absoluteURI); - setCookie(html2); + setCookie(html2, absoluteURI); webClientSession.getAbs(absoluteURI).send().onSuccess(res3 -> { - String html3= asText(res3); + String html3 = asText(res3); String jsText3 = getJsText(html3); if (jsText3 != null) { try { @@ -120,10 +141,8 @@ public class LzTool extends PanBase { } catch (ScriptException | NoSuchMethodException e) { fail(e, "引擎执行失败"); } - } else { - + } else { fail(SHARE_URL_PREFIX + iframePath + " -> " + sUrl + ": 获取失败0, 可能分享已失效"); - return; } }); } else { @@ -138,14 +157,29 @@ public class LzTool extends PanBase { } } - private void setCookie(String html2) { - int beginIndex = html2.indexOf("arg1='") + 6; - String arg1 = html2.substring(beginIndex, html2.indexOf("';", beginIndex)); + private void setCookie(String html, String url) { + int beginIndex = html.indexOf("arg1='") + 6; + int endIndex = html.indexOf("';", beginIndex); + if (beginIndex < 6 || endIndex == -1 || endIndex <= beginIndex) { + fail("蓝奏云反爬 arg1 Cookie 解析失败,页面内容异常"); + return; + } + String arg1 = html.substring(beginIndex, endIndex); String acw_sc__v2 = AcwScV2Generator.acwScV2Simple(arg1); + // 从 URL 中动态提取域名(如 lanzoum.com, lanzoux.com 等) + String domain = ".lanzn.com"; // 默认兜底 + try { + java.net.URL urlObj = new java.net.URL(url); + String host = urlObj.getHost(); // e.g. "dzvip.lanzoum.com" + int firstDot = host.indexOf('.'); + if (firstDot >= 0) { + domain = host.substring(firstDot); // e.g. ".lanzoum.com" + } + } catch (Exception ignored) {} // 创建一个 Cookie 并放入 CookieStore DefaultCookie nettyCookie = new DefaultCookie("acw_sc__v2", acw_sc__v2); - nettyCookie.setDomain(".lanzn.com"); // 设置域名 - nettyCookie.setPath("/"); // 设置路径 + nettyCookie.setDomain(domain); + nettyCookie.setPath("/"); nettyCookie.setSecure(false); nettyCookie.setHttpOnly(false); webClientSession.cookieStore().put(nettyCookie); @@ -234,10 +268,18 @@ public class LzTool extends PanBase { int beginIndex = text.indexOf("arg1='") + 6; String arg1 = text.substring(beginIndex, text.indexOf("';", beginIndex)); String acw_sc__v2 = AcwScV2Generator.acwScV2Simple(arg1); + // 从 downUrl 中动态提取域名 + String downDomain = ".lanrar.com"; + try { + java.net.URL du = new java.net.URL(downUrl); + String h = du.getHost(); + int dot = h.indexOf('.'); + if (dot >= 0) downDomain = h.substring(dot); + } catch (Exception ignored) {} // 创建一个 Cookie 并放入 CookieStore DefaultCookie nettyCookie = new DefaultCookie("acw_sc__v2", acw_sc__v2); - nettyCookie.setDomain(".lanrar.com"); // 设置域名 - nettyCookie.setPath("/"); // 设置路径 + nettyCookie.setDomain(downDomain); + nettyCookie.setPath("/"); nettyCookie.setSecure(false); nettyCookie.setHttpOnly(false); WebClientSession webClientSession2 = WebClientSession.create(clientNoRedirects); @@ -295,14 +337,14 @@ public class LzTool extends PanBase { String pwd = shareLinkInfo.getSharePassword(); webClientSession.getAbs(sUrl).send().onSuccess(res -> { - String html = res.bodyAsString(); + String html = asText(res); // 检查是否需要 cookie 验证 if (html.contains("var arg1='")) { webClientSession = WebClientSession.create(clientNoRedirects); - setCookie(html); + setCookie(html, sUrl); // 重新请求 webClientSession.getAbs(sUrl).send().onSuccess(res2 -> { - handleFileListParse(res2.bodyAsString(), pwd, sUrl, promise); + handleFileListParse(asText(res2), pwd, sUrl, promise); }).onFailure(err -> promise.fail(err)); return; } @@ -312,6 +354,11 @@ public class LzTool extends PanBase { } private void handleFileListParse(String html, String pwd, String sUrl, Promise> promise) { + // 检测是否为文件分享链接 (不含 /s/、/b/ 路径段且不含 b0 开头的路径段) + if (!sUrl.matches(".*/(s|b)/[^/]+.*") && !sUrl.matches(".*/b0[^/]+.*")) { + promise.fail(baseMsg() + "该链接为蓝奏云文件分享,请使用文件解析接口"); + return; + } try { String jsText = getJsByPwd(pwd, html, "var urls =window.location.href"); ScriptObjectMirror scriptObjectMirror = JsExecUtils.executeDynamicJs(jsText, "file"); @@ -321,12 +368,12 @@ public class LzTool extends PanBase { log.debug("解析参数: {}", map); MultiMap headers = getHeaders(sUrl); - String url = SHARE_URL_PREFIX + "/filemoreajax.php?file=" + data.get("fid"); + String url = SHARE_URL_PREFIX + "filemoreajax.php?file=" + data.get("fid"); webClientSession.postAbs(url).putHeaders(headers).sendForm(map).onSuccess(res2 -> { String resBody = asText(res2); // 再次检查是否需要 cookie 验证 if (resBody.contains("var arg1='")) { - setCookie(resBody); + setCookie(resBody, url); // 重新请求 webClientSession.postAbs(url).putHeaders(headers).sendForm(map).onSuccess(res3 -> { handleFileListResponse(asText(res3), promise); @@ -335,7 +382,7 @@ public class LzTool extends PanBase { } handleFileListResponse(resBody, promise); }).onFailure(err -> promise.fail(err)); - } catch (ScriptException | NoSuchMethodException e) { + } catch (ScriptException | NoSuchMethodException | RuntimeException e) { promise.fail(e); } } @@ -367,14 +414,20 @@ public class LzTool extends PanBase { Long sizeNum = FileSizeConverter.convertToBytes(size); String panType = shareLinkInfo.getType(); String id = fileJson.getString("id"); - fileInfo.setFileName(fileJson.getString("name_all")) + String fileName = fileJson.getString("name_all"); + // 构建 base64 参数,用于 /v2/redirectUrl 接口 + JsonObject paramJson = new JsonObject() + .put("id", id) + .put("fileName", fileName); + String param = CommonUtils.urlBase64Encode(paramJson.encode()); + fileInfo.setFileName(fileName) .setFileId(id) .setCreateTime(fileJson.getString("time")) .setFileType(fileJson.getString("icon")) .setSizeStr(fileJson.getString("size")) .setSize(sizeNum) .setPanType(panType) - .setParserUrl(getDomainName() + "/d/" + panType + "/" + id) + .setParserUrl(String.format("%s/v2/redirectUrl/%s/%s", getDomainName(), panType, param)) .setPreviewUrl(String.format("%s/v2/view/%s/%s", getDomainName(), shareLinkInfo.getType(), id)); log.debug("文件信息: {}", fileInfo); @@ -386,6 +439,15 @@ public class LzTool extends PanBase { } } + @Override + public Future parseById() { + JsonObject paramJson = (JsonObject) shareLinkInfo.getOtherParam().get("paramJson"); + String id = paramJson.getString("id"); + // 以文件ID重新构造标准访问URL,复用 parse() 流程 + shareLinkInfo.setStandardUrl(SHARE_URL_PREFIX + id); + return parse(); + } + void setFileInfo(String html, ShareLinkInfo shareLinkInfo) { // 写入 fileInfo FileInfo fileInfo = new FileInfo(); @@ -400,16 +462,17 @@ public class LzTool extends PanBase { String fileId = CommonUtils.extract(html, Pattern.compile("\\?f=(.*?)&|fid = (.*?);")); String createTime = CommonUtils.extract(html, Pattern.compile(">上传时间:(.*?)<")); try { - long bytes = FileSizeConverter.convertToBytes(sizeStr); fileInfo.setFileName(fileName) - .setSize(bytes) - .setSizeStr(FileSizeConverter.convertToReadableSize(bytes)) .setCreateBy(createBy) .setPanType(shareLinkInfo.getType()) .setDescription(description) .setFileType("file") .setFileId(fileId) .setCreateTime(createTime); + if (sizeStr != null && !sizeStr.isBlank()) { + long bytes = FileSizeConverter.convertToBytes(sizeStr); + fileInfo.setSize(bytes).setSizeStr(FileSizeConverter.convertToReadableSize(bytes)); + } } catch (Exception e) { log.warn("文件信息解析异常", e); } diff --git a/pom.xml b/pom.xml index 419dd8c..6b51100 100644 --- a/pom.xml +++ b/pom.xml @@ -74,7 +74,7 @@ cn.qaiu parser - 10.2.3 + 10.2.5