18.Python으로 주식 데이터 수집기 만들기 — yfinance부터 한국 주식까지 완벽 가이드

저도 처음에는 주가나 재무제표 데이터를 모으려고 매일 아침 HTS를 켜고 엑셀로 일일이 복사+붙여넣기를 했습니다. 종목이 수십 개로 늘어나니까 데이터 정리하는 데만 몇 시간이 걸려 정작 중요한 차트 분석이나 전략 검증은 시작도 못 하고 지치기 일쑤였죠. 안 되겠다 싶어 파이썬으로 자동화를 시작했는데, 진짜 난관은 그다음이었습니다. 해외 데이터와 달리 한국 주식은 API 제한도 까다롭고, 웹사이트 구조가 자주 바뀌어 안정적으로 데이터를 가져오는 방법을 찾느라 몇 주 동안 밤을 새우며 헤맸거든요. 수많은 시행착오 끝에 정착한 한국 주식 데이터 수집 노하우를 이 글에 아낌없이 정리했습니다.

이 글에서는 미국·한국 주식 데이터를 Python으로 자동 수집하는 방법, 수집한 데이터를 가공하고 CSV와 엑셀로 저장하는 방법, 그리고 기술적 지표 계산까지 — 실제 분석에 바로 활용할 수 있는 수준으로 안내해 드리겠습니다.


주식 데이터 수집을 자동화해야 하는 이유

주식 분석의 품질은 데이터의 품질에서 시작됩니다. 그런데 데이터를 수집하는 과정 자체에 시간을 소모하고 있다면, 정작 분석과 판단에 쓸 시간이 줄어듭니다.

HTS에서 차트를 보며 수치를 메모하거나, 금융 사이트에서 표를 복사해 엑셀에 붙여넣는 방식은 수집하는 사람이 됩니다. Python으로 데이터 수집기를 만들어두면 분석하는 사람이 될 수 있습니다. 데이터 수집은 코드가 맡고, 사람은 해석과 판단에만 집중할 수 있는 구조입니다.

택배 비유를 들자면, 직접 물건을 가지러 가는 것과 정기 배송을 신청해두는 것의 차이입니다. 한 번 설정해두면 이후에는 자동으로 원하는 데이터가 쌓입니다.

자동 수집이 주는 실질적 이점

  • 일관성 확보 — 동일한 기준과 시각에 수집되어 데이터 품질이 균일합니다
  • 과거 데이터 일괄 수집 — 수년치 일봉 데이터를 몇 초 만에 확보할 수 있습니다
  • 멀티 종목 동시 처리 — 관심 종목 수백 개를 코드 한 번으로 처리합니다
  • 분석 파이프라인 연결 — 수집 즉시 지표 계산, 시각화, 알림 발송까지 자동화됩니다

환경 설정 — 라이브러리 설치

pip install yfinance pandas requests beautifulsoup4 openpyxl
라이브러리 역할
yfinance 야후 파이낸스 기반 글로벌 주식 데이터 수집
pandas 데이터 가공 및 분석
requests + beautifulsoup4 한국 주식 데이터 크롤링
openpyxl 엑셀 저장

yfinance로 미국·글로벌 주식 데이터 수집하기

단일 종목 데이터 수집

yfinance는 야후 파이낸스를 통해 전 세계 주식, ETF, 지수 데이터를 무료로 제공합니다. 티커(Ticker) 심볼만 알면 됩니다.

import yfinance as yf
import pandas as pd

# 단일 종목 수집 — Apple
ticker = yf.Ticker("AAPL")

# 일봉 데이터 (최근 1년)
df = ticker.history(period="1y")
print(df.head())
print(f"\n수집된 데이터: {len(df)}일치")



기간 지정 및 주기 변경

# 특정 기간 지정
df = ticker.history(start="2023-01-01", end="2024-12-31")

# 주봉 데이터
df_weekly = ticker.history(period="2y", interval="1wk")

# 월봉 데이터
df_monthly = ticker.history(period="5y", interval="1mo")

# 1시간봉 (최근 60일까지만 가능)
df_hourly = ticker.history(period="60d", interval="1h")

기업 기본 정보 함께 수집하기

ticker = yf.Ticker("AAPL")
info = ticker.info

print(f"회사명: {info.get('longName')}")
print(f"섹터: {info.get('sector')}")
print(f"시가총액: ${info.get('marketCap', 0):,.0f}")
print(f"PER: {info.get('trailingPE', 'N/A')}")
print(f"배당수익률: {info.get('dividendYield', 0) * 100:.2f}%")

여러 종목 한 번에 수집하기

# 관심 종목 리스트
tickers = ["AAPL", "MSFT", "GOOGL", "NVDA", "TSLA"]

# 한 번에 다운로드
df_multi = yf.download(tickers, period="1y", auto_adjust=True)

# 종가만 추출
close_prices = df_multi["Close"]
print(close_prices.tail())




한국 주식 데이터 수집하기

한국 주식도 yfinance로 수집할 수 있습니다. 코스피 종목은 티커 뒤에 .KS, 코스닥은 .KQ를 붙입니다.

yfinance로 한국 주식 수집

# 삼성전자 (코스피)
samsung = yf.Ticker("005930.KS")
df_samsung = samsung.history(period="1y")
print(df_samsung[["Open", "High", "Low", "Close", "Volume"]].tail(10))

# 카카오 (코스피)
kakao = yf.Ticker("035720.KS")

# 셀트리온 (코스피)
celltrion = yf.Ticker("068270.KS")

# 에코프로 (코스닥)
ecopro = yf.Ticker("086520.KQ")

⚠️ 주의: yfinance의 한국 주식 데이터는 야후 파이낸스 서버 상태에 따라 간헐적으로 누락이 발생할 수 있습니다. 실시간성이 중요하거나 데이터 정확도가 필수인 경우 증권사 API 또는 한국거래소(KRX) 데이터를 병행 사용하시길 권장합니다.

KRX 정보데이터시스템에서 종목 코드 일괄 수집

한국거래소는 전체 상장 종목 목록을 공개 API로 제공합니다. pip 설치 없이 requests만으로 접근 가능합니다.

import requests
import pandas as pd

def get_krx_stock_list() -> pd.DataFrame:
    url = "https://kind.krx.co.kr/corpgeneral/corpList.do"
    params = {
        "method": "download",
        "searchType": "13",
    }
    headers = {"User-Agent": "Mozilla/5.0"}

    response = requests.get(url, params=params, headers=headers)
    response.encoding = "euc-kr"

    df = pd.read_html(response.text, header=0)[0]
    df = df[["회사명", "종목코드"]]
    df["종목코드"] = df["종목코드"].astype(str).str.zfill(6)
    df["yf_ticker"] = df["종목코드"] + ".KS"

    return df

stock_list = get_krx_stock_list()
print(f"코스피 상장 종목 수: {len(stock_list)}")
print(stock_list.head(10))




기술적 지표 직접 계산하기

수집한 가격 데이터로 분석에 필요한 기술적 지표를 직접 계산합니다.

이동평균선 (MA)

import yfinance as yf

df = yf.Ticker("005930.KS").history(period="1y")

# 이동평균선
df["MA5"]  = df["Close"].rolling(window=5).mean()
df["MA20"] = df["Close"].rolling(window=20).mean()
df["MA60"] = df["Close"].rolling(window=60).mean()

print(df[["Close", "MA5", "MA20", "MA60"]].tail(10))

RSI (상대강도지수)

def calculate_rsi(series: pd.Series, period: int = 14) -> pd.Series:
    delta = series.diff()
    gain = delta.clip(lower=0)
    loss = -delta.clip(upper=0)

    avg_gain = gain.rolling(window=period).mean()
    avg_loss = loss.rolling(window=period).mean()

    rs = avg_gain / avg_loss
    rsi = 100 - (100 / (1 + rs))
    return rsi

df["RSI14"] = calculate_rsi(df["Close"])
print(df[["Close", "RSI14"]].tail(10))

MACD

def calculate_macd(series: pd.Series,
                   fast: int = 12,
                   slow: int = 26,
                   signal: int = 9) -> pd.DataFrame:
    ema_fast = series.ewm(span=fast, adjust=False).mean()
    ema_slow = series.ewm(span=slow, adjust=False).mean()
    macd_line = ema_fast - ema_slow
    signal_line = macd_line.ewm(span=signal, adjust=False).mean()
    histogram = macd_line - signal_line

    return pd.DataFrame({
        "MACD": macd_line,
        "Signal": signal_line,
        "Histogram": histogram
    })

macd_df = calculate_macd(df["Close"])
df = pd.concat([df, macd_df], axis=1)
print(df[["Close", "MACD", "Signal", "Histogram"]].tail(10))




수집 데이터 저장하기 — CSV와 엑셀 모두 활용

CSV로 저장

from datetime import datetime

def save_stock_data(df: pd.DataFrame, ticker: str):
    today = datetime.now().strftime("%Y%m%d")
    filename = f"{ticker.replace('.', '_')}_{today}.csv"
    df.to_csv(filename, encoding="utf-8-sig")
    print(f"저장 완료: {filename}")
    return filename

save_stock_data(df, "005930.KS")

여러 종목을 시트별로 엑셀 저장

from openpyxl import Workbook
import yfinance as yf
import pandas as pd

tickers = {
    "삼성전자": "005930.KS",
    "SK하이닉스": "000660.KS",
    "NAVER": "035420.KS",
}

with pd.ExcelWriter("korean_stocks.xlsx", engine="openpyxl") as writer: for name, ticker in tickers.items(): df = yf.Ticker(ticker).history(period="6mo") df["MA20"] = df["Close"].rolling(20).mean() df["RSI14"] = calculate_rsi(df["Close"]) # [수정] 인덱스가 날짜 형식(DatetimeIndex)인 경우 시간대(Timezone) 제거 if isinstance(df.index, pd.DatetimeIndex): df.index = df.index.tz_localize(None) # 혹시 컬럼 중에도 날짜가 존재할 경우를 대비한 안전장치 for col in df.select_dtypes(include=['datetime64[ns, UTC]', 'datetimetz']).columns: df[col] = df[col].dt.tz_localize(None) df.to_excel(writer, sheet_name=name) print(f"{name} 저장 완료") print("\n전체 저장 완료: korean_stocks.xlsx")




실전 예제 — 관심 종목 일괄 스크리닝

RSI가 30 이하인 과매도 종목을 자동으로 필터링하는 스크리닝 예제입니다.

import yfinance as yf
import pandas as pd
from datetime import datetime

watch_list = {
    "삼성전자": "005930.KS",
    "SK하이닉스": "000660.KS",
    "카카오": "035720.KS",
    "NAVER": "035420.KS",
    "현대차": "005380.KS",
    "LG에너지솔루션": "373220.KS",
}

results = []

for name, ticker in watch_list.items():
    try:
        df = yf.Ticker(ticker).history(period="3mo")

        if len(df) < 20:
            continue

        close = df["Close"].iloc[-1]
        ma20  = df["Close"].rolling(20).mean().iloc[-1]
        rsi   = calculate_rsi(df["Close"]).iloc[-1]
        vol   = df["Volume"].iloc[-1]

        results.append({
            "종목명": name,
            "현재가": f"{close:,.0f}",
            "MA20": f"{ma20:,.0f}",
            "MA20대비": f"{(close/ma20 - 1)*100:+.1f}%",
            "RSI14": f"{rsi:.1f}",
            "과매도": "⚠️ 과매도" if rsi < 30 else "",
        })

    except Exception as e:
        print(f"{name} 수집 실패: {e}")

result_df = pd.DataFrame(results)
print(result_df.to_string(index=False))

result_df.to_csv(
    f"screening_{datetime.now().strftime('%Y%m%d')}.csv",
    encoding="utf-8-sig",
    index=False
)




자주 발생하는 오류와 해결법

오류 1 — 한국 주식 데이터가 빈 DataFrame으로 반환되는 경우

원인: 야후 파이낸스 서버 일시 장애 또는 티커 오류
해결: .KS / .KQ 접미사 재확인, 잠시 후 재시도. 지속되면 pykrx 라이브러리 병행 사용

pip install pykrx
from pykrx import stock

# pykrx로 삼성전자 일봉 데이터 수집
df = stock.get_market_ohlcv("20240101", "20241231", "005930")
print(df.head())

오류 2 — yfinance 데이터에 결측값(NaN)이 포함된 경우

원인: 거래정지일, 공휴일 등으로 데이터 공백 발생
해결: fillna() 또는 dropna()로 처리

df = df.dropna(subset=["Close"])           # Close가 NaN인 행 제거
df["MA20"] = df["MA20"].fillna(method="ffill")  # 이전 값으로 채우기

오류 3 — 대량 요청 시 속도 저하 또는 차단

원인: 야후 파이낸스 요청 속도 제한
해결: 종목 간 time.sleep(0.5~1) 추가, 한 번에 너무 많은 종목을 동시 요청하지 않도록 배치 처리


마무리 — 핵심 요약

✅ Python 주식 데이터 수집기 체크리스트

  1. 미국 주식: yf.Ticker("AAPL").history(period="1y")
  2. 한국 주식: 티커 뒤에 .KS(코스피) 또는 .KQ(코스닥) 추가
  3. KRX 종목 코드 목록: kind.krx.co.kr 공개 API 활용
  4. 기술적 지표: MA는 rolling().mean(), RSI·MACD는 직접 계산 함수 작성
  5. 저장: 단일 종목은 CSV, 다중 종목은 시트 분리 엑셀
  6. 데이터 불안정 시: pykrx 라이브러리를 보조 수단으로 활용
  7. 대량 수집 시: 반드시 time.sleep() 추가해 서버 부하 방지

다음으로 알아두면 좋은 것

데이터 수집기가 완성됐다면, 다음 단계는 수집된 데이터를 Streamlit 대시보드로 시각화하거나, 매일 자동 수집 후 이상 신호 발생 시 알림을 발송하는 파이프라인을 구축하는 것입니다. schedule 라이브러리로 장 마감 후 자동 수집을 예약하고, RSI 과매도·골든크로스 등 조건이 충족될 때 텔레그램이나 이메일로 알림을 받는 시스템까지 연결하면 완전한 자동화 분석 환경이 갖춰집니다.




댓글

이 블로그의 인기 게시물

1.Python 설치부터 실행까지 10분 만에 끝내기 — 초보자도 바로 따라 하는 완벽 가이드

30.Python pyautogui로 마우스와 키보드 자동화하기: VSCode 실전 가이드

29.Python subprocess로 외부 명령어 실행하기: VSCode 실전 활용법