본문 바로가기
Spring Framework/Web-service

올리브영 판매랭킹 웹 크롤링으로 차트 구현해보기

by 호두빵 2021. 7. 21.

오늘 수업 시간에 배웠던 웹 크롤링 기법으로, 올리브영에서 판매하는 화장품 랭킹을 차트로 구현해보고자 합니다. 수업 시간에는 영화 예매 사이트의 예매율을 대상으로 실습하였는데 저는 이번에 조금 다르게 해보고 싶었어요! 차트도 파이차트가 아닌 다른 차트를 사용해 볼 예정입니다. 일단 사이트에서 긁어오기 위해 jar 파일이 필요하므로 구글에 maven repository를 검색해서 들어갑니다. 거기서 jsoup을 검색해줘요. 이름만 보면 json과 친척인 것 같기도 하고 그런데, 원래는 soup이었다고 해요 그런데 자바에 맞춰서 다시 만들어져서 앞에 j가 붙게 됐다고 합니다. jsoup은 html 페이지 전체를 문서로, 즉 DOM 방식으로 파싱해오는데 여기서 DOM은 Document Oriented Model로 , document로 한 번에 싹 다 받아와서 map 형식으로 처리해주겠습니다. 

jsoup을 검색해서 제일 상단에 나온 결과를 클릭하면 아래에 여러 버전이 있는데 그 중 Usage가 많은 것을 골라보도록 합니다. 다른 사람들이 많이 썼다고 하니 뭔가 더 믿음이 가네요..저는 1.11.3을 다운 받았어요.

1.11.3을 클릭하면 이렇게 화면이 뜨는데 여기서 jar 파일을 클릭해서 다운 받아줍니다. 일단 이번 실습을 위해 새로 Dynamic Web Project를 하나 생성해주었어요. 이름은 Crawling_olive로 하였습니다.

다운 받은 jar 파일을 lib 폴더 안에 쏙 넣어줘요. 이게 없으면 모든 실습이 무용지물이랍니다. 필요한 기본적인 작업을 마쳤으니 먼저 dto부터 만들어줘볼게요. 일단 차트에 보여주고 싶은 것이 무엇인지? 그것부터 정해야겠어요. 올리브영 홈페이지에 들어가봅니다. 

이렇게 다양한 제품들이 판매되고 있어요. 저 중에 써본 것도 있고, 처음 보는 제품도 있네요! 저는 여기서 제품명보다 블랜드명과 가격을 차트에 띄우고 싶었습니다. 그리고 홈페이지 상에서는 100위까지 있는데, 일단 상위 8개만 뽑아오는 것으로 하겠습니다. 그러면 dto에 브랜드명과, 가격이 들어가야겠죠? dto를 만들러 가봅니다. 

 

package dto;

import java.io.Serializable;

public class OliveVo implements Serializable{

	private String brand;
	private Double price;
	
	public OliveVo() {
		
	}

	public OliveVo(String brand, Double price) {
		super();
		this.brand = brand;
		this.price = price;
	}

	public String getBrand() {
		return brand;
	}

	public void setBrand(String brand) {
		this.brand = brand;
	}

	public Double getPrice() {
		return price;
	}

	public void setPrice(Double price) {
		this.price = price;
	}

	@Override
	public String toString() {
		return "OliveVo [brand=" + brand + ", price=" + price + "]";
	}
	
}

dto 패키지 안에 OliveVo라는 클래스를 만들어서 그 안에 브랜드와 가격을 각각 brand와 price로 줬어요. 생성자를 만들고 야무지게 getter와 setter 챙기고 toString까지 만들어줍니다. 그러면 이제 실제로 차트를 만드는 부분과 차트에 들어갈 데이터를 정리하는 부분이 필요하겠어요. 

 

import java.util.ArrayList;
import java.util.List;

import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;

import dto.OliveVo;

public class OliveChart {

	public static void main(String[] args) throws Exception{
	
		Document doc = Jsoup.connect("https://www.oliveyoung.co.kr/store/main/getBestList.do?dispCatNo=900000100100001&fltDispCatNo=&pageIdx=0&rowsPerPage=0").get();

		Elements brands = doc.select("div.prd_info span.tx_brand");
		
		Elements prices = doc.select("span.tx_cur span.tx_num");
		
		List<OliveVo> list = new ArrayList<OliveVo>();
		
		for(int i = 0; i<8;i++) {
			Element brand = brands.get(i);
			Element price = prices.get(i);
			
			String t = brand.text();
			System.out.println(t);
			String p = price.text();
			System.out.println(p);
			}
         }

일단 값을 잘 긁어오는지를 콘솔 창에 띄워서 확인을 해보려고 Main class에 위와 같이 코드를 작성하였습니다. 내가 찾아내고 싶은 값이 어디에 들어가있는지는 페이지에서 페이지 소스 보기를 클릭하면 클래스를 찾을 수 있어요. 그런 다음에 doc.select 안에 넣어주면 됩니다. 제대로 잘 가져온다면 첫번째는 에스트라 20,400원, 마지막은 바이오더마 27,000원이 나와야 해요. 일단 돌려본 결과 결과는 다음과 같이 잘 가져왔습니다. 그럼 차트에 예쁘게 넣어주는 것만 남았네요 . 

 

저는 왼쪽의 데이터를 오른쪽의 차트 모양으로 만들어주고싶었어요. 구글에서 Highchart를 검색해서 들어가면 다양한 데모 버전을 실습해 볼 수 있습니다!  잘 가져오는 것을 확인했으니 데이터를 우리가 원하는 방향으로 차곡차곡 쌓아서 넣어주는 부분을 만들어야겠죠? OliveChart 클래스를 만들어서 다음과 같이 코드를 적어줍니다. 위에서는 콘솔창에서 바로 확인하기 위함이었다면 이번에는 실제 jsp에 뿌려질 데이터를 만드는 함수입니다. 

package Olive;

import java.util.ArrayList;
import java.util.List;

import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;

import dto.OliveVo;

public class OliveChart {

	public static List<OliveVo> getOliveRanking() throws Exception{
	
		Document doc = Jsoup.connect("https://www.oliveyoung.co.kr/store/main/getBestList.do?dispCatNo=900000100100001&fltDispCatNo=&pageIdx=0&rowsPerPage=0").get();

		Elements brands = doc.select("div.prd_info span.tx_brand");
		
		Elements prices = doc.select("span.tx_cur span.tx_num");
		
		List<OliveVo> list = new ArrayList<OliveVo>();
		
		for(int i = 0; i<8;i++) {
			Element brand = brands.get(i);
			Element price = prices.get(i);
			
			String t = brand.text();
			//System.out.println(t);
			String p = price.text();
			//System.out.println(p);
			
			int pvalue = Integer.parseInt(p.replace(",", ""));
			OliveVo vo = new OliveVo(t,pvalue);
			
			list.add(vo);
		}
		
		return list;
	}
}

그런 다음에 webapp에 index.jsp를 만들어줍니다. 그리고 일단 먼저 html 소스를 가져와볼게요. 

여기서 edit in codepen을 클릭하면 아래와 같은 화면이 나와요. 차례대로 하나하나 가져와줍니다. 

다 합쳐진 결과물은 아래와 같습니다. 

<%@page import="Olive.OliveChart"%>
<%@page import="dto.OliveVo"%>
<%@page import="java.util.List"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<script src="https://code.highcharts.com/highcharts.js"></script>
<script src="https://code.highcharts.com/modules/exporting.js"></script>
<script src="https://code.highcharts.com/modules/export-data.js"></script>
<script src="https://code.highcharts.com/modules/accessibility.js"></script>

<style type="text/css">
#container {
  height: 400px; 
}

.highcharts-figure, .highcharts-data-table table {
  min-width: 310px; 
  max-width: 800px;
  margin: 1em auto;
}

.highcharts-data-table table {
  font-family: Verdana, sans-serif;
  border-collapse: collapse;
  border: 1px solid #EBEBEB;
  margin: 10px auto;
  text-align: center;
  width: 100%;
  max-width: 500px;
}
.highcharts-data-table caption {
  padding: 1em 0;
  font-size: 1.2em;
  color: #555;
}
.highcharts-data-table th {
	font-weight: 600;
  padding: 0.5em;
}
.highcharts-data-table td, .highcharts-data-table th, .highcharts-data-table caption {
  padding: 0.5em;
}
.highcharts-data-table thead tr, .highcharts-data-table tr:nth-child(even) {
  background: #f8f8f8;
}
.highcharts-data-table tr:hover {
  background: #f1f7ff;
}
</style>
</head>
<body>

<%
	List<OliveVo> list = OliveChart.getOliveRanking();
	for(OliveVo v  : list){
		System.out.println(v.toString());
	}
	
	//list -> json
	String jsonData = "[";
	
	for(OliveVo v : list){
		jsonData += "{ name :'" + v.getBrand() + "', y:" + v.getPrice() + "},";
	}
	
	jsonData = jsonData.substring(0, jsonData.lastIndexOf(","));
	
	jsonData += "]";
	
	System.out.println(jsonData);
	
	request.setAttribute("jsonData",jsonData);
 %>
 
 <figure class="highcharts-figure">
  <div id="container"></div>
</figure>

<script type="text/javascript">
Highcharts.chart('container', {
	  chart: {
	    type: 'column'
	  },
	  title: {
	    text: '올리브영 화장품 판매 순위'
	  },
	  subtitle: {
	    text: 'Source: <a href="http://oliveyoung.co.kr">올리브영</a>'
	  },
	  xAxis: {
	    type: 'category',
	    labels: {
	      rotation: -45,
	      style: {
	        fontSize: '13px',
	        fontFamily: 'Verdana, sans-serif'
	      }
	    }
	  },
	  yAxis: {
	    min: 0,
	    title: {
	      text: '가격(원)'
	    }
	  },
	  legend: {
	    enabled: false
	  },
	  tooltip: {
	    pointFormat: '가격: <b>{point.y:.0f} 원</b>'
	  },
	  series: [{
	    name: '브랜드',
	    data: <%=request.getAttribute("jsonData")%>,
	    dataLabels: {
	      enabled: true,
	      rotation: -90,
	      color: '#FFFFFF',
	      align: 'right',
	      format: '{point.y:.0f}원', // 기존 one decimal 제거
	      y: 10, // 10 pixels down from the top
	      style: {
	        fontSize: '10px',
	        fontFamily: 'Verdana, sans-serif'
	      }
	    }
	  }]
	});
</script>
</body>
</html>

코드펜의 코드에서 약간 제가 원하는 방식으로만 변형을 하였어요. 특히 format에서 '{point.y:.1f}' 요렇게 되어있어서 계속 값이 20400.0원 이렇게 떴는데 사실 저렇게 보여지는 것보다 소숫점 아래자리를 버리는 게 깔끔하기 때문에 0f로 바꿔줬습니다. 

 

 

다 만들고 보니까 순위를 나타내기 보다는 각 브랜드의 대표 상품 가격을 비교하는 모습이 더 강해서 수정하고 보완할 부분이 여럿 있어보입니다. 특히나 라네즈에서 제품을 잘 만든건지 두개의 상품이 들어가있어서 중복을 제거해주는 작업도 필요할 듯 해요. 일단 오늘 배운 것을 복습하기 위한 실습으로 간단하게 만들어봤습니다!