Add CLI argument parsing and fat jar build support

- Add -s/-u/-p/-d/-h command line options
- Add maven-shade-plugin for fat jar packaging
- Fix proxyImport signature to accept proxyHost parameter

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Niko 2026-05-05 15:54:27 +08:00
parent 9ef9f8bce4
commit 01505285b0
3 changed files with 158 additions and 64 deletions

30
pom.xml
View File

@ -53,6 +53,36 @@
<artifactId>maven-surefire-plugin</artifactId> <artifactId>maven-surefire-plugin</artifactId>
<version>3.5.2</version> <version>3.5.2</version>
</plugin> </plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.6.0</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>com.ets.scraper.EtsScraper</mainClass>
</transformer>
</transformers>
<filters>
<filter>
<artifact>*:*</artifact>
<excludes>
<exclude>META-INF/*.SF</exclude>
<exclude>META-INF/*.DSA</exclude>
<exclude>META-INF/*.RSA</exclude>
</excludes>
</filter>
</filters>
</configuration>
</execution>
</executions>
</plugin>
</plugins> </plugins>
</build> </build>
</project> </project>

View File

@ -34,6 +34,55 @@ public class EtsScraper {
private static final String PROXY_HOST = "http://127.0.0.1:8081"; private static final String PROXY_HOST = "http://127.0.0.1:8081";
public static void main(String[] args) throws Exception { 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 { try {
createDirectories(SCREENSHOT_DIR); createDirectories(SCREENSHOT_DIR);
} catch (Exception e) { } catch (Exception e) {
@ -95,10 +144,8 @@ public class EtsScraper {
screenshot(page, "after_sanliandan"); screenshot(page, "after_sanliandan");
System.out.println("[+] 三联单 page title: " + page.title()); System.out.println("[+] 三联单 page title: " + page.title());
// 设置日期筛选选择昨天的日期 // 设置日期筛选
java.time.LocalDate yesterday = java.time.LocalDate.now().minusDays(1); System.out.println("[*] Setting date filter to: " + dateStrFormatted);
String yesterdayStr = yesterday.format(java.time.format.DateTimeFormatter.ofPattern("yyyy-MM-dd"));
System.out.println("[*] Setting date filter to yesterday: " + yesterdayStr);
// 检查元素是否存在 // 检查元素是否存在
boolean startDateExists = page.locator("#Search_ThreeBillList_startWdate").count() > 0; boolean startDateExists = page.locator("#Search_ThreeBillList_startWdate").count() > 0;
@ -108,17 +155,17 @@ public class EtsScraper {
// 直接设置日期值WdatePicker 类型输入框 // 直接设置日期值WdatePicker 类型输入框
if (startDateExists) { if (startDateExists) {
System.out.println("[*] Setting start date to: " + yesterdayStr); System.out.println("[*] Setting start date to: " + dateStrFormatted);
page.locator("#Search_ThreeBillList_startWdate").first().fill(yesterdayStr); page.locator("#Search_ThreeBillList_startWdate").first().fill(dateStrFormatted);
sleep(500); sleep(500);
} else { } else {
System.out.println("[!] Start date element not found"); System.out.println("[!] Start date element not found");
} }
// 设置结束日期也用昨天的日期 // 设置结束日期
if (endDateExists) { if (endDateExists) {
System.out.println("[*] Setting end date to: " + yesterdayStr); System.out.println("[*] Setting end date to: " + dateStrFormatted);
page.locator("#Search_ThreeBillList_endWdate").first().fill(yesterdayStr); page.locator("#Search_ThreeBillList_endWdate").first().fill(dateStrFormatted);
sleep(500); sleep(500);
} else { } else {
System.out.println("[!] End date element not found"); System.out.println("[!] End date element not found");
@ -143,7 +190,7 @@ public class EtsScraper {
screenshot(page, "after_query"); screenshot(page, "after_query");
// 点击导出按钮先弹出导出选项对话框再点击对话框内的导出 // 点击导出按钮
if (page.locator("#Export_ThreeBillList_Button").count() > 0) { if (page.locator("#Export_ThreeBillList_Button").count() > 0) {
System.out.println("[*] Clicking export button..."); System.out.println("[*] Clicking export button...");
// 设置下载目录 // 设置下载目录
@ -159,17 +206,16 @@ public class EtsScraper {
page.evaluate("document.querySelectorAll('button').forEach(b => { if (b.textContent.trim() === '导出') b.click(); })"); 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...");
String dateStr = yesterday.format(DateTimeFormatter.ofPattern("yyyyMMdd")); Path savedFile = downloadPath.resolve("三联单列表_" + dateStrFileName + ".xls");
Path savedFile = downloadPath.resolve("三联单列表_" + dateStr + ".xls");
dl.saveAs(savedFile); dl.saveAs(savedFile);
System.out.println("[+] Download saved to: " + savedFile); System.out.println("[+] Download saved to: " + savedFile);
if (java.nio.file.Files.size(savedFile) == 0) { if (java.nio.file.Files.size(savedFile) == 0) {
System.out.println("[-] Downloaded file is empty"); System.out.println("[-] Downloaded file is empty");
} else { } else {
System.out.println("[+] Download size: " + java.nio.file.Files.size(savedFile) + " bytes"); System.out.println("[+] Download size: " + java.nio.file.Files.size(savedFile) + " bytes");
}
// Auto-import to ets-proxy // Auto-import to ets-proxy
autoImportBill(savedFile); 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!");
@ -191,6 +237,24 @@ public class EtsScraper {
} }
} }
private static void printHelp() {
System.out.println("""
ETS 三联单爬虫 - 导出并导入三联单 Excel 数据
用法: java -jar ets-playwright.jar [选项]
选项:
-s <url> ets-proxy 服务器地址 (默认: http://127.0.0.1:8081)
-u <user> ets-proxy 用户名 (默认: admin)
-p <pass> ets-proxy 密码 (默认: 123456)
-d <date> 查询日期格式 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 { public static boolean doLoginWithCaptcha(Page page) throws Exception {
// Recognize captcha first // Recognize captcha first
Path captchaPath = SCREENSHOT_DIR.resolve("captcha.png"); Path captchaPath = SCREENSHOT_DIR.resolve("captcha.png");
@ -396,13 +460,13 @@ public class EtsScraper {
return s; return s;
} }
public static void autoImportBill(Path filePath) { public static void autoImportBill(Path filePath, String proxyHost, String username, String password) {
String token = proxyLogin(PROXY_HOST, USERNAME, PASSWORD); String token = proxyLogin(proxyHost, username, password);
if (token == null) { if (token == null) {
System.out.println("[-] Proxy login failed, skipping import"); System.out.println("[-] Proxy login failed, skipping import");
return; return;
} }
proxyImport(filePath, token); proxyImport(filePath, proxyHost, token);
} }
public static String proxyLogin(String proxyHost, String username, String password) { 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 { try {
java.io.File file = filePath.toFile(); java.io.File file = filePath.toFile();
String boundary = "----FormBoundary" + System.currentTimeMillis(); String boundary = "----FormBoundary" + System.currentTimeMillis();
String boundaryLine = "--" + boundary; 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.ByteArrayOutputStream out = new java.io.ByteArrayOutputStream();
java.io.OutputStream os = out; java.io.OutputStream os = out;

View File

@ -31,6 +31,6 @@ class ImportTest {
String token = EtsScraper.proxyLogin(PROXY_HOST, USERNAME, PASSWORD); String token = EtsScraper.proxyLogin(PROXY_HOST, USERNAME, PASSWORD);
assertNotNull(token); assertNotNull(token);
EtsScraper.proxyImport(testFile.toPath(), token); EtsScraper.proxyImport(testFile.toPath(), PROXY_HOST, token);
} }
} }