提要:html
查詢餘票網址、所需參數可見上篇博客(http://www.cnblogs.com/sei-cxt/p/8429069.html)。java
參考網上發送https請求的案例代碼寫出能實現查詢餘票的簡易程序,具體代碼原理詳情暫且無論。數組
關於對http和https的鏈接和請求,網上有多種版本,用的最多的就是HttpURLConnection、HttpsURLConnection和HttpClient。如下編寫的代碼參考於兩個網址:瀏覽器
http://blog.csdn.net/qh_java/article/details/52311226緩存
http://blog.csdn.net/guozili1/article/details/53995121app
1. 工具類(HttpsRequest):暫時只寫了GET方法,其中要注意的是:dom
GET方法的參數是直接追加在地址後面的,所以doOutput默認爲false便可。ide
在此例中,若setRequestMethod爲「POST」,會報411錯誤,是由於沒有提供相應的BODY數據。須要把doOutput設置爲true,而且使用OutputStream輸出數據。(雖然不會報錯了,但返回的數據爲空)函數
同時有的網站不容許使用「POST」方法,當用OutputStream輸出數據後會報405錯誤。工具
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.UnsupportedEncodingException; import java.net.URL; import java.net.URLEncoder; import java.security.KeyManagementException; import java.security.NoSuchAlgorithmException; import java.security.NoSuchProviderException; import java.util.Map; import java.util.Map.Entry; import javax.net.ssl.HttpsURLConnection; public class HttpsRequest { public static String methodGet(String urlStr, Map<String, Object> params) { String realUrl = urlStr; StringBuffer sb = new StringBuffer(); // GET方法須要將查詢字符串拼接在url後面 if(params != null && params.size() != 0) { realUrl += encodeParam(params); } try { // 鏈接url URL url = new URL(realUrl); HttpsURLConnection conn = (HttpsURLConnection) url.openConnection(); conn.setRequestMethod("GET"); // URL鏈接可用於輸入和/或輸出 // 若是打算使用 URL鏈接進行輸出,則將DoOutput標誌設置爲true,默認值爲false // 若是打算使用 URL鏈接進行輸入,則將DoInput標誌設置爲true,默認值爲true // conn.setDoOutput(false); // conn.setDoInput(true); // 容許鏈接使用任何可用的緩存,默認值爲true // conn.setUseCaches(true); conn.setSSLSocketFactory(MyX509TrustManager.getSSLSocketFactory()); // 設置通常請求屬性 conn.setRequestProperty("accept", "*/*"); // 客戶端能夠處理哪些數據類型 conn.setRequestProperty("connection", "Keep-Alive"); // 設置長鏈接,無過時時間 conn.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1)"); // 使用的何種瀏覽器 // 創建通訊連接 conn.connect(); // 讀取response InputStreamReader isr = new InputStreamReader(conn.getInputStream(), "utf-8"); BufferedReader br = new BufferedReader(isr); String res = null; while((res = br.readLine()) != null) { sb.append(res); } br.close(); isr.close(); } catch (IOException e) { e.printStackTrace(); } catch (KeyManagementException e) { e.printStackTrace(); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } catch (NoSuchProviderException e) { e.printStackTrace(); } return sb.toString(); } /** * 將查詢的map形式的參數轉碼並拼成查詢字符串 * @param params * @return */ private static String encodeParam(Map<String, Object> params) { if(params == null || params.size() == 0) { return ""; } StringBuffer sb = new StringBuffer("?"); for(Entry<String, Object> para : params.entrySet()) { try { sb.append(URLEncoder.encode(para.getKey(), "UTF-8")); sb.append("="); sb.append(URLEncoder.encode(para.getValue().toString(), "UTF-8")); sb.append("&"); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } } String query = sb.toString(); // 去掉最後一個& return query.substring(0, query.length() - 1); } }
2. 證書相關類(MyX509TrustManager):之後再弄明白原理。
import java.security.KeyManagementException; import java.security.NoSuchAlgorithmException; import java.security.NoSuchProviderException; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLSocketFactory; import javax.net.ssl.TrustManager; import javax.net.ssl.X509TrustManager; public class MyX509TrustManager implements X509TrustManager { @Override public void checkClientTrusted(X509Certificate[] arg0, String arg1) throws CertificateException { } @Override public void checkServerTrusted(X509Certificate[] arg0, String arg1) throws CertificateException { } @Override public X509Certificate[] getAcceptedIssuers() { return null; } public static SSLSocketFactory getSSLSocketFactory() throws NoSuchAlgorithmException, NoSuchProviderException, KeyManagementException{ TrustManager[] tm = {new MyX509TrustManager()}; SSLContext sslContext = SSLContext.getInstance("SSL", "SunJSSE"); sslContext.init(null, tm, new java.security.SecureRandom()); SSLSocketFactory ssf = sslContext.getSocketFactory(); return ssf; } }
3. main函數:
其中參數params我最開始用的HashMap,可是沒法獲得正確的返回數據。斷點後看出拼接後的網址中查詢字符串順序有顛倒,與前篇博客中抓取的網址有細微的不一樣,並驗證得知這就是錯誤緣由。因而換爲LinkedHashMap(能夠按照插入的順序遍歷的Map),成功。
之後考慮換爲JSON或者構造一個信息類,使用類的數組去解決問題。
正確:(使用LinkedHashMap)
錯誤:(使用HashMap)
public static void main(String[] args) { Map<String, Object> params = new LinkedHashMap<String, Object>(); params.put("leftTicketDTO.train_date", "2018-02-24"); params.put("leftTicketDTO.from_station", "BJP"); params.put("leftTicketDTO.to_station", "SHH"); params.put("purpose_codes", "ADULT"); String url = "https://kyfw.12306.cn/otn/leftTicket/queryZ"; String response = HttpsRequest.methodGet(url, params); System.out.println(response); }
4. 獲得結果:
{"data":{"flag":"1","map":{"AOH":"上海虹橋","BJP":"北京","SHH":"上海","VNP":"北京南"},"result":["|預訂|240000G1512M|G151|VNP|AOH|VNP|AOH|16:35|22:35|06:00|N|usY%2BUl57hWKitIUp1d4m3n3e0ys4iJTdDfedKU6oXk7F3bAb|20180224|3|P2|01|12|1|0|||||||||||無|無|無||O0M090|OM9|0","|預訂|2400000G2302|G23|VNP|AOH|VNP|AOH|17:00|22:39|05:39|N|usY%2BUl57hWKitIUp1d4m3n3e0ys4iJTdDfedKU6oXk7F3bAb|20180224|3|P3|01|10|1|0|||||||||||無|無|無||O0M090|OM9|0","|預訂|240000G1530E|G153|VNP|AOH|VNP|AOH|17:15|22:49|05:34|N|usY%2BUl57hWKitIUp1d4m3n3e0ys4iJTdDfedKU6oXk7F3bAb|20180224|3|P3|01|09|1|0|||||||||||無|無|無||O0M090|OM9|0","|預訂|240000G1551S|G155|VNP|AOH|VNP|AOH|17:20|23:08|05:48|N|usY%2BUl57hWKitIUp1d4m3n3e0ys4iJTdDfedKU6oXk7F3bAb|20180224|3|P4|01|11|1|0|||||||||||無|無|無||O0M090|OM9|1","|預訂|240000G1570K|G157|VNP|AOH|VNP|AOH|17:43|23:30|05:47|N|usY%2BUl57hWKitIUp1d4m3n3e0ys4iJTdDfedKU6oXk7F3bAb|20180224|3|P3|01|09|1|0|||||||||||無|無|無||O0M090|OM9|1","|預訂|240000G1591Z|G159|VNP|AOH|VNP|AOH|17:48|23:44|05:56|N|usY%2BUl57hWKitIUp1d4m3n3e0ys4iJTdDfedKU6oXk7F3bAb|20180224|3|P4|01|09|1|0|||||||||||無|無|無||O0M090|OM9|0","|預訂|24000000G704|G7|VNP|AOH|VNP|AOH|19:00|23:24|04:24|N|usY%2BUl57hWKitIUp1d4m3n3e0ys4iJTdDfedKU6oXk7F3bAb|20180224|3|P3|01|04|1|0|||||||||||無|無|無||O0M090|OM9|0","|預訂|24000000G900|G9|VNP|AOH|VNP|AOH|19:05|23:39|04:34|N|usY%2BUl57hWKitIUp1d4m3n3e0ys4iJTdDfedKU6oXk7F3bAb|20180224|3|P4|01|05|1|0|||||||||||無|無|無||O0M090|OM9|0","|預訂|240000T1090Z|T109|BJP|SHH|BJP|SHH|19:31|10:43|15:12|N|SPyTjS4UE%2BFObI0x4k%2BxWLp41d7MjhINm2wQh8uIolnaeFXRL%2FuqgWcGuRcAiAPUTzxg45bjonU%3D|20180224|3|P4|01|09|0|0||無||無|||無||無|無|||||1040601030|14613|0","UKEhCCI8hqymqGKMu%2B8o597E1HEFnc7PrfQE9WX27aIY0MUZDkZLk7N8xX7027vok5DOLqFeijxQ%0ArVQHPRvldY8ullIIyVt2YYVGSU4WB30nfCSyIgti51pNhRkDqgPRdDCYB34cRQ34xpSBesP53hzV%0Ay%2FqGj551m%2FeK%2BAxwtosRSxdmF95FvZwU6y0H4mbfrw5CAcLy4LfQ5lHEwRKlyOD8%2BHyUCCmiqAz8%0ATTniRABrPWmdQgu2a5nZ2yE%3D|預訂|240000D3130T|D313|VNP|SHH|VNP|SHH|19:34|07:41|12:07|Y|%2FeEHM%2BOX%2FhkUjkIf%2Bhc9UBJHyhWbXMzLqkOUJ61gyvzxghhA|20180224|3|P3|01|04|0|0||||有|||無||||無||||O0O040|OO4|0","B2ptaQsZngo3cuVpKR490RKFxw%2FujNVfaO8q%2Fz9nOn%2Bv9%2BlQsoxygraTjYr8hjp4ls6MDs0jnusA%0Alafi6AR3NiTZBBs4vA41Stjj2m%2Fty%2FyLO2TDRnjk5Mzt00a7ADMG6L56oaDDK7NpYbx8pNPr7ZPP%0AMeVYdrHdxA3VSz78Sx7mRlmOfko8C6ep%2FB4PGNKvywXfmmmiWPr7p%2BRm39gQT5ISQ5WBmGDRFvbJ%0Al3CPql7gu563|預訂|240000D3110J|D311|VNP|SHH|VNP|SHH|21:16|09:09|11:53|Y|%2BypbKTC2HZlFzZByUcQ3VvnTcGuNsar%2B|20180224|3|P2|01|04|0|0||||無||||||||||有|F040|F4|0","nr35RXw6%2BkkqgJ0axRZOQs8MeSPKVstyvt6jqk5NNZpZurw8b0HiI8mnxKbMGYPxoDKfGAvI4%2Bdv%0AsQoW9lSHwZK4PaPjoYpWM90B7gL1IjKy5NW6zsf95CtVyWh8Qgd0nIaDU%2F0NGpD%2FyJiX%2BnUa6igm%0AtPNlEGe2pjgpcCLkQeKs9z3dHdQS18rHxPTnChe8HRrsLTGcBzxzMW%2BbjNE%2FWQqQaLeYSwTJImP5%0Az%2F60kEOkSBw29fQO2Cbp%2Bps%3D|預訂|240000D3210D|D321|VNP|SHH|VNP|SHH|21:23|09:15|11:52|Y|Qu9exXtC3AVH27oNnBfVX5zFLnqdhQ1lY5vwmAt4du%2FqF0zQ|20180224|3|P2|01|04|0|0||||有|||無||||無||||O0O040|OO4|0"]},"httpstatus":200,"messages":"","status":true}