반응형
2. Data를 파싱하여 깔끔한 출력으로 만들기

앞에서 찾은 URL을 확인하면 4가지 검색조건이 존재함을 알 수 있다.

종목코드, 검색시작 날짜, 검색종료날짜, 일봉-주봉-월봉 이다. 이를 참고하여 코딩을 진행한다.

https://api.finance.naver.com/siseJson.naver?symbol=005930&requestType=1&startTime=20190624&
endTime=20210901&timeframe=day

 

먼저 기본적인 틀을 잡아준다.  함수에는 4가지 검색 조건을 변수로 넣어주었다. 기본적으로 변수값을 입력하지 않으면

종목은 삼성전자(005930), 기간은 오늘로부터 300일(거래일 1년), 봉은 일봉으로 설정하도록 하였다. 

from bs4 import BeautifulSoup	# 파싱용 라이브러리
from urllib.request import urlopen	# url을 가져오기위한 라이브러리
import ssl	# 인증서 충돌 방지용
import pandas as pd	# Dataframe 형태로 만들기 위한 판다스 라이브러리

def ohlcv_data(code='005930',starttime='default',endtime='today',timeframe='day'):
    pass
    
# 테스트를 위한 구간
if __name__ == '__main__':
    pass

 

이제 ohlcv_data 함수에서 html 주소에서 데이터를 가져오도록 하자. 

변수url에 파싱할 url을할당하고, urlopen을 이용해 소켓 객체용 ssl래퍼로 url을 지정하고 

soup 변수에 BeautifulSoup을 이용하여 위 res를 html 파서로 utf-8 인코딩으로 파싱을 지정한다.

def ohlcv_data(code='005930',starttime='default',endtime='today',timeframe='day'):
    url = 'https://api.finance.naver.com/siseJson.naver?symbol=005930&requestType=1&startTime=20190624&endTime=20210901&timeframe=day'
    res = urlopen(url, context=ssl.create_default_context())
    soup = BeautifulSoup(res.read(), 'html.parser', from_encoding='utf-8')
    print(soup)
if __name__ == '__main__':
    ohlcv_data()

 

파서를 [html.parser]를 이용하는 이유는 html형식 문서를 파싱하기 때문이고

인코딩을 utf-8로 따로 지정해주는 이유는 한글이 깨지는것을 방지해주기 위함이다(보통 utf-8을 사용한다.)

소켓 객체용 래퍼 지정에 대해서 심도있는 공부를 원한다면 아래 링크를 참고하면 된다.

 

https://docs.python.org/ko/3/library/ssl.html

 

ssl — 소켓 객체용 TLS/SSL 래퍼 — Python 3.9.7 문서

이 모듈은 클라이언트 쪽과 서버 쪽 네트워크 소켓에 대한 전송 계층 보안(Transport Layer Security) (《보안 소켓 계층(Secure Sockets Layer)》 이라고도 함) 암호화와 피어 인증 기능에 대한 액세스를 제공

docs.python.org

 

그리고 RUN을 진행하면 아래와 같이 파싱이 된것을 볼 수 있다.

 

이제 이 데이터를 list형태에 한 데이터씩 들어가도록 split할 것인데, 각 데이터들이 쉼표( , )로 나뉘어 있지만, 각 데이터 내부의 값들도 쉼표로 나뉘어있기 때문에, 더 스플릿이 명확하게 되는 " ], "으로 스플릿을 진행해보자

def ohlcv_data(code='005930',starttime='default',endtime='today',timeframe='day'):
    url = 'https://api.finance.naver.com/siseJson.naver?symbol=005930&requestType=1&startTime=20190624&endTime=20210901&timeframe=day'
    res = urlopen(url, context=ssl.create_default_context())
    soup = BeautifulSoup(res.read(), 'html.parser', from_encoding='utf-8')
    data = str(soup).split('],') # [, 로 스플릿 진행
    print("1번째 값" +data[0])
    print("2번째 값" +data[1])
    print("3번째 값" +data[2])
if __name__ == '__main__':
    ohlcv_data()

 

이렇게 스플릿 된 각각의 값을 출력해보면 아래와 같이 출력되는 것을 알 수 있다.

 

이제 각 값에서 다시 쉼표 띄어쓰기 ", " 기준으로 스플릿 하여 날짜, 시가, 고가, 저가, 종가, 거래량으로 나누도록 해보자. 대표로  2번째 값인 ["20190624", 45200, 45800, 45200, 45500, 6085066, 57.14

쉼표 띄어쓰기", " 기준으로 스플릿 해보자

def ohlcv_data(code='005930',starttime='default',endtime='today',timeframe='day'):
    url = 'https://api.finance.naver.com/siseJson.naver?symbol=005930&requestType=1&startTime=20190624&endTime=20210901&timeframe=day'
    res = urlopen(url, context=ssl.create_default_context())
    soup = BeautifulSoup(res.read(), 'html.parser', from_encoding='utf-8')
    data = str(soup).split('],')
    data2 = data[1].split(', ')
    print("1번째 값"+data2[0])
    print("2번째 값"+data2[1])
    print("3번째 값"+data2[2])
    print("4번째 값"+data2[3])
    print("5번째 값"+data2[4])
    print("6번째 값"+data2[5])
if __name__ == '__main__':
    ohlcv_data()

 

그럼 아래와 같이 data[1]이 6개의 값을 가지는 list인 data2로 스플릿 된 것을 확인 할 수 있다.

OHLCV 데이터는 별도의 수정이 필요하지 않게 파싱이 되었는데, 날짜 값이 지저분하게 파싱되었다. 

 

data2[0]이 날짜가 포함된 값이고 이것을 다시 "[ 를 기준으로 스플릿 해보면 

def ohlcv_data(code='005930',starttime='default',endtime='today',timeframe='day'):
    url = 'https://api.finance.naver.com/siseJson.naver?symbol=005930&requestType=1&startTime=20190624&endTime=20210901&timeframe=day'
    res = urlopen(url, context=ssl.create_default_context())
    soup = BeautifulSoup(res.read(), 'html.parser', from_encoding='utf-8')
    data = str(soup).split('],')
    data2 = data[1].split(', ')
    data3 = data2[0].split('["')  # data2의 리스트 1번 값인 날짜가 포함된 문자를 ["로 스플릿
    print(data3)
if __name__ == '__main__':
    ohlcv_data()

 

아래와같이 1번째 값(0번째랑 헷갈리지 않도록 한다)에 날짜가 나온다. 

 

def ohlcv_data(code='005930',starttime='default',endtime='today',timeframe='day'):
    url = 'https://api.finance.naver.com/siseJson.naver?symbol=005930&requestType=1&startTime=20190624&endTime=20210901&timeframe=day'
    res = urlopen(url, context=ssl.create_default_context())
    soup = BeautifulSoup(res.read(), 'html.parser', from_encoding='utf-8')
    data = str(soup).split('],')
    data2 = data[1].split(', ')
    data3 = data2[0].split('["')
    data4 = data3[1].split('"') # 쌍따옴표로 스플릿
    print(data4)
if __name__ == '__main__':
    ohlcv_data()

 

여기서 마지막으로 다시 쌍따옴표 " 를 기준으로 스플릿하면

0번 째 값으로 날짜를 출력할 수 있다.

즉 위 코드를 좀 정리하면 날짜는 data를 <],> 로 스플릿한 1번째 값을 <, >로 스플릿한 0번째 값을 <[">로 스플릿한 1번째 값을 <">로 스플릿한 0번째 값이 된다. 왜 이렇게 split만 쓰는가 의문이 들 수 있는데, html 게시자 (여기서는 네이버)가 html의 포맷을 바꾸게 될 경우 replace나 remove에 비해 split이 더욱 유연하게 대응이 가능하기 때문이다.

def ohlcv_data(code='005930',starttime='default',endtime='today',timeframe='day'):
    url = 'https://api.finance.naver.com/siseJson.naver?symbol=005930&requestType=1&startTime=20190624&endTime=20210901&timeframe=day'
    res = urlopen(url, context=ssl.create_default_context())
    soup = BeautifulSoup(res.read(), 'html.parser', from_encoding='utf-8')
    data = str(soup).split('],')
    data2 = data[1].split(', ')
    data3 = data2[0].split('["')
    data4 = data3[1].split('"')
    date = str(soup).split('],')[1].split(', ')[0].split('["')[1].split('"')[0]
    print(date)
if __name__ == '__main__':
    ohlcv_data()

 

이제 코드를 조금 더 정리해서 값을 각 변수에 할당하도록 하자 

def ohlcv_data(code='005930',starttime='default',endtime='today',timeframe='day'):
    url = 'https://api.finance.naver.com/siseJson.naver?symbol=005930&requestType=1&startTime=20190624&endTime=20210901&timeframe=day'
    res = urlopen(url, context=ssl.create_default_context())
    soup = BeautifulSoup(res.read(), 'html.parser', from_encoding='utf-8')
    data    = str(soup).split('],')
    date    = data[1].split(', ')[0].split('["')[1].split('"')[0] #data[1]에 대해서만
    open    = data[1].split(', ')[1] #data[1]에 대해서만
    high    = data[1].split(', ')[2] #data[1]에 대해서만
    close   = data[1].split(', ')[3] #data[1]에 대해서만
    low     = data[1].split(', ')[4] #data[1]에 대해서만
    volume  = data[1].split(', ')[5] #data[1]에 대해서만
    print(data[1])
    print(date)
    print(open)
    print(high)
    print(close)
    print(low)
    print(volume)
if __name__ == '__main__':
    ohlcv_data()

 

data[1]에 대해서 잘 작동하는것을 확인했으니, 이제 for문을 이용하여 모든 데이터에 대해서 작동하도록 바꿔보자

for i in range(1, len(data))를 사용하는데, 1번째부터인 이유는 위에서 봤듯이 0번째 값이 [날짜, 고가... ] 이기 때문이다. 

len(data)는 data에 포함된 요소의 갯수이다. 즉 1번째 부터 끝까지 for 문을 진행하는 것이다.

def ohlcv_data(code='005930',starttime='default',endtime='today',timeframe='day'):
    url = 'https://api.finance.naver.com/siseJson.naver?symbol=005930&requestType=1&startTime=20190624&endTime=20210901&timeframe=day'
    res = urlopen(url, context=ssl.create_default_context())
    soup = BeautifulSoup(res.read(), 'html.parser', from_encoding='utf-8')
    data    = str(soup).split('],')
    for i in range(1,len(data)): # 0번값은 [날짜, 고가 .. 이기 떄문에 1번째 값부터 시작한다.
        date    = data[i].split(', ')[0].split('["')[1].split('"')[0]
        open    = data[i].split(', ')[1]
        high    = data[i].split(', ')[2]
        close   = data[i].split(', ')[3]
        low     = data[i].split(', ')[4]
        volume  = data[i].split(', ')[5]
        print(date, open, high, close, low, volume)
if __name__ == '__main__':
    ohlcv_data()

 

그러면 아래와 같이 원하는 값으로 나오는 것을 확인 할 수 있다.

Pandas Dataframe으로의 변환은 다음 글에서 진행하도록 하겠다.

반응형

+ 최근 글