(이 글은 이번에 병행 운영하던 2개의 블로그를 통합하면서 기존 블로그의 내용을 일부 수정하여 옮겨온 글입니다.)
주식 투자를 하기 위해서 일단은 어떤 종목에 투자할 것인지를 결정하여야 합니다.
종목 결정을 위하여 먼저 재무제표를 기준으로 우량 기업을 선택하고 다음으로 선택한 기업들의 사업보고서를 분석하여 종목을 선택하도록 합시다.
이미 오랜 경험이 있는 투자자들의 경우, 자신들만의 포트폴리오를 구성하고 관심이 있는 분야에서부터 서서히 확장해 나가는 것이 가능하겠지만 처음 시작하는 경우에는 도대체 뭘 선택해야 할 것인지 알 길이 없습니다.
그래서 모든 종목의 재무제표 데이터를 몽땅 읽어들여 우량 기업을 선택하는 기준에 따라 걸러서 대상을 축소해 보기로 하였습니다.
이번 포스트에서는 모든 종목의 재무제표를 다 읽어서 데이터를 만들어보기로 하겠습니다.
일단 종목코드를 몽땅 가져와야 되겠죠.
검색 대상은 네이버 금융 코너로 선택하였습니다.
네이버 금융에서 국내증시 메뉴를 선택하면 왼쪽 사이드 메뉴에 시가총액 메뉴가 있는데 어떤 분류에 있는 회사든지 시가총액은 반드시 계산되어 있는 것이니까 모든 메뉴 중에서 모든 종목 코드를 확인할 수 있는 메뉴는 시가총액 메뉴라고 판단하였습니다.
시가총액 메뉴를 선택해 보면 코스피, 코스닥의 두 개의 탭을 볼 수 있고, 해당 탭 안에서 각 장에 상장된 기업의 목록(종목명)을 볼 수 있습니다.
여기서 각 종목명에 마우스 커서를 올려보면 링크 주소에 해당 종목코드가 포함되어 있는 것을 볼 수 있는데 이 데이터를 모두 긁어서 종목코드 데이터를 확보할 계획입니다.
먼저 코스피, 코스닥 탭 위에 마우스 커서를 올리면
https://finance.naver.com/sise/sise_market_sum.nhn?sosok=0 ---> 코스피 탭
https://finance.naver.com/sise/sise_market_sum.nhn?sosok=1 ---> 코스닥 탭
과 같이 sosok 이라는 파라미터로 구분하고 있음을 알 수 있습니다.
조회를 해 보면 각 탭은 종목명 50개를 기준으로 31페이지, 27페이지로 구성되어 있습니다.
이 내용을 기준으로 코스피, 코스닥에 상장된 모든 종목코드를 읽어오도록 하겠습니다.
import pandas as pd
import numpy as np
import requests
import re
from bs4 import BeautifulSoup
sosoks = ['0', '1']
item_code_list = []
# 코스피, 코스닥 두 장의 종목코드를 읽어온다.
for sosok in sosoks:
url_tmpl = 'https://finance.naver.com/sise/sise_market_sum.nhn?sosok=%s'
url = url_tmpl % sosok
html_text = requests.get(url).text
soup = BeautifulSoup(html_text, 'lxml')
# 하단의 페이지를 표시하는 부분 중 가장 마지막 페이지의 정보는
# 'pgRR' 이라는 class 명을 가진 td 태그 안에 있다.
item_info = soup.find_all('td', {'class':'pgRR'})
# href 파라미터가 가지고 있는 주소에서 숫자로 되어 있는 값을
# 찾아서 저장하면 sosok 값이 [0]에, 페이지 값은 [1]에 들어간다.
# '[\d]'+ 는 정규식 표현이며, 숫자 값을 계속해서 찾으라는 의미이다.
for item in item_info:
href_addr = item.a.get('href')
page_info = re.findall('[\d]+', href_addr)
page = page_info[1]
page = int(page) + 1
# 페이지만큼 반복해서 종목코드를 읽어온다.
for i in range(1, page, 1):
sub_url = '{}&page={}'.format(url, i)
page_text = requests.get(sub_url).text
page_soup = BeautifulSoup(page_text, 'lxml')
items = page_soup.find_all('a', {'class':'tltle'})
for item in items:
item_data = re.search('[\d]+', str(item))
if item_data:
item_code = item_data.group()
item_name = item.text
result = item_code, item_name
item_code_list.append(result)
df = pd.DataFrame(item_code_list)
df.columns = ['item_code','item_name']
print(df)
코드를 실행하면 아래와 같이 종목코드를 잘 읽어온 것을 확인 할 수 있습니다.
총 2851개의 종목코드를 읽어왔군요.
다음으로 모든 종목의 재무제표를 읽어올 차례입니다.
종목코드만큼 재무제표 데이터를 반복해서 읽어온 후 3차원으로 된 데이터를 2차원 형태로 변경하여 종목별로 데이터를 정리하였습니다.
구현 코드는 아래와 같습니다.
앞의 종목코드를 읽어오는 소스코드와 이어집니다. (item_code 때문에)
# 종목코드 읽어오기
item_codes = df['item_code']
# 전 종목에 대하여 재무제표 읽어오기
url_tmpl = 'https://finance.naver.com/item/main.nhn?code=%s'
data = []
for item_code in item_codes:
url = url_tmpl % item_code
tables = pd.read_html(url, encoding='euc-kr')
df = tables[3]
data.append(df)
df = pd.DataFrame(data)
df.to_csv('./총괄재무제표.csv')
plattened_data = []
for item in df[0]:
item_data = np.ravel(item)
plattened_data.append(item_data)
df = pd.DataFrame(plattened_data)
df.columns = [ 'N_CLS', 'N_B3Y', 'N_B2Y', 'N_B1Y', 'N_CYE', 'N_B5Q', 'N_B4Q', 'N_B3Q', 'N_B2Q', 'N_B1Q', 'N_CQE',
'OP_CLS', 'OP_B3Y', 'OP_B2Y', 'OP_B1Y', 'OP_CYE', 'OP_B5Q', 'OP_B4Q', 'OP_B3Q', 'OP_B2Q', 'OP_B1Q', 'OP_CQE',
'NP_CLS', 'NP_B3Y', 'NP_B2Y', 'NP_B1Y', 'NP_CYE', 'NP_B5Q', 'NP_B4Q', 'NP_B3Q', 'NP_B2Q', 'NP_B1Q', 'NP_CQE',
'OPM_CLS', 'OPM_B3Y', 'OPM_B2Y', 'OPM_B1Y', 'OPM_CYE', 'OPM_B5Q', 'OPM_B4Q', 'OPM_B3Q', 'OPM_B2Q', 'OPM_B1Q', 'OPM_CQE',
'NPM_CLS', 'NPM_B3Y', 'NPM_B2Y', 'NPM_B1Y', 'NPM_CYE', 'NPM_B5Q', 'NPM_B4Q', 'NPM_B3Q', 'NPM_B2Q', 'NPM_B1Q', 'NPM_CQE',
'ROE_CLS', 'ROE_B3Y', 'ROE_B2Y', 'ROE_B1Y', 'ROE_CYE', 'ROE_B5Q', 'ROE_B4Q', 'ROE_B3Q', 'ROE_B2Q', 'ROE_B1Q', 'ROE_CQE',
'DR_CLS', 'DR_B3Y', 'DR_B2Y', 'DR_B1Y', 'DR_CYE', 'DR_B5Q', 'DR_B4Q', 'DR_B3Q', 'DR_B2Q', 'DR_B1Q', 'DR_CQE',
'QR_CLS', 'QR_B3Y', 'QR_B2Y', 'QR_B1Y', 'QR_CYE', 'QR_B5Q', 'QR_B4Q', 'QR_B3Q', 'QR_B2Q', 'QR_B1Q', 'QR_CQE',
'RR_CLS', 'RR_B3Y', 'RR_B2Y', 'RR_B1Y', 'RR_CYE', 'RR_B5Q', 'RR_B4Q', 'RR_B3Q', 'RR_B2Q', 'RR_B1Q', 'RR_CQE',
'EPS_CLS', 'EPS_B3Y', 'EPS_B2Y', 'EPS_B1Y', 'EPS_CYE', 'EPS_B5Q', 'EPS_B4Q', 'EPS_B3Q', 'EPS_B2Q', 'EPS_B1Q', 'EPS_CQE',
'BPS_CLS', 'BPS_B3Y', 'BPS_B2Y', 'BPS_B1Y', 'BPS_CYE', 'BPS_B5Q', 'BPS_B4Q', 'BPS_B3Q', 'BPS_B2Q', 'BPS_B1Q', 'BPS_CQE',
'DPS_CLS', 'DPS_B3Y', 'DPS_B2Y', 'DPS_B1Y', 'DPS_CYE', 'DPS_B5Q', 'DPS_B4Q', 'DPS_B3Q', 'DPS_B2Q', 'DPS_B1Q', 'DPS_CQE',
'MDR_CLS', 'MDR_B3Y', 'MDR_B2Y', 'MDR_B1Y', 'MDR_CYE', 'MDR_B5Q', 'MDR_B4Q', 'MDR_B3Q', 'MDR_B2Q', 'MDR_B1Q', 'MDR_CQE',
'POR_CLS', 'POR_B3Y', 'POR_B2Y', 'POR_B1Y', 'POR_CYE', 'POR_B5Q', 'POR_B4Q', 'POR_B3Q', 'POR_B2Q', 'POR_B1Q', 'POR_CQE' ]
df.to_csv('./총괄재무제표_변경2.csv')
df = df.drop([ 'N_CLS', 'OP_CLS', 'NP_CLS', 'OPM_CLS', 'NPM_CLS', 'ROE_CLS', 'DR_CLS', 'QR_CLS', 'RR_CLS',
'EPS_CLS', 'BPS_CLS', 'DPS_CLS', 'MDR_CLS', 'POR_CLS'], 1)
df['ITEM_CODE'] = item_codes
음...
약간 무식한 방법이네요.
웹 스크레이핑은 원래 좀 무식한 방법입니다. ㅠㅠ
현재 시점(이 글을 작성했던 시점)으로 총 2,851개의 종목에 대한 재무제표를 모두 읽어오다보니 시간이 꽤 많이 소요되는군요.
한 건을 읽어오는데 1초로 계산하더라도 47.52분, 즉 50분 가까이 소요된다고 생각할 수 있겠네요.
실제로는 한 건에 1초까지는 걸리지 않겠지만.. 아무튼 오래 걸립니다.
소스코드가 무식한 방법으로 작성되어서 더욱 그래 보입니다..
차후를 위하여 읽어온 데이터를 파일로 저장해 두었습니다.
생성된 df를 출력하면 아래와 같이 각 종목별로 한 줄로 정리된 재무제표 정보가 2,851개의 모든 종목에 대하여 잘 저장된 것을 확인할 수 있습니다.
아래 쪽에 있는 최근에 상장된 업체는 아직 재무제표가 다 등록되지 않았는지 NaN, None으로 표시된 것이 많은데 NaN은 0으로 처리하거나 해야 할 것 같군요.
여기까지가 기존의 블로그에 등록되어 있던 내용입니다.
그 이후로 계속 실제 투자방향을 테스트 해 보면서 각 일반종목을 선택하는 것보다는 주가지수 데이터를 확보, 분석, 예측하여 활용해 보는 것으로 방향을 전환하게 되었습니다.
이후에 작성될 [주식투자] 관련의 실습해보기 글에서는 해당 방향으로 글을 올리도록 하겠습니다.
'기타 기술 > 주가분석' 카테고리의 다른 글
AI를 활용해서 주가 예측 시스템을 개발해보자 (2) (0) | 2020.12.29 |
---|---|
AI를 활용해서 주가 예측 시스템을 개발해보자 (1) (1) | 2020.12.15 |
네이버에서 현금흐름표 데이터 받아오기 (0) | 2019.11.29 |
네이버에서 재무제표 데이터 받아오기 (2) | 2019.11.29 |
네이버에서 주가 데이터 받아오기 (1) | 2019.11.29 |