测试用的,暂时同步
This commit is contained in:
1
conf/extra/indexJs.js
Normal file
1
conf/extra/indexJs.js
Normal file
File diff suppressed because one or more lines are too long
177
src/main/java/quant/rich/emoney/client/WebviewClient.java
Normal file
177
src/main/java/quant/rich/emoney/client/WebviewClient.java
Normal file
@@ -0,0 +1,177 @@
|
|||||||
|
package quant.rich.emoney.client;
|
||||||
|
|
||||||
|
import java.net.Proxy;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
|
import com.microsoft.playwright.Browser;
|
||||||
|
import com.microsoft.playwright.BrowserContext;
|
||||||
|
import com.microsoft.playwright.BrowserType;
|
||||||
|
import com.microsoft.playwright.Page;
|
||||||
|
import com.microsoft.playwright.Playwright;
|
||||||
|
import com.microsoft.playwright.Request;
|
||||||
|
import com.microsoft.playwright.BrowserType.LaunchOptions;
|
||||||
|
import com.microsoft.playwright.Route.ResumeOptions;
|
||||||
|
import com.microsoft.playwright.options.HttpHeader;
|
||||||
|
import com.microsoft.playwright.options.WaitUntilState;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.experimental.Accessors;
|
||||||
|
import quant.rich.emoney.component.LockByCaller;
|
||||||
|
import quant.rich.emoney.entity.config.EmoneyRequestConfig;
|
||||||
|
import quant.rich.emoney.entity.config.ProxyConfig;
|
||||||
|
import quant.rich.emoney.util.SpringContextHolder;
|
||||||
|
|
||||||
|
public class WebviewClient {
|
||||||
|
|
||||||
|
private static Playwright playwright;
|
||||||
|
private static boolean isReady = false;
|
||||||
|
private static volatile ProxyConfig proxyConfig;
|
||||||
|
private static volatile EmoneyRequestConfig emoneyRequestConfig;
|
||||||
|
|
||||||
|
static {
|
||||||
|
try {
|
||||||
|
playwright = Playwright.create();
|
||||||
|
isReady = true;
|
||||||
|
}
|
||||||
|
catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isReady() {
|
||||||
|
return isReady;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ProxyConfig getProxyConfig() {
|
||||||
|
if (proxyConfig == null) {
|
||||||
|
synchronized (WebviewClient.class) {
|
||||||
|
if (proxyConfig == null) {
|
||||||
|
proxyConfig = SpringContextHolder.getBean(ProxyConfig.class);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return proxyConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static EmoneyRequestConfig getEmoneyRequestConfig() {
|
||||||
|
if (emoneyRequestConfig == null) {
|
||||||
|
synchronized (WebviewClient.class) {
|
||||||
|
if (emoneyRequestConfig == null) {
|
||||||
|
emoneyRequestConfig = SpringContextHolder.getBean(EmoneyRequestConfig.class);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return emoneyRequestConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
@LockByCaller
|
||||||
|
public static WebviewResponseWrapper getIndexDetailWrapper(String indexCode) {
|
||||||
|
|
||||||
|
String proxyUrl = getProxyConfig().getProxyUrl();
|
||||||
|
LaunchOptions launchOptions = new BrowserType.LaunchOptions()
|
||||||
|
.setHeadless(true);
|
||||||
|
|
||||||
|
// 设置代理
|
||||||
|
if (StringUtils.isNotBlank(proxyUrl)) {
|
||||||
|
launchOptions.setProxy(proxyUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
Browser browser = playwright.chromium().launch(launchOptions);
|
||||||
|
BrowserContext context = browser.newContext(new Browser.NewContextOptions()
|
||||||
|
// 设置 Webview User-Agent
|
||||||
|
.setUserAgent(getEmoneyRequestConfig().getWebviewUserAgent())
|
||||||
|
// 设置是否忽略 HTTPS 证书
|
||||||
|
.setIgnoreHTTPSErrors(getProxyConfig().getIgnoreHttpsVerification())
|
||||||
|
);
|
||||||
|
|
||||||
|
// 设置全局请求头
|
||||||
|
// 根据抓包获得,当前目标版本 5.8.1
|
||||||
|
Map<String, String> headers = new HashMap<>();
|
||||||
|
headers.put("Upgrade-Insecure-Requests", "1");
|
||||||
|
headers.put("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9");
|
||||||
|
headers.put("X-Requested-With", "cn.emoney.emstock");
|
||||||
|
headers.put("Sec-Fetch-Site", "none");
|
||||||
|
headers.put("Sec-Fetch-Mode", "navigate");
|
||||||
|
headers.put("Sec-Fetch-User", "?1");
|
||||||
|
headers.put("Sec-Fetch-Dest", "document");
|
||||||
|
headers.put("Accept-Encoding", "gzip, deflate");
|
||||||
|
headers.put("Accept-Language", "zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7");
|
||||||
|
|
||||||
|
context.setExtraHTTPHeaders(headers);
|
||||||
|
context.route("**/*", route -> {
|
||||||
|
// 清除 Playwright 添加的额外请求头
|
||||||
|
Request request = route.request();
|
||||||
|
List<HttpHeader> requestHeaderList = request.headersArray();
|
||||||
|
Map<String, String> requestHeaders = new HashMap<>();
|
||||||
|
for (HttpHeader header : requestHeaderList) {
|
||||||
|
requestHeaders.put(header.name, header.value);
|
||||||
|
}
|
||||||
|
requestHeaders.remove("sec-ch-ua");
|
||||||
|
requestHeaders.remove("sec-ch-ua-mobile");
|
||||||
|
requestHeaders.remove("sec-ch-ua-platform");
|
||||||
|
requestHeaders.remove("Cache-Control");
|
||||||
|
requestHeaders.remove("Pragma");
|
||||||
|
requestHeaders.remove("cache-control");
|
||||||
|
requestHeaders.remove("pragma");
|
||||||
|
|
||||||
|
// 判断页面附属请求并进行个性化,以尽可能模仿原生 APP
|
||||||
|
System.out.format("url: %s, requestType: %s\r\n", request.url(), request.resourceType());
|
||||||
|
|
||||||
|
String resourceType = request.resourceType();
|
||||||
|
|
||||||
|
if (!"document".equals(resourceType)) {
|
||||||
|
// 非 document(html) 请求的
|
||||||
|
requestHeaders.put("Sec-Fetch-Mode", "no-cors");
|
||||||
|
requestHeaders.remove("Upgrade-Insecure-Requests");
|
||||||
|
}
|
||||||
|
if ("image".equals(request.resourceType())) {
|
||||||
|
// 图片请求
|
||||||
|
requestHeaders.put("Accept", "image/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8");
|
||||||
|
}
|
||||||
|
else if ("script".equals(request.resourceType())) {
|
||||||
|
// 图片请求
|
||||||
|
requestHeaders.put("Accept", "*/*");
|
||||||
|
}
|
||||||
|
route.resume(new ResumeOptions().setHeaders(requestHeaders));
|
||||||
|
});
|
||||||
|
|
||||||
|
Page page = context.newPage();
|
||||||
|
WebviewResponseWrapper wrapper = new WebviewResponseWrapper();
|
||||||
|
|
||||||
|
page.onResponse(handler -> {
|
||||||
|
String requestUrl = handler.request().url();
|
||||||
|
if (requestUrl.endsWith(".js")) {
|
||||||
|
String jsContent = handler.request().response().text();
|
||||||
|
wrapper.jsMap.put(requestUrl, jsContent);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
StringBuilder urlBuilder = new StringBuilder();
|
||||||
|
urlBuilder.append("https://appstatic.emoney.cn/html/emapp/stock/note/?name=");
|
||||||
|
urlBuilder.append(indexCode);
|
||||||
|
urlBuilder.append("&emoneyScaleType=0&emoneyLandMode=0&token=");
|
||||||
|
urlBuilder.append(getEmoneyRequestConfig().getAuthorization());
|
||||||
|
String url = urlBuilder.toString();
|
||||||
|
|
||||||
|
page.navigate(url, new Page.NavigateOptions().setWaitUntil(WaitUntilState.NETWORKIDLE));
|
||||||
|
|
||||||
|
wrapper.setRenderedHtml(page.content());
|
||||||
|
wrapper.setPage(page);
|
||||||
|
browser.close();
|
||||||
|
|
||||||
|
return wrapper;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@Accessors(chain=true)
|
||||||
|
public static class WebviewResponseWrapper {
|
||||||
|
private Map<String, String> jsMap = new HashMap<>();
|
||||||
|
private String renderedHtml;
|
||||||
|
private Page page;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user