본문으로 바로가기
728x90
반응형

이전글보기

[정보/Discord Bot] - [discord.py 2.0] 13. Modal 기능으로 사용자입력창 띄우기

[정보/Discord Bot] - [discord.py 2.0] 14. 오류 발생 시 예외 처리하기

[정보/Discord Bot] - [discord.py 2.0] 15. 로또복권 정보 출력하기 (beautifulsoup4 library) - #1 사전 library 준비

[정보/Discord Bot] - [discord.py 2.0] 15. 로또복권 정보 출력하기 (beautifulsoup4 library) - #2 최신 로또 번호 파싱하기

[정보/Discord Bot] - [discord.py 2.0] 15. 로또복권 정보 출력하기 - #3 모든 당첨내역 다운로드하기


저번 포스팅에서 모든 당첨번호의 리스트를 다운받았었죠 !

이번엔 다운받았던 리스트를 파싱하여 사용하기 편하도록 가공하도록 해보겠습니다.

 

다운받았던 lottery****.xls 을 열어보시면 아래처럼 엑셀로 열리지만 실상은

Insert title here
회차별 추첨결과
년도 회차 추첨일 1등 2등 3등 4등 5등 당첨번호
당첨자수 당첨금액 당첨자수 당첨금액 당첨자수 당첨금액 당첨자수 당첨금액 당첨자수 당첨금액 1 2 3 4 5 6 보너스
2022 1033 2022.09.17 13 1,913,414,943원 79 52,477,625원 3,083 1,344,708원 145,505 50,000원 2,376,004 5,000원 3 11 15 20 35 44 10
1032 2022.09.10 10 2,675,257,538원 90 49,541,807원 3,078 1,448,591원 149,807 50,000원 2,458,611 5,000원 1 6 12 19 36 42 28
1031 2022.09.03 8 3,213,957,563원 64 66,957,450원 2,789 1,536,493원 132,815 50,000원 2,247,015 5,000원 6 7 22 32 35 36 19
1030 2022.08.27 19 1,276,406,507원 104 38,864,942원 3,278 1,233,055원 154,929 50,000원 2,473,576 5,000원 2 5 11 17 24 29 9
1029 2022.08.20 10 2,527,848,450원 62 67,952,916원 2,614 1,611,738원 125,573 50,000원 2,136,972 5,000원 12 30 32 37 39 41 24

이렇게 html로 구성되어있는 파일입니다.

물론 윈도우에서 엑셀로 열리긴 해도 python에서는 엑셀을 파싱하는 라이브러리를 통한 파싱을 지원하지는 않더라구요..

 

이번에도 추가되는 코드는 비슷합니다 Lottery(commands.Cog) 에 동작호출을 위한 명령어 추가 -> class LotteryFunction 에 동작기능 추가 순이죠.

LotteryFunction 에 들어갈 함수부터 보겠습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
    # 다운받은 리스트를 통해 계산
    def calculate_list(self):
        if os.path.isfile(self.listFilePath):
            for idx in range(146):
                self.mCounterList[str(idx)] = 0  # 리스트 초기화
            list_xml = open(self.listFilePath, encoding='cp949')  # 저장된 list 불러오기
            soup = BeautifulSoup(list_xml, 'html.parser')  # 데이터 파싱
            total_info = soup.findAll('tr')
            default_row = 3   # 로또 list 에서 추첨 번호가 시작 하는 행
            for row in range(default_row, len(total_info)):
                row_data = total_info[row].findAll('td')
                max_col = len(row_data)
                for col in range(max_col-7, max_col):
                    # 각 숫자가 뽑힌 횟수 계산
                    self.mCounterList[row_data[col].text] += 1
        else:
            self.download_total()
            self.calculate_list()
cs

 

    def calculate_list(self):

2 : 계산기능이 포함된 함수를 정의합니다.

 

        if os.path.isfile(self.listFilePath):

3 : 이전 포스팅에서 작성한 모든 당첨내역 다운로드 기능을 통해 다운로드받은 파일이 있는 경우에 대한 조건문입니다.
만약 존재하지 않을경우, 16~18 line 에서 다시 다운로드 받고, 함수를 재호출합니다

 

            for idx in range(1, 46):
                self.mCounterList[str(idx)] = 0  # 리스트 초기화

4~5 : 멤버변수인 mCounterList 를 초기화합니다.
여기서 mCounterList의 자료형은 Dictinary 로, "로또번호":당첨횟수 쌍으로 구성하려고 합니다.

 

            list_xml = open(self.listFilePath, encoding='cp949')  # 저장된 list 불러오기

6 : 저장된 list 파일을 list_xml 변수에 open 합니다. 파일 내부에 한글이 포함되어있기 때문에 open시에  encoding='cp949'를 추가해줘야합니다.

 

            soup = BeautifulSoup(list_xml, 'html.parser')  # 데이터 파싱
            total_info = soup.findAll('tr')
            default_row = 3   # 로또 list 에서 추첨 번호가 시작 하는 행

7 : 6line 에서 open 한 파일을 BeatifulSoup 라이브러리의 html.parser 를 통해 soup 객체에 저장합니다.

8 : 저장된 soup 객체에서 태그가 "tr" 즉, 행 단위로 나누어 total_info 변수에 저장합니다.

9 : 맨 위에 첨부된 자료형태를 보자면 저희가 파싱해야 하는 정보(로또번호)는 4번째행(0-1-2-3) 부터 시작하는것을 알 수 있으므로 미리 보기 편하게 변수로 저장해놓았습니다.

 

            for row in range(default_row, len(total_info)):
                row_data = total_info[row].findAll('td')
                max_col = len(row_data)
                for col in range(max_col-7, max_col):
                    # 각 숫자가 뽑힌 횟수 계산
                    self.mCounterList[row_data[col].text] += 1

10 : 파싱해야 하는 첫행(default_row) 부터 자료의 맨 마지막 행(len(total_info)) 까지 행번호(row)를 반복하는 출력문입니다.

11 : 전체행(total_info)에서 반복하는 행(row)에 대한 정보(total_info[row]) 에서 모든 열 정보('td')를 찾아 row_data로 저장합니다.

12 : 저장된 열의 갯수(len(row_data)) 를 확인하여 max_col 변수에 저장합니다.

13 : 파싱해야하는 열의 열번호를 반복하는 출력문입니다
로또의 번호가 해당 행의 마지막부터 7번째까지에 저장되어 있으므로 range(max_col-7, max_col) 로 세팅합니다.

15 : 각 행,열 에 당첨번호를 key 로 하여 해당하는 mCounterList에 +1 을 해줍니다.

 

전체 동작 소스코드는 다음과 같습니다

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
import os
import glob
import discord
from discord.ext import commands
from bs4 import BeautifulSoup
import urllib.request
class LotteryFunction:
    def __init__(self):
        self.lastGameCount = None  # 1033
        self.lastGameDate = None  # (2022년 09월 17일 추첨)
        self.lastGameNumbers = []
        self.parse_newest()
        self.mCounterList = {}  # 로또 숫자 별 추첨 횟수
        self.listFilePath = f'./lottery{self.lastGameCount}.xls'
    # 현재 회차 정보 파싱
    def parse_newest(self):
        self.lastGameNumbers.clear()
        url = 'https://dhlottery.co.kr/gameResult.do?method=byWin'
        webpage = urllib.request.urlopen(url)
        soup = BeautifulSoup(webpage, 'html.parser')
        self.lastGameCount = soup.select_one('div.win_result > h4 > strong').get_text().split("회")[0]
        self.lastGameDate = soup.select_one('div.win_result > p').get_text()
        numberList = soup.select('div.win_result > div > .num > p .ball_645')
        for number in numberList:
            self.lastGameNumbers.append(number.get_text())
    def delete_list(self, filename: str):
        for name in glob.glob(filename):
            print(name + "deleted")
            os.remove(name)
    # 모든 추첨 번호 다운 로드
    def download_total(self):
        if os.path.isfile(self.listFilePath):
            print("lottery list exist")
        else:
            self.delete_list('*[0-9].xls')
            start_count = 1
            url = f'https://dhlottery.co.kr/gameResult.do?method=allWinExel&gubun=byWin&nowPage=1&' \
                  f'drwNoStart={start_count}&drwNoEnd={self.lastGameCount}'
            urllib.request.urlretrieve(url, self.listFilePath)
    # 다운받은 리스트를 통해 계산
    def calculate_list(self):
        if os.path.isfile(self.listFilePath):
            for idx in range(146):
                self.mCounterList[str(idx)] = 0  # 리스트 초기화
            default_row = 3   # 로또 list 에서 추첨 번호가 시작 하는 행
            list_xml = open(self.listFilePath, encoding='cp949')  # 저장된 list 불러오기
            soup = BeautifulSoup(list_xml, 'html.parser')  # 데이터 파싱
            total_info = soup.findAll('tr')
            for row in range(default_row, len(total_info)):
                row_data = total_info[row].findAll('td')
                max_col = len(row_data)
                for col in range(max_col-7, max_col):
                    # 각 숫자가 뽑힌 횟수 계산
                    self.mCounterList[row_data[col].text] += 1
        else:
            self.download_total()
            self.calculate_list()
class Lottery(commands.Cog):
    def __init__(self, app):
        self.app = app
        self.lottery = LotteryFunction()
    @commands.command(name="로또")
    async def lottery(self, ctx):
        self.lottery.parse_newest()
        embed = discord.Embed(title="로또 추첨 번호", description="최근 로또 번호를 출력합니다", colour=0xffffff)
        embed.set_thumbnail(url="https://dhlottery.co.kr/images/layout/logo-header.png")
        # 데이터로부터 추첨날짜 확인
        embed.add_field(name="추첨날짜", value=self.lottery.lastGameDate, inline=True)
        # 데이터로부터 회차 확인
        embed.add_field(name="회차", value=self.lottery.lastGameCount+"회", inline=True)
        embed.add_field(name="추첨번호", value=self.lottery.lastGameNumbers, inline=False)
        await ctx.send(embed=embed)
    @commands.command(name="다운")
    async def download_list(self, ctx):
        msg = await ctx.send("다운로드 시작")
        self.lottery.download_total()
        await ctx.message.delete()
        await msg.delete()
        await ctx.send("다운로드 완료", delete_after=5)
    @commands.command(name="계산")
    async def calc_list(self, ctx):
        msg = await ctx.send("계산시작")
        self.lottery.calculate_list()
        await msg.delete()
        embed = discord.Embed(title="로또 당첨번호 내역", description="번호별 당첨 횟수를 출력합니다")
        embed.add_field(name="내역", value=self.lottery.mCounterList, inline=True)
        await ctx.send("계산완료", embed=embed)
async def setup(app):
    await app.add_cog(Lottery(app))
 
cs

 

Cogs 에 추가된 "!계산" 명령어는 임시로 확인하기 위한 명령어 이기도 하고..

그저 이전에 사용해오던 코드의 복붙이나 다름없으니 설명은 생략하도록 할게요 

 

명령어 사용시 결과는 다음과 같습니다

 

동행복권 사이트를 통해 얻어온 정보 중 출력할만한것은 다 출력하였으니 `15.로또복권 정보 출력하기` 챕터는 여기까지만 이어가도록 할게요 !


포스팅에 사용된 모든 소스코드는 아래 Github에서 확인하실 수 있습니다.

https://github.com/aochfl/ChoRi_TestBot

 

GitHub - aochfl/ChoRi_TestBot

Contribute to aochfl/ChoRi_TestBot development by creating an account on GitHub.

github.com


참고자료

https://discord.com/developers/docs/reference - ( Discord Developer API )

https://discordpy.readthedocs.io/en/latest/index.html  -  ( discord.py library 문서 )

 

728x90
반응형