-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathparser.go
143 lines (115 loc) · 3.25 KB
/
parser.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
package kbo
import (
"net/http"
"strconv"
"strings"
"time"
"github.com/PuerkitoBio/goquery"
)
const (
URL = "https://sports.news.naver.com/kbaseball/schedule/index.nhn"
)
type Parser struct {
client *http.Client
url string
}
// NewParser는 초기화된 Parser의 포인터를 반환합니다.
func NewParser(url string, client *http.Client) *Parser {
return &Parser{
client: client,
url: url,
}
}
// Parse는 주어진 시간의 경기 결과를 반환합니다.
func (p *Parser) Parse(t time.Time) ([]Game, error) {
resp, err := p.request(int(t.Month()), t.Year())
if err != nil {
return []Game{}, err
}
defer resp.Body.Close()
doc, err := goquery.NewDocumentFromReader(resp.Body)
if err != nil {
return []Game{}, err
}
result := p.parse(t.Day(), doc)
return result, nil
}
func (p *Parser) request(month, year int) (*http.Response, error) {
req, err := http.NewRequest("GET", p.url, nil)
if err != nil {
return nil, err
}
q := req.URL.Query()
q.Add("month", strconv.Itoa(month))
q.Add("year", strconv.Itoa(year))
req.URL.RawQuery = q.Encode()
return p.client.Do(req)
}
func (p *Parser) parse(day int, doc *goquery.Document) []Game {
games := []Game{}
doc.Find(".tb_wrap > div[class^=sch_tb]").Each(func(i int, s *goquery.Selection) {
if Day(s) == day && !NoGame(s) {
s.Find("tr").Each(func(i int, s *goquery.Selection) {
games = append(games, Game{
Home: Home(s),
Away: Away(s),
Canceled: Canceled(s),
Score: Score(s),
})
})
}
})
return games
}
// NoGame은 주어진 selection에서 경기가 취소되었는지 확인합니다.
func NoGame(s *goquery.Selection) bool {
str, exists := s.Attr("class")
if !exists {
return false
}
return strings.Contains(str, "nogame")
}
// Day는 주어진 selection이 어떤 날짜를 가지는지 반환합니다.
// 만약 에러가 발생할 경우 -1를 반환합니다.
func Day(s *goquery.Selection) int {
var (
day int
err error
)
s.Find(".td_date > strong").Each(func(i int, s *goquery.Selection) {
day, err = strconv.Atoi(strings.Split(s.Text(), ".")[1])
})
if err != nil {
return -1
}
return day
}
// Home은 주어진 selection에서 홈 팀을 반환합니다.
func Home(s *goquery.Selection) Team { return team(s.Find(".team_rgt").Text()) }
// Away는 주어진 selection에서 원정 팀을 반환합니다.
func Away(s *goquery.Selection) Team { return team(s.Find(".team_lft").Text()) }
// Canceled는 주어진 selection에서 경기가 취소되면 true,
// 경기가 취소되지 않으면 false를 반환합니다.
func Canceled(s *goquery.Selection) bool { return s.Find(".td_stadium.cancel").Length() == 1 }
// Score는 주어진 selection에서 경기의 점수를 반환합니다.
// 배열의 첫번째 인덱스는 원정 팀, 두번째 인덱스는 홈 팀의 점수를 가리킵니다.
// 에러가 나면 빈 배열을 반환합니다.
func Score(s *goquery.Selection) [2]int {
var score [2]int
if !Canceled(s) {
strs := strings.Split(s.Find(".td_score").Text(), ":")
if len(strs) != 2 {
return [2]int{}
}
s1, err := strconv.Atoi(strs[0])
if err != nil {
return [2]int{}
}
s2, err := strconv.Atoi(strs[1])
if err != nil {
return [2]int{}
}
score = [2]int{s1, s2}
}
return score
}