본문 바로가기
Framework & Library/Spring Framework

[Spring Framework] : 웹 크롤링(수집)하기

by 오주현 2021. 10. 26.
반응형

www.cgv.co.kr/movies/

 

위에 링크에 있는 CGV 영화 정보를 크롤링 하는 것을 목표로 합니다.

 

인터넷 브라우저에 보여지는 사이트는 모두 HTML을 인터넷 브라우저가 읽어서 보여주는데 이런 특징을 활용해 HTML에 작성된 텍스트를 읽어 들이는 방법입니다.

 

크롤링을 위해서는 HTML 태그에 대한 이해도가 높아야 가능하고 HTML 문서 구조를 파악하는 능력이 중요합니다.

 

코드 아래 글을 쓰기가 애매해서 주석을 참고하면 됩니다.

 

-

 

데이터 베이스

기본 키는 복합키를 사용합니다.

 

-

 

JSOUP 외부 라이브러리 활용을 위해 pom.xml에 추가해 줍니다.

 

-

 

package poly.dto;

public class MovieDTO {
	
	private String rank_check_time;
	private String seq;
	private String movie_rank;
	private String movie_nm;
	private String movie_reserve;
	private String score;
	private String open_day;
	private String reg_id;
	private String reg_dt;
	private String chg_id;
	private String chg_dt;
	
	public String getRank_check_time() {
		return rank_check_time;
	}
	public void setRank_check_time(String rank_check_time) {
		this.rank_check_time = rank_check_time;
	}
	public String getSeq() {
		return seq;
	}
	public void setSeq(String seq) {
		this.seq = seq;
	}
	public String getMovie_rank() {
		return movie_rank;
	}
	public void setMovie_rank(String movie_rank) {
		this.movie_rank = movie_rank;
	}
	public String getMovie_nm() {
		return movie_nm;
	}
	public void setMovie_nm(String movie_nm) {
		this.movie_nm = movie_nm;
	}
	public String getMovie_reserve() {
		return movie_reserve;
	}
	public void setMovie_reserve(String movie_reserve) {
		this.movie_reserve = movie_reserve;
	}
	public String getScore() {
		return score;
	}
	public void setScore(String score) {
		this.score = score;
	}
	public String getOpen_day() {
		return open_day;
	}
	public void setOpen_day(String open_day) {
		this.open_day = open_day;
	}
	public String getReg_id() {
		return reg_id;
	}
	public void setReg_id(String reg_id) {
		this.reg_id = reg_id;
	}
	public String getReg_dt() {
		return reg_dt;
	}
	public void setReg_dt(String reg_dt) {
		this.reg_dt = reg_dt;
	}
	public String getChg_id() {
		return chg_id;
	}
	public void setChg_id(String chg_id) {
		this.chg_id = chg_id;
	}
	public String getChg_dt() {
		return chg_dt;
	}
	public void setChg_dt(String chg_dt) {
		this.chg_dt = chg_dt;
	}
	
	
}

DTO

데이터베이스에 입력한 값으로 DTO를 생성해 줬습니다.

 

-

 

package poly.persistance.mapper;

import config.Mapper;
import poly.dto.MovieDTO;

@Mapper("MovieMapper")
public interface IMovieMapper {
	
	// 수집된 내용 DB에 등록
	int InsertMovieInfo(MovieDTO pDTO) throws Exception;

}

Mapper

 

-

 

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- JAVA와 연결할 Mapper 파일 설정 -->
<mapper namespace="poly.persistance.mapper.IMovieMapper">

 <insert id="InsertMovieInfo" parameterType="MovieDTO">
 INSERT INTO MOVIE_INFO
 	(
 		RANK_CHECK_TIME,
 		SEQ,
 		MOVIE_RANK,
 		MOVIE_NM,
 		MOVIE_RESERVE,
 		SCORE,
 		OPEN_DAY,
 		REG_ID,
 		CHG_ID
	 )
 	VALUES(
 		#{rank_check_time},
 		(SELECT NVL(MAX(A.SEQ),0)+1 FROM MOVIE_INFO A WHERE A.RANK_CHECK_TIME = #{rank_check_time}),
 		#{movie_rank},
 		#{movie_nm},
 		#{movie_reserve},
 		#{score},
 		#{open_day},
 		#{reg_id},
 		#{reg_id}
 	)
 </insert>		
</mapper>

Mapper XML

 

-

 

package poly.util;

import java.text.SimpleDateFormat;
import java.util.Date;


public class DateUtil {
  
     /**
      * 날짜, 시간 출력하기
      * 
      * @param fm 날짜 출력 형식
      * @return 
      */
   public static String getDateTime(String fm) {
	   
	   Date today = new Date();
	   System.out.println(today);
	   
	   SimpleDateFormat date = new SimpleDateFormat(fm);
	   
	   
	   return date.format(today);
   }
   
   /** 
    * 날짜 , 시간 출력하기
    * @return 기본값은 년, 월 ,일
    */
   public static String getDateTime() {
	   
	   return getDateTime("yyyy.MM.dd");
   }
}

Date Util

 

-

 

package poly.service;

public interface IMovieService {

	int getMovieInfoFromWEB() throws Exception;
}

IService

 

-

 

package poly.service.impl;

import java.util.Iterator;

import javax.annotation.Resource;

import org.apache.log4j.Logger;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.springframework.stereotype.Service;

import poly.dto.MovieDTO;
import poly.persistance.mapper.IMovieMapper;
import poly.service.IMovieService;
import poly.util.CmmUtil;
import poly.util.DateUtil;

@Service("MovieService")
public class MovieService implements IMovieService {
	
	@Resource(name = "MovieMapper")
	private IMovieMapper movieMapper;
	
	private Logger log = Logger.getLogger(this.getClass());

	@Override
	public int getMovieInfoFromWEB() throws Exception {

		// 로그 찍기
		log.info(this.getClass().getName() + ".getMovieInfoFromWEB start !");
		
		//크롤링 결과 (0보다 크면 크롤링 성공)
		int res = 0;
		
		// CGV 영화 순위 가져올 사이트 주소
		String url = "http://www.cgv.co.kr/movies/";
		
		//JSOUP 라이브러리를 통해 사이트 접속되면, 그 사이트 전체 HTML 소스 저장할 변수
		Document doc = null;
		
		//사이트 접속 (http 프로토콜만 가능, https 프로토콜은 보안상 안 된다.)
		doc = Jsoup.connect(url).get();
		
		//CGV 웹 페이지의 전체 소스 중 일부 태그를 선택하기 위해 사용
		// <div class"set-movie-chart"> 이 태그 내에서 있는 HTML 소스만 element에 저장된다.
		Elements element = doc.select("div.sect-movie-chart");
		
		//Iterator를 사용하여 영화 순위 정보를 가져온다.
		// 영화 순위는 기본적으로 1개 이상의 영화가 존재하기 때문에 태그의 반복이 존재할 수 밖에 업다.
		Iterator<Element> movie_rank = element.select("strong.rank").iterator(); //영화 순위
		Iterator<Element> movie_name = element.select("strong.title").iterator(); //영화 이름
		Iterator<Element> movie_reserve = element.select("strong.percent span").iterator(); //영화 예매율
		Iterator<Element> score = element.select("span.percent").iterator(); //점수
		Iterator<Element> open_day = element.select("span.txt-info").iterator(); //개봉일
		
		MovieDTO pDTO = null;
		
		while(movie_rank.hasNext()) {
			
			pDTO = new MovieDTO(); //수집된 영화 정보를 DTO에 저장하기 위해 메모리에 올리기
			
			//수집 시간을 기본키로 사용
			pDTO.setRank_check_time(DateUtil.getDateTime("yyyyMMdd24hmmss"));
			
			//영화 순위(trim함수 추가 이유 : trim 함수는 글자의 앞,뒤 공백 삭제 역할을 수행하여, 데이터 수집시, 홈페이지 개발자들을 앞뒤 공백 집어넣을 수 있어서 추가)
			String rank = CmmUtil.nvl(movie_rank.next().text()).trim(); // No.1 들어옴
			pDTO.setMovie_rank(rank.substring(3, rank.length()));
			
			//영화 제목
			pDTO.setMovie_nm(CmmUtil.nvl(movie_name.next().text().trim()));
			
			//영화 예매율
			pDTO.setMovie_reserve(CmmUtil.nvl(movie_reserve.next().text().trim()));
			
			//영화 점수
			pDTO.setScore(CmmUtil.nvl(score.next().text().trim()));
			
			//수집되는 데이터가 '2019.10.23개봉'이기 때문에 앞에 10자리 (2019.10.23)만 저장
			pDTO.setOpen_day(CmmUtil.nvl(open_day.next().text().trim().substring(0,10)));
			
			// 등록자
			pDTO.setReg_id("admin");
			
			//영화 한개씩 추가
			res += movieMapper.InsertMovieInfo(pDTO);
		}
		
		log.info(this.getClass().getName() + ".getMovieInfoFormWEB end !");
		return res;
	}

}

Service

 

-

 

<%@ page language="java" contentType="text/html; charset=EUC-KR"
    pageEncoding="EUC-KR"%>
 <%@ page import = "poly.util.CmmUtil" %>
 <%
 //Controller로부터 전달받은 데이터
 String res = CmmUtil.nvl((String)request.getAttribute("res"), "0");
 %>
<!DOCTYPE html>
<html>
<head>
<meta charset="EUC-KR">
<title>영화 수집 결과</title>
</head>
<body>
CGV 영화 홈페이지에서 <%=res %>개의 영화 순위 정보가 수집되었습니다.
</body>
</html>

JSP

 

-

 

package poly.controller;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.log4j.Logger;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;

import poly.service.IMovieService;

@Controller
public class MovieController {
	private Logger log = Logger.getLogger(this.getClass());
	
	@Resource(name="MovieService")
	private IMovieService movieService;
	
	@RequestMapping(value = "movie/getMovieInfoFromWEB")
	public String getMovieInfoFromWEB(HttpServletRequest request, HttpServletResponse response, ModelMap model)throws Exception {
		
		log.info(this.getClass().getName() + ".getMovieInfoFromWEB start !");
		
		int res = movieService.getMovieInfoFromWEB();
		
		//크롤링 결과를 넣어주기
		model.addAttribute("res", String.valueOf(res));
		
		log.info(this.getClass().getName() + ".getMovieInfoFromWEB end !");
		
		
		return "/movie/RankForWEB";
	}
}

Controller

 

-

예제를 참고하며 코딩하여 웹 크롤링에 성공했습니다.

 

웹 페이지에서도 설정한 JSP에서 값을 잘 받아오는 것을 볼 수 있고 데이터 베이스에서도 웹에 있는 정보가 잘 들어가는 것을 확인할 수 있습니다.

 

반응형

댓글