0. 목표
보안을 위해
docker image에 api key 값을 저장한 뒤 이미지를 생성하는 방식을 docker container 를 생성할 때 키를 환경 변수로 입력 받는 방식으로 바꾸려고 한다.
1. python code 수정
다음 코드에서 import sys를 추가하고 getStockInfo 함수만을 수정 할 것 이다.
import requests
import pandas as pd
import time
from sqlalchemy import create_engine,text
from datetime import datetime, timedelta
from bs4 import BeautifulSoup as bs
def getStockCode():
"""
현재 있는 모든 종목의 종목명 , isin Code , 종목 코드를 크롤링 하여 가지고 Json형식으로 반환하는 함수
request와 BeautifulSoup4를 사용하였다.
"""
# requests를 이용하여 해당 페이지를 가지고 온다.
page = requests.get("https://www.ktb.co.kr/trading/popup/itemPop.jspx")
# BeautifulSoup4를 이용하여 해당 페이지의 html 내용을 전부 가지고 온다.
soup = bs(page.text ,"html.parser")
# 그 중 tbody.tbody_content 중 tr 태그 중 td 중 a 태그 안에 있는 데이터를 전부 가지도 온다.
elements = soup.select("tbody.tbody_content tr td a")
stockCodeJson = {}
for e in elements:
# 필요한 데이터만 추출 하여 dict 형식으로 저장한다.
data = str(e).split(",")
code , codeName, isincode = data[1][1:-1] , data[2][1:-1].strip(), data[-1][1:13]
stockCodeJson[code] = (codeName,isincode)
return stockCodeJson
def saveStockCode(stockCodeJson:dict ,db_connection):
"""
json 변수와 sqlalchemy의 create_engine으로 생성한 객체를 인수로 받아서 Json데이터를 MySql에 저장하는 함수
"""
# 데이터를 넣기 편하게 Pandas DataFrame 으로 변환
stockCodeDf = pd.DataFrame(stockCodeJson).T.reset_index()
# 컬럼명 설정
stockCodeDf.columns = ['srtnCd','itmsNm','isinCd']
# 과거 종목 정보 데이터 버리기
with db_connection.connect() as conn:
conn.execute(text('TRUNCATE TABLE stock_code;'))
conn.commit()
# 데이터 Mysql에 넣기
stockCodeDf.to_sql(name='stock_code', con=db_connection, if_exists='append',index=False)
def getStockInfo(db_connection):
## 날짜 가져오기
last_day = getLastDay(db_connection)
# request data to API
headers = {'Content-Type': 'application/json', 'charset': 'UTF-8', 'Accept': '*/*'}
# for school
# key_path = "/Users/c05/Desktop/learn/web/PipeLine_Project/key.txt"
# for home
# key_path = "C:\\Users\\AW17R4\\.appkey\\open_stock_api_key.txt"
# for ec2
key_path = "./key.txt"
url = "https://apis.data.go.kr/1160100/service/GetStockSecuritiesInfoService/getStockPriceInfo"
with open(key_path,'r',encoding="UTF-8") as f:
key = f.readline()
params = {'serviceKey' : key
, 'numOfRows' : 10000
, 'pageNo' : 1
, 'resultType' : "json"
, 'beginBasDt' : last_day
}
response = requests.get(url ,params=params)
total_count = response.json()['response']['body']['totalCount']
if total_count == 0 :
print("Nothing to Update")
return 0
df = pd.DataFrame(response.json()['response']['body']['items']['item'])
df.to_sql(name='stockinfotable', con=db_connection, if_exists='append',index=False)
if total_count > params['numOfRows']:
for i in range(2,total_count//10000+1):
params['pageNo'] = i
errorCount = 1
while True:
try:
response = requests.get(url ,params=params,verify=True)
temp_df = pd.DataFrame(response.json()['response']['body']['items']['item'])
temp_df.to_sql(name='stockinfotable', con=db_connection, if_exists='append',index=False)
break
except Exception:
time.sleep(5)
print(f'error:{errorCount}')
if errorCount==10:
print(f"Error: {temp_df}")
break
continue
time.sleep(0.5)
def getDatabaseConnection():
# connect to MySql DataBase
# db_connection_str = 'mysql+pymysql://root:@localhost/stockDB'
# for home
# db_connection_str = 'mysql+pymysql://root:1234@localhost/stock_db'
# for ec2
db_connection_str = 'mysql+pymysql://stock_user:1234asde@172.31.13.248/stockDB'
db_connection = create_engine(db_connection_str)
conn = db_connection.connect()
return db_connection ,conn
def getLastDay(db_connection):
with db_connection.connect() as conn:
last_day = pd.read_sql_query(text("SELECT basDt FROM stockDB.stockinfotable ORDER BY basDt DESC LIMIT 1;"), conn)
start_day = (last_day + timedelta(days=1))['basDt'][0]
result = str(start_day.date()).replace("-","")
return result
if __name__ == "__main__":
db_connection,conn = getDatabaseConnection()
getStockInfo(db_connection)
stockCodeJson = getStockCode()
saveStockCode(stockCodeJson,db_connection)
conn.close()
# sys.argv의 첫번째 값은 path/파일명 다음 값부터는 입력받은 인수값이다.
## ex) python test.py ho 에서의 sys.argv[1] 는 test.py이고 sys.argv[1] 는 "ho"이다.
# 다음의 코드를 getStockInfo에 추가
if len(sys.argv) != 2:
print(sys.argv)
print("키값이 입력이 되지 않았습니다.")
sys.exit()
key = sys.argv[1]
if key == "test":
print(sys.argv)
print("키값이 제대로 입력되지 않았습니다.")
sys.exit()
2. docker file 수정
ENV 를 추가해 주었다..
# docker file
#사용할 베이스 도커 이미지
FROM python:3.9.18
#API KEY 값
## 임의의 KEY값을 넣은 후에 container run 할 때 새로운 값을 입력 받는다.
ENV KEY test
# 해당 디렉토리로 이동
## copy . . 과 같이 쓰면 : 이미지를 빌드한 디렉터리의 모든 파일을 컨테이너의 /usr/src/app 디렉터리로 복사
WORKDIR /usr/src/app
COPY . .
# python pip 설치 후 필수 라이브러리 설치
RUN python -m pip install --upgrade pip
RUN pip install -r requirements.txt
CMD python daily.py ${KEY}
3. 전역 환경 변수 추가(옵션)
.bashrc 에 마지막 줄에
export API_KEY=`cat $HOME/apikey/key.txt`
을 추가해주었다.
그 후 source ~/.bashrc 명령어를 통해 바뀐 내용을 적용하였다.
4. docker container 생성
# container 명은 stock-data , --env 옵션을 통해 KEY값을 다시 지정해준다.
docker container run --name stock-data --env KEY=$API_KEY stock-data