Skip to content

Commit 4b73050

Browse files
committed
1. 优化配置文件格式
2. 同一任务多个日期支持 3. 非json错误处理 4. 任务并行支持 5. 请求错误重试机制
1 parent 83f1d79 commit 4b73050

File tree

3 files changed

+167
-101
lines changed

3 files changed

+167
-101
lines changed

conf/12306.ini

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,23 @@
11
[leftTicket]
2-
#12306售票时间;非本时间可查询,但是无法购票
2+
# 12306售票时间;非本时间可查询,但是无法购票
33
time = 06:00-23:00
4-
#票务信息查询网址
5-
url = https://kyfw.12306.cn/otn/leftTicket/queryX?leftTicketDTO.train_date=${day}&leftTicketDTO.from_station=${from}&leftTicketDTO.to_station=${to}&purpose_codes=ADULT
4+
# 票务信息查询网址
5+
url = https://kyfw.12306.cn/otn/leftTicket/query?leftTicketDTO.train_date=${day}&leftTicketDTO.from_station=${from}&leftTicketDTO.to_station=${to}&purpose_codes=ADULT
66

7-
#防止查询被禁,添加头部信息
7+
# 防止查询被禁,添加头部信息
88
[Header]
99
Connection = keep-alive
1010
Cache-Control = no-cache
1111
Pragma = no-cache
1212
If-Modified-Since = 0
1313
Host = kyfw.12306.cn
1414
Referer = https://kyfw.12306.cn/otn/leftTicket/init
15-
User-Agent = Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36
15+
User-Agent = Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.62 Safari/537.36
1616
# 查询持续失败,请自行更改Cookie
17-
Cookie = tk=7eLwS4i_WNbItxSk1jIBDdsP4kMRfrZbL86eTA36r1r0; JSESSIONID=99224930E474771B256924D3034982C1; _jc_save_detail=true; RAIL_OkLJUJ=FDaP0EUw8e3W6KCRz0KWiVtdP53XXyge; fp_ver=4.5.1; RAIL_EXPIRATION=1506334557518; RAIL_DEVICEID=IVZetN6RYSOnKrPZnmeboNvq5ahDMCpfd-_oBCwDZIyME9377b5SITHUAGpUnmc9JB5UUj5sghFZuvoHklGuaOqOEZTzIdm38dKge02Y1lyy2_cNi9XEjDpibOn4-T8d3MWi_e9xDG0mxxdD_-lz9hb7Coe6XTud; BIGipServerpool_passport=250413578.50215.0000; BIGipServerpassport=887619850.50215.0000; current_captcha_type=Z; route=495c805987d0f5c8c84b14f60212447d; BIGipServerotn=1206911498.64545.0000; _jc_save_fromStation=%u5317%u4EAC%2CVNP; _jc_save_toStation=%u6D4E%u5357%2CJGK; _jc_save_fromDate=2017-09-29; _jc_save_toDate=2017-09-26; _jc_save_wfdc_flag=dc
17+
Cookie = JSESSIONID=7CE0FE8D4FCD869E0FEB314477E07913; route=c5c62a339e7744272a54643b3be5bf64; BIGipServerotn=551813642.24610.0000; RAIL_EXPIRATION=1528547910943; RAIL_DEVICEID=ew1n902MWPwcwwrZzUfXLyXxSDRWNaO7cYAPVJ-nAYT0ZB3u03b-cQByaQDi0hxdbsAVkvbyIw-4SExRq1KNoE5BEtRrDkZiV2XjI7hpcDNSK4WCqaxhqQCIOlgPjOoCY_IMbl_H2U_XxHwUlXcKrPt2isGvESBu; _jc_save_fromStation=%u5317%u4EAC%2CBJP; _jc_save_toStation=%u6C88%u9633%2CSYT; _jc_save_fromDate=2018-06-06; _jc_save_toDate=2018-06-06; _jc_save_wfdc_flag=dc
1818

1919
[station]
20-
#是否更新本地station_name文件
20+
# 是否更新本地station_name文件
2121
updateStation = false
22-
#12306 station_name url
22+
# 12306 station_name url
2323
url = https://kyfw.12306.cn/otn/resources/js/framework/station_name.js?station_version=1.9026

conf/conf.ini

Lines changed: 29 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,21 @@
11
[leftTicket]
22
# 多个查询需要配置多个[section],名称不能相同,多个用英文,隔开
3-
all = first,second
3+
all = second
44
# 查询周期,整数,单位分钟
5-
period = 2
5+
period = 1
6+
# 任务并行
7+
async = true
8+
# 失败重试
9+
retry = 3
10+
# 重试休眠间隔,单位秒
11+
retryPeriod = 2
612

713
[first]
8-
#乘车日期,必须,yyyy-MM-dd e.g. 2017-09-30
14+
# 乘车日期,必须,yyyy-MM-dd e.g. 2017-09-30 ; 多个日期用英文 , 隔开
915
day = 2017-09-29
10-
#乘车站,必须 e.g. 北京
16+
# 乘车站,必须 e.g. 北京
1117
from = 北京
12-
#抵达站,必须 e,g, 济南
18+
# 抵达站,必须 e,g, 济南
1319
to = 济南
1420
# 开始时间 HH:mm, e.g.06:00;为空匹配所有
1521
starttime = 06:00
@@ -23,51 +29,46 @@ train =
2329
prior_seat = 一等座,二等座
2430

2531
[second]
26-
day = 2017-09-30
32+
day = 2018-08-12,2018-08-13,2018-08-14
2733
from = 北京
2834
to = 济南
2935
starttime = 06:00
3036
endtime = 12:00
3137
train_type = G
32-
prior_seat = 一等座,二等座
38+
prior_seat = 二等座
3339

34-
#smtp 协议发送邮件
40+
# smtp 协议发送邮件
3541
[smtpmail]
36-
#是否开启发送邮件
42+
# 是否开启发送邮件
3743
mailflag = false
38-
#地址:端口
44+
# 地址:端口
3945
address = smtp.163.com:25
40-
#发送方邮箱地址
46+
# 发送方邮箱地址
4147
username =
42-
#发送方邮箱密码
48+
# 发送方邮箱密码
4349
password =
44-
#接收方,只支持单个
50+
# 接收方,只支持单个
4551
to =
46-
#邮件主题
52+
# 邮件主题
4753
subject = "余票查询"
4854

49-
#使用企业微信
55+
# 使用企业微信
5056
[qyweixin]
51-
#是否开启微信
57+
# 是否开启微信
5258
weixinflag = false
53-
#企业微信号
59+
# 企业微信号
5460
corpid =
55-
#应用 secret
61+
# 应用 secret
5662
corpsecret =
57-
#应用 id
63+
# 应用 id
5864
agentid =
59-
#微信用户名,多个用|隔开
65+
# 微信用户名,多个用|隔开
6066
touser =
6167

6268
[logger]
63-
#ALL = console + file;FILE = 仅文件
69+
# ALL = console + file;FILE = 仅文件
6470
output = ALL
65-
dir = ./
71+
dir = ./logs
6672
name = leftTicket.log
67-
#DEBUG/INFO/ERROR
73+
# DEBUG/INFO/ERROR
6874
level = INFO
69-
70-
71-
72-
73-

lefttk/leftTicket.go

Lines changed: 130 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,12 @@ var (
3030
queryStart = "06:00" // 12306服务时间
3131
queryEnd = "23:00" // 12306服务时间
3232
queryXurl string // 查询票务信息url
33-
headers map[string]string //需要的header
34-
allMissions []QueryInfo //所有任务信息
35-
period = 2 //查询周期,单位分钟
33+
headers map[string]string // 需要的header
34+
allMissions []QueryInfo // 所有任务信息
35+
period = 2 // 查询周期,单位分钟
36+
async = true // 是否开启任务并行
37+
retry = 3 // 查询失败重试次数
38+
retryPeriod = 1 // 重试休眠间隔,单位s
3639
)
3740

3841
//查询票务信息
@@ -64,6 +67,11 @@ func queryX(queryInfo QueryInfo) []byte {
6467
return nil
6568
}
6669
defer resp.Body.Close()
70+
contentHeader := resp.Header.Get("Content-Type")
71+
if !strings.Contains(contentHeader, "application/json") {
72+
easylog.Debug("not json.", contentHeader)
73+
return nil
74+
}
6775
body, err := ioutil.ReadAll(resp.Body)
6876
if err != nil {
6977
easylog.Error("queryx can't read respBody..", err)
@@ -246,6 +254,55 @@ func filterTk(allTks []TicketInfo, queryInfo QueryInfo) (filterTks []TicketInfo,
246254
return filterTks, canbuy
247255
}
248256

257+
// 一次完整的查询任务
258+
func oneMission(jobIndex int, queryOnce QueryInfo, chDone chan string) {
259+
easylog.Debug("job", jobIndex+1, "start :", queryOnce)
260+
// 查询失败重试
261+
var ticketstr []byte
262+
for i := 0; ; i++ {
263+
ticketstr = queryX(queryOnce)
264+
if ticketstr != nil || i >= retry {
265+
break
266+
}
267+
time.Sleep(time.Duration(retryPeriod) * time.Second)
268+
easylog.Debug("job", jobIndex+1, queryOnce, "retry", i+1)
269+
}
270+
if ticketstr == nil {
271+
easylog.Info("job", jobIndex+1, queryOnce, "job failed.")
272+
chDone <- "query nil"
273+
return
274+
}
275+
allTickets := parseTicketStr(ticketstr)
276+
filterTks, canbuyTks := filterTk(allTickets, queryOnce)
277+
easylog.Info("job", jobIndex+1, queryOnce, " job done. all=", len(allTickets),
278+
", can buy=", canbuyTks, ", filter=", len(filterTks))
279+
if filterTks != nil && len(filterTks) > 0 {
280+
//拼接信息
281+
var textMsgBuf bytes.Buffer
282+
textMsgBuf.WriteString(queryOnce.Day)
283+
for _, tk := range filterTks {
284+
if tkbytes := tk.getTkMsg(queryOnce); tkbytes != nil {
285+
textMsgBuf.WriteRune('\r')
286+
textMsgBuf.WriteRune('\n')
287+
textMsgBuf.Write(tkbytes)
288+
}
289+
}
290+
textMsg := textMsgBuf.String()
291+
easylog.Info("job", jobIndex+1, "查询余票成功:\n", textMsg)
292+
//发送邮件
293+
if Mailflag {
294+
SendText(queryOnce.Day, textMsg)
295+
}
296+
//发送微信
297+
if Weixinflag {
298+
SendToQyweixin(textMsg)
299+
}
300+
chDone <- "send end"
301+
} else {
302+
chDone <- "filterTks nil"
303+
}
304+
}
305+
249306
//一次任务查询
250307
func oneJob() {
251308
//先判断是否在服务时间
@@ -255,41 +312,23 @@ func oneJob() {
255312
easylog.Debug("非售票时间")
256313
return
257314
}
258-
easylog.Debug("once job start. missions size =", len(allMissions))
315+
easylog.Info("once job start. missions size =", len(allMissions))
316+
chDone := make(chan string, len(allMissions))
317+
start := time.Now().UnixNano()
259318
for i, queryOnce := range allMissions {
260-
easylog.Debug("job", i, "start :", queryOnce)
261-
ticketstr := queryX(queryOnce)
262-
if ticketstr == nil {
263-
continue
264-
}
265-
allTickets := parseTicketStr(ticketstr)
266-
filterTks, canbuyTks := filterTk(allTickets, queryOnce)
267-
easylog.Info(queryOnce, " job done. all=", len(allTickets),
268-
", can buy=", canbuyTks, ", filter=", len(filterTks))
269-
if filterTks != nil && len(filterTks) > 0 {
270-
//拼接信息
271-
var textMsgBuf bytes.Buffer
272-
textMsgBuf.WriteString(queryOnce.Day)
273-
for _, tk := range filterTks {
274-
if tkbytes := tk.getTkMsg(queryOnce); tkbytes != nil {
275-
textMsgBuf.WriteRune('\r')
276-
textMsgBuf.WriteRune('\n')
277-
textMsgBuf.Write(tkbytes)
278-
}
279-
}
280-
textMsg := textMsgBuf.String()
281-
easylog.Info("查询余票成功:", textMsg)
282-
//发送邮件
283-
if Mailflag {
284-
SendText(queryOnce.Day, textMsg)
285-
}
286-
//发送微信
287-
if Weixinflag {
288-
SendToQyweixin(textMsg)
289-
}
319+
if async {
320+
go oneMission(i, queryOnce, chDone)
321+
} else {
322+
oneMission(i, queryOnce, chDone)
290323
}
291324
} //end for allMissions
292-
easylog.Info("once job end.")
325+
for range allMissions {
326+
// ignore result
327+
<-chDone
328+
}
329+
close(chDone)
330+
end := time.Now().UnixNano()
331+
easylog.Info("once job end. tookTime =", (end-start)/1000000, "ms")
293332
}
294333

295334
//初始任务配置
@@ -343,54 +382,79 @@ func InitConf() (bool, string) {
343382
return false, "period 配置有误"
344383
}
345384
}
385+
asyncstr, ok := cfg["async"]
386+
if ok {
387+
async, _ = strconv.ParseBool(asyncstr)
388+
}
389+
retrystr, ok := cfg["retry"]
390+
if ok {
391+
retry, _ = strconv.Atoi(retrystr)
392+
}
393+
retryPeriodstr, ok := cfg["retryPeriod"]
394+
if ok {
395+
retryPeriod, _ = strconv.Atoi(retryPeriodstr)
396+
if retryPeriod < 1 {
397+
retryPeriod = 1
398+
}
399+
}
346400
//初始化所有任务配置
347401
arr := strings.Split(all, ",")
348-
allMissions = make([]QueryInfo, len(arr))
349-
for i, sectionName := range arr {
402+
for _, sectionName := range arr {
350403
onceSecCfg := GetSectionCfg(CONF_FILE, sectionName)
351404
if onceSecCfg == nil {
352405
return false, "任务 " + sectionName + " 未配置"
353406
}
354407
easylog.Debug(sectionName, " cfg: ", onceSecCfg)
355-
var queryInfo QueryInfo
356-
//乘车日期
357-
if day, ok := onceSecCfg["day"]; !ok {
358-
return false, sectionName + " day 配置有误"
359-
} else {
360-
if _, err := time.Parse(DAY_FORMAT, day); err != nil {
361-
return false, sectionName + " day 格式不正确,必须为yyyy-MM-dd"
362-
} else {
363-
queryInfo.Day = day
364-
}
365-
}
366408
//始发站
367-
if from, ok := onceSecCfg["from"]; !ok {
409+
from, fromeCode := "", ""
410+
if from, ok = onceSecCfg["from"]; !ok {
368411
return false, sectionName + " from 配置有误"
369412
} else {
370-
queryInfo.From = from
371-
queryInfo.FromCode = GetCodeByChname(from)
372-
if queryInfo.FromCode == "" {
413+
fromeCode = GetCodeByChname(from)
414+
if fromeCode == "" {
373415
return false, sectionName + " 不存在的车站名:" + from
374416
}
375417
}
376418
//抵达站
377-
if to, ok := onceSecCfg["to"]; !ok {
419+
to, toCode := "", ""
420+
if to, ok = onceSecCfg["to"]; !ok {
378421
return false, sectionName + " to 配置有误"
379422
} else {
380-
queryInfo.To = to
381-
queryInfo.ToCode = GetCodeByChname(to)
382-
if queryInfo.ToCode == "" {
423+
toCode = GetCodeByChname(to)
424+
if toCode == "" {
383425
return false, sectionName + " 不存在的车站名:" + to
384426
}
385427
}
386-
queryInfo.Starttime = onceSecCfg["starttime"]
387-
queryInfo.Endtime = onceSecCfg["endtime"]
388-
queryInfo.Train_type = onceSecCfg["train_type"]
389-
queryInfo.Train = onceSecCfg["train"]
390-
queryInfo.Prior_seat = onceSecCfg["prior_seat"]
391-
allMissions[i] = queryInfo
392-
}
393-
easylog.Debug("allMissions:", allMissions)
428+
//乘车日期
429+
if days, ok := onceSecCfg["day"]; !ok {
430+
return false, sectionName + " day 配置有误"
431+
} else {
432+
for _, day := range strings.Split(days, ",") {
433+
if "" != strings.TrimSpace(day) {
434+
if _, err := time.Parse(DAY_FORMAT, day); err != nil {
435+
return false, sectionName + " day 格式不正确,必须为yyyy-MM-dd"
436+
} else {
437+
var queryInfo QueryInfo
438+
queryInfo.From = from
439+
queryInfo.FromCode = fromeCode
440+
queryInfo.To = to
441+
queryInfo.ToCode = toCode
442+
queryInfo.Day = day
443+
queryInfo.Starttime = onceSecCfg["starttime"]
444+
queryInfo.Endtime = onceSecCfg["endtime"]
445+
queryInfo.Train_type = onceSecCfg["train_type"]
446+
queryInfo.Train = onceSecCfg["train"]
447+
queryInfo.Prior_seat = onceSecCfg["prior_seat"]
448+
allMissions = append(allMissions, queryInfo)
449+
}
450+
}
451+
}
452+
}
453+
}
454+
if len(allMissions) < 1 {
455+
return false, ""
456+
}
457+
easylog.Info("allMissions:", allMissions)
394458

395459
initConfFlag = true
396460
return true, "任务配置初始化成功"
@@ -417,7 +481,8 @@ func StartMission() {
417481
}
418482
//开启定时任务
419483
ticker := time.NewTicker(time.Duration(period) * time.Minute)
420-
easylog.Info("任务开始.周期=", period, "总任务数=", len(allMissions))
484+
easylog.Info("任务开始.周期=", period, ",总任务数=", len(allMissions),
485+
",是否并行=", async, ",失败重试次数=", retry, ",重试休眠间隔=", retryPeriod, "s")
421486
go oneJob()
422487
for range ticker.C {
423488
go oneJob()

0 commit comments

Comments
 (0)