哈哈我又来了.又到周末了,留着鼻涕 本想偷个懒休息一下,可一想到还有小伙伴等着看我的拙文,最终没有战胜我的虚荣心 O(∩_∩)O,一狠心又在电脑面前坐了 12 个小时终于完成了一篇我认为稍微有点分析价值的爬虫了.惯例谢谢留言的小伙伴 @ Tibetan_Mastiff .
目标网站:
机票信息. png
首先还是说一下我的想法:
获取全国到某个城市的最近一个月的机票价格信息,并分析这一个月的机票价格波动,找出谷点.
实现思路:
如果要获取全国机场城市到我们选定的城市机票信息,那么我们首先需要获取有哪些城市,并将获取到的城市信息抓取出来存入数据库中.有了第一步思路我们接下来就好办了.
抓取城市信息
城市信息. png
老规矩 F12 获取接口地址.(注意的一个点,速度一定要慢!!!不然就会和我一样惨)
接下来就是代码来获取并解析信息了.
* @Description: 这里主要是获取淘宝机票网中的城市名称以及城市的cityCode为了后面获取机票详情提供基础数据支持
/**
*
* @ClassName: QueryCityCode
* @author liangchu
* @date 2018-1-13 上午12:06:59
* 根据url获取返回的城市code
*
*/
public class QueryCityCode {
private static MongoClient mongo = new MongoClient("127.0.0.1", 27017);
/*
* 根据返回的字符串解析成Json对象
* /
/ / @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对象并将JSON对象存入mongodb中
* /
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;
}
/**/
跨过 ssl 证书验证
* /
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;
}
}
}
}
城市码信息. png
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());
}
第一步准备工作到这里基本结束了,接下来准备获取机票信息.这里说一下用到的解析工具 FastJSON 阿里开源的,号称最快的.(PS 作为小白的我好像察觉不出其中的差距哈哈)
由于获取的 json 格式包的比较严实,剩下的就是漫长的解析之旅了.
* 根据url获取机票的信息 注意 这里的机票信息 暂时定义的是当天的最低价
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");
}
/**/
* deptCity 出发城市
* /
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);
}
/**/
* 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=深圳&arrCity=BJS&arrCityName=北京&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 代理去抓取,要么就降速抓取.不要把自己当老司机,容易翻车的.好了今天就弄到这里,如果想要我爬什么的欢迎留言.同时如果大牛路过的话,能有幸得您点播在下也不胜感激.下期见~~~
来源: http://www.jianshu.com/p/bb9da56f05ed