diff --git a/pom.xml b/pom.xml index 2edb223..ca9943d 100644 --- a/pom.xml +++ b/pom.xml @@ -53,6 +53,36 @@ maven-surefire-plugin 3.5.2 + + org.apache.maven.plugins + maven-shade-plugin + 3.6.0 + + + package + + shade + + + + + com.ets.scraper.EtsScraper + + + + + *:* + + META-INF/*.SF + META-INF/*.DSA + META-INF/*.RSA + + + + + + + diff --git a/src/main/java/com/ets/scraper/EtsScraper.java b/src/main/java/com/ets/scraper/EtsScraper.java index 7d1fa7a..7a8ce1c 100644 --- a/src/main/java/com/ets/scraper/EtsScraper.java +++ b/src/main/java/com/ets/scraper/EtsScraper.java @@ -34,48 +34,97 @@ public class EtsScraper { private static final String PROXY_HOST = "http://127.0.0.1:8081"; public static void main(String[] args) throws Exception { + // Parse CLI arguments + String proxyHost = null; + String proxyUser = null; + String proxyPass = null; + String dateStr = null; + + for (int i = 0; i < args.length; i++) { + switch (args[i]) { + case "-h": + printHelp(); + return; + case "-s": + proxyHost = args[++i]; + break; + case "-u": + proxyUser = args[++i]; + break; + case "-p": + proxyPass = args[++i]; + break; + case "-d": + dateStr = args[++i]; + break; + default: + System.err.println("[-] Unknown option: " + args[i]); + printHelp(); + return; + } + } + + if (proxyHost == null) { + proxyHost = "http://127.0.0.1:8081"; + } + if (proxyUser == null) { + proxyUser = "admin"; + } + if (proxyPass == null) { + proxyPass = "123456"; + } + + java.time.LocalDate targetDate; + if (dateStr != null) { + targetDate = java.time.LocalDate.parse(dateStr); + } else { + targetDate = java.time.LocalDate.now().minusDays(1); + } + String dateStrFormatted = targetDate.format(java.time.format.DateTimeFormatter.ofPattern("yyyy-MM-dd")); + String dateStrFileName = targetDate.format(DateTimeFormatter.ofPattern("yyyyMMdd")); + try { createDirectories(SCREENSHOT_DIR); - } catch (Exception e) { + } catch (Exception e) { System.err.println("Failed to create directories: " + e.getMessage()); - } + } try (Playwright playwright = Playwright.create()) { Browser browser = playwright.chromium().launch( new BrowserType.LaunchOptions().setHeadless(false) - ); + ); BrowserContext context = browser.newContext( new Browser.NewContextOptions().setIgnoreHTTPSErrors(true) - ); + ); Page page = context.newPage(); try { - // Navigate to frame.html first to establish session/cookies + // Navigate to frame.html first to establish session/cookies System.out.println("[*] Establishing session via " + FRAME_URL); page.navigate(FRAME_URL, new Page.NavigateOptions() - .setTimeout(30000) - .setWaitUntil(WaitUntilState.DOMCONTENTLOADED)); + .setTimeout(30000) + .setWaitUntil(WaitUntilState.DOMCONTENTLOADED)); sleep(3000); - // Navigate directly to the login page + // Navigate directly to the login page System.out.println("[*] Navigating to login page: " + LOGIN_URL); page.navigate(LOGIN_URL, new Page.NavigateOptions() - .setTimeout(30000) - .setWaitUntil(WaitUntilState.NETWORKIDLE)); + .setTimeout(30000) + .setWaitUntil(WaitUntilState.NETWORKIDLE)); sleep(2000); - // Close notification dialog FIRST (before filling credentials) + // Close notification dialog FIRST (before filling credentials) closeNotificationDialog(page); screenshot(page, "after_close_dialog"); - // Download captcha image + // Download captcha image downloadCaptcha(page); - // Close dialog again after page reload + // Close dialog again after page reload closeNotificationDialog(page); - // Recognize captcha and perform login + // Recognize captcha and perform login boolean loggedin = doLoginWithCaptcha(page); if (loggedin) { @@ -87,7 +136,7 @@ public class EtsScraper { System.out.println("[+] Page title: " + page.title()); System.out.println("[+] Page URL: " + page.url()); - // 点击三联单菜单 + // 点击三联单菜单 System.out.println("[*] Clicking 三联单 menu..."); page.locator("#module_2094F683-C542-4904-B33E-0D227C4DE199").first().click(); sleep(3000); @@ -95,101 +144,116 @@ public class EtsScraper { screenshot(page, "after_sanliandan"); System.out.println("[+] 三联单 page title: " + page.title()); - // 设置日期筛选:选择昨天的日期 - java.time.LocalDate yesterday = java.time.LocalDate.now().minusDays(1); - String yesterdayStr = yesterday.format(java.time.format.DateTimeFormatter.ofPattern("yyyy-MM-dd")); - System.out.println("[*] Setting date filter to yesterday: " + yesterdayStr); + // 设置日期筛选 + System.out.println("[*] Setting date filter to: " + dateStrFormatted); - // 检查元素是否存在 + // 检查元素是否存在 boolean startDateExists = page.locator("#Search_ThreeBillList_startWdate").count() > 0; boolean endDateExists = page.locator("#Search_ThreeBillList_endWdate").count() > 0; boolean queryBtnExists = page.locator("#Search_ThreeBillList_Button").count() > 0; System.out.println("[*] Elements found - startDate: " + startDateExists + ", endDate: " + endDateExists + ", queryBtn: " + queryBtnExists); - // 直接设置日期值(WdatePicker 类型输入框) + // 直接设置日期值(WdatePicker 类型输入框) if (startDateExists) { - System.out.println("[*] Setting start date to: " + yesterdayStr); - page.locator("#Search_ThreeBillList_startWdate").first().fill(yesterdayStr); + System.out.println("[*] Setting start date to: " + dateStrFormatted); + page.locator("#Search_ThreeBillList_startWdate").first().fill(dateStrFormatted); sleep(500); - } else { + } else { System.out.println("[!] Start date element not found"); - } + } - // 设置结束日期(也用昨天的日期) + // 设置结束日期 if (endDateExists) { - System.out.println("[*] Setting end date to: " + yesterdayStr); - page.locator("#Search_ThreeBillList_endWdate").first().fill(yesterdayStr); + System.out.println("[*] Setting end date to: " + dateStrFormatted); + page.locator("#Search_ThreeBillList_endWdate").first().fill(dateStrFormatted); sleep(500); - } else { + } else { System.out.println("[!] End date element not found"); - } + } - // 点击查询按钮,等待列表加载 + // 点击查询按钮,等待列表加载 if (queryBtnExists) { System.out.println("[*] Clicking query button..."); page.locator("#Search_ThreeBillList_Button").first().click(); - // 等待列表内容出现 + // 等待列表内容出现 try { page.waitForSelector("tbody tr", new Page.WaitForSelectorOptions() - .setTimeout(30000)); + .setTimeout(30000)); System.out.println("[+] Query completed, list loaded"); - } catch (Exception e) { + } catch (Exception e) { System.out.println("[!] Wait for list timeout, but query was submitted"); - } - } else { + } + } else { System.out.println("[!] Query button not found"); - } + } screenshot(page, "after_query"); - // 点击导出按钮(先弹出导出选项对话框,再点击对话框内的导出) + // 点击导出按钮 if (page.locator("#Export_ThreeBillList_Button").count() > 0) { System.out.println("[*] Clicking export button..."); - // 设置下载目录 + // 设置下载目录 Path downloadPath = Path.of("downloads").toAbsolutePath().normalize(); java.nio.file.Files.createDirectories(downloadPath); - // 点击主导出按钮打开对话框,再用 JS click 触发对话框内导出按钮 + // 点击主导出按钮打开对话框,再用 JS click 触发对话框内导出按钮 Download dl = page.waitForDownload( new Page.WaitForDownloadOptions().setTimeout(300000), - () -> { + () -> { page.locator("#Export_ThreeBillList_Button").first().click(); 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..."); - String dateStr = yesterday.format(DateTimeFormatter.ofPattern("yyyyMMdd")); - Path savedFile = downloadPath.resolve("三联单列表_" + dateStr + ".xls"); + Path savedFile = downloadPath.resolve("三联单列表_" + dateStrFileName + ".xls"); dl.saveAs(savedFile); System.out.println("[+] Download saved to: " + savedFile); if (java.nio.file.Files.size(savedFile) == 0) { System.out.println("[-] Downloaded file is empty"); - } else { + } else { System.out.println("[+] Download size: " + java.nio.file.Files.size(savedFile) + " bytes"); - } - // Auto-import to ets-proxy - autoImportBill(savedFile); - } + // Auto-import to ets-proxy + autoImportBill(savedFile, proxyHost, proxyUser, proxyPass); + } + } screenshot(page, "after_export"); System.out.println("[+] Query and export completed!"); String content = page.textContent("body"); if (content != null) { String preview = content.length() > 500 - ? content.substring(0, 500) + ".." - : content; + ? content.substring(0, 500) + ".." + : content; System.out.println("[+] Page content preview:\n" + preview); - } - } else { + } + } else { System.out.println("[-] Login failed. Check screenshots/ for debugging."); screenshot(page, "login_failed"); - } - } finally { + } + } finally { browser.close(); - } - } - } + } + } + } + + private static void printHelp() { + System.out.println(""" + ETS 三联单爬虫 - 导出并导入三联单 Excel 数据 + + 用法: java -jar ets-playwright.jar [选项] + + 选项: + -s ets-proxy 服务器地址 (默认: http://127.0.0.1:8081) + -u ets-proxy 用户名 (默认: admin) + -p ets-proxy 密码 (默认: 123456) + -d 查询日期,格式 yyyy-MM-dd (默认: 昨天) + -h 显示此帮助信息 + + 示例: + java -jar ets-playwright.jar -s https://api.ets.niko.red -u admin -p 123456 -d 2026-05-04 + """); + } public static boolean doLoginWithCaptcha(Page page) throws Exception { // Recognize captcha first @@ -396,13 +460,13 @@ public class EtsScraper { return s; } - public static void autoImportBill(Path filePath) { - String token = proxyLogin(PROXY_HOST, USERNAME, PASSWORD); + public static void autoImportBill(Path filePath, String proxyHost, String username, String password) { + String token = proxyLogin(proxyHost, username, password); if (token == null) { System.out.println("[-] Proxy login failed, skipping import"); return; } - proxyImport(filePath, token); + proxyImport(filePath, proxyHost, token); } public static String proxyLogin(String proxyHost, String username, String password) { @@ -440,12 +504,12 @@ public class EtsScraper { } } - public static void proxyImport(Path filePath, String token) { + public static void proxyImport(Path filePath, String proxyHost, String token) { try { java.io.File file = filePath.toFile(); String boundary = "----FormBoundary" + System.currentTimeMillis(); String boundaryLine = "--" + boundary; - java.net.URI uri = java.net.URI.create(PROXY_HOST + "/api/bill/import"); + java.net.URI uri = java.net.URI.create(proxyHost + "/api/bill/import"); java.io.ByteArrayOutputStream out = new java.io.ByteArrayOutputStream(); java.io.OutputStream os = out; diff --git a/src/test/java/com/ets/scraper/ImportTest.java b/src/test/java/com/ets/scraper/ImportTest.java index b91e821..acbb650 100644 --- a/src/test/java/com/ets/scraper/ImportTest.java +++ b/src/test/java/com/ets/scraper/ImportTest.java @@ -31,6 +31,6 @@ class ImportTest { String token = EtsScraper.proxyLogin(PROXY_HOST, USERNAME, PASSWORD); assertNotNull(token); - EtsScraper.proxyImport(testFile.toPath(), token); + EtsScraper.proxyImport(testFile.toPath(), PROXY_HOST, token); } }