본문 바로가기
프로젝트/금융

네이버 금융 크롤링 : 주식 종목 가져오기

by 으노으뇨 2022. 10. 26.
728x90
반응형
SMALL

안녕하세요 ~! ㅎㅎㅎ

연말이라 내년 프로젝트를 위해 기획하는데 옆에서 도와주고 본인 개발하고 그러느라 일과시간이 정신없이 지나가네요..

시작전에 앞서서


진행전 라이브러리 설치나 사용방법에 대해 조금 숙지하시고 하신다면 더욱 부드럽게 진행 하실 수 있으십니다!!ㅎㅎㅎ

Jsoup 설치 방법 (자세히)

https://uno-kim.tistory.com/213

 

[JAVA]Jsoup 라이브러리를 이용한 크롤링 - Jsoup 사용법 + 라이브러리 저장하는 법

안녕하세요~! ㅎㅎ 오늘 포스팅은 그동안 크롤링를 하여 네이버 금융의 정보를 가져왔었는데 크게 사용되었던 라이브러리인 Jsoup 라이브러리 사용법 에 대해서 알아보겠습니다! 1. 라이브러리

uno-kim.tistory.com

Jsoup 사용방법(3가지 방법 소개)

https://uno-kim.tistory.com/215

 

[JAVA]Jsoup 라이브러리를 이용한 크롤링 - Jsoup 사용법

안녕하세요! 오늘 포스팅은 Jsoup 라이브러리를 알아보려고 해요 해당 라이브러리는 가장 편한 API 를 제공하며 HTML5 DOM 메서드와 CSS 선택기를 사용하여, URL을 가져오고 데이터를 추출 및 조작하기

uno-kim.tistory.com


오늘은 바로

두구두구두구 바로...

네이버 금융 개별 종목 상세정보 가져오기

에 대해서 알아보겠습니다.!!!

네이버 금융 개별종목의 상세정보란

각 종목에 대해서 

아래와 같은 표의 데이터들을 가져오는 작업을 할거랍니다. ㅎㅎㅎ

??? : 헉!!!!! 표를 그대로 가져온다구요! 엄청 쉽겠네요 ㅎㅎ

네 맞습니다. 하지만 JS를 고대로 가져올 수 있지만 데이터 정제를 하고 저희 컬렉션프레임워크를 통해

원하는 정보만 담아서 자료구조를 통해서 클라이언트 단으로 보내주기위해서는

조금 생각할게 필요할뿐 간단하답니다. ㅎㅎㅎ

이렇게 정보들을 쉽게 가져와서 개인프로젝트 또는 개인적으로 

주식투자하실때 본인이 필요한곳에서 자유롭게 볼수 있다니 

참편리하겠죠?!


네이버 금융 : 개별 주식 종목 상세정보 가져오기

우리가 원하는 태그의 아이디나 클래스정보를 가져오기위해

간단한방법이 

개발자모드(F12)를 키시고

컨트롤 + 시프트 + C 를 누르면 해당 섹션에 대한 값을 찾아낼 수 있습니다.

우선 우리나라의 대표 주식인 삼성전자로 예를 들어보겠습니다!

위 사진은 해당 기능을 통해서 클래스 이름을 가져올 수 있었습니다.

이제 이 클래스를 이용해서 

이클립스단에서 자바로 한번 어떤 값들로 해당 클래스가 이루어 져있는지 확인해볼게요!!

우선 URL을 

삼성전자 상세정보가 나오는 URL로 설정을 해주셔야합니다.!!

https://finance.naver.com/item/main.naver?code=005930 

 

삼성전자 - 네이버 금융 : 네이버 금융

관심종목의 실시간 주가를 가장 빠르게 확인하는 곳

finance.naver.com

package ㅎJavaTests;

import java.io.IOException;

import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.select.Elements;
public class CrawlingNaverFins
{
	public static void main(String[] args) throws IOException
	{
		/* url 셋팅하고 파서셋팅하고, doc 셋팅하는 부분 */
		/* 매매 동향의 경우 URL을 수정해야한다. */
		final String URL = "https://finance.naver.com/item/main.naver?code=005930"; // <-- 가져올 url
		final String pharser = ".sub_section";
		Document doc = Jsoup.connect(URL).get(); // <-- 해당부분에서 doc에 url의 HTML 문서가 모두 들어가게 된다.
		/* 셋팅 부분 끝 */
		/* 구분자를 통해 데이터들을 잘게 쪼갠다. */
		Elements header = doc.select(pharser);
		System.out.println(header);
	}
}

실행을 해볼게요!!!

엥,,? 우리가 찾던 값이 아니네요...

뭐지... 그래서 한번 콘솔창을 따라 쭉 내려보니

중간부터 저희가 찾던 정보들이 나오네요!!ㅎㅎㅎㅎ

그럼 어떻게 우리가 원하는 해당 부분만 가져올 수 있을까요?

몇가지 방법이있는데

제가 이것저것 많이해봤는데

그나마 편리하고 

소스수가 적었던 방법은

웹 개발자 모드에서 해당하는 도큐먼트를 찾는겁니다.

이렇게 한 sub_section이라는 클래스가 총 5개가 있다는 것을 알수 있네요

이때 맨마지막이 우리가 찾고싶은 섹션입니다.!

하나하나 마우스를 조심스럽에 올려놓으면 해당 부분이 

하이라이트 처리가 되어 구불하기 쉽답니다. ㅎㅎㅎ

즉 우리가 뭔하는 값의 인덱스는 4번째이니까 소스는

package ㅎJavaTests;

import java.io.IOException;

import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.select.Elements;
public class CrawlingNaverFins
{
	public static void main(String[] args) throws IOException
	{
		/* url 셋팅하고 파서셋팅하고, doc 셋팅하는 부분 */
		/* 매매 동향의 경우 URL을 수정해야한다. */
		final String URL = "https://finance.naver.com/item/main.naver?code=005930"; // <-- 가져올 url
		final String pharser = ".sub_section";
		Document doc = Jsoup.connect(URL).get(); // <-- 해당부분에서 doc에 url의 HTML 문서가 모두 들어가게 된다.
		/* 셋팅 부분 끝 */
		/* 구분자를 통해 데이터들을 잘게 쪼갠다. */
		Elements header = doc.select(pharser);
		System.out.println(header.get(4));
		// System.out.println(header);
	}
}

이렇게 수정해주시면되겠습니다.

따라서 결과는

우리가 원하는 네이버 금융의 개별종목 상세보기 데이터만 가져와 지는것을 볼 수 있습니다!

그리고 지금 header.get(마지막인덱스) 이렇게 불안정한 하드코딩으로 개발을 하였는데 이를

'header.last()' 이렇게 사용하시면 더 안정적입니다.


그리고 표의 내용들을 가져오기

이제 내용들을 가져와보겠습니다.!!

우아.. 이제 거의 다되었습니다.

우선 간단하게 어떤 형태로 가져와서 클라이언트로 쏴줄것이냐면

모든데이터가 컬럼과 해당 값들이 좌-> 우로 되어있음을 알 수 있습니다.

그리고 소스의 계층구조는

<div>
	<table>
    	<thead>
        </thead>
        <tbody>
        	<tr>
            	<td>필요데이터</td>
            </tr>
        </tbody>
    </table>
</div>

위와 같이 div>table>tbody>tr>td 이렇게 되어있습니다!

따라서 저희가 필요한 정보를 가져오기 위해서는!!!

소스를

package ㅎJavaTests;

import java.io.IOException;

import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.select.Elements;
public class CrawlingNaverFins
{
	public static void main(String[] args) throws IOException
	{
		/* url 셋팅하고 파서셋팅하고, doc 셋팅하는 부분 */
		/* 매매 동향의 경우 URL을 수정해야한다. */
		final String URL = "https://finance.naver.com/item/main.naver?code=005930"; // <-- 가져올 url
		final String pharser = ".sub_section";
		Document doc = Jsoup.connect(URL).get(); // <-- 해당부분에서 doc에 url의 HTML 문서가 모두 들어가게 된다.
		/* 셋팅 부분 끝 */
		/* 구분자를 통해 데이터들을 잘게 쪼갠다. */
		Elements header = doc.select(pharser).last().select("table").select("tbody").select("tr");
		/* div>table>tbody>tr>td 계층 대로 출력 */
		System.out.println(header);
	}
}

header.last().select("table").select("tbody").select("tr")

위와같이 작성한다면

우리가 필요로하는 정보만 정제되는

더보기

<tr> 
 <th scope="row" class="h_th2 th_cop_anal8"><strong>매출액</strong></th> 
 <td class=""> 2,304,009 </td> 
 <td class=""> 2,368,070 </td> 
 <td class=""> 2,796,048 </td> 
 <td class=" t_line cell_strong"> 3,077,504 </td> 
 <td class=""> 636,716 </td> 
 <td class=""> 739,792 </td> 
 <td class=""> 765,655 </td> 
 <td class=""> 777,815 </td> 
 <td class=""> 772,036 </td> 
 <td class=" last cell_strong"> 784,422 </td> 
</tr>
<tr> 
 <th scope="row" class="h_th2 th_cop_anal9"><strong>영업이익</strong></th> 
 <td class=""> 277,685 </td> 
 <td class=""> 359,939 </td> 
 <td class=""> 516,339 </td> 
 <td class=" t_line cell_strong"> 480,062 </td> 
 <td class=""> 125,667 </td> 
 <td class=""> 158,175 </td> 
 <td class=""> 138,667 </td> 
 <td class=""> 141,214 </td> 
 <td class=""> 140,970 </td> 
 <td class=" last cell_strong"> 119,226 </td> 
</tr>
<tr class="line_end"> 
 <th scope="row" class="h_th2 th_cop_anal10"><strong>당기순이익</strong></th> 
 <td class=""> 217,389 </td> 
 <td class=""> 264,078 </td> 
 <td class=""> 399,074 </td> 
 <td class=" t_line cell_strong"> 377,061 </td> 
 <td class=""> 96,345 </td> 
 <td class=""> 122,933 </td> 
 <td class=""> 108,379 </td> 
 <td class=""> 113,246 </td> 
 <td class=""> 110,988 </td> 
 <td class=" last cell_strong"> 90,775 </td> 
</tr>
<tr> 
 <th scope="row" class="h_th2 th_cop_anal11"><strong>영업이익률</strong></th> 
 <td class=""> 12.05 </td> 
 <td class=""> 15.20 </td> 
 <td class=""> 18.47 </td> 
 <td class=" t_line cell_strong"> 15.60 </td> 
 <td class=""> 19.74 </td> 
 <td class=""> 21.38 </td> 
 <td class=""> 18.11 </td> 
 <td class=""> 18.15 </td> 
 <td class=""> 18.26 </td> 
 <td class=" last cell_strong"> 15.20 </td> 
</tr>
<tr> 
 <th scope="row" class="h_th2 th_cop_anal12"><strong>순이익률</strong></th> 
 <td class=""> 9.44 </td> 
 <td class=""> 11.15 </td> 
 <td class=""> 14.27 </td> 
 <td class=" t_line cell_strong"> 12.25 </td> 
 <td class=""> 15.13 </td> 
 <td class=""> 16.62 </td> 
 <td class=""> 14.16 </td> 
 <td class=""> 14.56 </td> 
 <td class=""> 14.38 </td> 
 <td class=" last cell_strong"> 11.57 </td> 
</tr>
<tr class="line_end"> 
 <th scope="row" class="h_th2 th_cop_anal13"><strong>ROE(지배주주)</strong></th> 
 <td class=""> 8.69 </td> 
 <td class=""> 9.98 </td> 
 <td class=""> 13.92 </td> 
 <td class=" t_line cell_strong"> 11.90 </td> 
 <td class=""> 12.04 </td> 
 <td class=""> 12.60 </td> 
 <td class=""> 13.92 </td> 
 <td class=""> 15.13 </td> 
 <td class=""> 15.10 </td> 
 <td class=" null last cell_strong"> &nbsp; </td> 
</tr>
<tr> 
 <th scope="row" class="h_th2 th_cop_anal14"><strong>부채비율</strong></th> 
 <td class=""> 34.12 </td> 
 <td class=""> 37.07 </td> 
 <td class=""> 39.92 </td> 
 <td class=" null t_line cell_strong"> &nbsp; </td> 
 <td class=""> 36.29 </td> 
 <td class=""> 38.30 </td> 
 <td class=""> 39.92 </td> 
 <td class=""> 39.34 </td> 
 <td class=""> 36.64 </td> 
 <td class=" null last cell_strong"> &nbsp; </td> 
</tr>
<tr> 
 <th scope="row" class="h_th2 th_cop_anal15"><strong>당좌비율</strong></th> 
 <td class=""> 233.57 </td> 
 <td class=""> 214.82 </td> 
 <td class=""> 196.75 </td> 
 <td class=" null t_line cell_strong"> &nbsp; </td> 
 <td class=""> 214.08 </td> 
 <td class=""> 210.70 </td> 
 <td class=""> 196.75 </td> 
 <td class=""> 202.26 </td> 
 <td class=""> 219.39 </td> 
 <td class=" null last cell_strong"> &nbsp; </td> 
</tr>
<tr class="line_end"> 
 <th scope="row" class="h_th2 th_cop_anal16"><strong>유보율</strong></th> 
 <td class=""> 28,856.02 </td> 
 <td class=""> 30,692.79 </td> 
 <td class=""> 33,143.62 </td> 
 <td class=" null t_line cell_strong"> &nbsp; </td> 
 <td class=""> 31,140.36 </td> 
 <td class=""> 32,225.78 </td> 
 <td class=""> 33,143.62 </td> 
 <td class=""> 34,110.56 </td> 
 <td class=""> 35,054.68 </td> 
 <td class=" null last cell_strong"> &nbsp; </td> 
</tr>
<tr> 
 <th scope="row" class="h_th2 th_cop_anal17"><strong>EPS(원)</strong></th> 
 <td class=""> 3,166 </td> 
 <td class=""> 3,841 </td> 
 <td class=""> 5,777 </td> 
 <td class=" t_line cell_strong"> 5,469 </td> 
 <td class=""> 1,391 </td> 
 <td class=""> 1,775 </td> 
 <td class=""> 1,567 </td> 
 <td class=""> 1,638 </td> 
 <td class=""> 1,613 </td> 
 <td class=" last cell_strong"> 1,332 </td> 
</tr>
<tr> 
 <th scope="row" class="h_th2 th_cop_anal20"><strong>PER(배)</strong></th> 
 <td class=""> 17.63 </td> 
 <td class=""> 21.09 </td> 
 <td class=""> 13.55 </td> 
 <td class=" t_line cell_strong"> 10.55 </td> 
 <td class=""> 16.99 </td> 
 <td class=""> 14.36 </td> 
 <td class=""> 13.55 </td> 
 <td class=""> 10.92 </td> 
 <td class=""> 8.65 </td> 
 <td class=" last cell_strong"> 39.85 </td> 
</tr>
<tr> 
 <th scope="row" class="h_th2 th_cop_anal18"><strong>BPS(원)</strong></th> 
 <td class=""> 37,528 </td> 
 <td class=""> 39,406 </td> 
 <td class=""> 43,611 </td> 
 <td class=" t_line cell_strong"> 48,280 </td> 
 <td class=""> 40,361 </td> 
 <td class=""> 42,447 </td> 
 <td class=""> 43,611 </td> 
 <td class=""> 45,106 </td> 
 <td class=""> 46,937 </td> 
 <td class=" null last cell_strong"> &nbsp; </td> 
</tr>
<tr class="line_end"> 
 <th scope="row" class="h_th2 th_cop_anal21"><strong>PBR(배)</strong></th> 
 <td class=""> 1.49 </td> 
 <td class=""> 2.06 </td> 
 <td class=""> 1.80 </td> 
 <td class=" t_line cell_strong"> 1.20 </td> 
 <td class=""> 2.00 </td> 
 <td class=""> 1.75 </td> 
 <td class=""> 1.80 </td> 
 <td class=""> 1.54 </td> 
 <td class=""> 1.21 </td> 
 <td class=" null last cell_strong"> &nbsp; </td> 
</tr>
<tr> 
 <th scope="row" class="h_th2 th_cop_anal19"><strong>주당배당금(원)</strong></th> 
 <td class=""> 1,416 </td> 
 <td class=""> 2,994 </td> 
 <td class=""> 1,444 </td> 
 <td class=" t_line cell_strong"> 1,534 </td> 
 <td class="no_data 2"> </td> 
 <td class="no_data 2"> </td> 
 <td class="no_data 2"> </td> 
 <td class="no_data 2"> </td> 
 <td class="no_data 2"> </td> 
 <td class="no_data 2 last cell_strong"> </td> 
</tr>
<tr> 
 <th scope="row" class="h_th2 th_cop_anal22"><strong>시가배당률(%)</strong></th> 
 <td class=""> 2.54 </td> 
 <td class=""> 3.70 </td> 
 <td class=""> 1.84 </td> 
 <td class="no_data 2 t_line cell_strong"> </td> 
 <td class="no_data 2"> </td> 
 <td class="no_data 2"> </td> 
 <td class="no_data 2"> </td> 
 <td class="no_data 2"> </td> 
 <td class="no_data 2"> </td> 
 <td class="no_data 2 last cell_strong"> </td> 
</tr>
<tr> 
 <th scope="row" class="h_th2 th_cop_anal23"><strong>배당성향(%)</strong></th> 
 <td class=""> 44.73 </td> 
 <td class=""> 77.95 </td> 
 <td class=""> 25.00 </td> 
 <td class="no_data 2 t_line cell_strong"> </td> 
 <td class="no_data 2"> </td> 
 <td class="no_data 2"> </td> 
 <td class="no_data 2"> </td> 
 <td class="no_data 2"> </td> 
 <td class="no_data 2"> </td> 
 <td class="no_data 2 last cell_strong"> </td> 
</tr>

접은글과 같이 매출~배당성향 까지 필요한 정보만 나오게됩니다.!!!

이제 결과에서 텍스트만 뽑아보겠습니다!!

package ㅎJavaTests;

import java.io.IOException;

import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.select.Elements;
public class CrawlingNaverFins
{
	public static void main(String[] args) throws IOException
	{
		/* url 셋팅하고 파서셋팅하고, doc 셋팅하는 부분 */
		/* 매매 동향의 경우 URL을 수정해야한다. */
		final String URL = "https://finance.naver.com/item/main.naver?code=005930"; // <-- 가져올 url
		final String pharser = ".sub_section";
		Document doc = Jsoup.connect(URL).get(); // <-- 해당부분에서 doc에 url의 HTML 문서가 모두 들어가게 된다.
		/* 셋팅 부분 끝 */
		/* 구분자를 통해 데이터들을 잘게 쪼갠다. */
		Elements header = doc.select(pharser).last().select("table").select("tbody").select("tr");
		/* div>table>tbody>tr>td 계층 대로 출력 */
		for (int i = 0; i < header.size(); i++)
		{
			System.out.println(header.get(i).text());
		}
	}
}

소스부분은 이제 for문을 돌려서

for (int i = 0; i < header.size(); i++)
{
     System.out.println(header.get(i).text());
}

이렇게 작성했습니다.

과연 결과는?!

짠! ㅎㅎㅎ우리가 필요한 정보가 모두 나왔습니다.!!!!

이제 이것을 어떻게 자료구조로 구현하냐구요? 

아주 간단하답니다.


주식 종목을 자료구조형태로 가져가기

이제 뽑은 형태를 자료형태로 가져가 보겠습니다. 

저는 예시를 위해서 조금 설명을 돕기위해 만들어보겠습니다.

우선 우리는 Map을 하나만듭니다.

매출액 이라는 이름을 가지고 있는 바구니(각 분기별로 매출액들이 들어있음) 

즉 

매출액 = { 20년도 1분기, 2분기, 3분기 ...  22년도 4분기 }

이렇게 값이 서로 

매출액이 K (key) 값으로 가지고

해당 분기별 매출액의 값들이 V (value) 형태로 들어가는 자료 구조를 만들어야합니다.

따라서 간단하게 

package ㅎJavaTests;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;

import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.select.Elements;
public class CrawlingNaverFins
{
	public static void main(String[] args) throws IOException
	{
		/* url 셋팅하고 파서셋팅하고, doc 셋팅하는 부분 */
		/* 매매 동향의 경우 URL을 수정해야한다. */
		final String URL = "https://finance.naver.com/item/main.naver?code=005930"; // <-- 가져올 url
		final String pharser = ".sub_section";
		Document doc = Jsoup.connect(URL).get(); // <-- 해당부분에서 doc에 url의 HTML 문서가 모두 들어가게 된다.
		/* 셋팅 부분 끝 */
		/* 구분자를 통해 데이터들을 잘게 쪼갠다. */
		Elements header = doc.select(pharser).last().select("table").select("tbody").select("tr");
		/* div>table>tbody>tr>td 계층 대로 출력 */
		HashMap<String, ArrayList<String>> outMap = new HashMap<String, ArrayList<String>>();
		for (int i = 0; i < header.size(); i++)
		{
			ArrayList<String> inList = new ArrayList<String>();
			for (int j = 0; j < header.get(i).select("td").size(); j++)
			{
				inList.add(header.get(i).select("td").get(j).text());
			}
			outMap.put(header.get(i).select("th").text(), inList);
		}
		System.out.println(outMap);
	}
}

위와 같이 간단하게 작성합니다.

HashMap<String, ArrayList<String>> outMap = new HashMap<String, ArrayList<String>>();

map 형태이고, 매출액이라는 String 타입의 키값과 각 분기별 매출액인 List형태의 밸류 값을 모두 저장할 수 있는 Map 

자료구조에 각각 들억게 되는것이죠

한번 위 소스를 실행하면 어떻게 될까요?

이것을 콘솔로 확인하기 위해서 

		Set<String> keyList = outMap.keySet();
		for (String key : keyList)
		{
			System.out.println(key + " : " + outMap.get(key));
		}

Set이라는 컬렉션 프레임워크를 이용해서 한번 key값으로 해당  Map의 전체를 출력하도록 해보겠습니다!!

결과는 과연!!!

짜잔!!

완벽하게 모든 정보가 안흘리고 들어갔음을 알 수 있습니다.!!!

삼성전자의 분기별 매출에서 배당률 까지 모두 현행화가 가능하게 되었습니다.!!!


여기까지

네이버 금융 개별종목 상세정보 가져오기

에 대해서 알아보았습니다.

다음차시에는

해당 주식 종목 코드들을 관리하고 매번 새롭게 업데이트 하는 방법에 대해서 

알아보겠습니다.!!

봐주셔서 감사합니다.

궁금하거나 이상하거나 더이상 진행이 안되거나 하시는분들을 지체없이 댓글남겨주시면 언제든 연락드리겠습니다.!!

728x90
반응형
LIST

댓글