package me.test.sto; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.net.MalformedURLException; import java.util.ArrayList; import java.util.Collection; import java.util.LinkedHashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Scanner; import java.util.concurrent.BlockingQueue; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import org.apache.commons.lang3.StringUtils; import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.CellStyle; import org.apache.poi.ss.usermodel.IndexedColors; import org.apache.poi.ss.usermodel.Row; import org.apache.poi.ss.usermodel.Sheet; import org.apache.poi.xssf.usermodel.XSSFCellStyle; import org.apache.poi.xssf.usermodel.XSSFWorkbook; import org.jsoup.Jsoup; import org.jsoup.nodes.Document; import org.jsoup.nodes.Element; import org.jsoup.select.Elements; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.gargoylesoftware.htmlunit.BrowserVersion; import com.gargoylesoftware.htmlunit.FailingHttpStatusCodeException; import com.gargoylesoftware.htmlunit.WebClient; import com.gargoylesoftware.htmlunit.html.HtmlPage; import com.gargoylesoftware.htmlunit.html.HtmlSubmitInput; import com.gargoylesoftware.htmlunit.html.HtmlTextArea; public class Sto implements Runnable { private static final Logger logger = LoggerFactory.getLogger(Sto.class); private static final String STO_QUERY_FORM_URL = "http://q.sto.cn/track.aspx"; private final WebClient webClient = new WebClient(BrowserVersion.FIREFOX_24); private final String expressNo; private final Map<String, List<Event>> resultSet; public Sto(Map<String, List<Event>> resultSet, String expressNo) { this.resultSet = resultSet; this.expressNo = expressNo; } @Override public void run() { try { List<Event> traceInfo = getTraceInfo(); resultSet.put(expressNo, traceInfo); logger.debug("[{}] : ok ", expressNo); } catch (Exception e) { logger.error("[" + expressNo + "] : error ", e); } finally { webClient.closeAllWindows(); } } public List<Event> getTraceInfo() throws FailingHttpStatusCodeException, MalformedURLException, IOException { webClient.getOptions().setThrowExceptionOnScriptError(false); webClient.closeAllWindows(); final HtmlPage queryFormPage = webClient.getPage(STO_QUERY_FORM_URL); HtmlTextArea input = (HtmlTextArea) queryFormPage.getElementById("wen"); input.click(); input.setText(expressNo); HtmlSubmitInput queryBtn = (HtmlSubmitInput) queryFormPage.getElementById("btnQuery"); HtmlPage resultPage = queryBtn.click(); Document doc = Jsoup.parse(resultPage.asXml()); Elements rows = doc.select("table.tab_result tr"); rows.remove(0); List<Event> resultList = new ArrayList<Event>(10); for (Element row : rows) { String time = row.child(0).text(); String event = row.child(1).text(); resultList.add(new Event(expressNo, time, event)); } return resultList; } public static void main(String[] args) throws IOException { if (args.length == 0) { System.err.println("Usage : java me.test.sto.Sto <expressNoListFile>"); System.exit(1); } File file = new File(args[0]); if (!file.exists()) { System.err.println("expressNoListFile not exist. " + file.getAbsolutePath()); System.exit(2); } BlockingQueue<Runnable> queue = new LinkedBlockingQueue<Runnable>(); ThreadPoolExecutor executor = new ThreadPoolExecutor(4, 8, 10, TimeUnit.SECONDS, queue); long startTime = System.currentTimeMillis(); logger.info("查询 —— 开始"); Scanner scanner = new Scanner(new FileInputStream(file)); Collection<Future<?>> futures = new LinkedList<Future<?>>(); Map<String, List<Event>> resultSet = new LinkedHashMap<String, List<Event>>(); try { while (scanner.hasNextLine()) { String expressNo = StringUtils.trimToNull(scanner.nextLine()); if (expressNo == null) { continue; } futures.add(executor.submit(new Sto(resultSet, expressNo))); } } finally { scanner.close(); } for (Future<?> future : futures) { try { future.get(); } catch (Exception e) { logger.error(e.getMessage(), e); } } long endTime = System.currentTimeMillis(); logger.info("查询 —— 结束, 耗时 :{} ms", endTime - startTime); exportXlsx(resultSet); } public static void exportXlsx(Map<String, List<Event>> resultSet) throws FileNotFoundException, IOException { long startTime = System.currentTimeMillis(); logger.info("导出 —— 开始"); final int cols = 3; XSSFWorkbook wb = new XSSFWorkbook(); Sheet sh = wb.createSheet(); XSSFCellStyle titleStyle = wb.createCellStyle(); titleStyle.setBorderBottom(XSSFCellStyle.BORDER_THIN); titleStyle.setBorderTop(XSSFCellStyle.BORDER_THIN); titleStyle.setBorderRight(XSSFCellStyle.BORDER_THIN); titleStyle.setBorderLeft(XSSFCellStyle.BORDER_THIN); titleStyle.setAlignment(CellStyle.ALIGN_CENTER); titleStyle.setFillForegroundColor(IndexedColors.GREY_25_PERCENT.index); titleStyle.setFillPattern(XSSFCellStyle.FINE_DOTS); XSSFCellStyle cellStyle = wb.createCellStyle(); cellStyle.setBorderBottom(XSSFCellStyle.BORDER_THIN); cellStyle.setBorderTop(XSSFCellStyle.BORDER_THIN); cellStyle.setBorderRight(XSSFCellStyle.BORDER_THIN); cellStyle.setBorderLeft(XSSFCellStyle.BORDER_THIN); Row row; Cell cell; // 行 int i = 0; // 列 int j = 0; row = sh.createRow(i); cell = row.createCell(j); cell.setCellType(Cell.CELL_TYPE_STRING); cell.setCellStyle(titleStyle); cell.setCellValue("STO 快递单号"); j++; cell = row.createCell(j); cell.setCellType(Cell.CELL_TYPE_STRING); cell.setCellStyle(titleStyle); cell.setCellValue("时间"); j++; cell = row.createCell(j); cell.setCellType(Cell.CELL_TYPE_STRING); cell.setCellStyle(titleStyle); cell.setCellValue("事件"); for (Entry<String, List<Event>> entry : resultSet.entrySet()) { String expressNo = entry.getKey(); List<Event> resultList = entry.getValue(); for (Event event : resultList) { i++; row = sh.createRow(i); j = 0; cell = row.createCell(j); cell.setCellStyle(cellStyle); cell.setCellType(Cell.CELL_TYPE_STRING); cell.setCellValue(expressNo); j++; cell = row.createCell(j); cell.setCellStyle(cellStyle); cell.setCellType(Cell.CELL_TYPE_STRING); cell.setCellValue(StringUtils.trimToNull(event.time)); j++; cell = row.createCell(j); cell.setCellStyle(cellStyle); cell.setCellType(Cell.CELL_TYPE_STRING); cell.setCellValue(StringUtils.trimToNull(event.event)); } } for (int col = 0; col < cols; col++) { sh.autoSizeColumn(col); } wb.write(new FileOutputStream("sto.xlsx")); wb.close(); long endTime = System.currentTimeMillis(); logger.info("导出 —— 结束, 耗时 :{} ms", endTime - startTime); } public static class Event { public String expressNo; public String time; public String event; public Event() { } public Event(String expressNo, String time, String event) { this.expressNo = expressNo; this.time = time; this.event = event; } } }