본문 바로가기
  • You find inspiration to create your own path !
업무 자동화/python

특정 종목의 기본 데이터 가져오기

by ToolBOX01 2026. 8. 3.
반응형

네이버 금융에서 특정 주식 종목의 지정한 연도(기본값: 2026년) 일별 주가 데이터를 수집하여 CSV 파일로 저장하는 크롤링 프로그램입니다.

📌 주요 기능 요약

1.종목명 자동 조회 (get_stock_name)

  • 입력한 종목코드(예: 삼성전자 005930)를 바탕으로 네이버 금융 메인 페이지에 접속합니다.
  • 웹페이지 타이틀에서 종목명을 추출한 뒤, 파일명으로 사용할 수 없는 특수문자(\, /, :, *, ?, ", <, >, |)를 자동으로 제거합니다.

2. 주가 수집 및 파싱 (get_stock_data)

  • 네이버 금융의 일별 시세 페이지(sise_day.naver)에서 페이징 처리(max_pages=30)를 통해 과거 데이터를 순차적으로 가져옵니다.
  • pandas.read_html을 활용해 HTML 테이블 데이터를 데이터프레임으로 변환합니다.
  • 날짜 데이터를 표준 날짜 형식(datetime)으로 변환하여, 사용자가 지정한 연도(target_year)의 데이터만 필터링합니다.
  • 지정한 연도보다 과거 데이터가 나타나면 크롤링을 효율적으로 조기에 중단합니다.

3.정제 및 CSV 저장

  • 수집된 데이터에서 필요한 컬럼(날짜, 종가, 시가, 고가, 저가, 거래량)만 선택합니다.
  • 날짜를 보기 쉬운 문자열 형태(YYYY-MM-DD)로 변환하고, 날짜 오름차순으로 정렬합니다.
  • 실행 경로에 StockData 폴더를 생성하고, 정제된 데이터를 {종목명}.csv 파일로 안전하게(utf-8-sig 인코딩) 저장합니다.

 

.... 전체적인 처리 순서는 다음과 같습니다.

더보기

사용자
    │
    ▼
종목코드 입력 (005930)
    │
    ▼
네이버 금융 일별시세 페이지 접속
    │
    ▼
HTML 다운로드
    │
    ▼
BeautifulSoup로 테이블 추출
    │
    ▼
Pandas DataFrame 변환
    │
    ▼
2026년 데이터만 추출
    │
    ▼
모든 페이지 반복
    │
    ▼
정렬 후 DataFrame 반환

 


  • ✅ Pandas 3.x 지원 (StringIO 사용)
  • ✅ 종목코드만 입력하면 종목명 자동 검색
  • ✅ 종목명으로 CSV 저장
  • ✅ StockData 폴더 자동 생성
  • ✅ UTF-8(BOM) 저장(엑셀 한글 깨짐 방지)
  • ✅ 네트워크 예외 처리
  • ✅ 진행상황 출력
  • ✅ 함수 분리로 유지보수 용이
 

📦 필수 패키지 목록

  • pandas: HTML 테이블 데이터 파싱, 데이터프레임 가공, 정렬 및 CSV 저장 기능 수행
  • requests: 네이버 금융 웹페이지에 HTTP 요청을 보내고 HTML 소스코드를 받아옴
  • beautifulsoup4: 받아온 HTML 문서를 파싱하여 원하는 태그(종목명 타이틀, 시세 테이블 등)를 추출
  • pandas.read_html()이 HTML 테이블을 정상적으로 읽어오려면 내부적으로 lxml 또는 html5lib 같은 파서 라이브러리가 필요하므로, lxml을 함께 설치하는 것을 권장합니다.

터미널(또는 명령 프롬프트)에서 아래 명령어를 입력하여 필요한 패키지들을 한 번에 설치할 수 있습니다.

pip install pandas requests beautifulsoup4 lxml

 

▶ 파이썬 코드

import os
from io import StringIO
import pandas as pd
import requests
from bs4 import BeautifulSoup


# ==========================================
# 공통 설정
# ==========================================
HEADERS = {
    "User-Agent":
        "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
        "AppleWebKit/537.36 (KHTML, like Gecko) "
        "Chrome/138.0.0.0 Safari/537.36"
}


# ==========================================
# 종목명 가져오기
# ==========================================
def get_stock_name(item_code):

    url = f"https://finance.naver.com/item/main.naver?code={item_code}"

    try:
        response = requests.get(
            url,
            headers=HEADERS,
            timeout=10
        )

        response.raise_for_status()

    except Exception as e:
        print("종목명 조회 실패 :", e)
        return item_code

    soup = BeautifulSoup(response.text, "html.parser")

    title = soup.find("title")

    if title is None:
        return item_code

    stock_name = title.text.split(":")[0].strip()

    # 파일명에 사용할 수 없는 문자 제거
    invalid = '\\/:*?"<>|'

    for ch in invalid:
        stock_name = stock_name.replace(ch, "")

    return stock_name


# ==========================================
# 주가 데이터 수집
# ==========================================
def get_stock_data(
        item_code,
        target_year=2026,
        max_pages=30):

    stock_name = get_stock_name(item_code)

    print("=" * 60)
    print(f"종목명   : {stock_name}")
    print(f"종목코드 : {item_code}")
    print("=" * 60)

    df_list = []

    for page in range(1, max_pages + 1):

        print(f"{page} 페이지 수집중...")

        url = (
            "https://finance.naver.com/item/sise_day.naver"
            f"?code={item_code}&page={page}"
        )

        try:

            response = requests.get(
                url,
                headers=HEADERS,
                timeout=10
            )

            response.raise_for_status()

        except Exception as e:

            print("페이지 오류 :", e)
            break

        soup = BeautifulSoup(
            response.text,
            "html.parser"
        )

        table = soup.find(
            "table",
            class_="type2"
        )

        if table is None:
            break

        # Pandas 3.x 대응
        html = StringIO(str(table))

        df = pd.read_html(html)[0]

        df = df.dropna(subset=["날짜"])

        if df.empty:
            break

        df["날짜"] = pd.to_datetime(
            df["날짜"],
            errors="coerce"
        )

        df = df.dropna(subset=["날짜"])

        df_year = df[
            df["날짜"].dt.year == target_year
        ]

        if not df_year.empty:
            df_list.append(df_year)

        # 목표연도보다 이전 데이터가 나오면 종료
        if df["날짜"].dt.year.min() < target_year:
            break

    if len(df_list) == 0:

        print(f"{target_year}년 데이터가 없습니다.")

        return pd.DataFrame()

    result = pd.concat(
        df_list,
        ignore_index=True
    )

    result = result.sort_values(
        by="날짜"
    ).reset_index(drop=True)

    columns = [
        "날짜",
        "종가",
        "시가",
        "고가",
        "저가",
        "거래량"
    ]

    result = result[columns]

    result["날짜"] = result["날짜"].dt.strftime(
        "%Y-%m-%d"
    )

    # ======================================
    # 저장 폴더 생성
    # ======================================

    save_folder = "StockData"

    os.makedirs(
        save_folder,
        exist_ok=True
    )

    file_path = os.path.join(
        save_folder,
        f"{stock_name}.csv"
    )

    result.to_csv(
        file_path,
        index=False,
        encoding="utf-8-sig"
    )

    print()
    print("CSV 저장 완료")
    print(file_path)
    print()

    return result


# ==========================================
# 메인
# ==========================================
if __name__ == "__main__":

    stock_code = "005930"

    df = get_stock_data(
        stock_code,
        target_year=2026
    )

    if not df.empty:

        print(df.head())

        print()

        print(f"총 {len(df)}건")

 

실행 결과

============================================================
종목명   : 삼성전자
종목코드 : 005930
============================================================
1 페이지 수집중...
2 페이지 수집중...
3 페이지 수집중...

CSV 저장 완료
StockData\삼성전자.csv

          날짜     종가     시가     고가     저가      거래량
0 2026-01-02  70100  69800  70300  69700  12345678
1 2026-01-05  70400  70200  70500  70100  11234567

 

▶ 여러 종목을 한 번에 수집하기

메인 부분만 아래처럼 바꾸면 됩니다.

if __name__ == "__main__":

    stock_codes = [
        "005930",   # 삼성전자
        "000660",   # SK하이닉스
        "010140",   # 삼성중공업
        "225570",   # 넥슨게임즈
        "005380",   # 현대차
    ]

    for code in stock_codes:

        try:
            get_stock_data(code, target_year=2026)
        except Exception as e:
            print(f"{code} 오류 :", e)
            print()
 

그러면 StockData 폴더 아래에 다음과 같이 자동 저장됩니다.

StockData/
 ├─ 삼성전자.csv
 ├─ SK하이닉스.csv
 ├─ 삼성중공업.csv
 ├─ 넥슨게임즈.csv
 └─ 현대차.csv
 

"이 데이터 구조는 이후 이동평균선, MACD, RSI, 거래량 분석 등으로 확장하기에도 적합합니다."

 

※ 개선하면 더 좋은 점 : AI로 위 코드에 아래 내용이 포함 되도록 합니다

이 코드는 실무에서도 사용할 수 있을 정도로 잘 작성되어 있지만, 몇 가지를 보완하면 더욱 견고해집니다.

  1. target_year를 함수 인자로 받아 임의의 연도(예: 2025, 2024 등)를 조회할 수 있도록 일반화할 수 있습니다.
  2. requests.get()에 timeout을 지정하여 네트워크 지연 시 무한 대기를 방지할 수 있습니다.
  3. pd.read_html()은 최신 Pandas 버전에서 문자열 입력 관련 경고가 발생할 수 있으므로 io.StringIO를 사용하는 방식으로 변경하면 호환성이 좋아집니다.
  4. 페이지 요청 사이에 time.sleep(0.5~1초) 정도를 추가하면 서버에 부담을 줄이고 차단 가능성을 낮출 수 있습니다.
  5. try-except를 사용해 네트워크 오류나 HTML 구조 변경에도 프로그램이 중단되지 않도록 예외 처리를 추가하면 더욱 안정적인 크롤러가 됩니다.

 


by korealionkk@gmail.com

반응형