jsoup으로 로또 홈페이지의 당첨번호 파싱하기

이번 포스팅에서는 jsoup을 이용해서 html을 파싱하는 법에 대해 알아보도록 하겠습니다.

# jsoup이란

jsoup은 html을 파싱하기 위해 만들어진 오픈소스 자바 라이브러리입니다. 웹페이지를 HTML5 DOM으로 변환하여 요소들을 간편하게 추출할 수 있습니다. 자바 라이브러리이니만큼 코틀린에서도 자유롭게 사용이 가능하지요.

jsoup에는 여러가지 클래스가 있는데 그 중 제 생각에 중요한 것들만 정리하면 다음과 같습니다.

클래스 설명
Document jsoup로 얻어온 html 문서
Element 데이터를 추출할 수 있는 개별 html 요소
Elements Iterable한 Element 집합의 자료형
Connection http 접속에 관련한 메소드를 가지는 클래스

# 파싱할 html 구조 확인

그럼 코루틴으로 로또번호 당첨 확인하기 강의에서 만들었던 코루틴으로 로또번호 당첨을 확인하는 앱에 jsoup을 적용해보도록 하겠습니다. 기존의 앱은 당첨번호를 가져오는데 API를 사용했지만 이번에는 웹페이지에서 가져와야 합니다. 동행복권 홈페이지에 접속하면 메인화면에 금주의 당첨번호를 표시하고 있는데 이 내용을 가져오면 될 것 같습니다.

이 화면을 브라우저의 개발자도구로 보면 html의 레이아웃을 확인할 수 있습니다. 우리가 원하는 정보는 containerWrap > article > wrap_box wrap1 > box win win645 > content 클래스 안에 위치하고 있네요. 여기서 회차와 당첨번호는 다음과 같은 요소로 정의되어 있습니다.

1
<strong id="lottoDrwNo">968</strong>
1
2
3
4
5
6
7
8
9
<span class="accessibility">당첨번호</span>
<span id="drwtNo1" class="ball_645 ball1">2</span>
<span id="drwtNo2" class="ball_645 ball1">5</span>
<span id="drwtNo3" class="ball_645 ball2">12</span>
<span id="drwtNo4" class="ball_645 ball2">14</span>
<span id="drwtNo5" class="ball_645 ball3">24</span>
<span id="drwtNo6" class="ball_645 ball4">39</span>
<span class="bonus">보너스번호</span>
<span id="bnusNo" class="ball_645 ball4">33</span>

# 라이브러리 추가

우선은 라이브러리를 추가합니다. 소스의 최신버전은 Github에서 확인할 수 있습니다.

1
implementation 'org.jsoup:jsoup:1.14.1'

# crawlLottoNumbers 작성

다음은 당첨번호를 가져오는 crawlLottoNumbers함수를 작성합니다. API로 당첨번호를 가져오는 getLottoNumbers와 동일하게 반환값이 당첨번호, 보너스번호, 회차 정보를 가지도록 하겠습니다.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
    private suspend fun crawlLottoNumbers() : ArrayList<Int> {
        val lottoNumbers = ArrayList<Int>()
        // var doc: Document? = null
        try {
            val doc = Jsoup.connect("https://dhlottery.co.kr/common.do?method=main").get()
            for (i in 1..6) {
                val drwtNo = doc.select("#drwtNo$i").text().toInt()
                lottoNumbers.add(drwtNo)
            }
            val bnusNo = doc.select("#bnusNo").text().toInt()
            lottoNumbers.add(bnusNo)

            val lottoDrwNo = doc.select("#lottoDrwNo").text().toInt()
            lottoNumbers.add(lottoDrwNo)

        } catch (e: IOException) {
            e.printStackTrace()
        }
        return lottoNumbers
    }

우선은 connect로 세션을 생성하고 get을 수행해 html을 Document 객체로 만들어 줍니다.

Document 객체에서 데이터를 뽑아낼때는 select 명령을 사용하는데 괄호 안에는 CSS or jquery-like selector syntax를 넣습니다. 주요한 셀렉터들은 다음과 같습니다.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
tagname: find elements by tag, e.g. a

ns|tag: find elements by tag in a namespace, e.g. fb|name finds <fb:name> elements

#id: find elements by ID, e.g. #logo

.class: find elements by class name, e.g. .masthead

[attribute]: elements with attribute, e.g. [href]

[^attr]: elements with an attribute name prefix, e.g. [^data-] finds elements with HTML5 dataset attributes

[attr=value]: elements with attribute value, e.g. [width=500] (also quotable, like [data-name='launch sequence'])

[attr^=value], [attr$=value], [attr*=value]: elements with attributes that start with, end with, or contain the value, e.g. [href*=/path/]

[attr~=regex]: elements with attribute values that match the regular expression; e.g. img[src~=(?i)\.(png|jpe?g)]

*: all elements, e.g. *

당첨번호와 회차정보는 id로 정의되어 있기 때문에 select 함수에 #id 쿼리를 전달해주면 됩니다. 추출한 정보가 String이니까 Int로 변환하여 lottoNumbers에 추가하면 되겠네요.

이렇게 해서 jsoup으로 html을 다운받고 파싱하는 법에 대해 알아보았습니다.

Built with Hugo
Theme Stack designed by Jimmy