|
|
@@ -0,0 +1,150 @@
|
|
|
+package com.jzg.quote.zhongmei.crawler.component;
|
|
|
+
|
|
|
+import com.jzg.commons.exception.SystemException;
|
|
|
+import com.jzg.quotation.commons.component.RequestComponent;
|
|
|
+import com.jzg.quotation.commons.constant.CacheConstant;
|
|
|
+import com.jzg.quotation.commons.entity.vo.base.LoginBase;
|
|
|
+import com.jzg.quotation.commons.entity.vo.base.R;
|
|
|
+import com.jzg.quotation.commons.service.ExteriorUtilsRequest;
|
|
|
+import com.jzg.quote.zhongmei.crawler.constant.enums.RequestUrl;
|
|
|
+import com.jzg.quote.zhongmei.crawler.entity.Request.ZmCrawlerAuthenticateRequest;
|
|
|
+import com.jzg.quote.zhongmei.crawler.entity.Response.ZmCrawlerAuthenticateResponse;
|
|
|
+import com.jzg.quote.zhongmei.crawler.entity.Response.ZmCrawlerCaptchaResponse;
|
|
|
+import com.jzg.quote.zhongmei.crawler.properties.ZmCrawlerProperties;
|
|
|
+import lombok.RequiredArgsConstructor;
|
|
|
+import lombok.extern.slf4j.Slf4j;
|
|
|
+import org.redisson.api.RMap;
|
|
|
+import org.redisson.api.RedissonClient;
|
|
|
+import org.springframework.http.HttpHeaders;
|
|
|
+import org.springframework.http.ResponseEntity;
|
|
|
+import org.springframework.stereotype.Component;
|
|
|
+import org.springframework.util.StringUtils;
|
|
|
+import org.springframework.web.reactive.function.client.WebClient;
|
|
|
+
|
|
|
+import java.time.Duration;
|
|
|
+import java.util.HashMap;
|
|
|
+import java.util.Map;
|
|
|
+import java.util.Optional;
|
|
|
+import java.util.UUID;
|
|
|
+
|
|
|
+/**
|
|
|
+ * @description:
|
|
|
+ * @author: shenwd
|
|
|
+ * @date: 2025/3/20 14:39
|
|
|
+ **/
|
|
|
+@Slf4j
|
|
|
+@Component
|
|
|
+@RequiredArgsConstructor
|
|
|
+public class ZmCrawlerRequestComponent {
|
|
|
+
|
|
|
+ private final WebClient webClient;
|
|
|
+
|
|
|
+ private final RequestComponent requestComponent;
|
|
|
+
|
|
|
+ private final RedissonClient redissonClient;
|
|
|
+
|
|
|
+ private final ExteriorUtilsRequest exteriorUtilsRequest;
|
|
|
+
|
|
|
+ private final ZmCrawlerProperties zmCrawlerProperties;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 请求次数
|
|
|
+ */
|
|
|
+ public static final int REQUEST_NUMBER = 3;
|
|
|
+
|
|
|
+ public static final String AUTHORIZATION = "AUTHORIZATION";
|
|
|
+
|
|
|
+ public static final String USER_CODE = "USER_CODE";
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 验证码
|
|
|
+ * @return
|
|
|
+ */
|
|
|
+ public ZmCrawlerCaptchaResponse captcha(int frequency) {
|
|
|
+ if (frequency <= 0) {
|
|
|
+ throw new SystemException("验证码异常");
|
|
|
+ }
|
|
|
+ Map<String, Object> map = Map.of("imgId", UUID.randomUUID().toString().replaceAll("-", ""));
|
|
|
+ // 请求
|
|
|
+ ZmCrawlerCaptchaResponse zmCrawlerCaptchaResponse = requestComponent.requestGET(RequestUrl.CAPTCHA.getRequestUrl(zmCrawlerProperties.getBaseUrl()), map, "", ZmCrawlerCaptchaResponse.class);
|
|
|
+ // 识别验证码
|
|
|
+ String s = exteriorUtilsRequest.captchaIdentify(zmCrawlerCaptchaResponse.getVerifyCode());
|
|
|
+ try {
|
|
|
+ int i = processCaptcha(s);
|
|
|
+ s = String.valueOf(i);
|
|
|
+ } catch (Exception e) {
|
|
|
+ frequency = frequency - 1;
|
|
|
+ return captcha(frequency);
|
|
|
+ }
|
|
|
+ zmCrawlerCaptchaResponse.setVerifyStr(s);
|
|
|
+ return zmCrawlerCaptchaResponse;
|
|
|
+ }
|
|
|
+
|
|
|
+ private Map<String, String> buildCache(ZmCrawlerAuthenticateResponse authenticateResponse) {
|
|
|
+ Map<String, String> authorizationMap = new HashMap<>();
|
|
|
+ authorizationMap.put(AUTHORIZATION, authenticateResponse.getHead().getToken());
|
|
|
+ return authorizationMap;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 登录次数
|
|
|
+ * @param username 用户名
|
|
|
+ * @param password 密码
|
|
|
+ * @param loginsNumber 登录次数
|
|
|
+ * @return token
|
|
|
+ */
|
|
|
+ public String authenticate(String username, String password, int loginsNumber) {
|
|
|
+ // 登录验证码获取
|
|
|
+ ZmCrawlerCaptchaResponse captchaResponse = captcha(REQUEST_NUMBER);
|
|
|
+ // 登录存入缓存当中
|
|
|
+ ZmCrawlerAuthenticateRequest authenticateRequest = new ZmCrawlerAuthenticateRequest(captchaResponse, username, password);
|
|
|
+ ResponseEntity<ZmCrawlerAuthenticateResponse> responseEntity = requestComponent.objectPost(RequestUrl.AUTHENTICATE.getRequestUrl(zmCrawlerProperties.getBaseUrl()), authenticateRequest, ZmCrawlerAuthenticateResponse.class);
|
|
|
+ ZmCrawlerAuthenticateResponse authenticateResponse = Optional.of(responseEntity).map(ResponseEntity::getBody).orElseThrow();
|
|
|
+
|
|
|
+ if ("0".equals(authenticateResponse.getHead().getReturnCode())) {
|
|
|
+ Map<String, String> authorizationMap = buildCache(authenticateResponse);
|
|
|
+ RMap<Object, Object> map = redissonClient.getMap(CacheConstant.ZHONGMEI_AUTHORIZATION + username);
|
|
|
+ map.putAll(authorizationMap);
|
|
|
+ map.expire(Duration.ofMinutes(CacheConstant.ZHONGMEI_AUTHORIZATION_TIME));
|
|
|
+ return authorizationMap.get(AUTHORIZATION);
|
|
|
+ }
|
|
|
+ loginsNumber = loginsNumber - 1;
|
|
|
+ if (loginsNumber <= 0) {
|
|
|
+ throw new SystemException(authenticateResponse.getHead().getReturnMessage());
|
|
|
+ }
|
|
|
+ return authenticate(username, password, loginsNumber);
|
|
|
+ }
|
|
|
+
|
|
|
+ public int processCaptcha(String res) {
|
|
|
+ // 检查字符串长度是否足够,并且第三个字符不是'='
|
|
|
+ if (res.length() >= 3 && res.charAt(2) != '=') {
|
|
|
+ char operator = res.charAt(1);
|
|
|
+ try {
|
|
|
+ int operand1 = Integer.parseInt(res.substring(0, 1));
|
|
|
+ int operand2 = Integer.parseInt(res.substring(2, 3));
|
|
|
+ // 注意:这里假设操作符和操作数都是有效的,实际应用中应该添加更多的错误处理
|
|
|
+
|
|
|
+ switch (operator) {
|
|
|
+ case '+':
|
|
|
+ case 'f': // 通常'f'不是加法运算符,但根据原代码保留
|
|
|
+ return operand1 + operand2;
|
|
|
+ case 'x':
|
|
|
+ case 'X':
|
|
|
+ case '4': // 通常'4'不是乘法运算符,但保留以匹配原代码
|
|
|
+ case '9': // 通常'9'也不是乘法运算符,但保留以匹配原代码
|
|
|
+ return operand1 * operand2;
|
|
|
+ case '-':
|
|
|
+ case 'e': // 通常'e'不是减法运算符,但根据原代码保留
|
|
|
+ case '=': // 通常'='不是减法运算符,但根据原代码保留
|
|
|
+ case '2': // 通常'2'也不是减法运算符,但根据原代码保留
|
|
|
+ return operand1 - operand2;
|
|
|
+ // 如果操作符不匹配,则获取新的验证码图片(模拟)
|
|
|
+ }
|
|
|
+ } catch (NumberFormatException ignored) {
|
|
|
+ }
|
|
|
+ }
|
|
|
+ throw new SystemException("");
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+}
|