fix download excel
This commit is contained in:
parent
b86d0bf82f
commit
1b7ebb4325
@ -99,8 +99,10 @@ public class EtsScraper {
|
|||||||
|
|
||||||
try (Playwright playwright = Playwright.create()) {
|
try (Playwright playwright = Playwright.create()) {
|
||||||
Browser browser = playwright.chromium().launch(
|
Browser browser = playwright.chromium().launch(
|
||||||
new BrowserType.LaunchOptions().setHeadless(true)
|
new BrowserType.LaunchOptions()
|
||||||
|
.setHeadless(false)
|
||||||
);
|
);
|
||||||
|
|
||||||
BrowserContext context = browser.newContext(
|
BrowserContext context = browser.newContext(
|
||||||
new Browser.NewContextOptions()
|
new Browser.NewContextOptions()
|
||||||
.setIgnoreHTTPSErrors(true)
|
.setIgnoreHTTPSErrors(true)
|
||||||
@ -127,7 +129,7 @@ public class EtsScraper {
|
|||||||
// Close notification dialog FIRST (before filling credentials)
|
// Close notification dialog FIRST (before filling credentials)
|
||||||
closeNotificationDialog(page);
|
closeNotificationDialog(page);
|
||||||
|
|
||||||
screenshot(page, "after_close_dialog");
|
// screenshot(page, "after_close_dialog");
|
||||||
|
|
||||||
// Download captcha image
|
// Download captcha image
|
||||||
downloadCaptcha(page);
|
downloadCaptcha(page);
|
||||||
@ -142,7 +144,7 @@ public class EtsScraper {
|
|||||||
System.out.println("[+] Login successful!");
|
System.out.println("[+] Login successful!");
|
||||||
sleep(2000);
|
sleep(2000);
|
||||||
|
|
||||||
screenshot(page, "after_login");
|
// screenshot(page, "after_login");
|
||||||
|
|
||||||
System.out.println("[+] Page title: " + page.title());
|
System.out.println("[+] Page title: " + page.title());
|
||||||
System.out.println("[+] Page URL: " + page.url());
|
System.out.println("[+] Page URL: " + page.url());
|
||||||
@ -152,7 +154,7 @@ public class EtsScraper {
|
|||||||
page.locator("#module_2094F683-C542-4904-B33E-0D227C4DE199").first().click();
|
page.locator("#module_2094F683-C542-4904-B33E-0D227C4DE199").first().click();
|
||||||
sleep(3000);
|
sleep(3000);
|
||||||
|
|
||||||
screenshot(page, "after_sanliandan");
|
// screenshot(page, "after_sanliandan");
|
||||||
System.out.println("[+] 三联单 page title: " + page.title());
|
System.out.println("[+] 三联单 page title: " + page.title());
|
||||||
|
|
||||||
// 设置日期筛选
|
// 设置日期筛选
|
||||||
@ -185,21 +187,16 @@ public class EtsScraper {
|
|||||||
// 点击查询按钮,等待列表加载
|
// 点击查询按钮,等待列表加载
|
||||||
if (queryBtnExists) {
|
if (queryBtnExists) {
|
||||||
System.out.println("[*] Clicking query button...");
|
System.out.println("[*] Clicking query button...");
|
||||||
page.locator("#Search_ThreeBillList_Button").first().click();
|
|
||||||
|
|
||||||
// 等待列表内容出现
|
// 等待列表内容出现
|
||||||
try {
|
page.waitForResponse("https://101.227.180.215/SHCityEnvCW/Services/CWSServ.asmx/ThreeBillQueryBiTripList", () -> {
|
||||||
page.waitForSelector("tbody tr", new Page.WaitForSelectorOptions()
|
page.locator("#Search_ThreeBillList_Button").first().click();
|
||||||
.setTimeout(30000));
|
});
|
||||||
System.out.println("[+] Query completed, list loaded");
|
page.waitForTimeout(3 * 1000);
|
||||||
} catch (Exception e) {
|
|
||||||
System.out.println("[!] Wait for list timeout, but query was submitted");
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
System.out.println("[!] Query button not found");
|
System.out.println("[!] Query button not found");
|
||||||
}
|
}
|
||||||
|
|
||||||
screenshot(page, "after_query");
|
// screenshot(page, "after_query");
|
||||||
|
|
||||||
// 点击导出按钮
|
// 点击导出按钮
|
||||||
if (page.locator("#Export_ThreeBillList_Button").count() > 0) {
|
if (page.locator("#Export_ThreeBillList_Button").count() > 0) {
|
||||||
@ -210,34 +207,25 @@ public class EtsScraper {
|
|||||||
() -> {
|
() -> {
|
||||||
page.locator("#Export_ThreeBillList_Button").first().click();
|
page.locator("#Export_ThreeBillList_Button").first().click();
|
||||||
sleep(2000);
|
sleep(2000);
|
||||||
System.out.println("[*] Triggering dialog export via JS...");
|
|
||||||
page.evaluate("document.querySelectorAll('button').forEach(b => { if (b.textContent.trim() === '导出') b.click(); })");
|
|
||||||
});
|
});
|
||||||
|
|
||||||
System.out.println("[*] Waiting for download to complete...");
|
System.out.println("[*] Waiting for download to complete...");
|
||||||
long[] totalBytes = {0};
|
dl.saveAs(savedFile);
|
||||||
try (java.io.InputStream stream = dl.createReadStream();
|
|
||||||
java.io.OutputStream out = Files.newOutputStream(savedFile)) {
|
long totalBytes = savedFile.toFile().length();
|
||||||
byte[] buf = new byte[8192];
|
System.out.println("[+] Download saved to: " + savedFile + " (" + totalBytes + " bytes)");
|
||||||
int n;
|
if (totalBytes == 0) {
|
||||||
while ((n = stream.read(buf)) > 0) {
|
|
||||||
out.write(buf, 0, n);
|
|
||||||
totalBytes[0] += n;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
System.out.println("[+] Download saved to: " + savedFile + " (" + totalBytes[0] + " bytes)");
|
|
||||||
if (totalBytes[0] == 0) {
|
|
||||||
System.out.println("[-] Downloaded file is empty");
|
System.out.println("[-] Downloaded file is empty");
|
||||||
} else {
|
} else {
|
||||||
System.out.println("[+] Download size: " + totalBytes[0] + " bytes");
|
System.out.println("[+] Download size: " + totalBytes + " bytes");
|
||||||
// Auto-import to ets-proxy
|
|
||||||
autoImportBill(savedFile, proxyHost, proxyUser, proxyPass);
|
autoImportBill(savedFile, proxyHost, proxyUser, proxyPass);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
screenshot(page, "after_export");
|
// screenshot(page, "after_export");
|
||||||
System.out.println("[+] Query and export completed!");
|
System.out.println("[+] Query and export completed!");
|
||||||
} else {
|
} else {
|
||||||
System.out.println("[-] Login failed. Check screenshots/ for debugging.");
|
System.out.println("[-] Login failed. Check screenshots/ for debugging.");
|
||||||
screenshot(page, "login_failed");
|
// screenshot(page, "login_failed");
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
browser.close();
|
browser.close();
|
||||||
|
|||||||
@ -1,90 +0,0 @@
|
|||||||
package com.ets.scraper;
|
|
||||||
|
|
||||||
import org.junit.jupiter.api.*;
|
|
||||||
import org.junit.jupiter.api.io.TempDir;
|
|
||||||
|
|
||||||
import javax.imageio.ImageIO;
|
|
||||||
import java.awt.image.BufferedImage;
|
|
||||||
import java.io.ByteArrayInputStream;
|
|
||||||
import java.io.ByteArrayOutputStream;
|
|
||||||
import java.nio.file.Files;
|
|
||||||
import java.nio.file.Path;
|
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.*;
|
|
||||||
|
|
||||||
class EtsScraperTest {
|
|
||||||
|
|
||||||
@TempDir
|
|
||||||
Path tempDir;
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testGifToPngConversion() throws Exception {
|
|
||||||
Path gifPath = tempDir.resolve("captcha.png");
|
|
||||||
// Copy the actual captcha (GIF stored as .png) to temp
|
|
||||||
Files.copy(
|
|
||||||
Path.of("screenshots/captcha.png").toAbsolutePath(),
|
|
||||||
gifPath
|
|
||||||
);
|
|
||||||
|
|
||||||
byte[] imageBytes = Files.readAllBytes(gifPath);
|
|
||||||
ByteArrayInputStream bais = new ByteArrayInputStream(imageBytes);
|
|
||||||
BufferedImage gifImage = ImageIO.read(bais);
|
|
||||||
|
|
||||||
assertNotNull(gifImage, "GIF should be readable by ImageIO");
|
|
||||||
assertTrue(gifImage.getWidth() > 0, "Image should have positive width");
|
|
||||||
|
|
||||||
ByteArrayOutputStream pngOut = new ByteArrayOutputStream();
|
|
||||||
ImageIO.write(gifImage, "png", pngOut);
|
|
||||||
byte[] pngBytes = pngOut.toByteArray();
|
|
||||||
|
|
||||||
assertTrue(pngBytes.length > 0, "PNG output should not be empty");
|
|
||||||
|
|
||||||
// Verify converted PNG is valid
|
|
||||||
ByteArrayInputStream bais2 = new ByteArrayInputStream(pngBytes);
|
|
||||||
BufferedImage pngImage = ImageIO.read(bais2);
|
|
||||||
assertNotNull(pngImage, "Converted PNG should be readable");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testGifToPngProducesValidPng() throws Exception {
|
|
||||||
byte[] gifBytes = Files.readAllBytes(Path.of("screenshots/captcha.png").toAbsolutePath());
|
|
||||||
ByteArrayInputStream bais = new ByteArrayInputStream(gifBytes);
|
|
||||||
BufferedImage image = ImageIO.read(bais);
|
|
||||||
|
|
||||||
ByteArrayOutputStream pngOut = new ByteArrayOutputStream();
|
|
||||||
ImageIO.write(image, "png", pngOut);
|
|
||||||
|
|
||||||
// PNG header: 89 50 4E 47 0D 0A 1A 0A
|
|
||||||
byte[] pngHeader = pngOut.toByteArray();
|
|
||||||
// bytes are signed in Java, mask with & 0xFF
|
|
||||||
assertEquals(0x89 & 0xFF, pngHeader[0] & 0xFF, "PNG magic number");
|
|
||||||
assertEquals(0x50 & 0xFF, pngHeader[1] & 0xFF, "P");
|
|
||||||
assertEquals(0x4E & 0xFF, pngHeader[2] & 0xFF, "N");
|
|
||||||
assertEquals(0x47 & 0xFF, pngHeader[3] & 0xFF, "G");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testBase64Encoding() throws Exception {
|
|
||||||
byte[] imageBytes = Files.readAllBytes(Path.of("screenshots/captcha.png").toAbsolutePath());
|
|
||||||
String base64 = java.util.Base64.getEncoder().encodeToString(imageBytes);
|
|
||||||
|
|
||||||
assertNotNull(base64);
|
|
||||||
assertTrue(base64.length() > 0, "Base64 should not be empty");
|
|
||||||
assertFalse(base64.contains("\n"), "Base64 should be single line");
|
|
||||||
|
|
||||||
// Verify roundtrip
|
|
||||||
byte[] decoded = java.util.Base64.getDecoder().decode(base64);
|
|
||||||
assertArrayEquals(imageBytes, decoded, "Base64 roundtrip should match original");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testCaptchaRecognition() throws Exception {
|
|
||||||
Path captchaPath = Path.of("screenshots/captcha.png");
|
|
||||||
String captchaText = EtsScraper.recognizeCaptcha(captchaPath);
|
|
||||||
|
|
||||||
System.out.println("[+] Recognized captcha: " + captchaText);
|
|
||||||
assertNotNull(captchaText, "Captcha recognition should return a result");
|
|
||||||
assertFalse(captchaText.isEmpty(), "Captcha text should not be empty");
|
|
||||||
System.out.println("[+] Captcha length: " + captchaText.length() + " chars");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,36 +0,0 @@
|
|||||||
package com.ets.scraper;
|
|
||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
import org.junit.jupiter.api.io.TempDir;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.nio.file.Path;
|
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.*;
|
|
||||||
|
|
||||||
class ImportTest {
|
|
||||||
|
|
||||||
private static final String PROXY_HOST = "https://api.ets.niko.red";
|
|
||||||
private static final String USERNAME = "admin";
|
|
||||||
private static final String PASSWORD = "123456";
|
|
||||||
|
|
||||||
@TempDir
|
|
||||||
Path tempDir;
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testImportExcel() throws Exception {
|
|
||||||
File testFile = tempDir.resolve("三联单列表_20260504.xls").toFile();
|
|
||||||
Path source = Path.of("downloads/三联单列表_20260504.xls");
|
|
||||||
if (source.toFile().exists()) {
|
|
||||||
java.nio.file.Files.copy(source, testFile.toPath(), java.nio.file.StandardCopyOption.REPLACE_EXISTING);
|
|
||||||
} else {
|
|
||||||
System.out.println("[!] Test file not found at " + source + ", using temp file as fallback");
|
|
||||||
}
|
|
||||||
assertTrue(testFile.exists(), "Test file must exist");
|
|
||||||
|
|
||||||
String token = EtsScraper.proxyLogin(PROXY_HOST, USERNAME, PASSWORD);
|
|
||||||
assertNotNull(token);
|
|
||||||
|
|
||||||
EtsScraper.proxyImport(testFile.toPath(), PROXY_HOST, token);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Loading…
x
Reference in New Issue
Block a user