Web Spring 유튜뷰 크롤링

유튜브 동영상 크롤링

1. 뷰에서 입력받은 검색어를 처음 받아들이는 컨트롤

package bit.com.spring.controller;

import java.util.List;

import javax.servlet.http.HttpSession;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;

import bit.com.spring.dto.MemberDto;
import bit.com.spring.dto.Youtube;
import bit.com.spring.dto.YoutubeSave;
import bit.com.spring.service.YoutubeService;
import bit.com.spring.util.YoutubeParser;

@Controller
public class YoutubeController {

	@Autowired
	private YoutubeParser youtubeParser;
	@Autowired
	YoutubeService service;
	
	@RequestMapping(value = "youtube.do", method = {RequestMethod.GET, RequestMethod.POST})
	public String youtube(String s_keyword, Model model) {  //s_keyword는 검색어
		model.addAttribute("doc_title", "유튜브");
		
		// 검색 기능이 있어야한다 ***
		if(s_keyword != null && !s_keyword.equals("")) {
			
			List<Youtube> getTtitles = youtubeParser.getTitles(s_keyword);
			
			model.addAttribute("yulist", getTtitles);
			model.addAttribute("s_keyword", s_keyword);
		}
		
		
		return "youtube.tiles";
	}

2. 아래 autowired를 해주기위해 클래스를 생성해 주어야 한다. 또한, youtubeParser를 만들어 크롤링해줄 url과 크롤링할 값들을 추려낸다.

@Autowired
	private YoutubeParser youtubeParser;

- youtubeParser

package bit.com.spring.util;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.json.JSONObject;
import org.springframework.stereotype.Component;

import bit.com.spring.dto.Youtube;

@Component //클래스가 자동 생성이 된다 
public class YoutubeParser {
	
	String urls = "https://www.youtube.com/results?search_query=";
	Map<String , String> htmls = new HashMap<String, String>();
	
	
	//제일 중요 (가장 핵심// 크롤링이 이루어지는 부분)
	public Map<String, String> search(String keyword) {
		System.out.println("YoutubeParser search");
		
		htmls.clear();//map을 비운다  (이거를 안넣고 , 멤버변수에 있는 Map을 여기 안에다가 생성해도 괜찮다)
		BufferedReader br = null;//읽어들인 데이터를 모아주는 역할
		
		// 웹에서 데이터를 취득하기 위한 list 초기화
		JsonUtils.list.clear();
		JsonUtils.titleList.clear();
		
		
		try {
			String ss = URLEncoder.encode(keyword, "utf-8");  //검색어 인코딩
			//System.out.println(ss);
			
			//import java.net.URL; url과 검색어를 합해서 동영상의 주소를 만들어 준다 (해당 주소에서 검색된 동영상들의 값들을 불러 올 것이다)
			URL url = new URL(urls + ss);
			
			br = new BufferedReader(new InputStreamReader(url.openStream(),"utf-8")); //위의 검색된 url의 모든 데이터(html문서를 통째)로 읽어들인다
			
			String msg="";
			int pos = 0;
			String text = "";
			while((msg = br.readLine()) != null) {  // 읽어들은 html문서를 읽는다
				text += msg.trim();	//검색이 된 url이 다 text로 들어간다
			}
			//System.out.println(text);		//responseContext 검색어
			
			//읽을 시작 위치
			pos = text.trim().indexOf("\"responseContext\""); //해당 단어는 html문서의 data값중에서 내가 취득할 data{}안에 들어가있는 단어중 수가 제일적어서 저것으로 자른다
		//	System.out.println(pos);
			
			// 끝 위치
			int endpos = text.indexOf("};", pos); //끝날지점 을 찾는다
		//	System.out.println(endpos);
			
			String str = text.substring(pos-1, endpos+1);	//json 데이터로 끌어오는거다 {responseContext ~ } 이렇게 가지고 온다
		//	System.out.println(str);
			
			JSONObject json = new JSONObject(str);	//json 형태로 바꿔준다.
			
			JsonUtils.jsonToMap(json);  //값들의 key 값들을 map에 넣어주고 list화 시킨다. 이부분은 아래 JsonUtils에서 더 자세히
			for (int i = 0; i < JsonUtils.titleList.size(); i++) {
				//System.out.println(JsonUtils.list.get(i));  이건 비디오 고유 코드
				//System.out.println(JsonUtils.titleList.get(i)); 이건 동영상 제목
				
				htmls.put(JsonUtils.list.get(i), JsonUtils.titleList.get(i)); 
			}
			
			
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		} catch (MalformedURLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		
		
		return htmls;
	}
	
	public List<Youtube> getTitles(String keyword) {
		
	//	search(keyword);
		
		List<Youtube> list = new ArrayList<Youtube>();
		Map<String, String> map = search(keyword);  //아까 비디오 코드와 제목을 넣은 map을 다시 넣어준다
		
		Iterator<String> it = map.keySet().iterator();  // 하나씩 꺼내면서 title, url 를 리스트에 넣어준다
		while(it.hasNext()) {
			String url = it.next();
			Youtube dto = new Youtube(map.get(url),url,""); //map.get(값)을 넣으면 key를 return한다
			list.add(dto);
		}
		
		return list;
	}
}

위에서 사용된 JsonUtil을 살펴본다

package bit.com.spring.util;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.json.JSONArray;	//처음 jsonObject simple이 아닌 org.json으로 해야한다
import org.json.JSONObject;



// https://www.it-swarm.dev/ko/java/json-%EB%AC%B8%EC%9E%90%EC%97%B4%EC%9D%84-hashmap%EC%9C%BC%EB%A1%9C-%EB%B3%80%ED%99%98/1045437367/

//JSONObject를 Map과 List로 저장하는  class

// http://json.parser.online.fr/ 	json 정렬해주는 사이트
public class JsonUtils {
	
	//원하는 값을 취득하기 위한 list를 준비
	public static List<String> list = new ArrayList<String>();
	public static List<String> titleList = new ArrayList<String>();
		
	

	public static Map<String, Object> jsonToMap(JSONObject json) {
        Map<String, Object> retMap = new HashMap<String, Object>();

        if(json != null) {
            retMap = toMap(json);
        }
        return retMap;
    }

    public static Map<String, Object> toMap(JSONObject object) {
        Map<String, Object> map = new HashMap<String, Object>();

        Iterator<String> keysItr = object.keySet().iterator();
        while(keysItr.hasNext()) {
            String key = keysItr.next();
            Object value = object.get(key);

            if(value instanceof JSONArray) {
                value = toList((JSONArray) value);
            }

            else if(value instanceof JSONObject) {
                value = toMap((JSONObject) value);
            }
            map.put(key, value);
            
            //데이터를 취득 할 수 있는 부분 , key
            
           // System.out.println("key:"+key);
            //videoId
            if(key.equals("videoId")) { // 전체 key값을 받아올때 비디오코드와 같은게 있는지 살핀다.
            	Iterator<String> it = list.iterator();  // 중복되는 비디오 코드를 제거
            	while(it.hasNext()) {
            		String k = it.next();
            		if(k.equals(value)) {
            			it.remove();
            		}
            	}
            	list.add((String)value);  // 비디오 코드를 리스트에 넣어준다
            }
            
            
            //key 값이 title 인것만 출력
            if(key.equals("title")) {
            	//System.out.println("key:"+key + "value:"+value);
            	
            	if(value.toString().contains("accessibility")) {  //title 안에서 accessibility 이 들어간 문장에 title명이 있다
            	//	System.out.println("key:"+key + " value:"+value);
            		int start = value.toString().indexOf("text=");
            		int end = value.toString().indexOf("}]");
            		
            		String title = value.toString().substring(start+5, end);  //text= 이 5글자라서 +5
            	//	System.out.println(title);
            		
            		// 중복된 제목을 추가 하지 않도록
            		Iterator<String> it = titleList.iterator();
            		while(it.hasNext()) {
            			String k = it.next();
            			if(k.equals(title)) {
            				it.remove();
            			}
            		}
            		
            		titleList.add(title);
            	}
            }
        }
        return map;
    }

    public static List<Object> toList(JSONArray array) {
        List<Object> list = new ArrayList<Object>();
        for(int i = 0; i < array.length(); i++) {
            Object value = array.get(i);
            if(value instanceof JSONArray) {
                value = toList((JSONArray) value);
            }

            else if(value instanceof JSONObject) {
                value = toMap((JSONObject) value);
            }
            list.add(value);
        }
        return list;
    }
}
  • 실질적으로 JsonUtil에서는 json을 Map으로 바꿔주는 코드만 사용하며, 그안에서도 return받은 map을 사용하는것이 아니라 중간에 걸러진 key값을 이용해 단어들을 슬라이싱하여 얻어낼 값들을 찾아내고 list에 따로 다시 담아준다.

  • 아까 youtubeParser에서 마지막에 youtubeDto 에 값들을 넣어 객체를 생성하고 list에 넣어 뷰에서 뿌려줄 수 있게 하였다.

    youtubeDto
package bit.com.spring.dto;

import java.io.Serializable;

public class Youtube implements Serializable {

	private String title;
	private String url;
	private String img;
	
	public Youtube() {
	}

	public Youtube(String title, String url, String img) {
		super();
		this.title = title;
		this.url = url;
		this.img = img;
	}

	public String getTitle() {
		return title;
	}

	public void setTitle(String title) {
		this.title = title;
	}

	public String getUrl() {
		return url;
	}

	public void setUrl(String url) {
		this.url = url;
	}

	public String getImg() {
		return img;
	}

	public void setImg(String img) {
		this.img = img;
	}

	@Override
	public String toString() {
		return "Youtube [title=" + title + ", url=" + url + ", img=" + img + "]";
	}
}

view 에서는 아래를 참고하여 url의 값을 iframe에 넣어 유튜브를 볼 수 있게 할 수 있다.

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>     
    
<div class="box_border" style="margin-top: 5px; margin-bottom: 10px">
<form id="frmForm" method="post">
<table style="margin-left: auto; margin-right: auto; margin-top: 3px; margin-bottom: 3px;">
<tr>
	<td>검색:</td>
	<td style="padding-left:5px">
		<input type="text" id="s_keyword" name="s_keyword" value="${empty s_keyword?'':s_keyword }">
	</td>

	<td style="padding-left: 5px">
		<span class="button blue">
			<button type="button" id="_btnSearch">검색</button>
		</span>
	</td>
	
</tr>

</table>
</form>
</div>

<div id="_youtube_">
	<iframe id="_youtube" width="640" height="360" src="http://www.youtube.com/embed/" allowfullscreen frameborder="0">
	
	</iframe>

</div>

<table class="list_table" style="width: 85%">
<colgroup>
	<col style="width: 70px"><col style="width: auto"><col style="width: 100px"><col style="width: 30px">
</colgroup>

<thead>
	<tr>
		<th>번호</th><th>제목</th><th>유뷰트 고유 번호</th><th>저장</th>
	</tr>

</thead>

<tbody>
	<c:if test="${empty yulist }">
	<tr>
		<td colspan="4">작성된 목록이 없습니다</tr>
	</c:if>
	
	<c:forEach items="${yulist }" var="you" varStatus="vs">
		<tr class="_hover_tr">
			<td>${vs.count }</td>
			<td style="text-align: left;">
				<div class="c_vname" vname="${you.url }">${you.title }</div>
			</td>
			<td>${you.url }</td>
			<td>
				<img alt="" src="image/save.png" class="ck_seq" id="${you.url }" loginIn="${login.id }" title="${you.title }">
			</td>
		</tr>
	</c:forEach>

</tbody>
</table>

<script>
$(document).ready(function(){
	$("#_youtube_").hide();

	$("._hover_tr").mousemove(function() {
		$(this).children().css("background-color","#f0f5ff");
	}).mouseout(function() {
		$(this).children().css("background-color","#ffffff");
	});
});

$("#_btnSearch").click(function(){
	$("#frmForm").attr('action',"youtube.do").submit();
	
});

$(".c_vname").click(function(){
	$("#_youtube_").show();

	$("#_youtube").attr("src","http://www.youtube.com/embed/"+$(this).attr("vname"));
});

$('.ck_seq').click(function(){

	let id = $(this).attr("loginIn");
	let title = $(this).attr("title");
	let url = $(this).attr("id");

	//alert(url);

	$.ajax({
		url:"./youtubesave.do",
		type:'post',
		async:true,	//비동기설정
		data:{"id":id,"title":title,"url":url},
		success:function(msg){
			alert(msg);
		},
		error:function(){
			alert("error");
		}


	});
	
});

</script>