├── README ├── README.md ├── pom.xml ├── s-cloud-common.rar └── src ├── main ├── java │ └── cn │ │ └── lwf │ │ └── framework │ │ └── train │ │ ├── SCloudTrainApplication.java │ │ ├── config │ │ └── DataSourceConfig.java │ │ ├── crawler │ │ ├── Train12306Crawler.java │ │ ├── TrainCtripCrawler.java │ │ └── TrainQunaerCrawler.java │ │ ├── dao │ │ ├── TrainDao.java │ │ ├── TrainFahrplanDao.java │ │ └── TrainStationDao.java │ │ ├── gateway │ │ └── TrainSyncGateway.java │ │ ├── mapper │ │ ├── TrainFahrplanMapper.java │ │ ├── TrainMapper.java │ │ └── TrainStationMapper.java │ │ ├── service │ │ ├── TrainFahrplanService.java │ │ ├── TrainService.java │ │ └── TrainStationService.java │ │ └── task │ │ └── TrainInitTask.java └── resources │ ├── application.yml │ └── mapper │ ├── TrainFahrplanMapper.xml │ ├── TrainMapper.xml │ └── TrainStationMapper.xml └── test └── java └── cn └── lwf └── framework └── train └── SCloudTrainApplicationTests.java /README: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuwenfeng554/s-sloud-train/c2a2a90a81db77936ff5a3aab43fa602a0e25517/README -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # s-sloud-train 2 | 2020年最新全各大(12306、携程、去哪儿)官网爬取/解析全路客运车站/车次、时刻表信息及数据实时同步 3 | 4 | 相关包: 5 | https://github.com/liuwenfeng554/s-sloud-train/blob/master/s-cloud-common.rar 6 | 7 | 参考文献: 8 | https://zhuanlan.zhihu.com/p/165626572 9 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | org.springframework.boot 7 | spring-boot-starter-parent 8 | 2.3.0.RELEASE 9 | 10 | 11 | cn.lwf.framwork 12 | s-cloud-train 13 | 0.0.1-SNAPSHOT 14 | s-cloud-train 15 | Demo project for Spring Boot 16 | 17 | 18 | UTF-8 19 | UTF-8 20 | 1.8 21 | Hoxton.SR5 22 | 23 | 24 | 25 | 26 | 27 | cn.lwf.framework 28 | s-cloud-common 29 | 1.0-SNAPSHOT 30 | 31 | 32 | 33 | org.projectlombok 34 | lombok 35 | true 36 | 37 | 38 | org.springframework.boot 39 | spring-boot-starter 40 | 41 | 42 | 43 | org.springframework.boot 44 | spring-boot-starter-test 45 | test 46 | 47 | 48 | org.junit.vintage 49 | junit-vintage-engine 50 | 51 | 52 | 53 | 54 | org.springframework.boot 55 | spring-boot-starter-web 56 | 57 | 63 | 69 | 75 | 89 | 90 | 91 | org.springframework.boot 92 | spring-boot-starter-jdbc 93 | 94 | 95 | org.mybatis 96 | mybatis-spring 97 | 1.3.2 98 | 99 | 100 | org.mybatis 101 | mybatis 102 | 3.4.6 103 | 104 | 105 | org.apache.tomcat 106 | tomcat-jdbc 107 | 108 | 109 | 110 | com.alibaba 111 | druid 112 | 1.1.10 113 | 114 | 115 | mysql 116 | mysql-connector-java 117 | 118 | 119 | org.apache.commons 120 | commons-lang3 121 | 122 | 123 | com.alibaba 124 | fastjson 125 | 1.2.49 126 | compile 127 | 128 | 129 | 130 | org.jsoup 131 | jsoup 132 | 1.12.1 133 | 134 | 135 | 136 | 137 | 138 | 139 | org.springframework.cloud 140 | spring-cloud-dependencies 141 | ${spring-cloud.version} 142 | pom 143 | import 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | org.springframework.boot 152 | spring-boot-maven-plugin 153 | 154 | 155 | 156 | 157 | 158 | -------------------------------------------------------------------------------- /s-cloud-common.rar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuwenfeng554/s-sloud-train/c2a2a90a81db77936ff5a3aab43fa602a0e25517/s-cloud-common.rar -------------------------------------------------------------------------------- /src/main/java/cn/lwf/framework/train/SCloudTrainApplication.java: -------------------------------------------------------------------------------- 1 | package cn.lwf.framework.train; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import org.springframework.boot.SpringApplication; 5 | import org.springframework.boot.autoconfigure.EnableAutoConfiguration; 6 | import org.springframework.boot.autoconfigure.SpringBootApplication; 7 | 8 | @EnableAutoConfiguration //自动读取配置 9 | @SpringBootApplication //springboot应用注解 10 | @Slf4j 11 | public class SCloudTrainApplication { 12 | 13 | public static void main(String[] args) { 14 | 15 | SpringApplication.run(SCloudTrainApplication.class, args); 16 | log.info("启动Spring boot服务成功"); 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/cn/lwf/framework/train/config/DataSourceConfig.java: -------------------------------------------------------------------------------- 1 | package cn.lwf.framework.train.config; 2 | 3 | import com.alibaba.druid.pool.DruidDataSource; 4 | import org.apache.ibatis.session.SqlSessionFactory; 5 | import org.mybatis.spring.SqlSessionFactoryBean; 6 | import org.mybatis.spring.annotation.MapperScan; 7 | import org.springframework.boot.context.properties.ConfigurationProperties; 8 | import org.springframework.context.annotation.Bean; 9 | import org.springframework.context.annotation.ComponentScan; 10 | import org.springframework.context.annotation.Configuration; 11 | import org.springframework.core.io.support.PathMatchingResourcePatternResolver; 12 | import org.springframework.jdbc.datasource.DataSourceTransactionManager; 13 | import org.springframework.transaction.PlatformTransactionManager; 14 | 15 | import javax.sql.DataSource; 16 | 17 | @Configuration 18 | @ComponentScan 19 | @MapperScan("cn.lwf.framework.train.mapper") //mapper包扫描 20 | public class DataSourceConfig { 21 | 22 | /** 23 | @Bean 24 | public ServletRegistrationBean druidServlet(){ 25 | ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(new StatViewServlet(), "/druid/*"); 26 | //ip白名单 27 | servletRegistrationBean.addInitParameter("allow", "127.0.0.1"); 28 | //ip黑名单 29 | //servletRegistrationBean.addInitParameter("deny",""); 30 | //控制台管理用户 31 | servletRegistrationBean.addInitParameter("loginUsername","admin"); 32 | servletRegistrationBean.addInitParameter("loginPassword", "123456"); 33 | //是否能重置数据,禁止HTML页面上的Rest ALL功能 34 | servletRegistrationBean.addInitParameter("restEnable", "false"); 35 | return servletRegistrationBean; 36 | } 37 | 38 | @Bean 39 | public FilterRegistrationBean filterRegistrationBean(){ 40 | FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(new WebStatFilter()); 41 | filterRegistrationBean.addUrlPatterns("/*"); 42 | filterRegistrationBean.addInitParameter("exclusions", "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*"); 43 | return filterRegistrationBean; 44 | } 45 | **/ 46 | 47 | @Bean 48 | @ConfigurationProperties(prefix = "spring.datasource") 49 | public DataSource dataSource(){ 50 | DruidDataSource dataSource = new DruidDataSource(); 51 | return dataSource; 52 | } 53 | 54 | @Bean 55 | public SqlSessionFactory sqlSessionFactory() throws Exception { 56 | SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean(); 57 | sqlSessionFactoryBean.setDataSource(dataSource()); 58 | PathMatchingResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver(); 59 | sqlSessionFactoryBean.setMapperLocations(resourcePatternResolver.getResources("classpath*:mapper/*.xml")); 60 | sqlSessionFactoryBean.getObject().getConfiguration().setMapUnderscoreToCamelCase(true); 61 | return sqlSessionFactoryBean.getObject(); 62 | } 63 | 64 | @Bean 65 | public PlatformTransactionManager transactionManager(){ 66 | return new DataSourceTransactionManager(dataSource()); 67 | } 68 | 69 | } 70 | -------------------------------------------------------------------------------- /src/main/java/cn/lwf/framework/train/crawler/Train12306Crawler.java: -------------------------------------------------------------------------------- 1 | package cn.lwf.framework.train.crawler; 2 | 3 | import cn.lwf.framework.constant.Constants; 4 | import cn.lwf.framework.model.Train; 5 | import cn.lwf.framework.model.TrainFahrplan; 6 | import cn.lwf.framework.train.dao.TrainDao; 7 | import cn.lwf.framework.train.mapper.TrainMapper; 8 | import cn.lwf.framework.util.HttpClientUtils; 9 | import cn.lwf.framework.vo.Node; 10 | import cn.lwf.framework.vo.TrainInfoNew; 11 | import com.alibaba.fastjson.JSON; 12 | import com.alibaba.fastjson.JSONArray; 13 | import com.alibaba.fastjson.JSONObject; 14 | import lombok.extern.slf4j.Slf4j; 15 | import org.apache.commons.lang3.StringUtils; 16 | import org.apache.commons.lang3.time.DateFormatUtils; 17 | import org.apache.commons.lang3.time.DateUtils; 18 | import org.springframework.beans.factory.annotation.Autowired; 19 | import org.springframework.stereotype.Component; 20 | 21 | import java.util.ArrayList; 22 | import java.util.Date; 23 | import java.util.HashMap; 24 | import java.util.Map; 25 | 26 | /** 27 | * 12306 官网获取车次时刻表信息 28 | */ 29 | @Slf4j 30 | @Component 31 | public class Train12306Crawler { 32 | 33 | @Autowired 34 | TrainDao trainDao; 35 | 36 | public void get12306TrainInfo(TrainFahrplan fahrplan, String localTrainCode, String trainNo, String fromcode, String tocode, String date){ 37 | JSONArray dataJsonArray = new JSONArray(); 38 | try{ 39 | StringBuffer uri = new StringBuffer(Constants.TRAIN_12306_QUERY_TRAIN_FAHRPLAN_BY_TRAINNO); 40 | uri.append("?train_no=").append(trainNo); 41 | uri.append("&from_station_telecode=").append(fromcode); 42 | uri.append("&to_station_telecode=").append(tocode); 43 | uri.append("&depart_date=").append(date); 44 | log.info("12306 官网时刻表获取请求地址:{}", uri.toString()); 45 | //根据车次编码获取时刻表信息 46 | String resultString = HttpClientUtils.httpGet(uri.toString()); 47 | if(StringUtils.isNotBlank(resultString)){ 48 | JSONObject result = JSON.parseObject(resultString); 49 | if(!result.isEmpty() && (result.getIntValue("httpstatus") == 200 && result.containsKey("data"))){ 50 | JSONObject data = result.getJSONObject("data"); 51 | if(!data.isEmpty() && data.containsKey("data")){ 52 | dataJsonArray = data.getJSONArray("data"); 53 | int size = dataJsonArray.size(); 54 | //infoNew 55 | TrainInfoNew infoNew = new TrainInfoNew(); 56 | ArrayList nodeList = new ArrayList(); 57 | infoNew.setItems(nodeList); 58 | infoNew.setTrainLength(String.valueOf(size)); 59 | //info 60 | Map infomap = new HashMap(); 61 | for(int index = 1;index <= size; index++){ 62 | JSONObject json = dataJsonArray.getJSONObject(index-1); 63 | Node node = new Node(); 64 | String startTime = json.getString("start_time"); 65 | String arriveTime = json.getString("arrive_time"); 66 | String overTime = json.getString("stopover_time"); 67 | String stationName = json.getString("station_name"); 68 | // String stationNo = json.getString("station_no"); 69 | if(index == 1){ 70 | String trainClassName = json.getString("train_class_name"); 71 | String trainCode = json.getString("station_train_code"); 72 | String startStationName = json.getString("start_station_name"); 73 | String endStationName = json.getString("end_station_name"); 74 | 75 | infoNew.setTrainCode(trainCode); 76 | infoNew.setStartStationName(startStationName); 77 | infoNew.setEndStationName(endStationName); 78 | node.setArriveTime("----"); 79 | node.setOverTime("----"); 80 | node.setStartTime(startTime); 81 | //info 82 | infomap.put("trainClassName", trainClassName); 83 | infomap.put("beginStation", stationName); 84 | infomap.put("beginTime", startTime); 85 | infomap.put("trainCode", trainCode); 86 | infomap.put("endStation", endStationName); 87 | }else{ 88 | //处理arriveTime 89 | node.setArriveTime(arriveTime); 90 | node.setStartTime(startTime); 91 | node.setOverTime(overTime); 92 | } 93 | node.setStationName(stationName); 94 | if(index < 10){ 95 | node.setStationNo("0"+index); 96 | }else{ 97 | node.setStationNo(String.valueOf(index)); 98 | } 99 | nodeList.add(node); 100 | if(index == size){ 101 | node.setOverTime("----"); 102 | node.setStartTime(arriveTime); 103 | } 104 | infomap.put("no_"+String.valueOf(index-1)+"_stationName", stationName); 105 | if(index < 10){ 106 | infomap.put("no_"+String.valueOf(index-1)+"_station_no", "0"+index); 107 | }else{ 108 | infomap.put("no_"+String.valueOf(index-1)+"_station_no", index+""); 109 | } 110 | if(index == 1){ 111 | infomap.put("no_"+String.valueOf(index-1)+"_aTime", "----"); 112 | infomap.put("no_"+String.valueOf(index-1)+"_lTime", "----"); 113 | infomap.put("no_"+String.valueOf(index-1)+"_sTime", startTime); 114 | }else{ 115 | infomap.put("no_"+String.valueOf(index-1)+"_aTime", arriveTime); 116 | infomap.put("no_"+String.valueOf(index-1)+"_lTime", overTime); 117 | infomap.put("no_"+String.valueOf(index-1)+"_sTime", startTime); 118 | } 119 | if(index == size){ 120 | infomap.put("no_"+String.valueOf(index-1)+"_lTime", "----"); 121 | infomap.put("no_"+String.valueOf(index-1)+"_sTime", arriveTime); 122 | } 123 | } 124 | fahrplan.setTrainInfoNew(JSONObject.toJSONString(infoNew)); 125 | fahrplan.setTrainInfo(JSONObject.toJSONString(infomap)); 126 | log.info("12306 - 根据车次编码获取时刻表信息 --> infoNew:{}",JSONObject.toJSONString(infoNew)); 127 | log.info("12306 - 根据车次编码获取时刻表信息 --> info:{}",JSONObject.toJSONString(infomap)); 128 | } 129 | } 130 | } 131 | }catch(Exception ex){ 132 | log.error("解析错误或无法爬取到数据"); 133 | ex.printStackTrace(); 134 | } 135 | //避免以上无法获取情况,补偿通过官方时刻表js方式先获取最新的trainNo再次查询 136 | if(!dataJsonArray.isEmpty() || dataJsonArray.size() == 0){ 137 | //调用官网上爬取数据 138 | log.debug("12306 - 再次进行官网数据获取数据:" + localTrainCode); 139 | get12306TrainInfo(fahrplan, localTrainCode, DateFormatUtils.format(new Date(), "yyyy-MM-dd")); 140 | } 141 | } 142 | 143 | /** 144 | * 采用通过官网爬取,官网地址:https://kyfw.12306.cn/otn/queryTrainInfo/init(原始12306接口数据2019年12月30之后未更新,去哪儿也查不到数据) 145 | * @param fahrplan 146 | * @param localTrainCode 147 | * @param date 148 | */ 149 | public void get12306TrainInfo(TrainFahrplan fahrplan, String localTrainCode, String date){ 150 | 151 | try{ 152 | //步骤1、新增或更新车次列表信息 153 | StringBuffer train_uri = new StringBuffer(Constants.TRAIN_12306_QUERY_TRAINNO); 154 | train_uri.append("?keyword=").append(localTrainCode); 155 | train_uri.append("&date=").append(date.replaceAll("-", "")); 156 | log.info("12306 官网获取车次编码请求地址:{}", train_uri.toString()); 157 | //根据车次编码获取时刻表信息 158 | String trainResultString = HttpClientUtils.httpGet(train_uri.toString()); 159 | if(StringUtils.isBlank(trainResultString)) { 160 | return; 161 | } 162 | JSONObject trainResult = JSON.parseObject(trainResultString); 163 | if (trainResult.isEmpty() 164 | || !trainResult.containsKey("data") 165 | || trainResult.getIntValue("httpstatus") != 200) { 166 | return; 167 | } 168 | JSONArray trainArray = trainResult.getJSONArray("data"); 169 | if(trainArray == null || trainArray.isEmpty()){ 170 | return; 171 | } 172 | //该请求是模糊查询,则取第一个值即可 173 | //{"date":"20200708","from_station":"九江","station_train_code":"G1581","to_station":"常州北","total_num":"14","train_no":"57000G15810C"} 174 | JSONObject trainDateJson = trainArray.getJSONObject(0); 175 | Train train = new Train(); 176 | train.setTrainCode(trainDateJson.getString("station_train_code")); 177 | train.setStartStationName(trainDateJson.getString("from_station")); 178 | train.setEndStationName(trainDateJson.getString("to_station")); 179 | train.setTrainNo(trainDateJson.getString("train_no")); 180 | String dateStr = trainDateJson.getString("date"); 181 | if (StringUtils.isNotBlank(dateStr)) { 182 | dateStr = DateFormatUtils.format(DateUtils.parseDate(dateStr, "yyyyMMdd"), "yyyy-MM-dd"); 183 | } 184 | train.setRunDate(dateStr);//运行时间 185 | //更新获取车次 186 | Train trainTemp = trainDao.findTrainByCode(train.getTrainCode()); 187 | if(trainTemp == null){ 188 | trainDao.addTrain(train); 189 | log.info("12306 - 新增车次 code:{},startStation:{},endStation:{}", train.getTrainCode(), train.getStartStationName(),train.getEndStationName() ); 190 | }else{ 191 | trainTemp.setStartStationName(train.getStartStationName()); 192 | trainTemp.setEndStationName(train.getEndStationName()); 193 | trainTemp.setTrainNo(train.getTrainNo()); 194 | trainTemp.setRunDate(train.getRunDate());//运行时间 195 | trainDao.updateTrain(trainTemp); 196 | } 197 | 198 | //步骤2、根据获取到的车次编码读取车次时刻表信息 199 | StringBuffer fahrplan_uri = new StringBuffer(Constants.TRAIN_12306_QUERY_TRAIN_FAHRPLAN); 200 | fahrplan_uri.append("?leftTicketDTO.train_no=").append(train.getTrainNo()); 201 | fahrplan_uri.append("&leftTicketDTO.train_date=").append(date); 202 | fahrplan_uri.append("&rand_code="); 203 | log.info("12306 官网获取时刻表信息请求地址:{}", fahrplan_uri.toString()); 204 | //根据车次编码获取时刻表信息 205 | String fahrplanResultString = HttpClientUtils.httpGet(fahrplan_uri.toString()); 206 | if(StringUtils.isBlank(fahrplanResultString)) { 207 | return; 208 | } 209 | JSONObject fahrplanResult = JSON.parseObject(fahrplanResultString); 210 | if (fahrplanResult.isEmpty() 211 | || !fahrplanResult.containsKey("data")) { 212 | return; 213 | } 214 | 215 | JSONObject fahrplanData = fahrplanResult.getJSONObject("data"); 216 | if (fahrplanData.isEmpty() 217 | || !fahrplanData.containsKey("data")) { 218 | return; 219 | } 220 | JSONArray fahrplanArray = fahrplanData.getJSONArray("data"); 221 | if(fahrplanArray == null || fahrplanArray.isEmpty()){ 222 | return; 223 | } 224 | int size = fahrplanArray.size(); 225 | //步骤3、解析车次时刻信息 226 | //infoNew 227 | TrainInfoNew infoNew = new TrainInfoNew(); 228 | ArrayList nodeList = new ArrayList(); 229 | infoNew.setItems(nodeList); 230 | infoNew.setTrainLength(String.valueOf(size)); 231 | //info 232 | Map infomap = new HashMap(); 233 | for(int index = 1; index <= size; index++){ 234 | JSONObject json = fahrplanArray.getJSONObject(index-1); 235 | Node node = new Node(); 236 | String startTime = json.getString("start_time"); 237 | String arriveTime = json.getString("arrive_time"); 238 | String overTime = json.getString("running_time"); 239 | String stationName = json.getString("station_name"); 240 | // String stationNo = json.getString"station_no"); 241 | if(index==1){ 242 | String trainClassName = json.getString("train_class_name"); 243 | String trainCode = json.getString("station_train_code"); 244 | String startStationName = json.getString("start_station_name"); 245 | String endStationName = json.getString("end_station_name"); 246 | 247 | infoNew.setTrainCode(trainCode); 248 | infoNew.setStartStationName(startStationName); 249 | infoNew.setEndStationName(endStationName); 250 | node.setArriveTime("----"); 251 | node.setOverTime("----"); 252 | node.setStartTime(startTime); 253 | //info 254 | infomap.put("trainClassName", trainClassName); 255 | infomap.put("beginStation", stationName); 256 | infomap.put("beginTime", startTime); 257 | infomap.put("trainCode", trainCode); 258 | infomap.put("endStation", endStationName); 259 | }else{ 260 | //处理arriveTime 261 | node.setArriveTime(arriveTime); 262 | node.setStartTime(startTime); 263 | node.setOverTime(overTime); 264 | } 265 | node.setStationName(stationName); 266 | if(index<10){ 267 | node.setStationNo("0"+index); 268 | }else{ 269 | node.setStationNo(String.valueOf(index)); 270 | } 271 | nodeList.add(node); 272 | if(index == size){ 273 | node.setOverTime("----"); 274 | node.setStartTime(arriveTime); 275 | } 276 | infomap.put("no_"+String.valueOf(index-1)+"_stationName", stationName); 277 | if(index<10){ 278 | infomap.put("no_"+String.valueOf(index-1)+"_station_no", "0"+index); 279 | }else{ 280 | infomap.put("no_"+String.valueOf(index-1)+"_station_no", index+""); 281 | } 282 | if(index==1){ 283 | infomap.put("no_"+String.valueOf(index-1)+"_aTime", "----"); 284 | infomap.put("no_"+String.valueOf(index-1)+"_lTime", "----"); 285 | infomap.put("no_"+String.valueOf(index-1)+"_sTime", startTime); 286 | }else{ 287 | infomap.put("no_"+String.valueOf(index-1)+"_aTime", arriveTime); 288 | infomap.put("no_"+String.valueOf(index-1)+"_lTime", overTime); 289 | infomap.put("no_"+String.valueOf(index-1)+"_sTime", startTime); 290 | } 291 | 292 | if(index == size){ 293 | infomap.put("no_"+String.valueOf(index-1)+"_lTime", "----"); 294 | infomap.put("no_"+String.valueOf(index-1)+"_sTime", arriveTime); 295 | } 296 | } 297 | fahrplan.setTrainInfoNew(JSONObject.toJSONString(infoNew)); 298 | fahrplan.setTrainInfo(JSONObject.toJSONString(infomap)); 299 | log.info("12306(补偿) - 根据车次编码获取时刻表信息 --> infoNew:{}",JSONObject.toJSONString(infoNew)); 300 | log.info("12306(补偿) - 根据车次编码获取时刻表信息 --> info:{}",JSONObject.toJSONString(infomap)); 301 | }catch(Exception ex){ 302 | log.error("解析错误或无法爬取到数据"); 303 | ex.printStackTrace(); 304 | } 305 | } 306 | } 307 | -------------------------------------------------------------------------------- /src/main/java/cn/lwf/framework/train/crawler/TrainCtripCrawler.java: -------------------------------------------------------------------------------- 1 | package cn.lwf.framework.train.crawler; 2 | 3 | import cn.lwf.framework.constant.Constants; 4 | import cn.lwf.framework.model.TrainFahrplan; 5 | import cn.lwf.framework.vo.Node; 6 | import cn.lwf.framework.vo.TrainInfoNew; 7 | import com.alibaba.fastjson.JSONObject; 8 | import lombok.extern.slf4j.Slf4j; 9 | import org.jsoup.Jsoup; 10 | import org.jsoup.nodes.Document; 11 | import org.jsoup.select.Elements; 12 | import org.springframework.stereotype.Component; 13 | 14 | import java.util.ArrayList; 15 | import java.util.HashMap; 16 | import java.util.Map; 17 | 18 | /** 19 | * 携程官网获取车次时刻表信息 20 | */ 21 | @Slf4j 22 | @Component 23 | public class TrainCtripCrawler { 24 | 25 | public TrainFahrplan getCtripTrainInfo(TrainFahrplan fahrplan, String trainNum){ 26 | Document doc; 27 | try { 28 | String uri = Constants.TRAIN_CTRIP_QUERY_TSEATDETAIL + trainNum; 29 | log.info("携程官网爬取时刻表请求地址:{}", uri); 30 | doc = Jsoup.connect(uri).get(); 31 | //获取div > ctl00_MainContentPlaceHolder_pnlResult 32 | Elements result = doc.select("div#ctl00_MainContentPlaceHolder_pnlResult"); 33 | //获取div中车次信息 34 | Elements trainTables = result.select("div.s_bd > table.tb_result");//tb_result 35 | TrainInfoNew infoNew = new TrainInfoNew(); 36 | //检测列对应值是否正确 37 | Elements trainHeadArray = trainTables.get(1).select("thead").select("tr"); 38 | Elements ths = trainHeadArray.get(0).select("th"); 39 | String arlabl = ths.get(3).text().trim();//进站时间 40 | String stlabl = ths.get(4).text().trim();//发车时间 41 | Elements trainDataArray = trainTables.get(1).select("tbody").select("tr"); 42 | Elements tainInfo = trainTables.get(0).select("tbody").select("tr"); 43 | String startStationName = tainInfo.select("td > a#ctl00_MainContentPlaceHolder_hyDepartureStationName").text().trim(); 44 | String endStationName = tainInfo.select("td > a#ctl00_MainContentPlaceHolder_hyArrivalStationName").text().trim(); 45 | infoNew.setStartStationName(startStationName); 46 | infoNew.setEndStationName(endStationName); 47 | infoNew.setTrainCode(trainNum); 48 | infoNew.setTrainLength(String.valueOf(trainDataArray.size())); 49 | ArrayList nodeList=new ArrayList(); 50 | infoNew.setItems(nodeList); 51 | Map infomap = new HashMap(); 52 | infomap.put("trainClassName", getTrainTypeName(trainNum)); 53 | infomap.put("beginStation", startStationName); 54 | infomap.put("beginTime", endStationName); 55 | infomap.put("trainCode", trainNum); 56 | String stationName = tainInfo.select("td").get(3).text().trim(); 57 | infomap.put("endStation", stationName); 58 | for(int i = 0;i= 10 ? String.valueOf(num) : "0"+num; 63 | node.setStationNo(serialNo); 64 | infomap.put("no_"+i+"_station_no", serialNo); 65 | for(int j = 0;j infoNew:{}",JSONObject.toJSONString(infoNew)); 107 | log.info("ctrip - 根据车次编码获取时刻表信息 --> info:{}",JSONObject.toJSONString(infomap)); 108 | } catch (Exception e) { 109 | // TODO Auto-generated catch block 110 | log.error("解析错误或无法爬取到数据"); 111 | e.printStackTrace(); 112 | } 113 | return fahrplan; 114 | 115 | } 116 | 117 | //GC-高铁/城际D-动车Z-直达T-特快K-快速 其他 118 | private String getTrainTypeName(String trainNum){ 119 | String prefixKey = trainNum.substring(0, 1); 120 | String trainClassName = ""; 121 | if("GC".contains(prefixKey)){ 122 | trainClassName = "高铁/城际"; 123 | }else if("D".contains(prefixKey)){ 124 | trainClassName = "动车"; 125 | }else if("Z".contains(prefixKey)){ 126 | trainClassName = "直达"; 127 | }else if("T".contains(prefixKey)){ 128 | trainClassName = "特快"; 129 | }else if("K".contains(prefixKey)){ 130 | trainClassName = "快速"; 131 | }else { 132 | trainClassName = "其他"; 133 | } 134 | return trainClassName; 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /src/main/java/cn/lwf/framework/train/crawler/TrainQunaerCrawler.java: -------------------------------------------------------------------------------- 1 | package cn.lwf.framework.train.crawler; 2 | 3 | import cn.lwf.framework.constant.Constants; 4 | import cn.lwf.framework.model.TrainFahrplan; 5 | import cn.lwf.framework.util.HttpClientUtils; 6 | import cn.lwf.framework.vo.Node; 7 | import cn.lwf.framework.vo.TrainInfoNew; 8 | import com.alibaba.fastjson.JSON; 9 | import com.alibaba.fastjson.JSONArray; 10 | import com.alibaba.fastjson.JSONObject; 11 | import lombok.extern.slf4j.Slf4j; 12 | import org.apache.commons.lang3.StringUtils; 13 | import org.springframework.stereotype.Component; 14 | 15 | import java.util.ArrayList; 16 | import java.util.HashMap; 17 | import java.util.Iterator; 18 | import java.util.Map; 19 | 20 | /** 21 | * 去哪儿官网获取车次时刻表信息 22 | */ 23 | @Slf4j 24 | @Component 25 | public class TrainQunaerCrawler { 26 | 27 | public TrainFahrplan getQunarTrainInfo(TrainFahrplan trainFahrplan, String trainNum, String departure, String arrive, String date){ 28 | try{ 29 | StringBuffer uri = new StringBuffer(Constants.TRAIN_QUNAR_QUERY_TSEATDETAIL); 30 | uri.append("?dptStation=").append(departure); 31 | uri.append("&arrStation=").append(arrive); 32 | uri.append("&date=").append(date); 33 | uri.append("&trainNo=").append(trainNum); 34 | uri.append("&user=").append("neibu"); 35 | uri.append("&source=").append("www"); 36 | uri.append("&needTimeDetail=").append("true"); 37 | log.info("去哪儿官网获取时刻表请求地址:{}", uri.toString()); 38 | String jsonStr = "{}"; 39 | //根据车次编码获取时刻表信息 40 | String resultString = HttpClientUtils.httpPost(uri.toString(),jsonStr); 41 | if(StringUtils.isNotBlank(resultString)) { 42 | JSONObject result = JSON.parseObject(resultString); 43 | if (!result.isEmpty() && (result.getIntValue("httpstatus") == 200 && result.containsKey("data"))) { 44 | JSONObject dataJsonObject = result.getJSONObject("data"); 45 | if(dataJsonObject.containsKey("stationItemList")){ 46 | String typeName = dataJsonObject.getString("typeName"); 47 | JSONArray array = dataJsonObject.getJSONArray("stationItemList"); 48 | int size = array.size(); 49 | TrainInfoNew infoNew = new TrainInfoNew(); 50 | infoNew.setTrainCode(trainNum); 51 | infoNew.setTrainLength(String.valueOf(size)); 52 | ArrayList nodeList = new ArrayList(); 53 | infoNew.setItems(nodeList); 54 | String stationName = ""; 55 | Map infomap = new HashMap(); 56 | infomap.put("trainClassName", typeName); 57 | int index=1; 58 | Iterator iterator = array.iterator(); 59 | while (iterator.hasNext()) { 60 | JSONObject jsonObject = (JSONObject) iterator.next(); 61 | stationName = jsonObject.getString("stationName"); 62 | String startTime = jsonObject.getString("startTime"); 63 | String arriveTime = jsonObject.getString("arriveTime"); 64 | Integer overTime = jsonObject.getInteger("overTime"); 65 | Node node = new Node(); 66 | if(index == 1){ 67 | infoNew.setStartStationName(stationName); 68 | node.setArriveTime("----"); 69 | node.setOverTime("----"); 70 | node.setStartTime(startTime); 71 | infomap.put("beginStation", stationName); 72 | infomap.put("beginTime", startTime); 73 | infomap.put("trainCode", trainNum); 74 | }else{ 75 | //处理arriveTime 76 | node.setArriveTime(arriveTime); 77 | node.setStartTime(startTime); 78 | node.setOverTime(overTime+"分钟"); 79 | } 80 | if(index < 10){ 81 | node.setStationNo("0"+index); 82 | }else{ 83 | node.setStationNo(String.valueOf(index)); 84 | } 85 | node.setStationName(stationName); 86 | nodeList.add(node); 87 | if(index == size){ 88 | node.setOverTime("----"); 89 | node.setStartTime(arriveTime); 90 | infoNew.setEndStationName(stationName); 91 | infomap.put("endStation", stationName); 92 | } 93 | infomap.put("no_"+String.valueOf(index-1)+"_stationName", stationName); 94 | if(index<10){ 95 | infomap.put("no_"+String.valueOf(index-1)+"_station_no", "0"+index); 96 | }else{ 97 | infomap.put("no_"+String.valueOf(index-1)+"_station_no", index+""); 98 | } 99 | if(index == 1){ 100 | infomap.put("no_"+String.valueOf(index-1)+"_aTime", "----"); 101 | infomap.put("no_"+String.valueOf(index-1)+"_lTime", "----"); 102 | infomap.put("no_"+String.valueOf(index-1)+"_sTime", startTime); 103 | }else{ 104 | infomap.put("no_"+String.valueOf(index-1)+"_aTime", arriveTime); 105 | infomap.put("no_"+String.valueOf(index-1)+"_lTime", overTime+"分钟"); 106 | infomap.put("no_"+String.valueOf(index-1)+"_sTime", startTime); 107 | } 108 | if(index == size){ 109 | infomap.put("no_"+String.valueOf(index-1)+"_lTime", "----"); 110 | infomap.put("no_"+String.valueOf(index-1)+"_sTime", arriveTime); 111 | } 112 | index++; 113 | } 114 | trainFahrplan.setTrainInfoNew(JSONObject.toJSONString(infoNew)); 115 | trainFahrplan.setTrainInfo(JSONObject.toJSONString(infomap)); 116 | 117 | log.info("qunaer - 根据车次编码获取时刻表信息 --> infoNew:{}",JSONObject.toJSONString(infoNew)); 118 | log.info("qunaer - 根据车次编码获取时刻表信息 --> info:{}",JSONObject.toJSONString(infomap)); 119 | } 120 | } 121 | } 122 | }catch(Exception ex){ 123 | log.error("解析错误或无法爬取到数据"); 124 | ex.printStackTrace(); 125 | } 126 | 127 | return trainFahrplan; 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /src/main/java/cn/lwf/framework/train/dao/TrainDao.java: -------------------------------------------------------------------------------- 1 | package cn.lwf.framework.train.dao; 2 | 3 | import cn.lwf.framework.model.Train; 4 | import cn.lwf.framework.train.mapper.TrainMapper; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.stereotype.Component; 7 | 8 | import java.util.List; 9 | 10 | @Component 11 | public class TrainDao { 12 | 13 | @Autowired 14 | TrainMapper trainMapper; 15 | 16 | public List findTrainList() { 17 | return trainMapper.findTrainList(); 18 | } 19 | 20 | public Train findTrainByCode(String trainCode) { 21 | return trainMapper.findTrainByCode(trainCode); 22 | } 23 | 24 | public int addTrain(Train train) { 25 | return trainMapper.insert(train); 26 | } 27 | 28 | public int updateTrain(Train train) { 29 | return trainMapper.update(train); 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/cn/lwf/framework/train/dao/TrainFahrplanDao.java: -------------------------------------------------------------------------------- 1 | package cn.lwf.framework.train.dao; 2 | 3 | import cn.lwf.framework.model.TrainFahrplan; 4 | import cn.lwf.framework.train.mapper.TrainFahrplanMapper; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.stereotype.Component; 7 | 8 | import java.util.List; 9 | 10 | @Component 11 | public class TrainFahrplanDao { 12 | 13 | @Autowired 14 | TrainFahrplanMapper trainFahrplanMapper; 15 | 16 | public List findFahrplanList() { 17 | return trainFahrplanMapper.findFahrplanList(); 18 | } 19 | 20 | public TrainFahrplan findFahrplanByCode(String trainCode) { 21 | return trainFahrplanMapper.findFahrplanByCode(trainCode); 22 | } 23 | 24 | public int addFahrplan(TrainFahrplan trainFahrplan) { 25 | return trainFahrplanMapper.insert(trainFahrplan); 26 | } 27 | 28 | public int updateFahrplan(TrainFahrplan trainFahrplan) { 29 | return trainFahrplanMapper.update(trainFahrplan); 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/cn/lwf/framework/train/dao/TrainStationDao.java: -------------------------------------------------------------------------------- 1 | package cn.lwf.framework.train.dao; 2 | 3 | import cn.lwf.framework.model.TrainStation; 4 | import cn.lwf.framework.train.mapper.TrainStationMapper; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.stereotype.Component; 7 | 8 | import java.util.List; 9 | 10 | @Component 11 | public class TrainStationDao { 12 | 13 | @Autowired 14 | TrainStationMapper trainStationMapper; 15 | 16 | public List findStationList() { 17 | return trainStationMapper.findStationList(); 18 | } 19 | 20 | public TrainStation findStationByName(String stationName) { 21 | return trainStationMapper.findStationByName(stationName); 22 | } 23 | 24 | public TrainStation findStationById(Long id) { 25 | return trainStationMapper.findStationById(id); 26 | } 27 | 28 | public int addStation(TrainStation station) { 29 | return trainStationMapper.insert(station); 30 | } 31 | 32 | public int updateStation(TrainStation station) { 33 | return trainStationMapper.update(station); 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/cn/lwf/framework/train/gateway/TrainSyncGateway.java: -------------------------------------------------------------------------------- 1 | package cn.lwf.framework.train.gateway; 2 | 3 | import cn.lwf.framework.model.Train; 4 | import cn.lwf.framework.model.TrainFahrplan; 5 | import cn.lwf.framework.train.crawler.Train12306Crawler; 6 | import cn.lwf.framework.train.crawler.TrainCtripCrawler; 7 | import cn.lwf.framework.train.crawler.TrainQunaerCrawler; 8 | import cn.lwf.framework.train.mapper.TrainMapper; 9 | import cn.lwf.framework.train.mapper.TrainStationMapper; 10 | import com.alibaba.fastjson.JSONObject; 11 | import org.apache.commons.lang3.StringUtils; 12 | import org.springframework.beans.factory.annotation.Autowired; 13 | import org.springframework.stereotype.Component; 14 | 15 | import java.text.SimpleDateFormat; 16 | import java.util.Calendar; 17 | 18 | @Component 19 | public class TrainSyncGateway { 20 | 21 | @Autowired 22 | TrainMapper trainMapper; 23 | @Autowired 24 | TrainStationMapper trainStationMapper; 25 | @Autowired 26 | Train12306Crawler train12306Crawler; 27 | @Autowired 28 | TrainCtripCrawler trainCtripCrawler; 29 | @Autowired 30 | TrainQunaerCrawler trainQunaerCrawler; 31 | 32 | /** 33 | * 优先爬携程,再12306,其次去哪儿 34 | * @param trainCode 35 | * @return 36 | */ 37 | public TrainFahrplan syncTrainFahrplan(String trainCode){ 38 | Train train = trainMapper.findTrainByCode(trainCode); 39 | TrainFahrplan trainFahrplan = new TrainFahrplan(); 40 | if(train == null){ 41 | //如果没有更新到车次表,直接爬取携程数据 42 | trainCtripCrawler.getCtripTrainInfo(trainFahrplan, trainCode); 43 | //没有爬取到数据或解析异常直接返回 44 | //return trainFahrplan; 45 | } 46 | String startStationName = train.getStartStationName(); 47 | String endStationName = train.getEndStationName(); 48 | Calendar cal = Calendar.getInstance(); 49 | cal.add(Calendar.DATE, 1); 50 | SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd"); 51 | String fromCode = trainStationMapper.findTelegraphCodeByName(startStationName); 52 | if(fromCode == null){ 53 | fromCode = ""; 54 | } 55 | String endCode = trainStationMapper.findTelegraphCodeByName(endStationName); 56 | if(endCode == null){ 57 | endCode = ""; 58 | } 59 | String rundate = train.getRunDate(); 60 | if(StringUtils.isNotBlank(rundate)){ 61 | rundate = format.format(cal.getTime()); 62 | } 63 | //优先去12306网站抓取 64 | train12306Crawler.get12306TrainInfo(trainFahrplan, trainCode, train.getTrainNo(), fromCode, endCode, rundate); 65 | //没有抓到再去去哪儿抓取 66 | String trainInfoNew = trainFahrplan.getTrainInfoNew(); 67 | Boolean isNull = false; 68 | if(StringUtils.isNotBlank(trainInfoNew)){ 69 | JSONObject jsonObject = JSONObject.parseObject(trainInfoNew); 70 | String length = jsonObject.getString("trainLength"); 71 | if(Integer.parseInt(length) == 0){ 72 | isNull = true; 73 | } 74 | } 75 | if(StringUtils.isBlank(trainInfoNew)||isNull){ 76 | trainQunaerCrawler.getQunarTrainInfo(trainFahrplan, trainCode, startStationName, endStationName, rundate); 77 | } 78 | return trainFahrplan; 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/main/java/cn/lwf/framework/train/mapper/TrainFahrplanMapper.java: -------------------------------------------------------------------------------- 1 | package cn.lwf.framework.train.mapper; 2 | 3 | import cn.lwf.framework.model.TrainFahrplan; 4 | 5 | import java.util.List; 6 | 7 | public interface TrainFahrplanMapper { 8 | 9 | List findFahrplanList(); 10 | 11 | TrainFahrplan findFahrplanByCode(String trainCode); 12 | 13 | int insert(TrainFahrplan trainFahrplan); 14 | 15 | int update(TrainFahrplan trainFahrplan); 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/cn/lwf/framework/train/mapper/TrainMapper.java: -------------------------------------------------------------------------------- 1 | package cn.lwf.framework.train.mapper; 2 | 3 | import cn.lwf.framework.model.Train; 4 | import cn.lwf.framework.model.TrainStation; 5 | 6 | import java.util.List; 7 | 8 | public interface TrainMapper { 9 | 10 | List findTrainList(); 11 | 12 | Train findTrainByCode(String trainCode); 13 | 14 | int insert(Train train); 15 | 16 | int update(Train train); 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/cn/lwf/framework/train/mapper/TrainStationMapper.java: -------------------------------------------------------------------------------- 1 | package cn.lwf.framework.train.mapper; 2 | 3 | import cn.lwf.framework.model.TrainStation; 4 | 5 | import java.util.List; 6 | 7 | public interface TrainStationMapper { 8 | 9 | List findStationList(); 10 | 11 | TrainStation findStationByName(String stationName); 12 | 13 | TrainStation findStationById(Long id); 14 | 15 | String findTelegraphCodeByName(String stationName); 16 | 17 | int insert(TrainStation station); 18 | 19 | int update(TrainStation station); 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/cn/lwf/framework/train/service/TrainFahrplanService.java: -------------------------------------------------------------------------------- 1 | package cn.lwf.framework.train.service; 2 | 3 | import cn.lwf.framework.constant.Constants; 4 | import cn.lwf.framework.model.Train; 5 | import cn.lwf.framework.model.TrainFahrplan; 6 | import cn.lwf.framework.model.TrainStation; 7 | import cn.lwf.framework.train.dao.TrainDao; 8 | import cn.lwf.framework.train.dao.TrainFahrplanDao; 9 | import cn.lwf.framework.train.dao.TrainStationDao; 10 | import cn.lwf.framework.train.gateway.TrainSyncGateway; 11 | import cn.lwf.framework.util.HttpClientUtils; 12 | import com.alibaba.fastjson.JSON; 13 | import com.alibaba.fastjson.JSONArray; 14 | import com.alibaba.fastjson.JSONObject; 15 | import lombok.extern.slf4j.Slf4j; 16 | import org.apache.commons.lang3.StringUtils; 17 | import org.apache.commons.lang3.time.DateFormatUtils; 18 | import org.springframework.beans.factory.annotation.Autowired; 19 | import org.springframework.stereotype.Service; 20 | 21 | import java.util.*; 22 | 23 | @Service 24 | @Slf4j 25 | public class TrainFahrplanService { 26 | 27 | @Autowired 28 | TrainFahrplanDao trainFahrplanDao; 29 | 30 | @Autowired 31 | TrainStationDao trainStationDao; 32 | 33 | @Autowired 34 | TrainDao trainDao; 35 | 36 | @Autowired 37 | TrainSyncGateway trainSyncGateway; 38 | 39 | /** 40 | * 自动同步车次时刻<由定时任务调取> 41 | */ 42 | public void autoSyncTrainFahrplan() { 43 | try{ 44 | //所有车次数据遍历 45 | /** 46 | List list = trainDao.findTrainList(); 47 | for (Train train : list) { 48 | trainSyncGateway.syncTrainFahrplan(train.getTrainCode()); 49 | } 50 | **/ 51 | //所有车次数据遍历(过滤冻结的车次) 52 | List list = trainFahrplanDao.findFahrplanList(); 53 | for (TrainFahrplan fahrplan : list) { 54 | trainSyncGateway.syncTrainFahrplan(fahrplan.getTrainCode()); 55 | } 56 | }catch (Exception e){ 57 | log.error("自动同步车次时刻信息数据异常:{}", e.getMessage()); 58 | e.printStackTrace(); 59 | } 60 | } 61 | 62 | /** 63 | * 手动同步车次时刻<可通过前端请求接口到方法> 64 | */ 65 | public void manualSyncTrainFahrplan(String trainCode) { 66 | try{ 67 | trainSyncGateway.syncTrainFahrplan(trainCode); 68 | }catch (Exception e){ 69 | log.error("自动同步车次时刻信息数据异常:{}", e.getMessage()); 70 | e.printStackTrace(); 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/main/java/cn/lwf/framework/train/service/TrainService.java: -------------------------------------------------------------------------------- 1 | package cn.lwf.framework.train.service; 2 | 3 | import cn.lwf.framework.constant.Constants; 4 | import cn.lwf.framework.model.Train; 5 | import cn.lwf.framework.model.TrainStation; 6 | import cn.lwf.framework.model.TrainFahrplan; 7 | import cn.lwf.framework.train.dao.TrainDao; 8 | import cn.lwf.framework.train.dao.TrainStationDao; 9 | import cn.lwf.framework.train.dao.TrainFahrplanDao; 10 | import cn.lwf.framework.util.HttpClientUtils; 11 | import com.alibaba.fastjson.JSON; 12 | import com.alibaba.fastjson.JSONArray; 13 | import com.alibaba.fastjson.JSONObject; 14 | import lombok.extern.slf4j.Slf4j; 15 | import org.apache.commons.lang3.StringUtils; 16 | import org.apache.commons.lang3.time.DateFormatUtils; 17 | import org.springframework.beans.factory.annotation.Autowired; 18 | import org.springframework.stereotype.Service; 19 | import org.springframework.transaction.annotation.Transactional; 20 | 21 | import java.util.*; 22 | 23 | @Service 24 | @Slf4j 25 | public class TrainService { 26 | 27 | @Autowired 28 | TrainFahrplanDao trainFahrplanDao; 29 | 30 | @Autowired 31 | TrainStationDao trainStationDao; 32 | 33 | @Autowired 34 | TrainDao trainDao; 35 | 36 | /** 37 | * version:1.0 2019之前版本 38 | * 通过js解析全路客运车站车次 39 | */ 40 | @Transactional 41 | public void analysisTrainList() { 42 | try{ 43 | //获取全路客运车站车次 44 | String result = HttpClientUtils.httpGet(Constants.TRAIN_12306_LIST_JS); 45 | if(StringUtils.isBlank(result)){ 46 | log.error("获取全路客运车站车次失败"); 47 | return; 48 | } 49 | String jsonStr = result.substring("var train_list =".length()); 50 | JSONObject json = JSONObject.parseObject(jsonStr); 51 | int index=1; 52 | Map trainMap = new HashMap<>(); 53 | Set keySet = json.keySet(); 54 | log.info("train_list遍历总数:" + keySet.size()); 55 | Iterator itt = keySet.iterator(); 56 | while(itt.hasNext()){ 57 | String date = (String)itt.next(); 58 | //某一天的数据 59 | JSONObject dateJson = json.getJSONObject(date); 60 | Set keyset = dateJson.keySet(); 61 | Iterator it = keyset.iterator(); 62 | while(it.hasNext()){ 63 | String key=(String)it.next(); 64 | JSONArray jsonArray = dateJson.getJSONArray(key); 65 | for(int i=0;i 0){ 97 | log.info("新增全路客运车站车次({})成功",train.getTrainCode()); 98 | //插入时刻表 99 | saveOrUpdateFahraplan(train); 100 | }else{ 101 | log.error("新增全路客运车站车次({})失败:{}",train.getTrainCode(), JSON.toJSONString(train)); 102 | } 103 | }else{ 104 | temp.setStartStationName(train.getStartStationName()); 105 | temp.setEndStationName(train.getEndStationName()); 106 | temp.setTrainNo(train.getTrainNo()); 107 | temp.setRunDate(train.getRunDate()); 108 | int passed = trainDao.updateTrain(temp); 109 | if(passed > 0){ 110 | log.info("更新全路客运车站车次({})成功",train.getTrainCode()); 111 | }else{ 112 | log.error("更新全路客运车站车次({})失败:{}",train.getTrainCode(), JSON.toJSONString(temp)); 113 | } 114 | } 115 | } 116 | }catch (Exception e){ 117 | log.error("根据车站获取所有在该站办客的车次异常:{}", e.getMessage()); 118 | e.printStackTrace(); 119 | } 120 | } 121 | 122 | /** 123 | * version:1.0 2020版本 124 | * 根据车站获取所有在该站办客的车次 125 | */ 126 | @Transactional 127 | public void syncTrainListByStation() { 128 | try{ 129 | String runDate = DateFormatUtils.format(new Date(), "yyyy-MM-dd"); 130 | List list = trainStationDao.findStationList(); 131 | for (TrainStation station: list) { 132 | StringBuffer uri = new StringBuffer(Constants.TRAIN_12306_QUERY_LIST); 133 | uri.append("?train_start_date=").append(runDate);//当前时间 134 | uri.append("&train_station_code=").append(station.getTelegraphCode()); 135 | log.info("根据车站获取所有在该站办客的车次请求地址:{}", uri.toString()); 136 | //根据车站获取所有在该站办客的车次 137 | String resultString = HttpClientUtils.httpGet(uri.toString()); 138 | if(StringUtils.isBlank(resultString)){ 139 | log.error("获取车站办客的车次响应失败:车站 - {},电报码 - {}", station.getStationName(), station.getTelegraphCode()); 140 | continue; 141 | } 142 | JSONObject result = JSON.parseObject(resultString); 143 | if(result.getIntValue("httpstatus") != 200){ 144 | log.error("获取车站办客的车次数据失败:车站 - {},电报码 - {}", station.getStationName(), station.getTelegraphCode()); 145 | continue; 146 | } 147 | JSONObject pdata = result.getJSONObject("data"); 148 | if(pdata.isEmpty()){ 149 | log.error("获取车站办客的车次数据失败:车站 - {},电报码 - {}", station.getStationName(), station.getTelegraphCode()); 150 | continue; 151 | } 152 | JSONArray items = pdata.getJSONArray("data"); 153 | if(!pdata.getBoolean("flag") || items.size() <= 0){ 154 | log.error("获取车站办客的车次无数据,请核查电报码是否正确:车站 - {},电报码 - {}", station.getStationName(), station.getTelegraphCode()); 155 | continue; 156 | } 157 | for (Object item: items) { 158 | JSONObject data = (JSONObject) item; 159 | //以下data参数可以根据实际需要的字段取值 160 | String trainCode = data.getString("station_train_code"); 161 | String startStationName = data.getString("start_station_name"); 162 | String endStationName = data.getString("end_station_name"); 163 | String trainNo = data.getString("train_no"); 164 | Train train = trainDao.findTrainByCode(trainCode); 165 | if(train == null){ 166 | train = new Train(); 167 | train.setTrainCode(trainCode); //车次', 168 | train.setStartStationName(startStationName); //始发站', 169 | train.setEndStationName(endStationName); //终点站', 170 | train.setTrainNo(trainNo); //车次编码', 171 | train.setRunDate(runDate); //运行时间', 172 | log.info("新增车站办客的车次:{}", JSON.toJSONString(train)); 173 | int passed = trainDao.addTrain(train); 174 | if(passed > 0){ 175 | log.info("新增车站办客的车次({})成功",train.getTrainCode()); 176 | //插入时刻表 177 | saveOrUpdateFahraplan(train); 178 | }else{ 179 | log.error("新增车站办客的车次({})失败:{}",train.getTrainCode(), JSON.toJSONString(train)); 180 | } 181 | }else{ 182 | train.setStartStationName(startStationName); //始发站', 183 | train.setEndStationName(endStationName); //终点站', 184 | train.setTrainNo(trainNo); //车次编码', 185 | train.setRunDate(runDate); //运行时间', 186 | log.info("更新车站办客的车次新车站办客的车次:{}", JSON.toJSONString(train)); 187 | int passed = trainDao.updateTrain(train); 188 | if(passed > 0){ 189 | log.info("更新车站办客的车次({})成功",train.getTrainCode()); 190 | }else{ 191 | log.error("更新车站办客的车次({})失败:{}",train.getTrainCode(), JSON.toJSONString(train)); 192 | } 193 | } 194 | } 195 | } 196 | log.info("获取车站办客的车次完成"); 197 | }catch (Exception e){ 198 | log.error("获取车站办客的车次异常:{}", e.getMessage()); 199 | e.printStackTrace(); 200 | } 201 | } 202 | 203 | @Transactional 204 | public void saveOrUpdateFahraplan(Train train){ 205 | TrainFahrplan trainFahrplan = trainFahrplanDao.findFahrplanByCode(train.getTrainCode()); 206 | if(trainFahrplan == null){ 207 | //新增时刻表 208 | trainFahrplan = new TrainFahrplan(); 209 | trainFahrplan.setTrainCode(train.getTrainCode()); 210 | trainFahrplan.setTrainNo(train.getTrainNo()); 211 | trainFahrplanDao.addFahrplan(trainFahrplan); 212 | }else{ 213 | //更新时刻表 214 | trainFahrplan.setTrainCode(train.getTrainCode()); 215 | trainFahrplan.setTrainNo(train.getTrainNo()); 216 | trainFahrplanDao.updateFahrplan(trainFahrplan); 217 | } 218 | } 219 | 220 | 221 | } 222 | -------------------------------------------------------------------------------- /src/main/java/cn/lwf/framework/train/service/TrainStationService.java: -------------------------------------------------------------------------------- 1 | package cn.lwf.framework.train.service; 2 | 3 | import cn.lwf.framework.constant.Constants; 4 | import cn.lwf.framework.model.TrainStation; 5 | import cn.lwf.framework.train.dao.TrainDao; 6 | import cn.lwf.framework.train.dao.TrainStationDao; 7 | import cn.lwf.framework.util.HttpClientUtils; 8 | import com.alibaba.fastjson.JSON; 9 | import lombok.extern.slf4j.Slf4j; 10 | import org.apache.commons.lang3.StringUtils; 11 | import org.springframework.beans.factory.annotation.Autowired; 12 | import org.springframework.stereotype.Service; 13 | import org.springframework.transaction.annotation.Transactional; 14 | 15 | import java.util.*; 16 | 17 | @Service 18 | @Slf4j 19 | public class TrainStationService { 20 | 21 | @Autowired 22 | TrainStationDao trainStationDao; 23 | 24 | @Autowired 25 | TrainDao trainDao; 26 | 27 | /** 28 | * version:1.0 2020版本 29 | * 通过js解析全路客运车站信息 30 | */ 31 | @Transactional 32 | public void analysisStationInfo() { 33 | try{ 34 | //获取全路客运车站信息 35 | String result = HttpClientUtils.httpGet(Constants.TRAIN_12306_STATION_JS); 36 | if(StringUtils.isBlank(result)){ 37 | log.error("获取全路客运车站信息失败"); 38 | return; 39 | } 40 | if(result.indexOf(Constants.TRAIN_STATION_JS_PREFIX_CHARACTER) == -1){ 41 | log.error("解析失败,请确认是否官网改版"); 42 | return; 43 | } 44 | // result.length() -1 去掉后面分号 45 | result = result.trim().replaceAll(Constants.TRAIN_STATION_JS_REGEX_TRIM,""); 46 | String str = result.substring(Constants.TRAIN_STATION_JS_PREFIX_CHARACTER.length(), result.length() - 1); 47 | String[] stations = str.split(Constants.TRAIN_STATION_JS_REGEX_SPLIT); 48 | log.info("全路客运车站信息总记录数:{}", stations.length - 1); 49 | List list = new ArrayList(); 50 | for (String station: stations) { 51 | //过滤站点切割为空的数据 52 | if(StringUtils.isNotBlank(station)){ 53 | //根据格式解析以下存储:bjb|北京北|VAP|beijingbei|bjb|0 54 | String[] array = station.split("\\|"); 55 | if(array.length < 6){ 56 | //检测格式和长度不对,直接终止 57 | log.error("全路客运车站信息解析格式长度错误:{}" + station); 58 | break; 59 | } 60 | TrainStation trainStation = new TrainStation(); 61 | trainStation.setId(Long.valueOf(array[5])); //ID 62 | trainStation.setTelegraphCode(array[2]); //电报码 63 | trainStation.setStationName(array[1]); //站名 64 | trainStation.setSpell(array[3]); //拼音 65 | trainStation.setInitial(array[0]); //首字母 66 | trainStation.setPinyinCode(array[4]); //拼音码 67 | 68 | list.add(trainStation); 69 | log.info("正在添加站点 - {}", trainStation.getStationName()); 70 | } 71 | } 72 | log.info("全路客运车站信息解析记录数:{}", list.size()); 73 | //遍历插入或更新车站信息 74 | if(list.size() >0){ 75 | for (TrainStation trainStation : list) { 76 | //通过name字段检测出现重复站点名称不会插入,避免数据不完整插入改由id字段检测 77 | //TrainStation station = trainStationDao.findStationByName(trainStation.getStationName()); 78 | TrainStation station = trainStationDao.findStationById(trainStation.getId()); 79 | if(station == null){ 80 | log.info("新增全路客运车站信息:{}", JSON.toJSONString(trainStation)); 81 | int passed = trainStationDao.addStation(trainStation); 82 | if(passed > 0){ 83 | log.info("新增全路客运车站({})信息成功", trainStation.getStationName()); 84 | }else{ 85 | log.error("新增全路客运车站({})信息失败:{}", trainStation.getStationName(), JSON.toJSONString(trainStation)); 86 | } 87 | }else{ 88 | log.info("更新全路客运车站信息:{}", JSON.toJSONString(trainStation)); 89 | int passed = trainStationDao.updateStation(trainStation); 90 | if(passed > 0){ 91 | log.info("更新全路客运车站({})信息成功", trainStation.getStationName()); 92 | }else{ 93 | log.error("更新全路客运车站({})信息失败:{}", trainStation.getStationName(), JSON.toJSONString(trainStation)); 94 | } 95 | } 96 | } 97 | } 98 | }catch (Exception e){ 99 | log.error("解析全路客运车站信息异常:{}", e.getMessage()); 100 | e.printStackTrace(); 101 | } 102 | } 103 | 104 | 105 | 106 | } 107 | -------------------------------------------------------------------------------- /src/main/java/cn/lwf/framework/train/task/TrainInitTask.java: -------------------------------------------------------------------------------- 1 | package cn.lwf.framework.train.task; 2 | 3 | import cn.lwf.framework.train.service.TrainFahrplanService; 4 | import cn.lwf.framework.train.service.TrainService; 5 | import cn.lwf.framework.train.service.TrainStationService; 6 | import lombok.extern.slf4j.Slf4j; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.beans.factory.annotation.Configurable; 9 | import org.springframework.scheduling.annotation.EnableScheduling; 10 | import org.springframework.scheduling.annotation.Scheduled; 11 | import org.springframework.stereotype.Component; 12 | 13 | import javax.annotation.PostConstruct; 14 | 15 | 16 | @Component 17 | @Configurable 18 | @EnableScheduling 19 | @Slf4j 20 | public class TrainInitTask{ 21 | 22 | @Autowired 23 | TrainService trainService; 24 | 25 | @Autowired 26 | TrainStationService trainStationService; 27 | 28 | @Autowired 29 | TrainFahrplanService trainFahrplanService; 30 | 31 | /** 32 | * version:1.0版本,2019.12 33 | * TO:项目启动执行一次,每次js执行只拉取30天数据(获取旧车次列表信息) 34 | */ 35 | @PostConstruct 36 | public void initTrainList() { 37 | log.info("执行车次列表数据 - start"); 38 | trainService.analysisTrainList(); 39 | log.info("执行车次列表数据 - end"); 40 | } 41 | 42 | /** 43 | * version:2.0版本,2020.08 44 | * TO:项目启动执行一次(获取全路客运车站) 45 | */ 46 | @PostConstruct 47 | public void initTrainStation() { 48 | log.info("执行全路客运车站信息数据 - start"); 49 | trainStationService.analysisStationInfo(); 50 | log.info("执行全路客运车站信息数据 - end"); 51 | } 52 | 53 | /** 54 | * version:2.0版本,2020.08 55 | * TO:每天凌晨0点执行(自动同步车次和车次时刻信息) 56 | * 建议:可采用手动获取,自动频繁获取可能会12306被拉黑 57 | */ 58 | //@Scheduled(cron = "0 0 0 * * *") 59 | @Scheduled(cron = "0 0 */3 * * ?") 60 | public void syncEverydayTrainList() { 61 | log.info("每天执行车次列表数据更新 - start"); 62 | //1、同步今日车次 63 | trainService.syncTrainListByStation(); 64 | //2、更新完,同步车次时刻信息 65 | trainFahrplanService.autoSyncTrainFahrplan(); 66 | log.info("每天执行车次列表数据更新 - start"); 67 | } 68 | 69 | } 70 | -------------------------------------------------------------------------------- /src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | server: 2 | port: 7904 3 | 4 | spring: 5 | application: 6 | name: train-service 7 | datasource: 8 | url: jdbc:mysql://localhost:3306/train_12306?useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&autoReconnect=true&allowMultiQueries=true&serverTimezone=UTC 9 | username: root 10 | password: 123456 11 | driverClassName: com.mysql.jdbc.Driver 12 | 13 | #eureka: 14 | # instance: 15 | # hostname: localhost 16 | # preferIpAddress: true 17 | # client: 18 | # serviceUrl: 19 | # defaultZone: http://admin:admin@${eureka.instance.hostname}:8761/eureka/ 20 | 21 | #开启负载均衡 22 | #ribbon: 23 | # eureka: 24 | # enabled: true 25 | 26 | #zuul: 27 | # ignoredServices: '*' 28 | # routes: 29 | # appA: 30 | # path: /u/** 31 | # serviceId: provider-user 32 | 33 | logging: 34 | level: 35 | root: INFO -------------------------------------------------------------------------------- /src/main/resources/mapper/TrainFahrplanMapper.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 24 | 25 | 28 | 29 | 30 | insert train_fahrplan ( 31 | id, train_code, is_frozen, frozen_reason, length_type, is_direct, split_type, train_info_new, train_info, train_company_id, train_small, train_no, train_info_update_time, create_time 32 | ) 33 | values ( 34 | #{id,jdbcType=BIGINT}, #{trainCode,jdbcType=VARCHAR}, 0, #{frozenReason,jdbcType=VARCHAR}, #{lengthType,jdbcType=INTEGER}, #{isDirect,jdbcType=INTEGER}, 35 | #{splitType,jdbcType=INTEGER}, #{trainInfoNew,jdbcType=VARCHAR}, #{trainInfo,jdbcType=VARCHAR}, #{trainCompanyId,jdbcType=BIGINT}, #{trainSmall,jdbcType=VARCHAR}, #{trainNo,jdbcType=VARCHAR}, 36 | #{trainInfoUpdateTime,jdbcType=TIMESTAMP}, now() 37 | ) 38 | 39 | 40 | 41 | update train_fahrplan 42 | set 43 | train_code = #{trainCode,jdbcType=VARCHAR}, 44 | is_frozen = #{isFrozen,jdbcType=INTEGER}, 45 | frozen_reason = #{frozenReason,jdbcType=VARCHAR}, 46 | length_type = #{lengthType,jdbcType=INTEGER}, 47 | is_direct = #{isDirect,jdbcType=INTEGER}, 48 | split_type = #{splitType,jdbcType=INTEGER}, 49 | train_info_new = #{trainInfoNew,jdbcType=VARCHAR}, 50 | train_info = #{trainInfo,jdbcType=VARCHAR}, 51 | train_company_id = #{trainCompanyId,jdbcType=BIGINT}, 52 | train_small = #{trainSmall,jdbcType=VARCHAR}, 53 | train_no = #{trainNo,jdbcType=VARCHAR}, 54 | train_info_update_time = now() 55 | where id = #{id,jdbcType=INTEGER} 56 | 57 | -------------------------------------------------------------------------------- /src/main/resources/mapper/TrainMapper.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 18 | 19 | 22 | 23 | 24 | insert train_list ( 25 | id, train_code, start_station_name, end_station_name, train_no, run_date, create_time, update_time 26 | ) 27 | values ( 28 | #{id,jdbcType=BIGINT}, #{trainCode,jdbcType=VARCHAR}, #{startStationName,jdbcType=VARCHAR}, #{endStationName,jdbcType=VARCHAR}, #{trainNo,jdbcType=VARCHAR},#{runDate,jdbcType=VARCHAR}, now(), now() 29 | ) 30 | 31 | 32 | 33 | update train_list 34 | set 35 | train_code = #{trainCode,jdbcType=VARCHAR}, 36 | start_station_name = #{startStationName,jdbcType=VARCHAR}, 37 | end_station_name = #{endStationName,jdbcType=VARCHAR}, 38 | train_no = #{trainNo,jdbcType=VARCHAR}, 39 | run_date = #{runDate,jdbcType=VARCHAR}, 40 | update_time = now() 41 | where id = #{id,jdbcType=INTEGER} 42 | 43 | -------------------------------------------------------------------------------- /src/main/resources/mapper/TrainStationMapper.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 18 | 19 | 22 | 23 | 26 | 27 | 30 | 31 | 32 | insert train_station ( 33 | id, telegraph_code, station_name, spell, initial, pinyin_code, create_time, update_time 34 | ) 35 | values ( 36 | #{id,jdbcType=BIGINT}, #{telegraphCode,jdbcType=VARCHAR}, #{stationName,jdbcType=VARCHAR}, #{spell,jdbcType=VARCHAR}, #{initial,jdbcType=VARCHAR},#{pinyinCode,jdbcType=VARCHAR}, now(), now() 37 | ) 38 | 39 | 40 | 41 | update train_station 42 | set 43 | telegraph_code = #{telegraphCode,jdbcType=VARCHAR}, 44 | station_name = #{stationName,jdbcType=VARCHAR}, 45 | spell = #{spell,jdbcType=VARCHAR}, 46 | initial = #{initial,jdbcType=VARCHAR}, 47 | pinyin_code = #{pinyinCode,jdbcType=VARCHAR}, 48 | update_time = now() 49 | where id = #{id,jdbcType=INTEGER} 50 | 51 | -------------------------------------------------------------------------------- /src/test/java/cn/lwf/framework/train/SCloudTrainApplicationTests.java: -------------------------------------------------------------------------------- 1 | package cn.lwf.framework.train; 2 | 3 | import cn.lwf.framework.train.service.TrainFahrplanService; 4 | import cn.lwf.framework.train.service.TrainService; 5 | import cn.lwf.framework.train.service.TrainStationService; 6 | import org.junit.jupiter.api.Test; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.boot.test.context.SpringBootTest; 9 | 10 | @SpringBootTest 11 | class SCloudTrainApplicationTests { 12 | 13 | @Autowired 14 | TrainService trainService; 15 | 16 | @Autowired 17 | TrainStationService trainStationService; 18 | 19 | @Autowired 20 | TrainFahrplanService trainFahrplanService; 21 | 22 | @Test 23 | void contextLoads() { 24 | //trainService.analysisTrainList(); 25 | //trainStationService.analysisStationInfo(); 26 | //trainService.syncTrainListByStation(); 27 | trainFahrplanService.autoSyncTrainFahrplan(); 28 | } 29 | 30 | } 31 | --------------------------------------------------------------------------------