@@ -7,20 +7,25 @@ import (
7
7
"encoding/gob"
8
8
"fmt"
9
9
"io/ioutil"
10
- "log"
11
10
"net/http"
12
11
"strconv"
13
12
"strings"
14
13
"time"
15
14
15
+ "github.com/gin-gonic/gin"
16
+ "google.golang.org/appengine"
17
+ "google.golang.org/appengine/log"
18
+ "google.golang.org/appengine/urlfetch"
19
+
16
20
"github.com/PuerkitoBio/goquery"
17
21
)
18
22
19
23
// AtCoderのコンテスト情報を管理するモジュール
20
24
type AtCoder struct {
21
- Contests []AtCoderContest
22
- RawContests []RawAtCoderContest
23
- ContestDocument * goquery.Document
25
+ Contests []AtCoderContest
26
+ RawContests []RawAtCoderContest
27
+ HttpClient * http.Client
28
+ Context * gin.Context
24
29
}
25
30
26
31
type RawAtCoderContest struct {
@@ -40,199 +45,195 @@ type AtCoderContest struct {
40
45
Rated string
41
46
}
42
47
43
- // 流れ
44
- func (atcoder * AtCoder ) SetData () {
45
- // goqueryが使える状態にする
46
- ok := atcoder .SetContestDocument ()
47
- if ! ok {
48
- return
48
+ // TODO: ファイルIOをスタンダード環境でできるようにする。
49
+
50
+ // gobとして書き込む
51
+ func (atcoder * AtCoder ) StoreGob (fileName string ) {
52
+ var request * http.Request = atcoder .Context .Request
53
+ context := appengine .NewContext (request )
54
+
55
+ buffer := new (bytes.Buffer )
56
+ encoder := gob .NewEncoder (buffer )
57
+ // err := encoder.Encode(data)
58
+ err := encoder .Encode (atcoder .Contests )
59
+ if err != nil {
60
+ log .Errorf (context , "Faild to encode(StoreGob): %v" , err )
49
61
}
50
62
51
- // 生コンテストデータ取得
52
- ok = atcoder .SetRawContest ()
53
- if ! ok {
54
- return
63
+ err = ioutil .WriteFile (fileName , buffer .Bytes (), 0600 )
64
+ if err != nil {
65
+ log .Errorf (context , "Faild to write file(StoreGob): %v" , err )
55
66
}
67
+ }
56
68
57
- // コンテストデータ取得
58
- ok = atcoder .SetContest ()
59
- if ! ok {
60
- return
69
+ // gobを読み出す
70
+ func (atcoder * AtCoder ) LoadGob (fileName string ) {
71
+ var request * http.Request = atcoder .Context .Request
72
+ context := appengine .NewContext (request )
73
+
74
+ raw , err := ioutil .ReadFile (fileName )
75
+ if err != nil {
76
+ log .Errorf (context , "Faild to read file(LoadGob): %v" , err )
61
77
}
78
+ buffer := bytes .NewBuffer (raw )
79
+ dec := gob .NewDecoder (buffer )
80
+ // err = dec.Decode(data)
81
+ err = dec .Decode (atcoder .Contests )
82
+ if err != nil {
83
+ log .Errorf (context , "Faild to decode(LoadGob): %v" , err )
84
+ }
85
+ }
86
+
87
+ func (atcoder * AtCoder ) GetAllContest (context * gin.Context ) {
88
+ // atcoder.HttpClient = client
89
+ atcoder .Context = context
90
+
91
+ // 予定されたコンテストの生データを取得
92
+ atcoder .GetFutureContest ()
93
+
94
+ // 過去のコンテストの生データを取得
95
+ atcoder .GetPastContest ()
96
+
97
+ // 5. 生のコンテストデータをパースする
98
+ for _ , rawContest := range atcoder .RawContests {
99
+ atcoder .Contests = append (atcoder .Contests , ParseSum (rawContest ))
100
+ }
101
+
102
+ atcoder .StoreGob ("contests" )
62
103
}
63
104
64
- // goqueryが使える状態にする
65
- func (atcoder * AtCoder ) SetContestDocument () bool {
66
- // GETリクエストを送る
67
- response , err := http .Get ("https://beta.atcoder.jp/contests/?lang=ja" )
105
+ // 予定されたコンテストデータを取得する
106
+ func (atcoder * AtCoder ) GetFutureContest () {
107
+ // var responseWriter http.ResponseWriter = atcoder.Context.Writer
108
+ var request * http.Request = atcoder .Context .Request
109
+
110
+ context := appengine .NewContext (request )
111
+ client := urlfetch .Client (context )
112
+
113
+ // 1. GETリクエスト
114
+ response , err := client .Get ("https://beta.atcoder.jp/contests/?lang=ja" )
115
+ time .Sleep (2 * time .Second )
68
116
if err != nil {
69
- log . Printf ( "failed GET request: %v" , err )
70
- return false
117
+ fmt . Print ( err )
118
+ return
71
119
}
72
- defer response .Body .Close ()
73
120
74
- // HTMLを読み込む
121
+ // 2. goqueryを使えるようにする
75
122
doc , err := goquery .NewDocumentFromReader (response .Body )
76
123
if err != nil {
77
124
fmt .Print (err )
78
- return false
79
125
}
80
126
81
- atcoder .ContestDocument = doc
82
- return true
83
- }
84
-
85
- func (atcoder * AtCoder ) SetRawContest () bool {
127
+ // 3. 予定されたコンテストのテーブルを取得
86
128
var tableSelection * goquery.Selection
87
- atcoder . ContestDocument .Find ("h3" ).Each (func (i int , s * goquery.Selection ) {
129
+ doc .Find ("h3" ).Each (func (i int , s * goquery.Selection ) {
88
130
if h3 := s .Text (); h3 == "予定されたコンテスト" {
89
131
tableSelection = s .Next ()
90
132
}
91
133
})
92
134
93
- // コンテストデータを取得
94
- var rawContest []RawAtCoderContest
95
- tableSelection .Find ("div > table > tbody > tr" ).Each (func (i int , trSelection * goquery.Selection ) {
96
- // とりあえず文字列でテーブル情報を取得
97
- var href string
98
- var rawData [4 ]string
99
- trSelection .Find ("td" ).Each (func (i int , tdSelection * goquery.Selection ) {
100
- rawData [i ] = tdSelection .Text ()
101
- if i == 1 {
102
- href , _ = tdSelection .Find ("a" ).Attr ("href" )
103
- }
104
- })
105
- rawContest = append (rawContest , RawAtCoderContest {
106
- StartTime : rawData [0 ],
107
- Title : rawData [1 ],
108
- Duration : rawData [2 ],
109
- Rated : rawData [3 ],
110
- Path : href ,
111
- })
112
- })
135
+ // 4. 生のコンテストデータを取得
136
+ atcoder .GetRawContestFromTable (tableSelection )
113
137
114
- atcoder .RawContests = rawContest
138
+ // 5. 生のコンテストデータをパースする
139
+ // for _, rawContest := range atcoder.RawContests {
140
+ // atcoder.Contests = append(atcoder.Contests, ParseSum(rawContest))
141
+ // }
115
142
116
- return true
117
143
}
118
144
119
- func (atcoder * AtCoder ) SetContest () bool {
120
- var contests []AtCoderContest
121
- for _ , rawContest := range atcoder .RawContests {
122
- contests = append (contests , atcoder .Parse (rawContest ))
145
+ // 過去のコンテストデータを取得する
146
+ func (atcoder * AtCoder ) GetPastContest () {
147
+ baseURL := "https://beta.atcoder.jp/contests/archive?lang=ja"
148
+ // 準備
149
+ var request * http.Request = atcoder .Context .Request
150
+ context := appengine .NewContext (request )
151
+ client := urlfetch .Client (context )
152
+
153
+ numberOfPage , ok := atcoder .GetNumberOfPage (baseURL )
154
+ // ページ番号の取得に失敗
155
+ if ! ok {
156
+ log .Infof (context , "Faild to get number of page!!" )
157
+ return
123
158
}
124
- atcoder .Contests = contests
125
- return true
126
- }
127
159
128
- func (atcoder * AtCoder ) Parse (rawContest RawAtCoderContest ) AtCoderContest {
129
- // Durationを求める
130
- str := strings .Replace (rawContest .Duration , ":" , "h" , 1 ) + "m"
131
- tim , _ := time .ParseDuration (str )
132
- duration := int64 (tim .Seconds ())
160
+ for page := 1 ; page <= numberOfPage ; page ++ {
161
+ // 1. GETリクエスト
162
+ response , err := client .Get (baseURL + "&page=" + strconv .Itoa (page ))
163
+ time .Sleep (2 * time .Second )
164
+ if err != nil {
165
+ fmt .Print (err )
166
+ return
167
+ }
133
168
134
- // StartTimeを求める
135
- start := rawContest .StartTime
136
- atoi := func (str string ) int {
137
- ret , _ := strconv .Atoi (str )
138
- return ret
139
- }
140
- // [2018-09-22 21:00:00+0900]の形式で抜き出した時間を無理矢理Timeオブジェクトにする
141
- year , month , day , hour , minute := atoi (start [:4 ]), atoi (start [5 :7 ]), atoi (start [8 :10 ]), atoi (start [11 :13 ]), atoi (start [14 :])
142
- // 取得する時間はJSTなので、日本時間をTimeオブジェクトにするように処理する
143
- jst , _ := time .LoadLocation ("Asia/Tokyo" )
144
- startTime := time .Date (year , time .Month (month ), day , hour , minute , 0 , 0 , jst )
145
- unix := startTime .Unix ()
169
+ // 2. goqueryを使えるようにする
170
+ doc , err := goquery .NewDocumentFromReader (response .Body )
171
+ if err != nil {
172
+ fmt .Print (err )
173
+ }
146
174
147
- return AtCoderContest {
148
- Title : rawContest .Title ,
149
- Path : rawContest .Path ,
150
- StartTime : unix ,
151
- Duration : duration ,
152
- Rated : rawContest .Rated ,
153
- }
154
- }
175
+ // 3. 予定されたコンテストのテーブルを取得
176
+ var tableSelection * goquery.Selection = doc .Find ("table" )
155
177
156
- func (atcoder * AtCoder ) WriteContestData () {
157
- buffer := new (bytes.Buffer )
158
- encoder := gob .NewEncoder (buffer )
159
- err := encoder .Encode (atcoder .Contests )
160
- if err != nil {
161
- log .Print (err )
162
- }
163
- err = ioutil .WriteFile ("contest" , buffer .Bytes (), 0600 )
164
- if err != nil {
165
- log .Print (err )
166
- }
167
- }
178
+ // 4. 生のコンテストデータを取得
179
+ atcoder .GetRawContestFromTable (tableSelection )
168
180
169
- func (atcoder * AtCoder ) ReadContestData () {
170
- raw , err := ioutil .ReadFile ("contest" )
171
- if err != nil {
172
- log .Print (err )
173
- }
174
- buffer := bytes .NewBuffer (raw )
175
- dec := gob .NewDecoder (buffer )
176
- err = dec .Decode (atcoder .Contests )
177
- if err != nil {
178
- log .Print ()
181
+ // 5. 生のコンテストデータをパースする
182
+ // for _, rawContest := range atcoder.RawContests {
183
+ // atcoder.Contests = append(atcoder.Contests, ParseSum(rawContest))
184
+ // }
179
185
}
186
+
180
187
}
181
188
182
- func sum () []AtCoderContest {
183
- var nomi []AtCoderContest
184
- nomi = append (nomi , AtCoderContest {Title : "hoge" })
189
+ func (atcoder * AtCoder ) GetNumberOfPage (baseURL string ) (int , bool ) {
190
+ var request * http.Request = atcoder .Context .Request
185
191
186
- // GETリクエストを送る
187
- response , err := http .Get ("https://beta.atcoder.jp/contests/?lang=ja" )
192
+ context := appengine .NewContext (request )
193
+ client := urlfetch .Client (context )
194
+
195
+ // 1. Getリクエスト
196
+ response , err := client .Get (baseURL )
197
+ time .Sleep (2 * time .Second )
188
198
if err != nil {
189
- log . Printf ( "failed GET request: %v" , err )
190
- return nomi
199
+ fmt . Print ( err )
200
+ return 0 , false
191
201
}
192
- defer response .Body .Close ()
193
202
194
- // HTMLを読み込む
203
+ // 2. goqueryを使えるようにする
195
204
doc , err := goquery .NewDocumentFromReader (response .Body )
196
205
if err != nil {
197
206
fmt .Print (err )
207
+ return 0 , false
198
208
}
199
209
200
- var tableSelection * goquery.Selection
201
- doc .Find ("h3" ).Each (func (i int , s * goquery.Selection ) {
202
- if h3 := s .Text (); h3 == "予定されたコンテスト" {
203
- tableSelection = s .Next ()
210
+ // 3. 番号を取得
211
+ numberOfPage := 1
212
+ doc .Find ("ul > li > a" ).Each (func (i int , s * goquery.Selection ) {
213
+ href , exists := s .Attr ("href" )
214
+ // aタグにhrefが存在しない
215
+ if ! exists {
216
+ return
217
+ }
218
+ // hrefに[page=]が含まれない
219
+ if ! strings .Contains (href , "page=" ) {
220
+ return
204
221
}
205
- })
206
222
207
- // コンテストデータを取得
208
- var rawContest []RawAtCoderContest
209
- tableSelection .Find ("div > table > tbody > tr" ).Each (func (i int , trSelection * goquery.Selection ) {
210
- // とりあえず文字列でテーブル情報を取得
211
- var href string
212
- var rawData [4 ]string
213
- trSelection .Find ("td" ).Each (func (i int , tdSelection * goquery.Selection ) {
214
- rawData [i ] = tdSelection .Text ()
215
- if i == 1 {
216
- href , _ = tdSelection .Find ("a" ).Attr ("href" )
223
+ // タグの中身を数値にできる
224
+ if page , err := strconv .Atoi (s .Text ()); err == nil {
225
+ // log.Infof(context, "pageNumger(nomikura): %+v", page)
226
+ if page > numberOfPage {
227
+ numberOfPage = page
217
228
}
218
- })
219
- rawContest = append (rawContest , RawAtCoderContest {
220
- StartTime : rawData [0 ],
221
- Title : rawData [1 ],
222
- Duration : rawData [2 ],
223
- Rated : rawData [3 ],
224
- Path : href ,
225
- })
226
- })
227
-
228
- var contests []AtCoderContest
229
- for _ , rawContest := range rawContest {
230
- contests = append (contests , ParseSum (rawContest ))
231
- }
229
+ }
232
230
233
- return contests
231
+ })
232
+ // log.Infof(context, "pageSize(nomikura): %+v", pageSize)
233
+ return numberOfPage , true
234
234
}
235
235
236
+ // 生のコンテストデータをパースする
236
237
func ParseSum (rawContest RawAtCoderContest ) AtCoderContest {
237
238
// Durationを求める
238
239
str := strings .Replace (rawContest .Duration , ":" , "h" , 1 ) + "m"
@@ -260,3 +261,26 @@ func ParseSum(rawContest RawAtCoderContest) AtCoderContest {
260
261
Rated : rawContest .Rated ,
261
262
}
262
263
}
264
+
265
+ // 生のコンテストデータをセットする
266
+ func (atcoder * AtCoder ) GetRawContestFromTable (tableSelection * goquery.Selection ) {
267
+ tableSelection .Find ("div > table > tbody > tr" ).Each (func (i int , trSelection * goquery.Selection ) {
268
+ // とりあえず文字列でテーブル情報を取得
269
+ var href string
270
+ var rawData [4 ]string
271
+ trSelection .Find ("td" ).Each (func (i int , tdSelection * goquery.Selection ) {
272
+ rawData [i ] = tdSelection .Text ()
273
+ if i == 1 {
274
+ href , _ = tdSelection .Find ("a" ).Attr ("href" )
275
+ }
276
+ })
277
+ rawContest := RawAtCoderContest {
278
+ StartTime : rawData [0 ],
279
+ Title : rawData [1 ],
280
+ Duration : rawData [2 ],
281
+ Rated : rawData [3 ],
282
+ Path : href ,
283
+ }
284
+ atcoder .RawContests = append (atcoder .RawContests , rawContest )
285
+ })
286
+ }
0 commit comments