java獲取全國到深圳一個月的機票價格資訊

weixin_33890499發表於2018-01-13

哈哈我又來了。又到週末了,留著鼻涕 本想偷個懶休息一下,可一想到還有小夥伴等著看我的拙文,最終沒有戰勝我的虛榮心O(∩_∩)O,一狠心又在電腦面前坐了12個小時終於完成了一篇我認為稍微有點分析價值的爬蟲了。慣例謝謝留言的小夥伴@Tibetan_Mastiff.

目標網站:


7907118-f1c3434f3dfb963f.png
機票資訊.png

首先還是說一下我的想法:

  • 獲取全國到某個城市的最近一個月的機票價格資訊,並分析這一個月的機票價格波動,找出谷點。

實現思路:

  • 如果要獲取全國機場城市到我們選定的城市機票資訊,那麼我們首先需要獲取有哪些城市,並將獲取到的城市資訊抓取出來存入資料庫中。有了第一步思路我們接下來就好辦了。
    1. 抓取城市資訊


      7907118-74de45d6668fc1f2.png
      城市資訊.png

      老規矩F12獲取介面地址。(注意的一個點,速度一定要慢!!!不然就會和我一樣慘)

接下來就是程式碼來獲取並解析資訊了。

/**
 * 
 * @ClassName: QueryCityCode
 * @Description: 這裡主要是獲取淘寶機票網中的城市名稱以及城市的cityCode為了後面獲取機票詳情提供基礎資料支援
 * @author liangchu
 * @date 2018-1-13 上午12:06:59
 * 
 */
public class QueryCityCode {
    
    private static MongoClient mongo = new MongoClient("127.0.0.1", 27017);
    /*
     * 根據url獲取返回的城市code
     */
    //@SuppressWarnings("finally")
    public static String getCodeJson(String url) {
        String html = null;
        try {
            /*
            // 建立httpclient例項
            CloseableHttpClient httpClient = HttpClients.createDefault();
            // 建立httpget例項
            HttpGet httpGet = new HttpGet(url);
            // 執行http get 請求
            CloseableHttpResponse response = null;

            response = httpClient.execute(httpGet);
            HttpEntity entity = response.getEntity();// 獲取返回實體
            // EntityUtils.toString(entity,"utf-8");//獲取網頁內容,指定編碼
            html = EntityUtils.toString(entity, "UTF-8");
            response.close();
            httpClient.close();
            */
            // 如果遇到ssl安全驗證通不過可以用下面的方法
            SslTest st = new SslTest();
            html = st.getRequest(url,3000);
            return html;
        } catch (Exception e) {
            e.printStackTrace();
        }
            return html;
            
    }
    
    /*
     * 根據返回的字串解析成Json物件
     */
    public static JSONObject analysis(String str){
        // 根據返回的字串 分析並建立解析規則 
        int first = str.indexOf("(");
        int last  = str.lastIndexOf(")");
        if(first >=0 && last >0){
            String newStr = str.substring(first+1, last);
            return JSON.parseObject(newStr);
        }
        return  null;
    }
    
    /*
     * 解析JSON物件並將JSON物件存入mongodb中
     */
    public static void analysisJsonAndSave(JSONObject json){
        // 選擇集合存取
        DB  db =  mongo.getDB("taobao");
        DBCollection emp = db.getCollection("cityCode2");
        // 利用aili的JSON包解析
        JSONArray datas = 
            JSON.parseArray((JSON.parseObject(JSON.parseObject(json.getString("267040"))
            .getString("value")).
            getString("cityArray")));
        // 這裡說明一下為什麼i=1因為第一個json資料不是我們想要的資料所以直接截掉
        for(int i=1;i<datas.size();i++){
            JSONObject jo = datas.getJSONObject(i);
            // 這裡解析出來同樣是一個JSON陣列
            JSONArray joTemp = JSON.parseArray(jo.getString("tabdata"));
            // 在一層for迴圈
            for(int j=0;j<joTemp.size();j++){
                JSONObject joj = joTemp.getJSONObject(j);
                // 這是dd層的 下面就是我們最終需要的城市資料了
                JSONArray jsond = JSON.parseArray(joj.getString("dd"));
                for(int m=0;m < jsond.size();m++){
                    JSONObject jom = jsond.getJSONObject(m);
                    DBObject obj = new BasicDBObject();
                    obj.put("cityCode", jom.getString("cityCode"));
                    obj.put("cityName", jom.getString("cityName"));
                    obj.put("tce_rule_count", jom.getString("tce_rule_count"));
                    obj.put("isVister", 0);//0表示未訪問過 為後面獲取機票資訊準備
                    emp.insert(obj);
                    obj = null;
                }
            }
        }
    }

跨過ssl證照驗證

public class SslTest {
    public String getRequest(String url,int timeOut) throws Exception{
        URL u = new URL(url);
        if("https".equalsIgnoreCase(u.getProtocol())){
            SslUtils.ignoreSsl();
        }
        URLConnection conn = u.openConnection();
                //setRequestProperty("User-agent", "Mozilla/5.0");
        conn.setRequestProperty("User-Agent", "ozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Safari/537.36");
        conn.setRequestProperty("Cookie", "t=49fa205140c4f940349fc1a0efd8da2c; _tb_token_=f33f533398b5d; cookie2=18c83127673f8672abe76a0569b187a1; cna=KL3gEjkBG2kCAXkil7IqSeTH; isg=AlBQD0vJPorWPuKOKsOCNSTNIZ6o3zVqfSNk20ohHKt-hfAv8ikE86a3KXue");
        conn.setRequestProperty("Accept-Language","zh-CN,zh;q=0.8");
        conn.setConnectTimeout(timeOut);
        conn.setReadTimeout(timeOut);
        
        //System.out.println(conn.getInputStream());
        return IOUtils.toString(conn.getInputStream());
    }
    public String postRequest(String urlAddress,String args,int timeOut) throws Exception{
        URL url = new URL(urlAddress);
        if("https".equalsIgnoreCase(url.getProtocol())){
            SslUtils.ignoreSsl();
        }
        URLConnection u = url.openConnection();
        u.setDoInput(true);
        u.setDoOutput(true);
        u.setConnectTimeout(timeOut);
        u.setReadTimeout(timeOut);
        OutputStreamWriter osw = new OutputStreamWriter(u.getOutputStream(), "UTF-8");
        osw.write(args);
        osw.flush();
        osw.close();
        u.getOutputStream();
        return IOUtils.toString(u.getInputStream());
    }
7907118-af7ebafea4b4e1da.png
城市碼資訊.png

第一步準備工作到這裡基本結束了,接下來準備獲取機票資訊。這裡說一下用到的解析工具FastJSON阿里開源的,號稱最快的。(PS作為小白的我好像察覺不出其中的差距哈哈)
由於獲取的json格式包的比較嚴實,剩下的就是漫長的解析之旅了。

package com.xdl.flightInfo;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.mongodb.BasicDBObject;
import com.mongodb.DB;
import com.mongodb.DBCollection;
import com.mongodb.DBCursor;
import com.mongodb.DBObject;
import com.mongodb.MongoClient;
import com.xdl.cityCode.QueryCityCode;

public class Flight {
    private static DB db ;
    private static DBCollection emp2 ;
    
    static {
        MongoClient mongo = new MongoClient("127.0.0.1", 27017);
        // 選擇集合存取
        db = mongo.getDB("taobao");
        emp2 = db.getCollection("cityCode2");
    }
    /*
     * 根據url獲取機票的資訊 注意 這裡的機票資訊 暫時定義的是當天的最低價
     */
    public static void getFlightInfo(String url) {
        // 這裡根據url獲取返回的資料
        String str = QueryCityCode.getCodeJson(url);
        //System.out.println(str);
        // 解析返回的字串 並返回json物件
        JSONObject jo = QueryCityCode.analysis(str);
        // 獲取資訊中我們需要的資料 ()
        JSONObject jof = JSON.parseObject(jo.getString("data"));
        // 獲取 aircodeNameMap 資訊
        JSONObject joAirInfo = JSON
                .parseObject(jof.getString("aircodeNameMap"));
        // 獲取airportMap 機場資訊
        JSONObject joAirPortInfo = JSON
                .parseObject(jof.getString("airportMap"));
        // 獲取出發地資訊以及目的地資訊
        String depCityCode = jof.getString("depCityCode");
        String depCityName = jof.getString("depCityName");
        String arrCityCode = jof.getString("arrCityCode");
        String arrCityName = jof.getString("arrCityName");
        // 獲取航班資訊 flight 這裡只獲取最低價 取第一個
        JSONObject joFlight = JSON.parseArray(jof.getString("flight"))
                .getJSONObject(0);

        // 獲取資訊詳情
        String airlineCode = joFlight.getString("airlineCode");// 航線資訊
        String arrAirport = joFlight.getString("arrAirport");// 目的機場code
        String arrTime = joFlight.getString("arrTime");// 到達時間
        String depAirport = joFlight.getString("depAirport");// 出發機場code
        String depTime = joFlight.getString("depTime");// 出發時間
        String flightNo = joFlight.getString("flightNo");// 航班編號
        String stop = joFlight.getString("stop");// 是否有中轉 1是0否
        // 獲取機票價格資訊 最低價
        JSONObject joP = JSON.parseObject(joFlight.getString("cabin"));
        String bestPrice = joP.getString("bestPrice");// 最低價格
        String cabinNum = joP.getString("cabinNum");// 票數
        String bestDiscount = joP.getString("bestDiscount");// 最高的折扣
        // 將資訊存入資料庫中
        
        DBCollection emp = db.getCollection("sz-bj");
        DBObject obj = new BasicDBObject();
        obj.put("airlineCode", airlineCode);
        obj.put("arrAirport", arrAirport);
        obj.put("arrTime", arrTime);
        obj.put("depAirport", depAirport);
        obj.put("depTime", depTime);
        obj.put("flightNo", flightNo);
        obj.put("stop", stop);
        obj.put("bestPrice", bestPrice);
        obj.put("cabinNum", cabinNum);
        obj.put("bestDiscount", bestDiscount);
        obj.put("depCityCode", depCityCode);
        obj.put("depCityName", depCityName);
        obj.put("arrCityCode", arrCityCode);
        obj.put("arrCityName", arrCityName);
        emp.insert(obj);
    }
    /*
     * deptCity 出發城市
     * arrCity  目的城市
     * deptTime 出發時間
     */
    public static String updatURL(String deptCityName,String arrCityName,String deptTime){
        String url = "";
        // 這裡需要根據城市的名稱獲取城市的code碼
        BasicDBObject queryObjectDept = new BasicDBObject("cityName",deptCityName);
        DBObject objDept = emp2.findOne(queryObjectDept);
        BasicDBObject queryObjectArr = new BasicDBObject("cityName",arrCityName);
        DBObject objArr = emp2.findOne(queryObjectArr);
        // 判斷是否存在這樣的城市碼 存在在進行下面的url拼接
        if(objDept != null && objArr != null && !("".equals(deptTime))){
            String depCityCode = (String) objDept.get("cityCode");
            String arrCityCode = (String) objArr.get("cityCode");
            url = "https://sjipiao.alitrip.com/searchow/search.htm" +
                    "?_ksTS=1515770795165_176&callback=jsonp177&" +
                    "tripType=0&depCity="+depCityCode+"&" +
                    "depCityName="+deptCityName+"&" +
                    "arrCity="+arrCityCode+"&arrCityName="+arrCityName+"&depDate="+deptTime;
        }
        return url;
    }
    
    /*
     * 轉化日期並將時間加一天
     */
    public static String changeTimeFormat(String date){
        SimpleDateFormat sdf =   new SimpleDateFormat( "yyyy-MM-dd" );
        Calendar c = Calendar.getInstance();  
        String time = "";
        try {
            Date d = sdf.parse(date);
            c.setTime(d);
            c.add(Calendar.DAY_OF_MONTH, 1);
            Date tomorrow = c.getTime();
            time = sdf.format(tomorrow);
            return time;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return time;
    }
    
    public static void main(String[] args) {
        //String url = "https://sjipiao.alitrip.com/searchow/search.htm?_ksTS=1515770795165_176&callback=jsonp177&tripType=0&depCity=SZX&depCityName=%E6%B7%B1%E5%9C%B3&arrCity=BJS&arrCityName=%E5%8C%97%E4%BA%AC&depDate=2018-01-19";
        DBCursor dbc = emp2.find();
        String deptName = "深圳";
        while(dbc.hasNext()){
            DBObject obj = dbc.next();
            // 過濾 出發地
            String arrCityName = (String) obj.get("cityName");
            if(!deptName.equals(arrCityName)){
                String time = "2018-1-20";
                for(int i=0;i<30;i++){
                    String url = updatURL(deptName,arrCityName,time);
                    getFlightInfo(url);
                    System.out.println(deptName+"==>"+arrCityName+time+"機票資訊資料抓取成功");
                    time = changeTimeFormat(time);
                    try {
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
        
    }

}

因為程式碼中的註釋個人感覺也還是算解釋的比較清楚了,所以我下面也就不做過多描述。

7907118-677890f795baa568.png
機票.png

注:這裡最好用ip動態代理訪問,防止被封。下次統一補上。
這裡本來還需要對獲取到的機票資訊進行統計分析的,由於時間的緣故留到下次統一放出,有興趣的小夥伴可以擴充套件開放。這裡在提醒各位小夥伴,要麼用ip代理去抓取,要麼就降速抓取。不要把自己當老司機,容易翻車的。好了今天就弄到這裡,如果想要我爬什麼的歡迎留言。同時如果大牛路過的話,能有幸得您點播在下也不勝感激。下期見~~~

相關文章