• 【毕设梳理】开始尝试写代码——查票篇


    提要:

    查询余票网址、所需参数可见上篇博客(http://www.cnblogs.com/sei-cxt/p/8429069.html)。

    参考网上发送https请求的案例代码写出能实现查询余票的简易程序,具体代码原理详情暂且不管。


    关于对http和https的连接和请求,网上有多种版本,用的最多的就是HttpURLConnection、HttpsURLConnection和HttpClient。以下编写的代码参考于两个网址:

    http://blog.csdn.net/qh_java/article/details/52311226

    http://blog.csdn.net/guozili1/article/details/53995121

    1. 工具类(HttpsRequest):暂时只写了GET方法,其中要注意的是:

    GET方法的参数是直接追加在地址后面的,因此doOutput默认为false即可。

    在此例中,若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}
  • 相关阅读:
    BOZJ 3551&BZOJ 3545 kruskal重构树
    [Poi2014]FarmCraft
    NOIP 2015 斗地主
    POJ 1704 Georgia and Bob
    BZOJ 1409 快速幂+欧拉定理
    最长公共子序列(LCS)
    神奇的口袋(百练2755)
    最长上升子序列(LIS)
    《Single Image Haze Removal Using Dark Channel Prior》去雾代码实现分析
    MATLAB中的nargin与varargin
  • 原文地址:https://www.cnblogs.com/sei-cxt/p/8435921.html
Copyright © 2020-2023  润新知