java获取全国到深圳一个月的机票价格信息

哈哈我又来了。又到周末了,留着鼻涕 本想偷个懒休息一下,可一想到还有小伙伴等着看我的拙文,最终没有战胜我的虚荣心O(∩_∩)O,一狠心又在电脑面前坐了12个小时终于完成了一篇我认为稍微有点分析价值的爬虫了。惯例谢谢留言的小伙伴@Tibetan_Mastiff.

目标网站:


机票信息.png

首先还是说一下我的想法:

  • 获取全国到某个城市的最近一个月的机票价格信息,并分析这一个月的机票价格波动,找出谷点。

实现思路:

  • 如果要获取全国机场城市到我们选定的城市机票信息,那么我们首先需要获取有哪些城市,并将获取到的城市信息抓取出来存入数据库中。有了第一步思路我们接下来就好办了。
    1. 抓取城市信息


      城市信息.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());
    }
城市码信息.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();
                    }
                }
            }
        }
        
    }

}

因为代码中的注释个人感觉也还是算解释的比较清楚了,所以我下面也就不做过多描述。

机票.png

注:这里最好用ip动态代理访问,防止被封。下次统一补上。
这里本来还需要对获取到的机票信息进行统计分析的,由于时间的缘故留到下次统一放出,有兴趣的小伙伴可以扩展开放。这里在提醒各位小伙伴,要么用ip代理去抓取,要么就降速抓取。不要把自己当老司机,容易翻车的。好了今天就弄到这里,如果想要我爬什么的欢迎留言。同时如果大牛路过的话,能有幸得您点播在下也不胜感激。下期见~~~

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,776评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,527评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,361评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,430评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,511评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,544评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,561评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,315评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,763评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,070评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,235评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,911评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,554评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,173评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,424评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,106评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,103评论 2 352