From d91460d2e245b24972f173d7b32da57a1058a58e Mon Sep 17 00:00:00 2001 From: QAIU <736226400@qq.com> Date: Sat, 4 Jan 2025 15:21:15 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E8=93=9D=E5=A5=8F=E4=BC=98?= =?UTF-8?q?=E4=BA=AB=E8=A7=A3=E6=9E=90=E5=A4=B1=E8=B4=A5,=20=20gz=E5=8E=8B?= =?UTF-8?q?=E7=BC=A9=E7=9A=84json=E8=A7=A3=E6=9E=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/java/cn/qaiu/parser/PanBase.java | 49 +++++++- .../main/java/cn/qaiu/parser/impl/IzTool.java | 112 ++++++++++++------ 2 files changed, 119 insertions(+), 42 deletions(-) diff --git a/parser/src/main/java/cn/qaiu/parser/PanBase.java b/parser/src/main/java/cn/qaiu/parser/PanBase.java index d0e0ff8..59bf910 100644 --- a/parser/src/main/java/cn/qaiu/parser/PanBase.java +++ b/parser/src/main/java/cn/qaiu/parser/PanBase.java @@ -5,7 +5,7 @@ import io.vertx.core.Future; import io.vertx.core.Handler; import io.vertx.core.Promise; -import io.vertx.core.json.DecodeException; +import io.vertx.core.buffer.Buffer; import io.vertx.core.json.JsonObject; import io.vertx.core.net.ProxyOptions; import io.vertx.core.net.ProxyType; @@ -17,8 +17,14 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.io.BufferedReader; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.Iterator; +import java.util.zip.GZIPInputStream; /** * 解析器抽象类包含promise, HTTP Client, 默认失败方法等; @@ -146,21 +152,56 @@ protected Handler handleFail() { return handleFail(""); } - /** * bodyAsJsonObject的封装, 会自动处理异常 + * * @param res HttpResponse * @return JsonObject */ protected JsonObject asJson(HttpResponse res) { try { - return res.bodyAsJsonObject(); - } catch (DecodeException e) { + // 检查响应头中的Content-Encoding是否为gzip + String contentEncoding = res.getHeader("Content-Encoding"); + if ("gzip".equalsIgnoreCase(contentEncoding)) { + // 如果是gzip压缩的响应体,解压 + return new JsonObject(decompressGzip((Buffer) res.body())); + } else { + return res.bodyAsJsonObject(); + } + + } catch (Exception e) { fail("解析失败: json格式异常: {}", res.bodyAsString()); throw new RuntimeException("解析失败: json格式异常"); } } + /** + * 解压gzip数据 + * @param compressedData compressedData + * @return String + * @throws IOException IOException + */ + private String decompressGzip(Buffer compressedData) throws IOException { + try (ByteArrayInputStream bais = new ByteArrayInputStream(compressedData.getBytes()); + GZIPInputStream gzis = new GZIPInputStream(bais); + BufferedReader reader = new BufferedReader(new InputStreamReader(gzis, + StandardCharsets.UTF_8))) { + + // 用于存储解压后的字符串 + StringBuilder decompressedData = new StringBuilder(); + + // 逐行读取解压后的数据 + String line; + while ((line = reader.readLine()) != null) { + decompressedData.append(line); + } + + // 此时decompressedData.toString()包含了解压后的字符串 + return decompressedData.toString(); + } + + } + protected void complete(String url) { promise.complete(url); } diff --git a/parser/src/main/java/cn/qaiu/parser/impl/IzTool.java b/parser/src/main/java/cn/qaiu/parser/impl/IzTool.java index 2939d08..d69148a 100644 --- a/parser/src/main/java/cn/qaiu/parser/impl/IzTool.java +++ b/parser/src/main/java/cn/qaiu/parser/impl/IzTool.java @@ -15,6 +15,7 @@ * */ public class IzTool extends PanBase { + private static final String API_URL_PREFIX = "https://api.ilanzou.com/unproved/"; private static final String FIRST_REQUEST_URL = API_URL_PREFIX + "recommend/list?devType=6&devModel=Chrome" + @@ -24,12 +25,38 @@ public class IzTool extends PanBase { "&devType=6&uuid={uuid}×tamp={ts}&auth={auth}&shareId={dataKey}"; // downloadId=x&enable=1&devType=6&uuid=x×tamp=x&auth=x&shareId=lGFndCM + private static final String VIP_REQUEST_URL = API_URL_PREFIX + "/buy/vip/list?devType=6&devModel=Chrome&uuid" + + "={uuid}&extra=2×tamp={ts}"; + private static final MultiMap header; + + static { + header = MultiMap.caseInsensitiveMultiMap(); + header.set("Accept", "application/json, text/plain, */*"); + header.set("Accept-Encoding", "gzip, deflate, br, zstd"); + header.set("Accept-Language", "zh-CN,zh;q=0.9,en;q=0.8"); + header.set("Cache-Control", "no-cache"); + header.set("Connection", "keep-alive"); + header.set("Content-Length", "0"); + header.set("DNT", "1"); + header.set("Host", "api.ilanzou.com"); + header.set("Origin", "https://www.ilanzou.com/"); + header.set("Pragma", "no-cache"); + header.set("Referer", "https://www.ilanzou.com/"); + header.set("Sec-Fetch-Dest", "empty"); + header.set("Sec-Fetch-Mode", "cors"); + header.set("Sec-Fetch-Site", "cross-site"); + header.set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36"); + header.set("sec-ch-ua", "\"Google Chrome\";v=\"131\", \"Chromium\";v=\"131\", \"Not_A Brand\";v=\"24\""); + header.set("sec-ch-ua-mobile", "?0"); + header.set("sec-ch-ua-platform", "\"Windows\""); + } + public IzTool(ShareLinkInfo shareLinkInfo) { super(shareLinkInfo); } public Future parse() { - String dataKey = shareLinkInfo.getShareKey(); + String shareId = shareLinkInfo.getShareKey(); long nowTs = System.currentTimeMillis(); String tsEncode = AESUtils.encrypt2HexIz(Long.toString(nowTs)); String uuid = UUID.randomUUID().toString(); @@ -38,45 +65,54 @@ public Future parse() { // String shareId = String.valueOf(AESUtils.idEncryptIz(dataKey)); // 第一次请求 获取文件信息 - // POST https://api.feijipan.com/ws/recommend/list?devType=6&devModel=Chrome&extra=2&shareId=146731&type=0&offset=1&limit=60 - client.postAbs(UriTemplate.of(FIRST_REQUEST_URL)) - .setTemplateParam("shareId", dataKey) + // POST https://api.ilanzou.com/ws/recommend/list?devType=6&devModel=Chrome&extra=2&shareId=146731&type=0&offset=1&limit=60 + + client.postAbs(UriTemplate.of(VIP_REQUEST_URL)) .setTemplateParam("uuid", uuid) .setTemplateParam("ts", tsEncode) - .send().onSuccess(res -> { - JsonObject resJson = asJson(res); - if (resJson.getInteger("code") != 200) { - fail(FIRST_REQUEST_URL + " 返回异常: " + resJson); - return; - } - if (resJson.getJsonArray("list").size() == 0) { - fail(FIRST_REQUEST_URL + " 解析文件列表为空: " + resJson); - return; - } - // 文件Id - JsonObject fileInfo = resJson.getJsonArray("list").getJsonObject(0); - String fileId = fileInfo.getString("fileIds"); - String userId = fileInfo.getString("userId"); - // 其他参数 - // String fidEncode = AESUtils.encrypt2HexIz(fileId + "|"); - String fidEncode = AESUtils.encrypt2HexIz(fileId + "|" + userId); - String auth = AESUtils.encrypt2HexIz(fileId + "|" + nowTs); - // 第二次请求 - clientNoRedirects.getAbs(UriTemplate.of(SECOND_REQUEST_URL)) - .setTemplateParam("fidEncode", fidEncode) - .setTemplateParam("uuid", uuid) - .setTemplateParam("ts", tsEncode) - .setTemplateParam("auth", auth) - .setTemplateParam("shareId", dataKey).send().onSuccess(res2 -> { - MultiMap headers = res2.headers(); - if (!headers.contains("Location")) { - fail(SECOND_REQUEST_URL + " 未找到重定向URL: \n" + res.headers()); - return; - } - promise.complete(headers.get("Location")); - }).onFailure(handleFail(SECOND_REQUEST_URL)); - }).onFailure(handleFail(FIRST_REQUEST_URL)); - + .send().onSuccess(r0 -> { // 忽略res + // 第一次请求 获取文件信息 + // POST https://api.feijipan.com/ws/recommend/list?devType=6&devModel=Chrome&extra=2&shareId=146731&type=0&offset=1&limit=60 + client.postAbs(UriTemplate.of(FIRST_REQUEST_URL)) + .putHeaders(header) + .setTemplateParam("shareId", shareId) + .setTemplateParam("uuid", uuid) + .setTemplateParam("ts", tsEncode) + .send().onSuccess(res -> { + JsonObject resJson = asJson(res); + if (resJson.getInteger("code") != 200) { + fail(FIRST_REQUEST_URL + " 返回异常: " + resJson); + return; + } + if (resJson.getJsonArray("list").size() == 0) { + fail(FIRST_REQUEST_URL + " 解析文件列表为空: " + resJson); + return; + } + // 文件Id + JsonObject fileInfo = resJson.getJsonArray("list").getJsonObject(0); + String fileId = fileInfo.getString("fileIds"); + String userId = fileInfo.getString("userId"); + // 其他参数 + // String fidEncode = AESUtils.encrypt2HexIz(fileId + "|"); + String fidEncode = AESUtils.encrypt2HexIz(fileId + "|" + userId); + String auth = AESUtils.encrypt2HexIz(fileId + "|" + nowTs); + // 第二次请求 + clientNoRedirects.getAbs(UriTemplate.of(SECOND_REQUEST_URL)) + .setTemplateParam("fidEncode", fidEncode) + .setTemplateParam("uuid", uuid) + .setTemplateParam("ts", tsEncode) + .setTemplateParam("auth", auth) + .setTemplateParam("shareId", shareId) + .putHeaders(header).send().onSuccess(res2 -> { + MultiMap headers = res2.headers(); + if (!headers.contains("Location")) { + fail(SECOND_REQUEST_URL + " 未找到重定向URL: \n" + res.headers()); + return; + } + promise.complete(headers.get("Location")); + }).onFailure(handleFail(SECOND_REQUEST_URL)); + }).onFailure(handleFail(FIRST_REQUEST_URL)); + }); return promise.future(); } }