오늘 배워 오늘 쓰는 OpenAPI/프로젝트

[Python] 주식 분석 보고서 만들기 (자동화)

ai-creator 2020. 6. 13. 13:31
반응형

오.오.쓰 에 오신 것을 환영합니다.

[주식 분석 보고서 자동화 프로젝트] 입니다.

아래와 같은 순서로 배워보겠습니다.

 

1. 학습 목표

2. 사전 준비

3. 사전 지식 쌓기

4. 구현

5. 요약정리

 


1. 학습 목표

요즘은 재테크가 선택이 아닌 필수 입니다. 금리가 낮을수록 주식으로 재테크를 하고자 하는 분들이 많이 늘어납니다. 그래서 그런지 퇴근후에 재테크 공부하시는 분들도 많은것 같습니다.
그런데, 주식의 경우는 장이 열리고, 끝나는 시간이 오전 9시~15시30분인데요. 회사에서 집중적으로 업무를 하는 시간이기도 하고, 학생들은 한창 수업이 있는 시간입니다. 또 어떤 경우는 회사는 주식 관련 사이트를 접근 차단을 해 두어, 주식 정보를 보기가 참 어렵죠.

 

그래서 이런 불편점을 해소가 위해서 주식정보를 받아 분석 보고서를 만들고, 이메일로 전송해주면 어떨까? 하는 생각이들었습니다. 이번 프로젝트는 주식 일별 시세 정보를 보고서로 만들어서 이메일로 전송해 주는 주식 분석 보고서 자동화 프로젝트를 준비했습니다.

 

 

[구현 순서]

주식 분석 보고서 자동화 프로젝트를 위한 구현 순서는 아래와 같습니다.

주식 종목 코드를 사용하여, 일별 시세를 크롤링합니다. 크롤링한 자료로 간단한 차트 그리고, 파일로 저장합니다. 이 차트를 사용하여 분석보고서를 만듭니다. 마지막으로 분석 보고자료를 이메일로 전송합니다.

[그림 1]  주식 분석 보고서 자동화 프로젝트 구현 순서

 

 

[최종 결과]

전송된 이메일입니다. 주식 분석 보고서 (stock_report.pptx)가 잘 첨부되어 있습니다.

[그림 2] 주식 분석 보고서 메일 내용 상세

 

 

[언어 & 환경(IDE)]

Python 3.7 & Jupter notebook

 

[프로젝트 리소스 및 소스 코드]

- res/stock_report : 동작 확인을 위한 리스소 파일이 위치한 폴더

- stock_report.ipynb : 구현된 소스 코드

 

> 접속 URL :  drive.google.com/drive/folders/10KXyPrJ_KEesvAvjnMpx1DS_md7gaN6H?usp=sharing

 

ai-creator 공유폴더 - Google 드라이브

 

drive.google.com

 

※ 프로젝트에 필요한 리소스 및 소스코드의 경로 구성은 자유롭게 설정하면 됩니다. 그러나, 아래 설명되어 있는 코드 구현이 제시된 구성으로 되어 있으니, 동일하게 경로와 파일명으로 구성해 둔다면 별다른 코드 수정 없이 실행해 볼 수 있습니다.

 

2. 사전 준비

주식 분석 보고서 자동화 프로젝트를 위해서 세가지 사전 준비가 필요합니다.

 

1) 보고서 자동화 프로젝트

'보고서 자동화 프로젝트 (Link)'를 수행해 보셨다면, 쉽게 이해할 수 있습니다.

 

2) 이메일 전송 자동화 프로젝트

'이메일 전송 자동화 프로젝트(Link)'를 수행해 보세요. 

이번 프로젝트는 사전에 배운 지식을 두가지나 활용합니다. 융합프로젝트답죠? 필요한 사전 준비가 필요하므로, 아직 보지 않으셨다면, 한번 수행해보세요.

 

3) 라이브러리 설치하기

다음은 설치가 필요한 라이브러리 목록입니다.

$ pip install matplotlib
$ pip install pandas

matplotlib 라이브러리는 그래프를 그리는데 도움을 주는 라이브러리 입니다. 

[그림 3] matplotlib에서 지원하는 그래프 예시

 

> 접속 URL : matplotlib.org/ 

해당 url로 접속해 보면, 다양한 종류의 그래프를 볼 수 있으며 마음에 드는 그래프를 클릭하면 소스코드들도 제공하고 있는 유용한 사이트라고 생각이 들겁니다.

 

pandas 라이브러리는 데이터분석용에 특화된 라이브러리 입니다. 이번 장에서는 주식데이터를 크롤링하고, 주식 데이터를 예쁘게 정제할 때 사용합니다. 

3. 사전 지식 쌓기

주식 분석 보고서 자동화 프로젝트 진행에 앞서 지식을 쌓을 내용은 다음과 같습니다.

 

3-1) 종목 코드 가져오기

3-2) 일별 시세 가져오기

 

3-1)  종목 코드 가져오기

특정 회사의 일별시세를 가져오고 싶다면, 제일 먼저 알아야 하는 것이 '종목 코드'입니다.

다음 그림은 '네이버 금융'에서 '삼성전자'를 검색한 결과 인데요,

삼성전자 글자 옆에 '005930'이라는 6자리 숫자값과, url에서 보면 code= 옆에 6자리 숫자가 동일합니다.

[그림 4] 종목 코드 

주식 종목코드는 상장사의 식별코드인데요, 마치 사람에게 있어서는 주민번호랑 동일합니다. 그러므로, 회사별 종목 코드를 알아야 프로그램으로 일별 시세를 가지고 오기 편하겠죠.

 

이러한 종목코드는 공개되어 있고, 한국거래소에서 다운로드 받을 수 있습니다.

> 한국 거래소 종목 코드 다운로드 : kind.krx.co.kr/corpgeneral/corpList.do?method=download

해당 URL을 접속하면 '상장법인목록.xls' 파일이 다운로드 되는데요. 이 파일을 열어보면 다음과 같습니다.

 

원하는 종목코드 값 뿐만이 아니라, 업종, 대표자명, 지역까지 정보가 들어가 있네요. 여기서 '회사명'과 '종목코드'를 사용할 것입니다.

[그림 5] '상장법인목록.xls' 파일

코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import pandas as pd
 
def get_stock_code():
    # 종목코드 다운로드 
    stock_code = pd.read_html('http://kind.krx.co.kr/corpgeneral/corpList.do?method=download', header=0)[0] 
 
    # 필요없는 column들은 제외
    stock_code = stock_code[['회사명', '종목코드']] 
 
    # 한글 컬럼명을 영어로 변경 
    stock_code = stock_code.rename(columns={'회사명': 'company', '종목코드': 'code'}) 
 
    # 종목코드가 6자리이기 때문에 6자리를 맞춰주기 위해 설정해줌 
    stock_code.code = stock_code.code.map('{:06d}'.format) 
    
    return stock_code
cs

실행 결과

get_stock_code()를 호출하면, 회사명(company)와 종목코드(code)만 있는 데이터 프로그램이 출력됩니다.

 

[그림 6] get_stock_code() 호출 결과

 

코드 설명

1라인 : 필요한 라이브러리를 import 합니다.

3~22라인 :  종목코드를 가지고와서 필요한 정보만 추출하는 함수를 정의합니다.  5라인에서 종목 코드를 다운로드 받습니다.   url은 한국거래소에서 상장법인목록을 엑셀로 다운로드하는 링크입니다. 다운로드를 받고, pandas에서 관리하는 data frame 자료구조로 변경시킵니다.  8라인은 사용할 컬럼만 추출합니다. [그림 5] '상장법인목록.xls' 파일에서 보이는 많은 컬럼 중에 "회사명"과 "종목코드" 만  사용할 것입니다. 11라인은 컬럼명을 한글에서 영어로 변경합니다. 코딩을 편하게 하기 위해서 변경한 것이고, 특별한 이유는 없습니다.  14라인에서는 종목 코드는 6자리를 의미하고, 부족한 숫자는 앞에 0으로 채워져 있습니다. 이 형태에 맞춰서 변경을 시켰습니다. 

3-2)  일별 시세 가져오기

특정 회사의 일별 시세 정보를 가져와 보겠습니다. 네이버 금융에서 크롤링합니다.

[그림 4] 종목 코드에서 네이버 금융의 URL 구조를 보면, code=종목코드 6자리로 설정해주어야 합니다.

일별시세 URL은 finance.naver.com/item/sise_day.nhn?code={종목코드} 라는 것을 알 수 있습니다.

 

그리고, 한 페이지당 2주치의 정보만 보여주고 있는데요, 더 많은 정보를 보려면 아래 숫자를 클릭하면 됩니다. 클릭하면서 변화하는 URL을 관찰해보세요. page={페이지숫자}로

변경됨을 알 수 있습니다.

[그림 7] 네이버 금융 URL 구조

이제 느낌이 오시죠? code={종목코드}&page={페이지숫자} 로 URL을 구성해서 크롤링하면 됩니다. 종목코드는 3-1) 에서 가지고 정보를 사용하면 되겠죠?

(정리)

- 종목 검색 : https://finance.naver.com/item/sise_day.nhn?code={종목코드}

- 일별 시세 구조 : https://finance.naver.com/item/sise_day.nhn?code={종목코드}&page={페이지번호}

 

코드

1
2
3
4
5
6
7
8
9
10
11
12
13
import pandas as pd
 
def get_stock(code):
    df = pd.DataFrame()
    for page in range(1,21):
        # 일별 시세 url  
        url = 'http://finance.naver.com/item/sise_day.nhn?code={code}'.format(code=code)     
        url = '{url}&page={page}'.format(url=url, page=page)
        print(url)
        current_df = pd.read_html(url, header=0)[0]
        df = df.append(current_df, ignore_index=True)
        
    return df
cs

 

실행 결과

get_stock() 호출해보세요.

code = '005930' # 삼성전자 종목코드
df = get_stock(code)
df.head()

 

[그림 8] 일별 시세 함수 호출 결과

코드 설명

1라인 : 필요한 라이브러리를 import합니다.

3~12라인 : 일별 시세를 가져오는 함수를 정의합니다. 종목코드를 입력받소, 일별시세 정보를 전달하는 함수입니다. 5라인에서 반복문을 도는데요, 원하는 페이지 수만큼 반복하여 일별시세를 크롤링하려고 합니다. 여기서는 총 20페이지까지만 크롤링 합니다. 20페이지면 총 40주간의 데이터이네요. 더 필요하신 분들은 숫자를 늘려보세요. 7~8라인은 네이버금융의 URL구조에 맞춰 필요한 정보를 추가하였습니다. 10라인에서는 url에 해당하는 일별 시세를 가지고 옵니다. 20번 반복하는 동안 데이터가 계속 누적되어야 하니, df.append()를 호출합니다. df라는 이름을 가진 변수에 누적하는 역할을 합니다. 

 

종목코드를 모두 외워서 사용할 수는 없겠지요. 우리에게 더 친숙한 것은 회사명입니다. 그래서 '4. 구현' 에서는 회사명을 주면, 회사명에 해당하는 종목 코드를 추출하여 일별시세를 요청하도록 코딩해보겠습니다.

 

4. 구현

주식 분석 보고서 자동화 프로젝트를 위한 구현 순서는 아래와 같습니다.

주식 종목 코드를 사용하여, 일별 시세를 크롤링합니다. 크롤링한 자료로 간단한 차트 그리고, 파일로 저장합니다. 이 차트를 사용하여 분석보고서를 만듭니다. 마지막으로 분석 보고자료를 이메일로 전송합니다.

 

주식 분석 보고서 자동화 프로젝트 구현 순서

[구현 순서]

점진적으로 구현을 완성해 나가봅시다.

Step1 종목 코드 및 일별 시세 가져오기
Step2 보고 자료 준비하기
Step3 보고서 작성하기
Step4 보고서를 이메일로 전송하기

Step1) 종목 코드 및 일별 시세 가져오기

우리에게 더 친숙한 것은 종목코드 보다는 회사명입니다.

회사명으로 종목코드를 구하고, 일별 시세를 가져오는 순서로 진행합니다.

 

코드

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
import pandas as pd
 
#################################
## 함수 정의
#################################
def get_stock_code():
    # 종목코드 다운로드 
    stock_code = pd.read_html('http://kind.krx.co.kr/corpgeneral/corpList.do?method=download', header=0)[0
 
    # 필요없는 column들은 제외
    stock_code = stock_code[['회사명''종목코드']] 
 
    # 한글 컬럼명을 영어로 변경 
    stock_code = stock_code.rename(columns={'회사명''company''종목코드''code'}) 
 
    # 종목코드가 6자리이기 때문에 6자리를 맞춰주기 위해 설정해줌 
    stock_code.code = stock_code.code.map('{:06d}'.format
    
    return stock_code
 
def get_stock(code):
    df = pd.DataFrame()
    for page in range(1,21):
        # 일별 시세 url  
        url = 'http://finance.naver.com/item/sise_day.nhn?code={code}'.format(code=code)     
        url = '{url}&page={page}'.format(url=url, page=page)
        print(url)
        current_df = pd.read_html(url, header=0)[0]
        df = df.append(current_df, ignore_index=True)
        
    return df
 
def clean_data(df):
    # df.dropna()를 이용해 결측값 있는 행 제거 
    df = df.dropna() 
 
    # 한글로 된 컬럼명을 영어로 바꿔줌 
    df = df.rename(columns= {'날짜''date''종가''close''전일비''diff''시가''open''고가''high''저가''low''거래량''volume'}) 
    # 데이터의 타입을 int형으로 바꿔줌 
    df[['close''diff''open''high''low''volume']] = df[['close''diff''open''high''low''volume']].astype(int
 
    # 컬럼명 'date'의 타입을 date로 바꿔줌 
    df['date'= pd.to_datetime(df['date']) 
 
    # 일자(date)를 기준으로 오름차순 정렬 
    df = df.sort_values(by=['date'], ascending=True
    
    return df
 
#################################
## 함수 호출
#################################
# 종목 코드 가져오기
company='삼성전자' 
stock_code = get_stock_code()
 
# 일별 시세 가져오기
code = stock_code[stock_code.company==company].code.values[0].strip() ## strip() : 공백제거
df = get_stock(code)
 
# 일별 시세 클린징
df = clean_data(df)
cs

 

실행 결과

삼성전자의 일별 시세 입니다.

현재부터 총 40주의 데이터가 누적되어 있습니다.

데이터 기간은 2020년 1월 10일 ~ 2020년 10월 30일이네요.

[그림 9] 40주간의 일별 시세 정보

 

코드 설명

1라인 : 필요한 라이브러리를 import 합니다.

6~19라인 : 3. 사전 지식 쌓기의 1) 종목 코드 가져오기에서 설명한 함수입니다. 해당 장을 참고하세요.

21~31라인 : 3. 사전 지식 쌓기의 2) 일별 시세 가져오기에서 설명한 함수입니다. 해당 장을 참고하세요.

33~48라인 : 일별 시세 함수를 호출한 결과를 [그림 8] 일별 시세 함수 호출 결과 에서 확인해 볼 수 있는데요. 약간 이상하다는 느낌이 드나요? 네, NaN이라는 글자가 보입니다. 이는 관측되지 않은 값을 의미하는데요, 중간중간에 관측되지 않은 결과들이 들어가 있습니다. 관측되지 않았다면, 제거하는게 좋겠지요. 35라인이 관측되지 않은 값 (=결측치)를 제거하는 코드 입니다. 38라인에서는 한글로된 컬럼명을 영어로 바꾸고, 40라인에서는 숫자값(integer)로 표현되어야 할 컬럼에 대해서는 숫자로 데이터 자료형을 변경합니다. 좀 더 설명을 하자면, 시세는 가격이므로 종가/시가/고가 등은 가격으로 표기되길 원하므로 숫자로 변경했다고 생각하면 됩니다. 43라인에서는 날짜 정보를 date자료형을 사용하고자 합니다. 단순히 문자열이 아닌, 연산을 할 수 있는 date 자료형입니다. 즉, '2020-10-31'에 +1을 하면, 에러가 날 것입니다. 왜냐하면 문자에다가 1을 더하는 꼴이니깐요. 즉 'a'에 +1을 하면 결과가 무엇인가요? 여러분도 모르시겠죠? 그런데, date라면 이야기가 다름니다. 2020년 10월 31일에서 +1을 하면 어떻게 되나요? 2020년 11월 1일이 되겠죠. 이렇듯 해당 데이터가 날짜로 인식되도록 date자료형으로 변경하였습니다. 46라인에서는 날짜를 기준으로 오름차순 정렬을 하였습니다. 그래프를 그릴때는 과거데이터를 왼쪽에 최신데이터를 오른쪽에 그려야 하므로, 이렇게 정렬을 해줍니다. 

54라인 : 삼성전자에 대한 일별 시세를 가져 올 예정입니다.

55라인 : 전체 종목 코드를 가져옵니다.

58라인 : 전체 종목 코드 중 company에 해당하는 종목 코드를 추출합니다.

59라인 : 일별 시세 정보를 가져옵니다.

62라인 : 일별 시세에서 결측데이터를 삭제하는 등의 전처리를 마무리한 최종 결과를 가져옵니다.

 

Step2) 보고 자료 준비하기

일별 시세 정보를 이용하여 차트와 테이블을 그려보고, 각각을 파일로 저장해보겠습니다.

 

코드

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
import matplotlib.pyplot as plt
from pandas.plotting import table 
import os
 
# %matplotlib inline 은 jupyter notebook 사용자 용 - jupyter notebook 내에 그래프가 그려지게 한다.
%matplotlib inline 
#################################
## 차트 그리기
#################################
plt.figure(figsize=(10,4))
plt.plot(df['date'], df['close'])
plt.xlabel('date')
plt.ylabel('close')
 
#################################
## 차트 저장 및 출력하기
#################################
chart_fname = os.path.join("res/stock_report" ,'{company}_chart.png'.format(company=company))
plt.savefig(chart_fname)
plt.show()
 
#################################
## 일별 시세 그리기
#################################
plt.figure(figsize=(15,4))
ax = plt.subplot(111, frame_on=False# no visible frame
ax.xaxis.set_visible(False)  # hide the x axis
ax.yaxis.set_visible(False)  # hide the y axis
df = df.sort_values(by=['date'], ascending=False
table(ax, df.head(10), loc='center', cellLoc = 'center', rowLoc = 'center')  # where df is your data frame
 
#################################
## 일별 시세 저장하기
#################################
table_fname = os.path.join("res/stock_report" ,'{company}_table.png'.format(company=company))
plt.savefig(table_fname)
cs

 

실행 결과

2개의 파일이 저장되었습니다.

1) res/stock_report/삼성전자_chart.png

2) res/stock_report/삼성전자_table.png

 

각 이미지를 클릭해서 보죠. 일별 시세로 그래프와 테이블 이미지가 잘 생성됨을 알 수 있습니다.

수행하는 일자에 따라 그래프의 모양과 테이블의 값은 당연히 다르겠죠?

 

[그림 10] 삼성전자_chart.png
[그림 11] 삼성전자_table.png

 

코드 설명

1~3라인 : 필요한 라이브러리를 import 합니다.

6라인 : jupyter notebook이나 colab을 사용하시는 분들께만 필요한 옵션입니다. pycharm과 같은 IDE를 사용하신다면, 주석처리 해주시세요. 해당 코드의 의미는 jupyter notebook 내에 그래프를 그리라는 의미입니다.  마치 결과를 출력해주듯 말이죠. 주석 처리를 하면 별도의 창이 생성되어 그래프가 출력됩니다.  

19~13라인 : matplotlib 라이브러리로 주식 차트를 그립니다.  10라인에서 차트의 크기를 설정합니다. 11라인은 x축은 date를 y측은 close 값을 사용해서 차트를 그리겠다는 의미입니다.  12~13라인에서는  x축은 date로 y축에는 close로 이름을 붙여 주었습니다.  

18~20라인 : 보고서에 첨부할 차트를 이미지 파일로 저장합니다. 

25~30라인 : 일별 시세를 표 형태를 가진 그래프로 표현합니다. 25라인은 표의 크기를 설정하였습니다. 시세의 경우는 과거부터 보는 것보다는 최신데이터를 먼저 보는 것이 좋으므로, 29라인에서 date기준으로 내림차순으로 정렬하였습니다.

34~35라인 : 일별 시세를 이미지 파일로 저장합니다.

 

저장된 두 이미지 파일로 보고서를 만들어 봅시다.

Step3) 보고서 작성하기

보고서는 파워포인트로 만들어보겠습니다.

제목 장표 1장, 차트 및 테이블로 구성된 장표 1장으로 총 2장을 만들어 보겠습니다.

파워포인트로 보고서를 작성하는 방법은'보고서 자동화 프로젝트 (Link)' 에서 다뤄보았습니다. 코드에서 궁금한 내용들이 있다면,   해당 장을 참고하세요.

 

코드

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
import datetime
from pptx import Presentation # 라이브러리 
from pptx.util import Inches # 사진, 표등을 그리기 위해
import os
 
#################################
## 파워포인트 객체 선언
#################################
today = datetime.datetime.today().strftime('%Y%m%d')
prs = Presentation() # 파워포인트 객체 선언
 
#################################
## 제목 장표 추가
#################################
title_slide_layout = prs.slide_layouts[0# 0 : 제목슬라이드에 해당
slide = prs.slides.add_slide(title_slide_layout) # 제목 슬라이드를 파워포인트 객체에 추가
 
# 제목 - 제목에 값넣기
title = slide.shapes.title  # 제목
title.text = "주식 보고서" # 제목에 값 넣기
# 부제목
subtitle = slide.placeholders[1# 제목상자는 placeholders[0], 부제목상자는 [1]
subtitle.text = "보고서 작성일 : {date}".format(date=today) 
 
#################################
## 차트 및 테이블 장표 추가
#################################
title_only_slide_layout = prs.slide_layouts[5
slide = prs.slides.add_slide(title_only_slide_layout)
 
shapes = slide.shapes
shapes.title.text = '{company}, {close}원에 거래 마감'.format(company=company, close=df.iloc[0]['close'])
print(shapes.title.text)
 
# 차트 추가
left = Inches(0.5)
height = Inches(2.5)
width = Inches(9)
top = Inches(2)
# width, hegith가 없을 경우 원본 사이즈로 
pic = slide.shapes.add_picture(chart_fname, left, top, width=width,height=height)
 
# 테이블 추가
left = Inches(-1)
height = Inches(3)
width = Inches(12)
top = Inches(4)
 
pic = slide.shapes.add_picture(table_fname, left, top, width=width,height=height)
cursor_sp = slide.shapes[0]._element 
cursor_sp.addprevious(pic._element) # 해당 요소를 뒤로 보내기 합니다.
 
#################################
## 보고서 저장
#################################
ppt_fname = os.path.join("res/stock_report" ,'stock_report.pptx')
prs.save(ppt_fname)
cs

 

실행 결과

 

저장된 stock_report.pptx를 열어보면 다음과 같이 2개의 장표로 구성되어 있습니다.

 

[그림 12] stock_report.pptx 구성 장표

주식 보고서에 추가하고 싶은 정보가 있다면, 자유롭게 추가해서 구성해 보세요. 나만의 주식 보고서를 만들면 더욱 재미난 프로젝트가 될 것입니다. 

 

코드 설명

1~4라인 : 필요한 라이브러리를 import 합니다.

9~10라인 : 파워포인트 객체를 선언합니다.

15~24라인 : 제목 장표를 추가합니다. 제목은 '주식 보고서'이며, 부제목으로 '보고서 작성일'을 기재하였습니다.

29~52라인 : 장표를 하나 더 추가하여 차트와 테이블 정보를 위치시킵니다.

57~58라인 : 보고서를 저장합니다.  

 

 

Step4) 보고서를 이메일로 전송하기

작성된 보고서를 이메일로 전송해보겠습니다. 이메일 보내기도 '이메일 전송 자동화 프로젝트(Link)'에서 다뤘는데요. 코드를 보다가 궁금해지면 해당 장을 참고하시면 됩니다.

 

코드

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
91
92
93
94
95
96
97
import smtplib
# 이메일 메시지에 다양한 형식을 중첩하여 담기 위한 객체
from email.mime.multipart import MIMEMultipart
 
# 이메일 메시지를 이진 데이터로 바꿔주는 인코더
from email import encoders
 
# 텍스트 형식
from email.mime.text import MIMEText
# 이미지 형식
from email.mime.image import MIMEImage
# 오디오 형식
from email.mime.audio import MIMEAudio
 
# 위의 모든 객체들을 생성할 수 있는 기본 객체
# MIMEBase(_maintype, _subtype)
# MIMEBase(<메인 타입>, <서브 타입>)
from email.mime.base import MIMEBase
 
#################################
## 함수 정의
#################################
def send_email(smtp_info, msg):
    with smtplib.SMTP(smtp_info["smtp_server"], smtp_info["smtp_port"]) as server:
        # TLS 보안 연결
        server.starttls() 
        # 로그인
        server.login(smtp_info["smtp_user_id"], smtp_info["smtp_user_pw"])
 
        # 로그인 된 서버에 이메일 전송
        response = server.sendmail(msg['from'], msg['to'], msg.as_string()) # 메시지를 보낼때는 .as_string() 메소드를 사용해서 문자열로 바꿔줍니다.
 
        # 이메일을 성공적으로 보내면 결과는 {}
        if not response:
            print('이메일을 성공적으로 보냈습니다.')
        else:
            print(response)
            
def make_multimsg(msg_dict):
    multi = MIMEMultipart(_subtype='mixed')
    
    for key, value in msg_dict.items():
        # 각 타입에 적절한 MIMExxx()함수를 호출하여 msg 객체를 생성한다.
        if key == 'text':
            with open(value['filename'], encoding='utf-8'as fp:
                msg = MIMEText(fp.read(), _subtype=value['subtype'])
        elif key == 'image':
            with open(value['filename'], 'rb'as fp:
                msg = MIMEImage(fp.read(), _subtype=value['subtype'])
        elif key == 'audio':
            with open(value['filename'], 'rb'as fp:
                msg = MIMEAudio(fp.read(), _subtype=value['subtype'])
        else:
            with open(value['filename'], 'rb'as fp:
                msg = MIMEBase(value['maintype'],  _subtype=value['subtype'])
                msg.set_payload(fp.read())
                encoders.encode_base64(msg)
                
        # 경로가 있는 경우, 파일의 이름만 추출 ex) res/stock_report/stock_report.pptx -> stock_report.pptx     
        _, fname = os.path.split(value['filename'])
        # 파일 이름을 첨부파일 제목으로 추가
        msg.add_header('Content-Disposition''attachment', filename = fname)
 
        # 첨부파일 추가
        multi.attach(msg)
    
    return multi
 
#################################
## 함수 호출
#################################
smtp_info = dict({"smtp_server" : "smtp.naver.com"# SMTP 서버 주소
                  "smtp_user_id" : "<송신자(sender) 메일 계정>@naver.com" ,
                  "smtp_user_pw" : "<송신자(sender) 메일 패스워드>" ,
                  "smtp_port" : 587}) # SMTP 서버 포트
 
msg_dict = {
  'application' : {'maintype' : 'application''subtype' : 'octect-stream''filename' : 'res/email_sending/test.pdf'# 그외 첨부파일
}
 
# 메일 내용 작성
title = '({date}). 주식 보고서 분석 자료 입니다'.format(date=today)
content = '주식 보고서 분석 자료 입니다'
sender = "<송신자(sender) 메일 계정>@naver.com"
receiver = "<수신자(receiver) 메일 주소>@naver.com" 
msg = MIMEText(_text = content, _charset = "utf-8"
 
# 첨부파일 추가
msg_dict['application']['filename'= ppt_fname
multi = make_multimsg(msg_dict)
multi['Subject'= title  
multi['From'= sender  
multi['To'= receiver     
multi.attach(msg)
 
# 이메일 전송
send_email(smtp_info, multi )
cs

 

실행 결과

이메일이 도착했습니다.

[그림 13] 이메일 도착

메일 제목도, 첨부파일도 설정한대로 잘 전송이 되었습니다.

[그림 14] 주식 분석 보고서 메일 내용 상세

 

코드 설명

1~67라인 : todo '5장 이메일 전송 자동화 프로젝트'에서 학습한 내용입니다.

72~75라인 : 이메일 내용을 작성에 필요한 정보입니다. 제목, 내용, 송신자, 수신자가 필요합니다.

77~78라인 : 첨부파일에 대해서 객체를 만듭니다. 2개이상의 첨부파일을 넣을 경우, 추가를 하면 됩니다. filename이 임시로 test.pdf로 설정되어 있지만, 89라인에서 보고서 파일명으로 변경됩니다.

81~86라인 : 생성된 객체에 제목, 송신자, 수신자 정보를 추가하여 Text형의 메일 형태를 만듭니다.

89~94라인 : text객체와 첨부파일 객체를 attach함수를 통해 추가해줍니다.

97라인 : 이메일 전송을 요청합니다.

 

5. 요약정리

이번 장에서는 주식 분석 보고서 자동화 프로젝트를 완성해보았습니다.

주식 종목 코드를 사용하여, 일별 시세를 크롤링했습니다. 일별 시세 값을 저장할 수 있다는것만으로도 다양한 활용을 할 수 있게 됩니다. 또한 크롤링한 데이터를 사용해서 간단한 차트를 그리고, 이 차트를 사용하여 분석보고서를 만들어보았습니다. 그리고 마지막으로 분석 보고자료를 이메일로 전송합니다.

 

한단계 한단계가 버릴 정보가 없습니다. 주식에 대해서 잘 아시는 분들이라면, 일별 시세 값을 이용해서 다양한 차트를 그리실 수 있을 것이고, 필요한 정보들을 추가 구성한 파워포인트를 만들 수 있습니다. 즉, 나만의 주식 보고서를 만들 수 있는 것이지요.

어떠세요? 재테크를 도와줄 멋진 프로젝트인가요?

 

이번 프로젝트에서는 '삼성전자'만 수행하도록 만들었습니다. 혹시  두곳 이상의 회사 정보를 받고 싶다면, 반복문만 추가하면 됩니다. 코드의 어느 부분을 반복하고, 어느 부분은 반복하지 말아야 하는지도 같이 고민해보세요. 여러분의 실력이 향상될 것입니다.

 


도움이 되셨다면, 좋아요 / 구독 버튼 눌러주세요~

 

저작물의 저작권은 작성자에게 있습니다.
공유는 자유롭게 하시되 댓글 남겨주세요~
상업적 용도로는 무단 사용을 금지합니다.
끝까지 읽어주셔서 감사합니다^^

반응형