개발

따라하는 python 로또 분석(3)

Jest 2016. 7. 4. 12:52



이 글은 python 초보자들을 대상으로 하는 로또 데이터 분석에 관련된 글입니다.

이전 포스트는 아래 링크를 따라가시기 바랍니다.


2016/06/21 - [개발] - 따라하는 python 로또 분석 (1)


2016/06/28 - [개발] - 따라하는 python 로또 분석(2)





이번 포스트부터는 드디어 제목에 맞게 로또 데이터를 분석해 보도록 하겠습니다.


첫 시간에는 각 숫자의 출현 빈도를 알아보도록 하겠습니다.


뭐, 이거는 컴퓨터 전공이라면 자료구조나 알고리즘 시간에 다들 배우셨을 텐데요,

굳이 설명할 필요가 있나 싶지만 혹시 또 모르니까 진행해 보도록 하겠습니다.


숫자의 출현 빈도를 어떻게 계산할 수 있을까요?


리스트 또는 배열을 이용할 수 있는데요, 간단한 배열을 이용한 방식을 사용해 보도록 하겠습니다.


먼저 1부터 45까지의 배열을 생성합니다.

그리고 데이터베이스에 저장된 각 회차별 당첨번호들을 하나씩 순회하면서 해당 숫자의 배열의 카운트를 하나씩 증가시키면 됩니다.



배열 초기화

인덱스       ->          1    2   3   4   ....   45

데이터       ->        | 0 | 0 | 0 | 0 | .... | 0 |



최초의 배열은 위와 같이 모두 0으로 초기화 되어 있습니다.

만약 보너스 번호를 포함한 1회차 당첨 번호가 1, 11, 21, 31, 41,  2, 12 라면,

배열[1] = 배열[1]++, 배열[11] = 배열[11]++... 이런 식으로 증가시켜 주면 됩니다.



요 부분을 코드에 추가시켜 보겠습니다.

수정된 코드는 아래와 같습니다.



# 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])

	cursor.close()
	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

	cursor.close()
	db.close()

def analysis(myarray):
	db = MySQLdb.connect(host="localhost", user="lotto", passwd="lotto", db="lotto")
	cursor = db.cursor()

	# 처음 뽑힌 숫자들 전체를 조회
	sql = "select `1` from data"
    
	try:
		cursor.execute(sql)
		results = cursor.fetchall()

		# 해당 숫자의 뽑힌 횟수를 하나씩 증가
		for row in results:
			i = row[0]
			count = myarray[i]
			myarray[i] = count + 1
	except:
		print(sys.exc_info()[0])
		
	cursor.close()
	db.close()

def main():
	last = getLast()
	dblast = checkLast()

	if dblast < last:
		print("최신 회차는 " + str(last) + " 회 이며, 데이터베이스에는 " + str(dblast) + "회 까지 저장되어 있습니다.")
		print("업데이트를 시작합니다.")
		crawler(dblast, last)
		
	insert()

	# 0부터 45까지의 배열을 생성하고 0으로 초기화
	myarray = [0 for i in range(46)]
	analysis(myarray)

	for i in range(1, len(myarray)):
		if (i % 10) == 0:
			print("")
		print("[" + str(i) + ":" + str(myarray[i]) + "]", end=" ")
	print()

		
if __name__ == "__main__":
    main()


anaysis() 함수가 추가되었습니다.


myarray라는 0부터 45까지의 배열을 생성하여 0으로 초기화 한 후 analysis 함수에 전달합니다.



위 코드를 실행하면 아래와 같은 결과가 나옵니다.




로또 공식 사이트에서 제공하는 당첨 번호는 정렬된 번호가 아니라 뽑힌 순서별로 되어 있습니다. 


그러므로 위의 데이터 분석 결과를 보면,

매 회차 로또에서 첫 번째로 뽑히는 숫자의 출현 빈도는 1이 105번으로 가장 많습니다.

두 번째는 2가 73회의 출현 빈도를 가졌습니다.



이번에는 위를 모든 순서에 적용해서 가장 많이 출현한 숫자대로 정렬을 해 보겠습니다.

즉, 첫 번째 추첨에서 가장 많이 나온 숫자부터 보너스 숫자 추첨에서 가장 많이 나온 숫자까지를 뽑아보도록 하겠습니다.


analysis() 함수에 for 루프만 살짝 추가하면 됩니다. ^^

이렇게 수정된 코드는 아래와 같습니다.


# 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])

    cursor.close()
    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

    cursor.close()
    db.close()


def analysis():
    db = MySQLdb.connect(host="localhost", user="lotto", passwd="lotto", db="lotto")
    cursor = db.cursor()

    for i in range(1, 8):

        sql = "select `"
        sql += str(i)
        sql += "` from data"

        try:
            cursor.execute(sql)
            results = cursor.fetchall()

            # 해당 숫자의 뽑힌 횟수를 하나씩 증가
            myarray = [0 for i in range(46)]
            for row in results:
                k = row[0]
                count = myarray[k]
                myarray[k] = count + 1
            print(i, myarray.index(max(myarray)))
        except:
            print(sys.exc_info()[0])

    cursor.close()
    db.close()


def main():
    last = getLast()
    dblast = checkLast()

    if dblast < last:
        print("최신 회차는 " + str(last) + " 회 이며, 데이터베이스에는 " + str(dblast) + "회 까지 저장되어 있습니다.")
        print("업데이트를 시작합니다.")
        crawler(dblast, last)

    insert()
    analysis()

if __name__ == "__main__":
    main()



위 코드를 실행하면 아래와 같은 결과를 볼 수 있습니다.




로또 추첨 708회차가 진행되는 동안 첫 번째 자리에 가장 많이 나온 번호는 1, 두 번째 자리에 가장 많이 나온 번호는 8, 세 번째는 20, 순서대로 27, 38, 45 였고, 보너스 번호는 43이 가장 많이 나왔습니다.



전체 소스 코드는 아래에 첨부하였습니다.

lotto.py




이것으로 이번 포스트를 마치도록 하겠습니다.


다음 포스트에서는 조금 더 다양한 방법으로 로또 데이터 분석을 시도해 보도록 하겠습니다.



읽어 주셔서 감사합니다.