다른 명령
R에서 전자공시시스템(DART) API를 이용한 크롤링
- 금융감독원의 전자공시 시스템(http://dart.fss.or.kr/)
- 오픈 API 인증키 신청 필요 , API KEY 발급
- 하루 동안 요청할 수 있는 횟수 10,000 번
- 한번에 최대 10개까지의 데이터 받을수 있음.
- 아무런 쿼리도 입력하지 않으면 단순히 최신 공시 10건을 출력
자기주식 취득공시 크롤링
- 해당 주소 http://dart.fss.or.kr/api/search.xml?auth=APIKEY&crp_cd=005930&start_dt=19990101&bsn_tp=A001
- :(A002은 반기, A003은 분기)
- auth 는 발급받은 API key 입력
- corp_code 는 회사코드 다운로드(https://opendart.fss.or.kr/api/corpCode.xml?crtfc_key=API키)
- 데이터를 xml,json 형태( .../api/search. 뒤의 xml을 json으로만 바꾸어주면 됩니다.)
library(httr) library(rvest) library(jsonlite) library(XML) library(methods) library(RMySQL) corpCode <- xmlToDataFrame("D:/dev/R/corpCode.xml") data.df = corpCode$list api.key = "OPEN API 키값" #start.date = "19990101" list_bsns_year <- c('2015', '2016', '2017', '2018', '2019','2020') # list_reprt_code <- c('11011', '11012', '11013', '11014') # 분기 리포트 # # # # if (mysqlHasDefault()) { # # DB Connection con <- dbConnect(MySQL(), host = '주소', dbname = 'db명', user = '유저명', password='비번') dbSendQuery(con, "SET NAMES utf8;") dbSendQuery(con, "SET CHARACTER SET utf8;") dbSendQuery(con, "SET character_set_connection=utf8;") # # # connection info & table schma info # dbListTables(con) # dbListFields(con, "tb_self_meme_company") # # } # meme_comp.table <- dbGetQuery(con, "select '한글테스트',a.* from tb_self_meme_company2 a") # Query Test 2 head(meme_comp.table) # class(df.table) # Company List corpCode <- xmlToDataFrame("D:/dev/R/corpCode.xml") compList.df = corpCode$list #start.date = "19990101" corp_code = "00385363" for(i in list_bsns_year){ for(j in list_reprt_code){ url = paste0("https://opendart.fss.or.kr/api/tesstkAcqsDspsSttus.json?crtfc_key=",api.key,"&corp_code=",corp_code,"&bsns_year=",i,"&reprt_code=",j) # print(url) data = fromJSON(url) # data.df = data$list alldata <- rbind(alldata,data$list) # data.df.rcept_no = data.df$rcept_no # data.df %>% head() } } alldata %>% head() alldata %>% tail()
공시번호로 사업보고서 확인
- 필요한 값은 공시번호에 해당하는 rcp_no 값 (data.df$rcept_no) 만 data.df.rcp 변수에 저장
- dart 홈페이지 공시들의 url 파악 필요
- 정기공시를 검색해보면 00 년 사업보고서 예시
재무제표 파일 다운로드
- 첨부되어 있는 재무제표 파일들을 다운로드 받기 위한 각 공시별 dcm 구하기
- dcm을 뽑아내기 위해 크롬 > 개발자도구 > xpath를 이용하여 추출
- //*[@id="north"]/div[2]/ul/li[1]/a
- 해당 부분의 값을 R에서 추출하는 법
# 위 페이지 url 정보를 GET() 함수를 통해 가져오며,read_html()을 통해 html 정보를 읽을 후,xpath 값으로 해당 노드의 정보를 읽어오도록 합니다. url.business.report = 'http://dart.fss.or.kr/dsaf001/main.do?rcpNo=20180402005019' req = GET(url.business.report) req = read_html(req) %>% html_node(xpath = '//*[@id="north"]/div[2]/ul/li[1]/a')
req {xml_node} "#download"
onclick="openPdfDownload('20180402005019', '6060273'); return false;"> [1] "/images/common/viewer_down.gif" style="cursor:pointer;" alt="다운로드" title="다운로드">
req 변수 확인해보면 onclick 뒤의 openPdfDownload 부분이 있으며 앞의 20180402005019 부분은 위에서 나온 rcp_no,위의 6060273 부분이 해당 파일의 dcm에 해당.
dcm은 각 공시에 해당하는 문서번호로 이해.
req = req %>% html_attr('onclick') dcm = stringr::str_split(req, ' ')[[1]][2] %>% readr::parse_number()
- html_attr() 함수를 이용해 'onclick' 부분의 데이터만 뽑아
- stringr 패키지의 str_split() 함수를 통해 캐릭터 값을 나눠준 후 두번째 값만을 뽑아내기
- 마지막으로 readr 패키지의 parse_number() 함수를 통해 해당 값에서 숫자 값만을 뽑아주도록 합니다.
- 정규표현식을 아신다면 해당작업이 훨씬 쉽게 가능
홈페이지 첨부된 엑셀 파일이 다운로드
- 상단의 다운로드 부분을 클릭한 후 팝업창이 뜨면, 다시 개발자 도구를 열어줍니다.
- 재무제표 항목을 클릭하면 파일이 다운로드
http://dart.fss.or.kr/pdf/download/excel.do
위의 url에 데이터를 요청하며 쿼리에 해당하는 부분은 rcp_no는 위에서 구한 값(공시번호),dcm_no 역시 위에서 찾아낸 문서번호,lang은 한국어인 ko가 있습니다.
R에서 post 형식으로 나타내면 다음과 같습니다.
query.base = list( rcp_no = '20180402005019', dcm_no = dcm ) down.excel = POST('http://dart.fss.or.kr/pdf/download/excel.do', query = query.base) down.excel Response [http://dart.fss.or.kr/pdf/download/excel.do?rcp_no=20180402005019&dcm_no=5026126] Date: 2019-02-18 14:08 Status: 200 Content-Type: application/vnd.ms-excel Size: 74.8 kB down.excel 변수를 확인해보면 엑셀 파일이 연결되어 있음이 확인됩니다. writeBin(content(down.excel, "raw"), paste0(ticker, "_", data.df.rcp[i], '.xls')) writeBin() 함수를 통해 해당 파일을 다운로드 받으며 저장 이름은 티커_rcp.xls 로 하도록 합니다. 모든 재무제표 항목이 포함된 엑셀파일이 잘 다운로드 됨이 확인됩니다. df = readxl::read_excel( paste0(ticker, "_", data.df.rcp[i], '.xls'), sheet = 2) df # A tibble: 62 x 4 `연결 재무상태표` ..2 ..3 ..4 <chr> <chr> <chr> <chr> 1 제 49 기 2017.12.31 현재 NA NA NA 2 제 48 기 2016.12.31 현재 NA NA NA 3 제 47 기 2015.12.31 현재 NA NA NA 4 (단위 : 백만원) NA NA NA 5 NA 제 49 기 제 48 기 제 47 기 6 자산 NA NA NA 7 유동자산 146982464 141429704 124814725 8 현금및현금성자산 30545130 32111442 22636744 9 단기금융상품 49447696 52432411 44228800 10 단기매도가능금융자산 3191375 3638460 4627530 # ... with 52 more rows read_excel() 함수를 통해 다운로드 받은 파일을읽어올 수도 있습니다. 처음 API를 통한 json 형태로 얻은 10개의 rcp를 통해 최근 10년치 엑셀 파일을 모두 다운로드 받는 방법은 for loop를 통해 해결할 수 있습니다. <source lang=r> for (i in 1 : length(data.df.rcp)) { url.business.report = paste0('http://dart.fss.or.kr/dsaf001/main.do?rcpNo=',data.df.rcp[i]) req = GET(url.business.report) req = read_html(req) %>% html_node(xpath = '//*[@id="north"]/div[2]/ul/li[1]/a') req = req %>% html_attr('onclick') dcm = stringr::str_split(req, ' ')[[1]][2] %>% readr::parse_number() query.base = list(rcp_no = '20180402005019',dcm_no = dcm,lang = 'ko') down.excel = POST('http://dart.fss.or.kr/pdf/download/excel.do',query = query.base) writeBin(content(down.excel, "raw"), paste0(ticker, "_", data.df.rcp[i], '.xls')) Sys.sleep(2) print(i) }
네이버 증권 재무제표
https://engkimbs.tistory.com/625
- 파이썬 코드
import requests from bs4 import BeautifulSoup URL = "https://finance.naver.com/item/main.nhn?code=005930" samsung_electronic = requests.get(URL) html = samsung_electronic.text soup = BeautifulSoup(html, 'html.parser') finance_html = soup.select('div.section.cop_analysis div.sub_section')[0] th_data = [item.get_text().strip() for item in finance_html.select('thead th')] annual_date = th_data[3:7] quarter_date = th_data[7:13] finance_index = [item.get_text().strip() for item in finance_html.select('th.h_th2')][3:] finance_data = [item.get_text().strip() for item in finance_html.select('td')] import numpy as np finance_data = np.array(finance_data) finance_data.resize(len(finance_index), 10) finance_date = annual_date + quarter_date import pandas as pd finance = pd.DataFrame(data=finance_data[0:,0:], index=finance_index, columns=finance_date) annual_finance = finance.iloc[:, :4] quarter_finance = finance.iloc[:, 4:]
네이버 증권 재무제표 WITH 판다스
import requests from bs4 import BeautifulSoup import os import datetime as dt import pandas as pd # user_agent = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebkit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.120 Safari/537.36' session = requests.Session() # session.headers.update( {'User-agent': user_agent, 'referer': None}) # url_test = 'https://comp.fnguide.com/SVO2/asp/SVD_Finance.asp?pGB=1&gicode=A005380&cID=&MenuYn=Y&ReportGB=D&NewMenuID=103&stkGb=701' url_test = 'https://m.stock.naver.com/api/html/item/financialInfo.nhn?type=annual&code=284740' r = session.get(url_test) # r.encoding='utf-8' # r.text # print(r.text) data = pd.read_html(r.text) # print(data) # print(type(data)) # print(len(data)) print(data[0]) KRX = pd.read_excel('D:/DEV/R/all_company.xlsx') KRX = KRX[['종목코드', '종목명']] # KRX['종목코드'] = KRX['종목코드'].apply(lambda x: '{0:0>6}'.format(x)) # print(KRX) code = KRX['종목코드'] code_list = [] for cd in code.values: cd = str(cd) cd = ('0'*(6-len(cd)))+cd code_list.append(cd) # print(code_list) for z, code in enumerate(code_list[:int(len(code_list) * 0.3)]): try: url_test = 'https://m.stock.naver.com/api/html/item/financialInfo.nhn?type=annual&code=%s' % code r = session.get(url_test) # r.encoding = 'utf-8' data = pd.read_html(r.text) IS_temp = data[0] # print(IS_temp) # IS_temp.index = IS_temp['기간'].values # IS_temp.drop(['IFRS(연결)', '전년동기', '전년동기(%)'], inplace=True, axis=1) # for i, name in enumerate(IS_temp.index): # if '참여한' in name: # name = name.strip() # name = name.replace('계산에 참여한 계정 펼치기', '') # name = name.replace(' ', '') # IS_temp.rename(index={str(IS_temp.index[i]): str(name)}, inplace=True) # IS_temp = IS_temp.T # IS_temp = IS_temp.reset_index() IS_temp.rename(columns={'index': 'date'}, inplace=True) IS_temp.insert(0, '종목코드', code) print(IS_temp[3]) if z == 0: IS_data = IS_temp else: IS_data = pd.concat([IS_data, IS_temp]) except KeyError as e: print(e, code) except ValueError as e: print(e, code) # print(IS_data) # IS_data.to_excel('D:/DEV/R/export_sample.xlsx', sheet_name='new_name') # IS_temp = data[0]4 # IS_temp.index =IS_temp['IFRS(연결)'].values # IS_temp.drop(['IFRS(연결)', '전년동기', '전년동기(%)'], inplace=True, axis=1) # for i, name in enumerate(IS_temp.index): # # 저 글씨가 껴있는 애들만 걸리도록 설정을 해서 # if '참여한' in name: # name = name.strip().replace('계산에 참여한 계정 펼치기', '') # # 공백이 있는지 모르곘는데, 그냥 지우고, 우리가 지우고자 하는 글씨도 지워버린다음에 # name = name.replace(' ', '') # # 빈ㅌ칸이 있을 수 있으니, 빈칸들도 날려버립시다. # IS_temp.rename(index = {str(IS_temp.index[i]): str(name)}, inplace = True) # # rename으로 index를 다시 설정해주면 됩니다. # code = 'A005380' # IS_temp.insert(0, '종목코드', code) # print(IS_temp)