Type something to search...

06 주식분석보고서

1. 주식 분석 보고서 자동화 프로젝트

1.1. 프로젝트 개요

주식 정보를 자동으로 수집하여 분석 보고서를 만들고 이메일로 전송

  • [최종 결과물]
  • 주식 일별 시세 데이터 수집
  • 차트 및 테이블 이미지 생성
  • PowerPoint 보고서 자동 작성
  • 이메일 자동 전송

1.2. 필요한 라이브러리 설치

라이브러리 — 다른 사람이 만들어 놓은 도구 모음이다. 파이썬에 기본 포함되지 않아 따로 설치한다.

  • matplotlib — 차트(그래프)를 그리는 도구
  • pandas — 표 형태의 데이터를 다루는 도구
  • python-pptx — 파워포인트 파일을 만드는 도구
  • beautifulsoup4 — 파이썬용 HTML 및 XML 데이터 추출
  • html5lib — 웹 브라우저(크롬, 파이어폭스 등)와 동일한 방식으로 HTML을 분석
Terminal window
1
pip install matplotlib
2
pip install pandas
3
pip install python-pptx
4
pip install lxml
5
pip install beautifulsoup4
6
pip install html5lib
7
pip install request

1.3. Step 1: 종목코드 및 일별 시세 가져오기

1.3.1. 종목코드 이해하기

종목코드 — 주식 시장에 등록된 회사마다 붙는 고유 번호이다. 사람의 주민번호와 같은 개념이다.

1
삼성전자 → 005930 (6자리 숫자)

1.3.2. 종목코드 다운로드

한국 거래소(KRX)에서 공개한 상장법인목록을 다운로드한다.

KRX 자료 다운로드
app.py
1
import pandas as pd
2
3
def get_stock_code():
4
stock_code = pd.read_html("http://kind.krx.co.kr/corpgeneral/corpList.do?method=download", encoding='cp949', header=0)[0]
5
stock_code = stock_code[['회사명', '종목코드']]
6
stock_code = stock_code.rename(columns={'회사명': 'company', '종목코드': 'code'})
7
stock_code.code = stock_code.code.map('{:06d}'.format)
8
print(stock_code)
9
return stock_code
10
get_stock_code()

1: import pandas as pd — pandas 라이브러리를 pd라는 짧은 이름으로 가져온다

3: def get_stock_code(): — 종목코드를 가져오는 함수를 만든다

4~5: pd.read_html(...) — 웹페이지의 표(HTML 테이블)를 읽어온다. header=0은 첫 번째 행을 열 이름으로 사용한다. [0]은 페이지에 있는 여러 표 중 첫 번째를 선택한다

6: stock_code[['회사명', '종목코드']] — 전체 열 중 회사명과 종목코드 두 개만 남긴다

7: .rename(columns={...}) — 열 이름을 한글에서 영어로 바꾼다

8: .map('{:06d}'.format) — 종목코드를 6자리로 맞춘다. 예: 5930005930

9: return stock_code — 완성된 데이터를 돌려준다

11: stock_code = get_stock_code() — 함수를 실행하여 결과를 변수에 저장한다

12: print(stock_code.head()) — 상위 5개 데이터를 출력하여 확인한다

1.3.3. 일별 시세 가져오기

크롤링 — 웹페이지에서 데이터를 자동으로 수집하는 것이다. 네이버 금융에서 특정 회사의 일별 시세를 가져온다.

URL 구조:

1
http://finance.naver.com/item/sise_day.nhn?code={종목코드}&page={페이지번호}
app.py
1
import requests
2
3
def get_stock(code):
4
df = pd.DataFrame()
5
6
for page in range(1, 21):
7
url = 'http://finance.naver.com/item/sise_day.nhn?code={code}'.format(code=code)
8
url = '{url}&page={page}'.format(url=url, page=page)
9
header = {'User-Agent': '<your-user-agent>'}
10
res = requests.get(url, headers=header)
11
current_df = pd.read_html(res.text, header=0)[0]
12
df = df.append(current_df, ignore_index=True)
13
14
return df
15
16
code = '005930'
17
df = get_stock(code)
18
print(df.head())

1: import requests — 웹페이지에 접속하는 라이브러리를 가져온다

3: def get_stock(code): — 종목코드를 받아 일별 시세를 가져오는 함수를 만든다

4: df = pd.DataFrame() — 빈 표(데이터프레임)를 만든다. 여기에 데이터를 쌓는다

6: for page in range(1, 21): — 1페이지부터 20페이지까지 반복한다. 약 40주간의 데이터이다

7: url = '...{code}'.format(code=code) — 종목코드를 넣어 URL을 만든다

8: url = '{url}&page={page}'.format(...) — 페이지 번호를 URL에 추가한다

9: header = {'User-Agent': '...'} — 브라우저인 척 접속하기 위한 설정이다. 없으면 차단된다

10: res = requests.get(url, headers=header) — 해당 URL에 접속하여 페이지 내용을 가져온다

11: current_df = pd.read_html(res.text, header=0)[0] — 가져온 HTML에서 표 데이터를 추출한다

12: df = df.append(current_df, ignore_index=True) — 추출한 데이터를 기존 표에 이어 붙인다

14: return df — 모든 페이지의 데이터가 합쳐진 표를 돌려준다

16: code = '005930' — 삼성전자의 종목코드를 지정한다

17: df = get_stock(code) — 함수를 실행하여 시세 데이터를 가져온다

18: print(df.head()) — 상위 5개 데이터를 출력하여 확인한다

1.3.4. 데이터 정제 (Clean)

정제 — 수집한 데이터에서 빈 값이나 형식이 맞지 않는 부분을 정리하는 과정이다.

app.py
1
def clean_data(df):
2
df = df.dropna()
3
df = df.rename(columns={
4
'날짜': 'date',
5
'종가': 'close',
6
'전일비': 'diff',
7
'시가': 'open',
8
'고가': 'high',
9
'저가': 'low',
10
'거래량': 'volume'
11
})
12
df[['close', 'diff', 'open', 'high', 'low', 'volume']] = \
13
df[['close', 'diff', 'open', 'high', 'low', 'volume']].astype(int)
14
df['date'] = pd.to_datetime(df['date'])
15
df = df.sort_values(by=['date'], ascending=True)
16
return df
17
18
df = clean_data(df)
19
print(df)

1: def clean_data(df): — 데이터를 정제하는 함수를 만든다

2: df.dropna() — 빈 값(NaN)이 있는 행을 삭제한다

3~11: .rename(columns={...}) — 한글 열 이름을 영어로 바꾼다. 코드에서 다루기 편하다

12~13: .astype(int) — 문자형 숫자를 정수(int)로 변환한다. 계산할 수 있게 된다

14: pd.to_datetime(...) — 날짜 문자열을 날짜 형식으로 변환한다. 정렬이나 비교가 가능해진다

15: .sort_values(by=['date'], ascending=True) — 날짜 기준으로 오래된 순서부터 정렬한다

16: return df — 정제된 데이터를 돌려준다

18: df = clean_data(df) — 함수를 실행하여 정제된 데이터를 저장한다

19: print(df) — 정제된 데이터를 출력하여 확인한다

요약

정제 전: NaN 값, 문자형 숫자, 형식 불일치 정제 후: 정렬된 숫자 데이터, datetime 형식


1.4. Step 2: 보고자료 준비하기

수집한 데이터를 차트(그래프)와 테이블(표) 이미지로 만든다. 나중에 파워포인트에 넣기 위한 준비 단계이다.

1.4.1. 주식 차트 생성

matplotlib — 파이썬에서 그래프를 그리는 도구이다. 날짜별 종가(마감 가격)를 꺾은선 그래프로 그린다.

app.py
1
import matplotlib.pyplot as plt
2
from pandas.plotting import table
3
import os
4
5
plt.figure(figsize=(10, 4))
6
plt.plot(df['date'], df['close'])
7
plt.xlabel('date')
8
plt.ylabel('close')
9
10
chart_fname = os.path.join("res/stock_report", f'{company}_chart.png')
11
plt.savefig(chart_fname)
12
plt.show()

1: import matplotlib.pyplot as plt — 그래프 그리기 도구를 plt라는 이름으로 가져온다

2: from pandas.plotting import table — 표를 이미지로 만드는 도구를 가져온다

3: import os — 파일 경로를 다루는 도구를 가져온다

5: plt.figure(figsize=(10, 4)) — 가로 10, 세로 4 크기의 그래프 틀을 만든다

6: plt.plot(df['date'], df['close']) — x축은 날짜, y축은 종가로 꺾은선 그래프를 그린다

7: plt.xlabel('date') — x축 이름을 ‘date’로 지정한다

8: plt.ylabel('close') — y축 이름을 ‘close’로 지정한다

10: os.path.join(...) — 저장할 파일 경로를 만든다

11: plt.savefig(chart_fname) — 그래프를 PNG 이미지 파일로 저장한다

12: plt.show() — 그래프를 화면에 표시한다

1.4.2. 일별 시세 테이블 생성

최근 10일간의 시세를 표 이미지로 만든다.

app.py
1
plt.figure(figsize=(15, 4))
2
ax = plt.subplot(111, frame_on=False)
3
ax.xaxis.set_visible(False)
4
ax.yaxis.set_visible(False)
5
6
df_sorted = df.sort_values(by=['date'], ascending=False)
7
table(ax, df_sorted.head(10), loc='center', cellLoc='center', rowLoc='center')
8
9
table_fname = os.path.join("res/stock_report", f'{company}_table.png')
10
plt.savefig(table_fname)

1: plt.figure(figsize=(15, 4)) — 가로 15, 세로 4 크기의 그래프 틀을 만든다

2: plt.subplot(111, frame_on=False) — 테두리 없는 영역을 만든다. 표만 보이게 하기 위한 설정이다

3: ax.xaxis.set_visible(False) — x축 눈금을 숨긴다

4: ax.yaxis.set_visible(False) — y축 눈금을 숨긴다

6: .sort_values(by=['date'], ascending=False) — 최신 날짜가 위에 오도록 정렬한다

7: table(ax, df_sorted.head(10), ...) — 상위 10개 데이터를 표로 그린다. 가운데 정렬이다

9: os.path.join(...) — 저장할 파일 경로를 만든다

10: plt.savefig(table_fname) — 표를 PNG 이미지 파일로 저장한다


1.5. Step 3: 보고서 작성하기

python-pptx — 파이썬으로 파워포인트(.pptx) 파일을 만드는 도구이다. 앞에서 만든 차트와 테이블 이미지를 슬라이드에 넣는다.

1.5.1. PowerPoint 기본 설정

app.py
1
import datetime
2
from pptx import Presentation
3
from pptx.util import Inches
4
5
today = datetime.datetime.today().strftime('%Y%m%d')
6
prs = Presentation()

1: import datetime — 날짜/시간을 다루는 도구를 가져온다

2: from pptx import Presentation — 파워포인트 파일을 만드는 도구를 가져온다

3: from pptx.util import Inches — 인치 단위로 크기를 지정하는 도구를 가져온다

5: .today().strftime('%Y%m%d') — 오늘 날짜를 20260308 형식의 문자열로 만든다

6: prs = Presentation() — 빈 파워포인트 파일을 만든다

1.5.2. 제목 슬라이드 추가

app.py
1
title_slide_layout = prs.slide_layouts[0]
2
slide = prs.slides.add_slide(title_slide_layout)
3
4
title = slide.shapes.title
5
title.text = "주식 보고서"
6
7
subtitle = slide.placeholders[1]
8
subtitle.text = f"보고서 작성일: {today}"

1: prs.slide_layouts[0] — 0번 레이아웃(제목 슬라이드)을 선택한다

2: prs.slides.add_slide(...) — 선택한 레이아웃으로 새 슬라이드를 추가한다

4: slide.shapes.title — 슬라이드의 제목 영역을 가져온다

5: title.text = "주식 보고서" — 제목에 텍스트를 넣는다

7: slide.placeholders[1] — 1번 자리표시자(부제목 영역)를 가져온다

8: subtitle.text = f"..." — 부제목에 오늘 날짜를 넣는다

1.5.3. 차트 & 테이블 슬라이드 추가

app.py
1
title_only_slide_layout = prs.slide_layouts[5]
2
slide = prs.slides.add_slide(title_only_slide_layout)
3
4
shapes = slide.shapes
5
6
latest_close = df.iloc[0]['close']
7
shapes.title.text = f'{company}, {latest_close} 원에 거래 마감'
8
9
left = Inches(0.5)
10
top = Inches(2)
11
width = Inches(9)
12
height = Inches(2.5)
13
pic1 = slide.shapes.add_picture(chart_fname, left, top, width=width, height=height)
14
15
left = Inches(-1)
16
top = Inches(4)
17
width = Inches(12)
18
height = Inches(3)
19
pic2 = slide.shapes.add_picture(table_fname, left, top, width=width, height=height)

1: prs.slide_layouts[5] — 5번 레이아웃(제목만 있는 슬라이드)을 선택한다

2: prs.slides.add_slide(...) — 새 슬라이드를 추가한다

4: slide.shapes — 슬라이드 안의 도형 모음을 가져온다

6: df.iloc[0]['close'] — 첫 번째 행의 종가(마감 가격)를 가져온다

7: shapes.title.text = f'...' — 슬라이드 제목에 회사명과 종가를 넣는다

9~12: Inches(...) — 차트 이미지의 위치와 크기를 인치 단위로 지정한다

13: add_picture(...) — 차트 이미지를 슬라이드에 삽입한다

15~18: Inches(...) — 테이블 이미지의 위치와 크기를 지정한다

19: add_picture(...) — 테이블 이미지를 슬라이드에 삽입한다

1.5.4. 보고서 저장

app.py
1
ppt_fname = os.path.join("res/stock_report", 'stock_report.pptx')
2
prs.save(ppt_fname)
3
print(f"보고서 저장 완료: {ppt_fname}")

1: os.path.join(...) — 저장할 파일 경로를 만든다

2: prs.save(ppt_fname) — 파워포인트 파일을 저장한다

3: print(...) — 저장 완료 메시지를 출력한다


1.6. Step 4: 이메일 전송

SMTP — 이메일을 보내는 통신 규약이다. 우체국에서 편지를 배달하는 규칙과 같다. 파이썬의 smtplib로 네이버 메일 서버에 접속하여 보고서를 첨부한 이메일을 보낸다.

1.6.1. SMTP로 이메일 보내는 함수

app.py
1
import smtplib
2
from email.mime.multipart import MIMEMultipart
3
from email.mime.base import MIMEBase
4
from email import encoders
5
import os
6
7
def send_email(smtp_info, msg):
8
with smtplib.SMTP(smtp_info["smtp_server"], smtp_info["smtp_port"]) as server:
9
server.starttls()
10
server.login(smtp_info["smtp_user_id"], smtp_info["smtp_user_pw"])
11
response = server.sendmail(msg['from'], msg['to'], msg.as_string())
12
13
if not response:
14
print("이메일 전송 성공")
15
else:
16
print(f"오류: {response}")

1: import smtplib — 이메일 전송 도구를 가져온다

2: from email.mime.multipart import MIMEMultipart — 첨부파일이 있는 메일을 만드는 도구이다

3: from email.mime.base import MIMEBase — 파일 첨부 형식을 다루는 도구이다

4: from email import encoders — 첨부파일을 인코딩(변환)하는 도구이다

5: import os — 파일 경로를 다루는 도구를 가져온다

7: def send_email(smtp_info, msg): — SMTP 정보와 메시지를 받아 이메일을 보내는 함수이다

8: smtplib.SMTP(...) — 메일 서버에 접속한다. with를 쓰면 끝나면 자동으로 연결을 끊는다

9: server.starttls() — TLS 보안 연결을 시작한다. 이메일 내용을 암호화한다

10: server.login(...) — 아이디와 비밀번호로 로그인한다

11: server.sendmail(...) — 발신자, 수신자, 메일 내용을 넣어 전송한다

13~16: 전송 결과를 확인한다. 응답이 비어 있으면 성공, 아니면 오류를 출력한다

1.6.2. 첨부파일 포함 메시지 생성

MIME — 이메일에 텍스트, 이미지, 파일 등을 함께 담는 형식이다. 편지 봉투에 여러 서류를 넣는 것과 같다.

app.py
1
def make_multimsg(msg_dict):
2
multi = MIMEMultipart(_subtype='mixed')
3
4
for key, value in msg_dict.items():
5
if key == 'text':
6
from email.mime.text import MIMEText
7
with open(value['filename'], encoding='utf-8') as fp:
8
msg = MIMEText(fp.read(), _subtype=value['subtype'])
9
elif key == 'image':
10
from email.mime.image import MIMEImage
11
with open(value['filename'], 'rb') as fp:
12
msg = MIMEImage(fp.read(), _subtype=value['subtype'])
13
else:
14
with open(value['filename'], 'rb') as fp:
15
msg = MIMEBase(value['maintype'], _subtype=value['subtype'])
16
msg.set_payload(fp.read())
17
encoders.encode_base64(msg)
18
19
_, fname = os.path.split(value['filename'])
20
msg.add_header('Content-Disposition', 'attachment', filename=fname)
21
multi.attach(msg)
22
23
return multi

1: def make_multimsg(msg_dict): — 첨부파일 정보를 받아 메시지를 만드는 함수이다

2: MIMEMultipart(_subtype='mixed') — 여러 종류의 첨부파일을 담을 수 있는 빈 메시지를 만든다

4: for key, value in msg_dict.items(): — 첨부파일 정보를 하나씩 꺼내 반복한다

5~8: key == 'text' — 텍스트 파일이면 UTF-8로 읽어 텍스트 형식으로 첨부한다

9~12: key == 'image' — 이미지 파일이면 바이너리(rb)로 읽어 이미지 형식으로 첨부한다

13~17: 그 외 파일(pptx 등)은 범용 형식(MIMEBase)으로 읽고 base64로 인코딩한다

19: os.path.split(...) — 전체 경로에서 파일명만 분리한다

20: add_header(...) — 첨부파일의 이름을 지정한다

21: multi.attach(msg) — 만들어진 첨부파일을 메시지에 추가한다

23: return multi — 완성된 메시지를 돌려준다

1.6.3. 메일 전송 실행

app.py
1
smtp_info = {
2
"smtp_server": "smtp.naver.com",
3
"smtp_user_id": "your_email@naver.com",
4
"smtp_user_pw": "your_password",
5
"smtp_port": 587
6
}
7
8
msg_dict = {
9
'application': {
10
'maintype': 'application',
11
'subtype': 'octet-stream',
12
'filename': 'res/stock_report/stock_report.pptx'
13
}
14
}
15
16
msg = make_multimsg(msg_dict)
17
msg['from'] = smtp_info["smtp_user_id"]
18
msg['to'] = "recipient@example.com"
19
msg['subject'] = "주식 분석 보고서"
20
21
send_email(smtp_info, msg)

1~6: smtp_info — 메일 서버 접속 정보를 딕셔너리에 저장한다

  • smtp_server — 네이버 메일 서버 주소이다

  • smtp_port — 587번 포트를 사용한다 (TLS 보안 전송용)

8~14: msg_dict — 첨부할 파일 정보를 지정한다

  • maintype, subtype — 파일 종류를 지정한다. octet-stream은 범용 바이너리 파일이다

  • filename — 첨부할 파일 경로이다

16: make_multimsg(msg_dict) — 첨부파일이 포함된 메시지를 만든다

17: msg['from'] — 보내는 사람 이메일을 지정한다

18: msg['to'] — 받는 사람 이메일을 지정한다

19: msg['subject'] — 메일 제목을 지정한다

21: send_email(smtp_info, msg) — 이메일을 전송한다

주의

smtp_user_idsmtp_user_pw에 실제 아이디와 비밀번호를 넣어야 한다. 코드에 비밀번호를 직접 적으면 보안에 위험하므로 환경변수나 별도 설정 파일을 사용하는 것이 좋다.


1.7. 전체 코드 통합

app.py
1
import pandas as pd
2
import requests
3
import matplotlib.pyplot as plt
4
from pandas.plotting import table
5
from pptx import Presentation
6
from pptx.util import Inches
7
import smtplib
8
import datetime
9
import os
10
from email.mime.multipart import MIMEMultipart
11
from email.mime.base import MIMEBase
12
from email.mime.text import MIMEText
13
from email.mime.image import MIMEImage
14
from email import encoders
15
16
# ─── 함수 정의 ───
17
18
def get_stock_code():
19
stock_code = pd.read_html('http://kind.krx.co.kr/corpgeneral/corpList.do?method=download', header=0)[0]
20
stock_code = stock_code[['회사명', '종목코드']]
21
stock_code = stock_code.rename(columns={'회사명': 'company', '종목코드': 'code'})
22
stock_code.code = stock_code.code.map('{:06d}'.format)
23
return stock_code
24
25
def get_stock(code):
26
df = pd.DataFrame()
27
for page in range(1, 21):
28
url = 'http://finance.naver.com/item/sise_day.nhn?code={code}'.format(code=code)
29
url = '{url}&page={page}'.format(url=url, page=page)
30
header = {'User-Agent': '<your-user-agent>'}
31
res = requests.get(url, headers=header)
32
current_df = pd.read_html(res.text, header=0)[0]
33
df = df.append(current_df, ignore_index=True)
34
return df
35
36
def clean_data(df):
37
df = df.dropna()
38
df = df.rename(columns={
39
'날짜': 'date',
40
'종가': 'close',
41
'전일비': 'diff',
42
'시가': 'open',
43
'고가': 'high',
44
'저가': 'low',
45
'거래량': 'volume'
46
})
47
df[['close', 'diff', 'open', 'high', 'low', 'volume']] = \
48
df[['close', 'diff', 'open', 'high', 'low', 'volume']].astype(int)
49
df['date'] = pd.to_datetime(df['date'])
50
df = df.sort_values(by=['date'], ascending=True)
51
return df
52
53
def create_chart(df, company):
54
plt.figure(figsize=(10, 4))
55
plt.plot(df['date'], df['close'])
56
plt.xlabel('date')
57
plt.ylabel('close')
58
chart_fname = os.path.join("res/stock_report", f'{company}_chart.png')
59
plt.savefig(chart_fname)
60
plt.show()
61
return chart_fname
62
63
def create_table(df, company):
64
plt.figure(figsize=(15, 4))
65
ax = plt.subplot(111, frame_on=False)
66
ax.xaxis.set_visible(False)
67
ax.yaxis.set_visible(False)
68
df_sorted = df.sort_values(by=['date'], ascending=False)
69
table(ax, df_sorted.head(10), loc='center', cellLoc='center', rowLoc='center')
70
table_fname = os.path.join("res/stock_report", f'{company}_table.png')
71
plt.savefig(table_fname)
72
return table_fname
73
74
def create_pptx(df, company, chart_fname, table_fname):
75
today = datetime.datetime.today().strftime('%Y%m%d')
76
prs = Presentation()
77
78
title_slide_layout = prs.slide_layouts[0]
79
slide = prs.slides.add_slide(title_slide_layout)
80
title = slide.shapes.title
81
title.text = "주식 보고서"
82
subtitle = slide.placeholders[1]
83
subtitle.text = f"보고서 작성일: {today}"
84
85
title_only_slide_layout = prs.slide_layouts[5]
86
slide = prs.slides.add_slide(title_only_slide_layout)
87
shapes = slide.shapes
88
latest_close = df.iloc[-1]['close']
89
shapes.title.text = f'{company}, {latest_close} 원에 거래 마감'
90
91
left = Inches(0.5)
92
top = Inches(2)
93
width = Inches(9)
94
height = Inches(2.5)
95
slide.shapes.add_picture(chart_fname, left, top, width=width, height=height)
96
97
left = Inches(-1)
98
top = Inches(4)
99
width = Inches(12)
100
height = Inches(3)
101
slide.shapes.add_picture(table_fname, left, top, width=width, height=height)
102
103
ppt_fname = os.path.join("res/stock_report", 'stock_report.pptx')
104
prs.save(ppt_fname)
105
print(f"보고서 저장 완료: {ppt_fname}")
106
return ppt_fname
107
108
def send_email(smtp_info, msg):
109
with smtplib.SMTP(smtp_info["smtp_server"], smtp_info["smtp_port"]) as server:
110
server.starttls()
111
server.login(smtp_info["smtp_user_id"], smtp_info["smtp_user_pw"])
112
response = server.sendmail(msg['from'], msg['to'], msg.as_string())
113
if not response:
114
print("이메일 전송 성공")
115
else:
116
print(f"오류: {response}")
117
118
def make_multimsg(msg_dict):
119
multi = MIMEMultipart(_subtype='mixed')
120
for key, value in msg_dict.items():
121
if key == 'text':
122
with open(value['filename'], encoding='utf-8') as fp:
123
msg = MIMEText(fp.read(), _subtype=value['subtype'])
124
elif key == 'image':
125
with open(value['filename'], 'rb') as fp:
126
msg = MIMEImage(fp.read(), _subtype=value['subtype'])
127
else:
128
with open(value['filename'], 'rb') as fp:
129
msg = MIMEBase(value['maintype'], _subtype=value['subtype'])
130
msg.set_payload(fp.read())
131
encoders.encode_base64(msg)
132
_, fname = os.path.split(value['filename'])
133
msg.add_header('Content-Disposition', 'attachment', filename=fname)
134
multi.attach(msg)
135
return multi
136
137
# ─── 실행 ───
138
139
company = '삼성전자'
140
141
# Step 1: 데이터 수집
142
stock_code_df = get_stock_code()
143
code = stock_code_df[stock_code_df.company == company].code.values[0].strip()
144
df = get_stock(code)
145
df = clean_data(df)
146
147
# Step 2: 보고자료 생성
148
chart_fname = create_chart(df, company)
149
table_fname = create_table(df, company)
150
151
# Step 3: 보고서 작성
152
ppt_fname = create_pptx(df, company, chart_fname, table_fname)
153
154
# Step 4: 이메일 전송
155
smtp_info = {
156
"smtp_server": "smtp.naver.com",
157
"smtp_user_id": "your_email@naver.com",
158
"smtp_user_pw": "your_password",
159
"smtp_port": 587
160
}
161
162
msg_dict = {
163
'application': {
164
'maintype': 'application',
165
'subtype': 'octet-stream',
166
'filename': ppt_fname
167
}
168
}
169
170
msg = make_multimsg(msg_dict)
171
msg['from'] = smtp_info["smtp_user_id"]
172
msg['to'] = "recipient@example.com"
173
msg['subject'] = "주식 분석 보고서"
174
175
send_email(smtp_info, msg)
176
177
print("모든 작업이 완료되었다!")