Skip to content

Commit 8121e5c

Browse files
committed
develop
1 parent fe070b0 commit 8121e5c

File tree

2 files changed

+190
-208
lines changed

2 files changed

+190
-208
lines changed

atcoder.go

Lines changed: 172 additions & 148 deletions
Original file line numberDiff line numberDiff line change
@@ -7,20 +7,25 @@ import (
77
"encoding/gob"
88
"fmt"
99
"io/ioutil"
10-
"log"
1110
"net/http"
1211
"strconv"
1312
"strings"
1413
"time"
1514

15+
"github.com/gin-gonic/gin"
16+
"google.golang.org/appengine"
17+
"google.golang.org/appengine/log"
18+
"google.golang.org/appengine/urlfetch"
19+
1620
"github.com/PuerkitoBio/goquery"
1721
)
1822

1923
// AtCoderのコンテスト情報を管理するモジュール
2024
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
2429
}
2530

2631
type RawAtCoderContest struct {
@@ -40,199 +45,195 @@ type AtCoderContest struct {
4045
Rated string
4146
}
4247

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)
4961
}
5062

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)
5566
}
67+
}
5668

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)
6177
}
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")
62103
}
63104

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)
68116
if err != nil {
69-
log.Printf("failed GET request: %v", err)
70-
return false
117+
fmt.Print(err)
118+
return
71119
}
72-
defer response.Body.Close()
73120

74-
// HTMLを読み込む
121+
// 2. goqueryを使えるようにする
75122
doc, err := goquery.NewDocumentFromReader(response.Body)
76123
if err != nil {
77124
fmt.Print(err)
78-
return false
79125
}
80126

81-
atcoder.ContestDocument = doc
82-
return true
83-
}
84-
85-
func (atcoder *AtCoder) SetRawContest() bool {
127+
// 3. 予定されたコンテストのテーブルを取得
86128
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) {
88130
if h3 := s.Text(); h3 == "予定されたコンテスト" {
89131
tableSelection = s.Next()
90132
}
91133
})
92134

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)
113137

114-
atcoder.RawContests = rawContest
138+
// 5. 生のコンテストデータをパースする
139+
// for _, rawContest := range atcoder.RawContests {
140+
// atcoder.Contests = append(atcoder.Contests, ParseSum(rawContest))
141+
// }
115142

116-
return true
117143
}
118144

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
123158
}
124-
atcoder.Contests = contests
125-
return true
126-
}
127159

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+
}
133168

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+
}
146174

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")
155177

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)
168180

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+
// }
179185
}
186+
180187
}
181188

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
185191

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)
188198
if err != nil {
189-
log.Printf("failed GET request: %v", err)
190-
return nomi
199+
fmt.Print(err)
200+
return 0, false
191201
}
192-
defer response.Body.Close()
193202

194-
// HTMLを読み込む
203+
// 2. goqueryを使えるようにする
195204
doc, err := goquery.NewDocumentFromReader(response.Body)
196205
if err != nil {
197206
fmt.Print(err)
207+
return 0, false
198208
}
199209

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
204221
}
205-
})
206222

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
217228
}
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+
}
232230

233-
return contests
231+
})
232+
// log.Infof(context, "pageSize(nomikura): %+v", pageSize)
233+
return numberOfPage, true
234234
}
235235

236+
// 生のコンテストデータをパースする
236237
func ParseSum(rawContest RawAtCoderContest) AtCoderContest {
237238
// Durationを求める
238239
str := strings.Replace(rawContest.Duration, ":", "h", 1) + "m"
@@ -260,3 +261,26 @@ func ParseSum(rawContest RawAtCoderContest) AtCoderContest {
260261
Rated: rawContest.Rated,
261262
}
262263
}
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

Comments
 (0)