구현 사례
Why? 왜 이걸 만들었는가
- 회사 동기가 '이런 기능을 해주는 프로그램이 있었으면 좋겠다' 고 해서 만들어 보았다.
- 본인은 환율을 유심히 들여다 보는 경우가 아니지만, 환율에 민감한 분들은 당일 하나은행의 1회,2회, 그리고 가장 마지막으로 업데이트된 매매기준율을 참고한다고 한다.
- '그냥 최신 매매기준율만 참고하면 되는데 왜 1,2회를 참고하는지 물어보니, 실제 당일의 매매기준율 추이를 결정하는 매우 중요한 Factor가 1,2회 매매기준율이라고 한다. (역시 모든 심리는 첫 거래에 다 반영이 되나 보다)
프로젝트 개요
언어 : Python
주요 패키지(Framework) :
- Python-Telegram-Bot - 대화형 로직을 쉽고 빠르게 짤 수 있는 툴이다 (Telegram API가 애초에 너무 좋긴 하다)
- Pandas : 데이터 전처리 필터링 수행
- Requests : 데이터 크롤링
작동 방식 :
- 유저가 매매기준율을 알려달라고 메시지를 보낸다
- 그 시점에서 하나은행 환율 조회 사이트의 html table를 데이터프레임으로 가져온다
- Pandas 전처리를 이용해서 1회,2회 그리고 최신 매매기준율을 뽑아낸 후, 유저에게 회신한다.
서버 : Raspberry Pi4 background에서 계속 실행 (Long Polling)
- 컨테이너를 AWS Lambda 위에서 올려서 구현하는 방식도 가능 (단, 이 경우 Webhook 방식을 이용해야 함)
구현 코드
import logging
import os
import pandas as pd
import requests
from telegram import Bot
from telegram.ext import Updater, CommandHandler
from get_ex import get_usd_ex
from datetime import datetime, timedelta
# Enable logging
# logging이 콘솔이랑 파일 형태로 모두 갈 수 있도록 설정 (with StreamHandler)
logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
level=logging.INFO,
handlers=[
logging.FileHandler(f"{os.path.splitext(os.path.basename(__file__))[0]}.log"),
logging.StreamHandler()])
logger = logging.getLogger(__name__)
TOKEN = 'mybot_token'
URL = f"https://api.telegram.org/bot{TOKEN}/"
bot = Bot(token=TOKEN)
# 데이터를 가져오는 함수
def get_usd_ex():
url5 = 'https://www.kebhana.com/cms/rate/wpfxd651_07i_01.do'
today_date = datetime.today().strftime('%Y%m%d')
today_string = datetime.today().strftime('%Y-%m-%d')
headers = {
'User=Agent':'Mozilla/5.0 (Macintosh; Intel Mac OS X 11_2_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.82 Safari/537.36',
'Content-type':'application/x-www-form-urlencoded; charset=UTF-8',
'Accept':'text/javascript, text/html, application/xml, text/xml, */*',
'Origin':'https://www.kebhana.com',
'Host':'www.kebhana.com',
'Referer':'https://www.kebhana.com/cms/rate/index.do?contentUrl=/cms/rate/wpfxd651_01i.do'}
data = f'ajax=true&inqType=0&curCd=USD&tmpPbldDvCd=1&inqDt={today_date}&inqDvCd=1&requestTarget=searchContentDiv'
response = requests.post(url=url5, headers=headers, data=data,allow_redirects=True)
## 2단계 pd.read_html를 통해서 특정 테이블을 df로 바로 가져오기
df = pd.read_html(response.text)[0]
df.columns = ['회차', '시간', '현찰_살때','현찰_팔때','송금_보낼때','송금_받을때','T/C','외화','매매기준율','직전대비','환가료율','미화환산율']
df = df[['회차','시간','매매기준율']]
first_value = df.loc[df['회차']== 1,'매매기준율'].values[0]
second_value = df.loc[df['회차']== 2,'매매기준율'].values[0]
last_value = df.loc[df['회차'] == df['회차'].max(),'매매기준율'].values[0]
last_num = df['회차'].max()
mylist = [first_value, second_value, last_value]
mymessage = f"기준일 : {today_string} 외화 : USD \n {mylist[0]} (1회) \n {mylist[1]} (2회) \n {mylist[2]} ({last_num}회, 최종)"
return mymessage
## 유저가 명령어를 입력하면 동작하는 함수 정의
def start(update, context):
text = "하나은행 환율 매매기준율 알리미 봇입니다. \n최근 매매기준율을 확인하려면 /fx를 입력해 주세요 \n기타 문의사항은 /admin 로 전달해주세요."
update.message.reply_text(text)
def get_hana(update, context):
mymessage = get_usd_ex()
update.message.reply_text(mymessage)
## 에러는 일괄 처리
def error(update, context):
"""Log Errors caused by Updates."""
logger.warning('Update "%s" caused error "%s"', update, context.error)
def main():
"""Start the bot."""
updater = Updater(TOKEN, use_context=True)
## 유저 Command와 함수의 연결
# Get the dispatcher to register handlers
dp = updater.dispatcher
dp.add_handler(CommandHandler("start", start))
dp.add_handler(CommandHandler("fx", get_hana, pass_args=True))
dp.add_error_handler(error)
updater.start_polling()
updater.idle()
if __name__ == '__main__':
main()
Comments
- 텔레그램 api에서 제공하는 date는 Unix Timestamp 형태인데, 한국 시간으로 바꿔주려면 9시간을 더해줘야 한다.
- 텔레그램 봇에 부여된 명령어를 사용하려면 빗금?(slash, /)를 쳐야 하는데, 유저 입장에서는 조금 불편하다. 다행히BotFather에서 Command List를 설정하면 유저 화면에 [/] 아이콘이 떠서 클릭만으로 명령어를 실행할 수 있다
- Command List 설정법 : BotFather와 대화 → /setcommands → 봇 선택 → command 입력
- Pandas의 read_html 정말 매우 강력한 툴이다. Requests 라이브러리와 적절히 결합하면 대부분의 데이터를 read_html로 효율적으로 가져올 수 있다.
'Projects > 업무-무역' 카테고리의 다른 글
신재생에너지 공급인증서(REC) 대시보드 (feat. RPS 제도) (0) | 2021.08.17 |
---|---|
[VBA] 파파고 API를 활용한 워드 번역기 (2) | 2021.06.17 |