├── 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 |
--------------------------------------------------------------------------------