2010년 4월 28일 수요일

나눔로또 당첨 정보 가져오기

이 글은 Google App Engine 공부 - 로또 정보 트위터 게시 글의 뒷 이야기 중 하나입니다. 몇 가지 주제들이 있지만, 가장 먼저 다룰 것이 '로또 정보를 어떻게 수집할까' 입니다.

가장 단순한 방법은 로또 추첨 때 지켜보고 있다가 추첨 결과가 나오면 사람이 직접 입력하는 겁니다. 하지만, 이건 좀 비효율적이고, 사람이 하는 일인 만큼 오타, 착시(?) 등으로 인해 잘못된 정보가 게시될 가능성이 있습니다.

결국, 프로그램을 통해 자동화 하는 것이 바람직한 것 같은데, 여기에는 고려할 것이 몇 가지 있습니다.

1. 정보 제공자
매우 당연한 이야기로, 인터넷 상에서 로또 당첨 정보를 제공하는 곳이 있어야 자동화를 구상이라도 해볼 수 있을 겁니다. 어디가 적당할까요? 정말 많은 곳이 있지만, 아무래도 나눔로또 공식 홈페이지 만한 곳이 또 있을까요?

2. 정보 추출 방법
나눔로또 공식 홈페이지를 봅시다. 로또 당첨 정보를 아름답게(?) 보여주고 있습니다.


여기서 우리에게 꼭 필요한 당첨 정보만 추출해야 합니다. 어떻게 할까요? 일단, RSS feed 같은 형식으로 제공해주는 게 있는지 살펴봅니다. 역시나 없습니다.
그런데, 가만히 보면 로또 당첨 정보를 보여주는 부분에서 '이전회차/다음회차'를 부분적으로 갱신해주는 버튼이 있는 걸 알 수 있습니다. Flash로 만들어진 게 아니고 JavaScript를 사용했군요. 이것을 힌트로 HTML 페이지 소스와 JavaScript 소스를 뒤지다 보면 가장 '최근회차'의 당첨 정보를 제공하는 URL이 있다는 것을 알 수 있습니다.
http://www.645lotto.net/resultall/dummy.asp
그리고 그 결과는 이렇게 돌아옵니다.
[{
 GRWNO : "386",
 GRWDate : "2010-04-24",
 FirstBall : "4",
 SecondBall : "7",
 ThirdBall : "10",
 FourthBall : "19",
 FifthBall : "31",
 SixthBall : "40",
 BonusBall : "26"
}]
어디서 많이 본 모양입니다. 그렇습니다. 바로 JSON 형식입니다. 이렇게 되고 보면 저 URL은 비공식적인 로또 정보 제공 API라고 봐도 될 듯합니다.
그럼, 이 정보를 사용해서 간단하게 우리가 필요한 정보만 추출해봅시다. 다음은 Python으로 제가 만든 소스 중 필요한 부분만 추린 간단한 예제입니다. (이 예제에서는 Google App Engine에 포함된 simplejson 라이브러리를 사용하지 않고 간단한 문자열 치환과 eval 함수를 통해 dict 형식으로 가공하는 방법을 썼습니다.)
import httplib
import re

LOTTO645_SITE = "www.645lotto.net"
API_LATEST_URL = "/resultall/dummy.asp"

conn = httplib.HTTPConnection(LOTTO645_SITE)
conn.request("GET", API_LATEST_URL)
res = conn.getresponse()
raw_dat = res.read()
conn.close()

dat = raw_dat.replace('\n', '').replace('\r', '').replace('\t', ' ')
p = re.compile('(\s+)(\w+)(\s+:)')
dic = eval(p.sub('\g<1>"\g<2>"\g<3>', dat))[0]

BALL_NAMES = ['First', 'Second', 'Third', 'Fourth', 'Fifth', 'Sixth']
nums = [dic.get(l + 'Ball') for l in BALL_NAMES]
bnum = int(dic.get('BonusBall'))
lcnt = int(dic.get('GRWNO'))
ldate = dic.get('GRWDate')

print "회차:", lcnt
print "추첨일:", ldate 
print "당첨번호: %s + %d" % (str(nums).replace("'",'')[1:-1], bnum)
출력 결과는 다음과 같습니다.
회차: 386
추첨일: 2010-04-24
당첨번호: 4, 7, 10, 19, 31, 40 + 26

3. 최적화(함께 하는 세상)
자, 이제는 원하는 것을 얻었습니다. 그렇지만, 다 같이 사는 세상입니다. 정보를 얻어 쓰는 주제에 정보 제공자에게 폐를 끼쳐서는 안될 것입니다. 로또 당첨 정보가 필요할 때마다 공식 홈페이지에서 가져오는 것은 아무래도 서버에 부하를 줄 가능성이 있습니다. 처음에는 얼마 안 되겠지만, 차츰 이런 식으로 정보를 가져오는 사람이 늘어나게 되면 서버 관리하는 입장에서는 갑자기 늘어나는 트래픽에 신경이 쓰이게 될 테고 결국 URL을 막아버릴 수도 있을 겁니다.
그러니, 공식 홈페이지에서 정보를 가져오는 빈도를 최소화 해야 합니다. 여러 가지 방법을 생각해볼 수 있겠지만 제가 생각한 방법은, 일 주일에 한 번만 공식 홈페이지에서 정보를 가져와 DB에 저장해두고 필요할 때마다 DB로부터 꺼내어 쓰는 겁니다.

[*] 나눔로또 공식 홈페이지 운영팀에 바랍니다.
어차피 로또 추첨 결과를 널리 알리는 게 목적인 것이라면 단순히 공식 홈페이지에만 게시하고 말 것이 아니라, 'RSS feed 제공'이나 'Twitter 게시' 같은 좀더 적극적인 방법을 통해 공유하는 것이 더 바람직하지 않을까 생각합니다. 고려해주실 거죠?! ;-)


2010년 4월 27일 화요일

Emacs와 Mercurial(hg)에 관한 두 가지 팁

몇 년 전까지만 해도 개발 소스 버전 관리를 위해서 주로 CVS나 Subversion을 많이 사용했지만(대형 상용 프로젝트에는 Perforce도 많이 사용됩니다.), 근래에 와서는 DVCS에 대한 관심이 증가하게 되고 여러 가지 장점들이 소개되면서, Mercurial / Git / Bazaar 같은 DVCS도 차츰 많이 사용하는 추세입니다. 특히 Open Source Project 그룹에서 이런 현상이 두드러집니다.
아무튼 최근에 개인적으로 개발 중인 자잘한 소스들을 Mercurial(이하 hg)로 버전 관리하고 있는데, Emacs에 기본 탑재되어 있는 vc-hg를 사용하다 보니 몇 가지 불편한 점이 있더군요.

1. encoding 문제
한글 Windows 환경에서 hg의 기본 encoding은 cp949입니다. 그런데, Emacs에서 buffer의 기본 encoding을 utf-8으로 두고 사용하다 보니, Emacs 상에서 log나 diff를 볼 때 한글로 작성한 내용이 모두 \xxx 같은 형식으로 출력되어 버리는 겁니다.
이 문제를 해결하기 위해서는 .emacs 파일 등에 다음 라인을 추가해주면 됩니다.
(setq vc-hg-global-switches "--encoding=utf-8")
eshell 상에서 hg를 직접 실행하는 경우에는 위 설정이 적용되지 않기 때문에 alias를 사용해서 간단하게 encoding 옵션을 붙여 주면 됩니다.
alias hg 'hg --encoding=utf-8 $*'

2. HTTP 인증 문제
로컬에서만 버전 관리를 하면 크게 상관이 없지만, Bitbucket 같은 호스팅 서비스를 이용하게 될 경우에는 pull 또는 push 같은 작업을 할 때 HTTP 인증이 필요하게 됩니다. 이 때 console 상에서 바로 실행하면 password를 물어보는 prompt가 뜨게 되지만, Emacs eshell이나 shell 상에서는 standard input이 Emacs에 의해 별도 처리되기 때문에 prompt가 뜨는 대신 HTTP 인증이 바로 실패하게 됩니다.
이 문제를 해결하기 위해서는 저장소 폴더(.hg) 내에 있는 hgrc 파일에 인증 정보를 기록해주면 됩니다.
[paths]
default = https://username:password@bitbucket.org/myproject1/
이렇게 하면 인증 정보가 자동으로 전달되기 때문에 매번 password를 입력하는 번거로움도 함께 해결됩니다. 대신, hgrc 파일에 기록된 인증 정보가 타인에게 유출되는 것은 주의할 필요가 있습니다.