따라하는 python 로또 분석(2)
이 글은 python 초보자들을 대상으로 하는 로또 데이터 분석에 관련된 글입니다.
이전 포스트는 아래 링크를 따라가시기 바랍니다.
2016/06/21 - [개발] - 따라하는 python 로또 분석 (1)
3. 추출한 데이터 저장하기
지금까지 우리는 python을 이용하여 로또 사이트에서 데이터를 추출하는 법을 다루었습니다.
이번엔 받아온 데이터를 데이터베이스에 저장해 볼 차례입니다.
sqlite나 mongodb등이 유행인데요, 저는 이미 mysql이 깔려있던 관계로 mysql을 그대로 사용하였습니다.
python 모듈 : MySQLdb
MySQLdb - python에서 mysql을 사용하기 위한 라이브러리.
이번에도 pip3를 이용하여 위의 모듈을 설치해 줍니다.
저는 lotto라는 데이터베이스를 만들고 data라는 테이블을 추가하였습니다.
data 테이블 구조는 아래와 같습니다.
보너스 숫자를 포함한 전체 7개의 로또 숫자를 각 column으로 구분하여서 저장하도록 했습니다.
또 당첨횟수, 당첨자수, 당첨금액은 각각 count, persion, amount라는 column에 저장하도록 하였습니다.
모든 column들은 not null 설정을, 당첨금액을 제외한 모든 column들은 INT형으로 설정하였습니다.
당첨횟수 count는 인덱스로 사용하기 위해 pk, unique, unsigned를 지정해 주었습니다.
그리고 해당 데이터베이스에 연결되는 유저와 패스워드는 lotto/lotto로 설정하였습니다.
그럼 해당 설정을 이용하여 python에서 mysql로 연결이 잘 되는지를 테스트 해 볼 차례입니다.
수정된 코드는 아래와 같습니다.
# lotto.py
import requests
from bs4 import BeautifulSoup
import MySQLdb
def main():
basic_url = "https://www.nlotto.co.kr/lotto645Confirm.do?method=byWin&drwNo="
for i in range(1, 707):
resp = requests.get(basic_url + str(i))
soup = BeautifulSoup(resp.text, "lxml")
line = str(soup.find("meta", {"id" : "desc", "name" : "description"})['content'])
print("당첨회차: " + str(i))
begin = line.find("당첨번호")
begin = line.find(" ", begin) + 1
end = line.find(".", begin)
numbers = line[begin:end]
print("당첨번호: " + numbers)
begin = line.find("총")
begin = line.find(" ", begin) + 1
end = line.find("명", begin)
persons = line[begin:end]
print("당첨인원: " + persons)
begin = line.find("당첨금액")
begin = line.find(" ", begin) + 1
end = line.find("원", begin)
amount = line[begin:end]
print("당첨금액: " + amount)
db = MySQLdb.connect(host="localhost", user="lotto", passwd="lotto", db="lotto")
cursor = db.cursor()
sql = "SELECT count(*) FROM data"
cursor.execute(sql)
results = cursor.fetchone()
print("저장된 총 회차: " + str(results[0]))
db.close()
break
if __name__ == "__main__":
main()
import MySQLdb를 추가하였고, 데이터베이스에 연결하기 위해서 connect를 호출합니다.
MySQLdb.connect(host, user, password, database)
cursor()를 받아와서 실행시키기 원하는 쿼리를 execute() 함수를 이용해 실행시키면 됩니다.
사용이 끝나면 close()를 이용해 연결을 닫아야 합니다.
위 코드를 실행한 결과는 아래와 같습니다.
저는 이미 데이터베이스에 707회차 까지의 데이터를 저장했기 때문에 707 이라는 숫자가 나왔지만, 이 글을 보고 따라하시는 분들은 아마 0이 나올 겁니다.
자 이제 지금까지 추출한 데이터를 이용해 데이터베이스에 저장하기만 하면 됩니다.
데이터베이스 저장은 일반적인 mysql 쿼리인 insert 구문을 이용하면 됩니다.
데이터베이스에 저장하는 코드를 추가하고 지금까지 만들어진 코드를 정리해보도록 하겠습니다.
먼저 웹 크롤링을 하는 부분과 데이터베이스 저장을 하는 부분을 함수로 구분했습니다.
웹 크롤링을 하는 부분에서는 다운로드 받은 HTML에서 필요한 데이터를 추출하여 리스트에 추가합니다.
웹 크롤링이 끝나면 리스트를 순회하면서 추출한 데이터를 하나씩 데이터베이스에 저장합니다.
데이터베이스 저장 중 에러 발생 시에는 rollback 하도록 에러 핸들링 부분을 추가하였습니다.
수정된 코드는 아래와 같습니다.
# lotto.py
import requests
from bs4 import BeautifulSoup
import MySQLdb
import sys
# 웹 크롤링 한 결과를 저장할 리스트
lotto_list = []
basic_url = "https://www.nlotto.co.kr/lotto645Confirm.do?method=byWin&drwNo="
def crawler():
for i in range(1, 707):
crawler_url = basic_url + str(i)
print("crawler: " + crawler_url)
resp = requests.get(crawler_url)
soup = BeautifulSoup(resp.text, "lxml")
line = str(soup.find("meta", {"id" : "desc", "name" : "description"})['content'])
begin = line.find("당첨번호")
begin = line.find(" ", begin) + 1
end = line.find(".", begin)
numbers = line[begin:end]
begin = line.find("총")
begin = line.find(" ", begin) + 1
end = line.find("명", begin)
persons = line[begin:end]
begin = line.find("당첨금액")
begin = line.find(" ", begin) + 1
end = line.find("원", begin)
amount = line[begin:end]
info = {}
info["회차"] = i
info["번호"] = numbers
info["당첨자"] = persons
info["금액"] = amount
lotto_list.append(info)
def insert():
db = MySQLdb.connect(host="localhost", user="lotto", passwd="lotto", db="lotto")
cursor = db.cursor()
for dic in lotto_list:
count = dic["회차"]
numbers = dic["번호"]
persons = dic["당첨자"]
amounts = dic["금액"]
print("insert to database at " + str(count))
numberlist = str(numbers).split(",")
sql = "INSERT INTO `lotto`. `data`(`count`, `1`, `2`, `3`, `4`, `5`, `6`, `7`, `persion`, `amount`) " \
"VALUES('%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%s')" \
% (count,
int(numberlist[0]),
int(numberlist[1]),
int(numberlist[2]),
int(numberlist[3]),
int(numberlist[4]),
int(numberlist[5].split("+")[0]),
int(numberlist[5].split("+")[1]),
int(persons),
str(amounts))
try:
cursor.execute(sql)
db.commit()
except:
print(sys.exc_info()[0])
db.rollback()
break
db.close()
def main():
crawler()
insert()
if __name__ == "__main__":
main()
해당 코드를 실행시키면 데이터베이스에 우리가 필요로 하는 데이터들이 무사히(?) 저장되었을 거라는 기대와는 달리 707회차 데이터가 빠져있는 것을 확인하실 수 있습니다.
for i in range(1, 707) 요 부분 때문에 실제로는 1부터 706까지만 웹 크롤링이 됩니다.
이 부분도 수정할겸 새로운 기능도 넣어볼까요?
현재까지 작성된 코드는 무식하게도 실행할 때마다 1회차부터의 결과를 다시 받아옵니다.
요즘처럼 바쁜 현대인이 귀중한 시간을 이렇게 낭비할 수는 없습니다.
그럼 어떻게 하면 될까요?
저는 로또 웹 사이트를 방문해서 가장 최신 회차와 데이터베이스에 저장된 가장 최신 회차를 비교하는 방식을 사용했습니다.
만약 데이터베이스에 저장된 회차보다 웹 사이트의 최신 회차가 더 크다면 해당 회차의 정보를 받아서 데이터베이스에 넣도록 합니다.
수정된 코드는 아래와 같습니다.
# lotto.py
import requests
from bs4 import BeautifulSoup
import MySQLdb
import sys
# 웹 크롤링 한 결과를 저장할 리스트
lotto_list = []
# 로또 웹 사이트의 첫 주소
main_url = "https://www.nlotto.co.kr/lotto645Confirm.do?method=byWin"
# 각 회차별 당첨정보를 알 수 있는 주소
basic_url = "https://www.nlotto.co.kr/lotto645Confirm.do?method=byWin&drwNo="
def getLast():
resp = requests.get(main_url)
soup = BeautifulSoup(resp.text, "lxml")
line = str(soup.find("meta", {"id" : "desc", "name" : "description"})['content'])
begin = line.find(" ")
end = line.find("회")
if begin == -1 or end == -1:
print("not found last lotto number")
exit()
return int(line[begin + 1 : end])
def checkLast():
db = MySQLdb.connect(host="localhost", user="lotto", passwd="lotto", db="lotto")
cursor = db.cursor()
sql = "SELECT MAX(count) FROM data"
try:
cursor.execute(sql)
result = cursor.fetchone()
except:
print(sys.exc_info()[0])
db.close()
return result[0]
def crawler(fromPos, toPos):
for i in range(fromPos + 1, toPos + 1):
crawler_url = basic_url + str(i)
print("crawler: " + crawler_url)
resp = requests.get(crawler_url)
soup = BeautifulSoup(resp.text, "lxml")
line = str(soup.find("meta", {"id" : "desc", "name" : "description"})['content'])
begin = line.find("당첨번호")
begin = line.find(" ", begin) + 1
end = line.find(".", begin)
numbers = line[begin:end]
begin = line.find("총")
begin = line.find(" ", begin) + 1
end = line.find("명", begin)
persons = line[begin:end]
begin = line.find("당첨금액")
begin = line.find(" ", begin) + 1
end = line.find("원", begin)
amount = line[begin:end]
info = {}
info["회차"] = i
info["번호"] = numbers
info["당첨자"] = persons
info["금액"] = amount
lotto_list.append(info)
def insert():
db = MySQLdb.connect(host="localhost", user="lotto", passwd="lotto", db="lotto")
cursor = db.cursor()
for dic in lotto_list:
count = dic["회차"]
numbers = dic["번호"]
persons = dic["당첨자"]
amounts = dic["금액"]
print("insert to database at " + str(count))
numberlist = str(numbers).split(",")
sql = "INSERT INTO `lotto`. `data`(`count`, `1`, `2`, `3`, `4`, `5`, `6`, `7`, `persion`, `amount`) " \
"VALUES('%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%s')" \
% (count,
int(numberlist[0]),
int(numberlist[1]),
int(numberlist[2]),
int(numberlist[3]),
int(numberlist[4]),
int(numberlist[5].split("+")[0]),
int(numberlist[5].split("+")[1]),
int(persons),
str(amounts))
try:
cursor.execute(sql)
db.commit()
except:
print(sys.exc_info()[0])
db.rollback()
break
db.close()
def main():
last = getLast()
dblast = checkLast()
if dblast < last:
print("최신 회차는 " + str(last) + " 회 이며, 데이터베이스에는 " + str(dblast) + "회 까지 저장되어 있습니다.")
print("업데이트를 시작합니다.")
crawler(dblast, last)
insert()
if __name__ == "__main__":
main()
위 코드를 실행하면 아래와 같이 최신 회차만 크롤링 한 후 데이터베이스에 저장하는 것을 볼 수 있습니다.
전체 소스코드는 아래에 첨부하였습니다.
이것으로 이번 포스트를 마치도록 하겠습니다.
다음 포스트에서는 이렇게 저장된 데이터를 이용해서 로또 사이트에서 제공하는 것과 같이 데이터를 분석하는 방법을 알아보도록 하겠습니다.
읽어주셔서 감사합니다.