본문 바로가기

게임/리그오브레전드 - Riot API

리그오브레전드 게임 데이터 수집기 만들기 [3화: 데이터 수집기_Riot-API 비사용 함수들]

 우선 사용한 모듈들입니다. 사용처에 대해서는 차차...

import requests
import json
import operator
import datetime
import time
from skimage import io # 미니맵 처리
from sklearn.preprocessing import MinMaxScaler
import matplotlib.pyplot as plt
import numpy as np
import pathlib
import smtplib # 메일을 보내기 위한 라이브러리 모듈
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.mime.application import MIMEApplication 
import smtplib
import imaplib
import poplib
import email
from email.mime.text import MIMEText
import random

 


 

 

api_key.txt에서 개발용 key를 가져오는 부분입니다. collector.py에 최신 key를 계속 전달하기 위한 기능으로 알고 넘어가도 무방합니다. 

#동일디렉터리에 있는 api_key.txt에서 api사용에 필요한 key 읽어옴
#프로그램 초기 실행시 or 실행도중 키 만료되서 교체해야될 때 쓰임
def getkey():
	f= open('api_key.txt','r')
	key=f.readline()[:-2]
	f.close()
	return key

#quit_sign: api에 요청멈춰야하는 경우 1로 바뀜
#처음 실행시 0
quit_sign = 0
#처음 실행시 메모장에 있는 key 가져옴
api_key=getkey()

 

 


 

 

외부에서 자료 수집용 컴퓨터의 키 교체시에 사용되는 메일 읽기 함수입니다.

 

get_key_from_mail() 함수의 sendto, user, password 변수를 여러분의 상황에 맞게 변경해주세요.

파이썬으로 구글 메일을 사용하기 위해서는 앱비밀번호를 발급받아야 합니다. 아래 링크를 참고해주세요.

yanoos.tistory.com/34?category=894933

 

#get_key_from_mail과 세트입니다. 메일을 읽들 때 사용됩니다.
#https://oceancoding.blogspot.com/2019/11/imap.html
def findEncodingInfo(txt):    
    info = email.header.decode_header(txt)
    s, encoding = info[0]
    return s, encoding

#집에 켜두는 자료수집용 컴퓨터의 key 교체에 사용됩니다. 제가 메일로 보내준 새 api-key로 기존 api-key를 변경합니다.
def get_key_from_mail():
    global api_key
    sendto = '받을이메일@gmail.com'
    user = '당신의이메일@gmail.com'
    password = "당신의앱비밀번호16자리"

    # 메일서버 로그인
    imapsrv = "smtp.gmail.com"
    #아래행 imap = imaplib.IMAP4_SSL('imap.gmail.com')에서 수정하니까 작동됨/원리는 모름/우연
    imap = imaplib.IMAP4_SSL(imapsrv, "993")
    id = user
    pw = password
    imap.login(id, pw)
     
    # 받은 편지함
    imap.select('inbox')
     
    # 받은 편지함 모든 메일 검색
    resp, data = imap.uid('search', None, 'All')
     
    # 여러 메일 읽기 (반복)
    all_email = data[0].split()
    all_email.reverse()
    #del(all_email[0])
    for mail in all_email:
     
        #fetch 명령을 통해서 메일 가져오기 (RFC822 Protocol)
        result, data = imap.uid('fetch', mail, '(RFC822)')
     
        #사람이 읽기 힘든 Raw 메세지 (byte)
        raw_email = data[0][1]
     
        #메시지 처리(email 모듈 활용)    
        email_message = email.message_from_bytes(raw_email)
     
        #이메일 정보 keys
        #print(email_message.keys())
        #print('FROM:', email_message['From'])
        #print('SENDER:', email_message['Sender'])
        #print('TO:', email_message['To'])
        #print('DATE:', email_message['Date'])
     
        b, encode = findEncodingInfo(email_message['Subject'])
        #제목
        #print('SUBJECT:', str(b, encode))
        subject = str(b, encode)
        if subject != 'riotapikey':
            #print(subject)
            return 0
        text = ''
        #이메일 본문 내용 확인
        #print('[CONTENT]')
        #print('='*80)
        if email_message.is_multipart():
            for part in email_message.get_payload():
                bytes = part.get_payload(decode = True)    
                encode = part.get_content_charset()        
                #print(str(bytes, encode))
                text=str(bytes, encode)
                break
        #print('='*80)
        break
     
    imap.close()
    imap.logout()
    f=open('api_key.txt','r')
    existing = f.readline()
    f.close()

    if existing!=text:
        f=open('api_key.txt','w')
        f.write(text)
        f.close()
        print(existing[:-2],'----->',text[:-2],'key 교체 완료')
        api_key = getkey()
        return 1
    return 0

 

 


 

 

데이터 수집 중 key가 만료되면 그 즉시 그때까지의 자료를 백업합니다. 여러분의 메일로 '롤 본컴 접속 끊김'이라는 내용의 메일이 발송되어 메일 알림이 설정되어 있으시다면 key가 만료되는 즉시 알아채실 수 있습니다. 앞의 get_key_from_mail()함수와 연계하여 key의 교체까지 외부에서 가능합니다.

 

send_final() 함수의 sendEmail, recvEmail, password 변수를 여러분의 상황에 맞게 변경해주세요.

위에도 말씀드렸듯, 파이썬으로 구글 메일을 사용하기 위해서는 앱비밀번호를 발급받아야 합니다.

아래 링크를 참고해주세요.

yanoos.tistory.com/34?category=894933

#수집한 데이터를 제 이메일로 백업합니다. 자료수집용 컴퓨터의 key가 만료된다면 저에게 이메일을 보내 key가 만료되었음을 알립니다.
#자료수집용 컴퓨터가 메일을 보내면 다시 제 메일로 새 api-key를 보내주고 get_key_from_mail이 그 메일을 읽어 key를 교체하는 방식입니다
#https://wikidocs.net/36465
def send_final():
	sendEmail = "당신의이메일@gmail.com"
	recvEmail = "백업파일을 받을 이메일"
	password = "당신의구글앱비밀번호16자리"

	smtpName = "smtp.gmail.com"
	smtpPort = 587

	#여러 MIME을 넣기위한 MIMEMultipart 객체 생성
	msg = MIMEMultipart()

	msg['Subject'] ="롤 본컴 접속 끊김"
	msg['From'] = sendEmail 
	msg['To'] = recvEmail 

	#본문 추가
	text = "."
	contentPart = MIMEText(text) #MIMEText(text , _charset = "utf8")
	msg.attach(contentPart) 

	#파일 추가
	etcFileName = 'new_before.json'
	with open(etcFileName, 'rb') as etcFD : 
	    etcPart = MIMEApplication( etcFD.read() )
	    #첨부파일의 정보를 헤더로 추가
	    etcPart.add_header('Content-Disposition','attachment', filename=etcFileName)
	    msg.attach(etcPart) 
	#파일 추가
	etcFileName = 'gameids.json'
	with open(etcFileName, 'rb') as etcFD : 
	    etcPart = MIMEApplication( etcFD.read() )
	    #첨부파일의 정보를 헤더로 추가
	    etcPart.add_header('Content-Disposition','attachment', filename=etcFileName)
	    msg.attach(etcPart) 

	etcFileName = 'backup_gameids.json'
	with open(etcFileName, 'rb') as etcFD : 
	    etcPart = MIMEApplication( etcFD.read() )
	    #첨부파일의 정보를 헤더로 추가
	    etcPart.add_header('Content-Disposition','attachment', filename=etcFileName)
	    msg.attach(etcPart) 

	etcFileName = 'backup_new_before.json'
	with open(etcFileName, 'rb') as etcFD : 
	    etcPart = MIMEApplication( etcFD.read() )
	    #첨부파일의 정보를 헤더로 추가
	    etcPart.add_header('Content-Disposition','attachment', filename=etcFileName)
	    msg.attach(etcPart) 

	s=smtplib.SMTP( smtpName , smtpPort )
	s.starttls()
	s.login( sendEmail , password ) 
	s.sendmail( sendEmail, recvEmail, msg.as_string() )  
	s.close()

 

 


 

 

 

데이터 저장과 읽기에 사용됩니다. 이 함수들 덕에 데이터를 계속 모을 수 있었습니다.

이 방법을 몰라서 한참 고생하다 찾아낸 기억이 납니다. 제가 가장 좋아하는 함수들입니다.

#아래 두 함수 load_json과 write_json은 딕셔너리, 리스트 같은 것들을 그 자체로 저장하고 읽을 수 있습니다.
#가령 dict_a={}를 텍스트'dict_a={}'가 아닌 dict_a={} 그 자체로 저장하고 읽을 수 있습니다.
#이걸 쓰면 큰 데이터들을 미리 모아두고 코드에서 읽어서 바로 딕셔너리로 사용할 수 있습니다
def load_json(filename):
	file = pathlib.Path(str(filename)+'.json')
	file_text = file.read_text(encoding='utf-8-sig')
	return json.loads(file_text)


def write_json(filename,data):
	with open(str(filename)+'.json','w',encoding='UTF-8-sig') as file:
		file.write(json.dumps(data,ensure_ascii=False))

 


 

riot API에 요청이 필요할때 사용했습니다. api를 사용하는 부분들을 표시해두면 편하기 때문에 만들었습니다.
url에 해당하는 자료를 요청하고 자주 발생하는 문제인 429(요청제한초과)나 403(key만료)의 처리를 합니다

#riot API에 요청이 필요할때 사용했습니다.
#url에 해당하는 자료를 요청하고 자주 발생하는 문제인 429(요청제한초과)나 403(key만료)의 처리를 합니다
def req_api(url):
	global quit_sign
	global api_key
	response = requests.get(url)
	
	#반응이 200이 아니면 뭔가 잘못된 것입니다.
	if str(response)!='<Response [200]>':
		if str(response) =='<Response [429]>':
			#time.sleep(600)
			print(response)
			time.sleep(600)
			return -1
		elif str(response)=='<Response [403]>':
			print('403이다!!!!!!!!!!!!!!!!!!!!!!!!!')
			now = datetime.datetime.now()
			print('403발생시각:',now)
			send_final()
			quit_sign=1
			return 0
		else:
			print(response)
			time.sleep(10)
			return 0
	#반응이 200이면 정상입니다.
	else:
		time.sleep(1.5)
		return response.json()

 

 


이상으로 Riot API를 사용하지 않는 함수들에 대해 알아보았습니다.

다음 글에서는 Riop API 사용법과 제 코드에서 Riot API를 사용한 함수들에 대해 알아보겠습니다.