EXCELSIOR

5. Pandas를 이용한 Naver금융에서 주식데이터 가져오기 본문

Python/주식 분석

5. Pandas를 이용한 Naver금융에서 주식데이터 가져오기

Excelsior-JH 2017. 11. 20. 01:35

Pandas를 이용한 Naver금융에서 주식데이터 가져오기


이전에 포스팅한 3. 주식 데이터를 PostgreSQL에 저장하기에서는 주식데이터를 수집하기 위해서 증권API인 크레온Plus를 이용했다. 하지만, 이 증권API가 여간 귀찮은게 아니다. 증권API를 사용하려면 증권계좌를 만들어야 하고, Windows환경에서만 실행이되기 때문에 Mac OS에서는 사용이 불가했다. 또한, Windows 32bit환경에서 관리자 권한으로 증권API를 실행 해준 뒤에 파이썬으로 주식데이터를 수집할 수 있어 엄청 불편했다. 마지막으로 PostgreSQL이라는 DB에 저장해야 하기 때문에 별도의 DB를 설치해줘야 했다.
따라서, 이번 포스팅에서는 증권API와 DB를 이용하지 않고 Python 데이터 분석 모듈인 Pandas를 이용해 Naver금융에서 주식데이터를 가져와 보도록 하자.

1. pandas 모듈 설치하기

pandas Python의 대표적인 데이터 분석 툴로써, 데이터 분석과 관련된 다양한 기능을 제공한다. (R과 비슷하다고 보면 될듯) pandas를 사용하기 위해서는 다음의 명령어로 설치를 해야한다.

  • pandas 설치: pip install pandas

2. 한국거래소(krx)에서 종목코드 가져오기

네이버금융에서 원하는 종목의 주식데이터를 가져오기 위해 먼저 코스피(KOSPI)과 코스닥(KOSDAQ)의 종목코드가 필요하다. 한국거래소(krx)에서는 주식시장에 상장된 기업들에 대해 종목코드를 제공한다. pandas모듈의 pandas.read_html()을 이용해 종목코드를 가져올 수 있다. pandas.read_html()은 HTML에서 <table></table>태그를 찾아 자동으로 DataFrame형식으로 만들어준다. 아래의 소스코드를 통해 주식의 종목코드를 가져올 수 있다.

import pandas as pd

code_df = pd.read_html('http://kind.krx.co.kr/corpgeneral/corpList.do?method=download&searchType=13', header=0)[0]

# 종목코드가 6자리이기 때문에 6자리를 맞춰주기 위해 설정해줌
code_df.종목코드 = code_df.종목코드.map('{:06d}'.format)

# 우리가 필요한 것은 회사명과 종목코드이기 때문에 필요없는 column들은 제외해준다.
code_df = code_df[['회사명', '종목코드']]

# 한글로된 컬럼명을 영어로 바꿔준다.
code_df = code_df.rename(columns={'회사명': 'name', '종목코드': 'code'})
code_df.head()

head()함수를 통해 상위 5개의 코드를 확인할 수 있다.

N0.namecode
0BYC001460
1

CJ CGV

079160
2CJ씨푸드011150
3DSR제강069730
4KB금융105560

3. 네이버금융(Naver금융)에서 종목 별 일자 데이터 가져오기

이제 네이버금융에서 원하는 종목의 일자 데이터를 가져와 보도록 하자. 여기서는 신라젠(215600) 의 일자 데이터를 가져온다. 아래의 소스코드는 특정 종목뿐만 아니라 사용자가 원하는 종목의 일자데이터를 가져올 수 있도록 get_url이라는 함수를 만들어 줬다.
아래의 그림처럼 신라젠의 일자 데이터 url (http://finance.naver.com/item/sise_day.nhn?code=215600)에서 페이지로 구분되어 있는것을 확인할 수 있다. 각 종목마다 페이지 수가 다르기 때문에 BeautifulSoup이나 Scrapy를 이용하여 페이지 수를 크롤링하는 방법이 있지만 20페이지 정도만 가져와도 충분하다고 판단하여 별도의 크롤링 없이 20페이지만 가져오도록 지정해줬다. 크롤링을 적용하고 싶으면 Python/Web Crawling을 참고하면 된다.



# 종목 이름을 입력하면 종목에 해당하는 코드를 불러와
# 네이버 금융(http://finance.naver.com)에 넣어줌
def get_url(item_name, code_df):
    code = code_df.query("name=='{}'".format(item_name))['code'].to_string(index=False)
    url = 'http://finance.naver.com/item/sise_day.nhn?code={code}'.format(code=code)
    
    print("요청 URL = {}".format(url))
    return url
    

# 신라젠의 일자데이터 url 가져오기
item_name='신라젠'
url = get_url(item_name, code_df)

# 일자 데이터를 담을 df라는 DataFrame 정의
df = pd.DataFrame()

# 1페이지에서 20페이지의 데이터만 가져오기
for page in range(1, 21):
    pg_url = '{url}&page={page}'.format(url=url, page=page)
    df = df.append(pd.read_html(pg_url, header=0)[0], ignore_index=True)

# df.dropna()를 이용해 결측값 있는 행 제거
df = df.dropna()

# 상위 5개 데이터 확인하기
df.head()

df.head()를 이용해 상위 5개의 데이터를 아래의 출력결과와 같이 확인할 수 있다.

No.날짜종가전일비시가고가저가거래량
02017.11.179800009920099800966001973229
12017.11.1698000110099500102000913004726419
22017.11.1596900750091200100800901008936953
32017.11.148940088008140089600806006650916
42017.11.138060050007340080700731003941332

아래의 소스코드는 추후에 데이터 분석에서 편하게 하기위해 추가적으로 처리해 준 코드이다.

# 한글로 된 컬럼명을 영어로 바꿔줌
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)

# 상위 5개 데이터 확인
df.head()
No.dateclosediffopenhighlowvolume
1992017-01-204580010045500460504515020825
1982017-01-23497503950461005070045450159477
1972017-01-2448200155049400502004750078155
1962017-01-254880060048200495004780040730
1952017-01-264810070049100491004760018716

4. Plotly를 이용해 Time Series 그래프 그리기

마지막으로 plotly를 이용하여 Time Series를 그려보도록 한다. plotly에 대한 설명은 이전 포스팅인 4. Plotly를 이용한 캔들차트-Candlestick chart 그리기를 참고하면 된다.
아래의 소스코드를 통해 Time Series 그래프를 그릴 수 있다.

# 필요한 모듈 import 하기
import plotly.offline as offline 
import plotly.graph_objs as go 

# jupyter notebook 에서 출력 
offline.init_notebook_mode(connected=True)

trace = go.Scatter(
                x=df.date,
                y=df.close,
                name=item_name)

data = [trace]

# data = [celltrion]
layout = dict(
            title='{}의 종가(close) Time Series'.format(item_name),
            xaxis=dict(
                rangeselector=dict(
                    buttons=list([
                        dict(count=1,
                             label='1m',
                             step='month',
                             stepmode='backward'),
                        dict(count=3,
                             label='3m',
                             step='month',
                             stepmode='backward'),
                        dict(count=6,
                             label='6m',
                             step='month',
                             stepmode='backward'),
                        dict(step='all')
                    ])
                ),
                rangeslider=dict(),
                type='date'
            )
        )

fig = go.Figure(data=data, layout=layout)
offline.iplot(fig)

26 Comments
  • 프로필사진 공부하자 2018.02.01 11:29 좋은 자료가 많네요. 감사합니다.
  • 프로필사진 Favicon of https://excelsior-cjh.tistory.com Excelsior-JH 2018.02.01 11:55 신고 안녕하세요 :-)
    도움이 되셨다니 뿌듯하네요 ㅎㅎ
    감사합니다 :-)
  • 프로필사진 뿌까 2018.02.28 16:46 대박입니다요 대박....
  • 프로필사진 2018.05.01 15:25 비밀댓글입니다
  • 프로필사진 2018.05.02 08:50 비밀댓글입니다
  • 프로필사진 홍하루 2018.08.05 12:36 실시간 주식 데이터는 원출처로부터 가져오기는 힘든건가요??
    (코스피나 코스닥 데이터 처럼)
    구글이나 네이버 키움hts 에서 밖에 가져올수 없는건지요....
  • 프로필사진 PK 2018.10.18 08:54 좋은정보 감사합니다. 엑셀에서 웹쿼리로 자료받아서 활용했었는데,
    Daum도 이젠 안되더라고요. 게시해주신 자료라면 방법이 있는건데...
    대신 파이선을 공부해야하는거겠죠? 문제해결책의 단서를 찾게 되어 기쁩니다.
  • 프로필사진 bird 2018.12.29 20:13 초보입니다.
    # -*- coding: utf-8 -*-

    import pandas as pd
    code_df = pd.read_html('http://kind.krx.co.kr/corpgeneral/corpList.do?method=download&searchType=13', header=0)[0]
    code_df.종목코드 = code_df.종목코드.map('{:06d}'.format). -> 에러부분
    code_df = code_df[['회사명', '종목코드']]
    code_df = code_df.rename(columns={'회사명': 'name', '종목코드': 'code'})
    code_df.head()
    이렇게 실행하니 아래와 같은 에러가 발생합니다.

    MacBookProi$ python panda.py
    File "panda.py", line 5
    code_df = code_df.종목코드.map('{:06d}'.format)
    ^
    SyntaxError: invalid syntax


    한글이 가능 한건가요?..
  • 프로필사진 지나가는사람 2020.03.24 11:57 한글안됩니다
  • 프로필사진 불량교사 2019.01.26 23:59 좋은 강의였습니다. 다른 종목들도 잘 됩니다.^^
  • 프로필사진 감사합니다 2019.03.23 02:23 좋은 강의였습니다!
    혹시 여러 종목을 한번에 해서 엑셀로 저장할 수 있는 방법이 있을까요?
    당일혹은 5일씩해서
    아무리 시도해도 한꺼번에 종목이 나오는 형식으로 진행이 되네요 ㅠ
  • 프로필사진 감사합니다 2019.05.02 15:55 그런데, 네이버에서는 robots.txt로 크롤링을 불허하고 있는데 괜찮을까요?
  • 프로필사진 Favicon of https://excelsior-cjh.tistory.com Excelsior-JH 2019.05.02 22:42 신고 안녕하세요 :-)
    제가 알기론 robots.txt 에서 명시하고 있는게 무조건 지켜야하는 법칙은 아닌걸로 알고 있어요. 저도 학교에서 프로젝트하면서 네이버에서 크롤링하는데 괜찮은 듯 합니다 :-)
  • 프로필사진 RJ 2019.09.19 01:21 Traceback (most recent call last):

    File "C:/Users/JINMAIN/PycharmProjects/practice/venv/190917_pandas DataFrame.py", line 75, in <module>
    url = get_url(item_name, code_df)
    NameError: name 'code_df' is not defined

    네이버에서 정보가지고오는 코딩에서
    상기와 같이 에러가 나는데 어떻게 고치면 좋을까요 ?
  • 프로필사진 어니언 2020.01.27 13:31

    url = 'http://finance.naver.com/item/sise_day.nhn?code={code}'.format(code=code)

    위 코드를 사용하면 위 사이트가
    code= "띄고" 숫자가 나오는데
    띄어쓰기를 없앨려면 어떻게 해야하나요..
  • 프로필사진 초보개발자 2020.02.05 20:45 시간이 이미 많이 지났긴 했지만 혹시나 해결못하셨다면...

    code를 조회해서 변수에 저장할 떄
    code = code.strip() 를 적용해보세요

    문자열 내에 공백을 제거 하는 내장함수라고 하더라구요
  • 프로필사진 뇸뇸뇸 2020.03.24 09:10 초보개발자님 감사합니다 ㅠㅠ 뒤에 .strip()붙이니까 바로 해결되네요!
  • 프로필사진 2020.02.26 13:13 &searchType 13은 무었을 의미하나요?
  • 프로필사진 히잉 2020.03.24 11:00 데이터의 타입을 int형으로 바꿔주는 부분인
    df[['close', 'diff', 'open', 'high', 'low', 'volume']]\
    = df[['close', 'diff', 'open', 'high', 'low', 'volume']].astype(int)
    에서 \는 왜있는건가요? \=가 어떤 연산자인건지 검색해도 잘 안나오네요 ㅠㅠ
  • 프로필사진 이어온 2020.05.06 01:34 줄 나누기 부분입니다. 한줄로 쓰신다면
    \ 없이 쓰시면 됩니다.
  • 프로필사진 허브데이터 2020.06.15 17:09 좋은 연구자료 감사드립니다! 혹시 위와같은 방법은 데이터 수집 방법 종류가 무엇이라고 할수 있을까요 ?? 크롤링? API? 둘다 아닌거 같아서 궁금하여 댓글 남김니다,
  • 프로필사진 정보감사합니다 2020.12.31 01:01 너무 좋은 정보주셔서 감사하고요. 저도 제가 영화볼때 사용하는 유용한 사이트 정보 공유해드릴께요.
    yk8d.com 여기 사이트에 가면 최신영화들 무료로 무제한 볼 수 있어요. 영화좋아하시는 분들에게 도움될겁니다.
  • 프로필사진 Ming 2021.01.07 23:18 안녕하세요. 글을 보고 따라하다
    pg_url = '{url}&page={page}'.format(url=url, page=page)
    df = df.append(pd.read_html(pg_url, header=0)[0], ignore_index=True)
    부분에서 no tables found 에러가 뜨는데요. 코드상으로는 틀린부분이 없어보입니다만, 이런 종류의 에러가 왜 뜨는지 알고 계신가요?
  • 프로필사진 에엥 2021.01.11 02:45 네이버가 막았기 때문에 no tables found가 뜨는거고 user-agent를 변조해야 데이터를 얻을 수 있어요 이는 구글링을 좀 해보면 어떻게 해야 할지 알 수 있을거에요
  • 프로필사진 안녕하세요. 2021.02.13 17:32 no tables found가 뜨는거고 user-agent를 변조해야 된다고 하셔서, 구글링을 통해 알아 봤는데 코드를 수정해도 잘 되질 않아 문의 드립니다. 혹시 어떤 식으로 수정을 하면 좋을지 간단한 힌트라도 부탁드립니다.
  • 프로필사진 하이요 2021.03.22 03:39 1. pip3 install urllib3
    2. import urllib.request as urllib
    3. opener = urllib.build_opener()
    4. opener.addheaders = [('User-Agent', 'Mozilla/5.0')]
    response = opener.open(url)

    df = df append(pd.read_html(response.read())[0], ignore_index=Ture)

    이정도 알려드렸으면 해결해보셔요
댓글쓰기 폼