SpringBoot 크롤링(Crawling)

예림·2024년 1월 24일
0
  1. 크롤링이란?(Crawling)

    '기다'라는 뜻의 crawl의 명사형인데, 소프트웨어와 같은 무언가가 인터넷을 돌아다니며 정보를 수집해 오는 작업을 의미

  2. 동적 크롤링 vs 정적 크롤링

  • 정적 크롤링 : 로그인과 같은 사전 작업 없이, 한 페이지 내부에서 원하는 데이터를 수집할 때 사용 (속도 빠름) /Jsoup
  • 동적 크롤링 : 입력, 클릭, 로그인 등과 같이 
    페이지 내부에서 조작이 필요한 데이터를 수집할 때 사용 (속도 느림) /selenium

적용하기

개발 환경
SpringBoot 2.7.17 gradle-groovy
security5
JPA
Oralcle Develper

  1. jsoup 의존성 추가

의존성을 추가하면 굳이 다운로드 할 필요 없음 !

implementation("org.jsoup:jsoup:1.17.2")

  1. 객체 만들기

yes24 에서 검색한 자격증에 관한 책을 인기순으로 정리된 순서대로 크롤링할 건데 그중에서 이번엔 '책이름', '책가격', '책이미지'를 가져올거다

package com.web.domain;

import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;

import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;

@Getter
@Setter
@ToString
@Builder
@Entity
@Table(name ="BOOKCRAWL")
public class Yes24BookCrawl {
	
	@Id
	private String bookName;
	
	private int bookPrice;
	private String imageName;
	
	// 기본 생성자 추가
    public Yes24BookCrawl() {
    }
    
    public Yes24BookCrawl(String bookName, int bookPrice, String imageName) {
        this.bookName = bookName;
        this.bookPrice = bookPrice;
        this.imageName = imageName;
    }
    

}
  1. Service 만들기
  • 크롤링할 사이트 URL을 변수에 담아서 그 정보를 jsoup으로 전체 데이터 가져옵니다
    저의 경우엔 URL에 한글이 들어가서 그대로 복붙을 하면 한글이 깨지기 때문에 인코더를 이용해서 한글까지 입력받을 수 있게 처리했습니다
  • 그 후에 필요한 부분의 데이터를 불러옵니다
  • 데이터를 담을 리스트 객체를 생성해줍니다
  • for문을 통해 정보를 가져오고 그 데이터를 리스트에 담아줍니다
  • 데이터베이스에 저장해줍니다
  • 객체에 저장된 데이터를 다시 전부 불러줄 메서드를 미리 만들어줍니다
@Service
public class BookCrawlService {
	
	@Autowired
	private CrawlRepository crawlRepository;
	
	public BookCrawlService(CrawlRepository crawlRepository) {
        this.crawlRepository = crawlRepository;
    }

    public void saveYes24BookCrawl(Yes24BookCrawl bookCrawl) {
        crawlRepository.save(bookCrawl);
    }

    public void saveYes24BookCrawls(List<Yes24BookCrawl> bookCrawls) {
        crawlRepository.saveAll(bookCrawls);
    }
	
	
	public String generateSearchUrl() {
        try {
        	String searchQuery = "컴퓨터활용";
        	
        	//URL에 한글을 입력받기 위헤 인코더 입력
            String encodedQuery = URLEncoder.encode(searchQuery, StandardCharsets.UTF_8.toString());
            
            return "https://www.yes24.com/Product/Search?domain=ALL&query=" + encodedQuery;           
            
        } catch (UnsupportedEncodingException e) {
            // 예외 처리: 인코딩에 실패한 경우
            e.printStackTrace();
            return null; // 또는 예외를 다시 던질 수 있음
        }
    }
	
	@PostConstruct
	public void getBooktDates() throws IOException {
		
		String Book_Data_URL = generateSearchUrl();	//인코딩한 URL String 변수에 담기
		
		Document doc = Jsoup.connect(Book_Data_URL).get();	//URL에 담긴 정보 전부 불러오기
		
		//필요한 정보만 가져오기
		String cssSelector = "#yesWrap #ySchContSec #sGoodsWrap .sGoodsSec section#goodsListWrap div.sGoodsSecArea ul#yesSchList li div.itemUnit";
		
		//전체 가져왔던 데이터중 내가 필요한 정보만 불러오기
		Elements contents = doc.select(cssSelector);
		
		//확인용
		System.out.println(contents);
		
		//리스트에 저장할 객체 생성
		List<Yes24BookCrawl> bookCrawls = new ArrayList<>();
		
		for(Element content : contents) {
			String name = content.select("div.item_info div.info_row.info_name a.gd_name").text();
			String priceString = content.select("div.item_info div.info_row.info_price strong.txt_num em.yes_b").text();			
			 // 숫자만 추출하여 변환
			int price = Integer.parseInt(priceString.replaceAll("[^0-9]", ""));
			
			// 이미지 크롤링
			Element imgElement = content.select("div.item_img div.img_canvas span.img_item span.img_grp a.lnk_img em.img_bdr img.lazy").first();
			String imageUri = imgElement.attr("data-original");
			
			Yes24BookCrawl bookCrawl = Yes24BookCrawl.builder()
					.bookName(name)
					.bookPrice(price)
					.imageName(imageUri)
					.build();
			System.out.println(bookCrawl.toString());
			
			bookCrawls.add(bookCrawl);
		}
		
		//JPA로 DB에 저장
		saveYes24BookCrawls(bookCrawls);
	}
	
	//전체 출력해줄 메서드
	public List<Yes24BookCrawl> getAllBooks() {
		return crawlRepository.findAll();
	}  
}
  1. repository 생성

3번에서 사용한 JPA를 위한 Repository 만들어줍니다

package com.web.persistence;

import org.springframework.data.jpa.repository.JpaRepository;

import com.web.domain.Yes24BookCrawl;

public interface CrawlRepository extends JpaRepository<Yes24BookCrawl, String> {

}
  1. 리스트를 보여줄 페이지를 이동하기 위한 경로 설정

컨트롤러를 생성해서 url을 통해 페이지로 이동할 수 있게 처리해줍니다

package com.web.controller;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;

import com.web.domain.Yes24BookCrawl;
import com.web.service.BookCrawlService;

@Controller
public class MainController {
	
	@Autowired
	private BookCrawlService bookCrawlService;
	
	@GetMapping("/books")
	public String showBooks(Model model) {
		List<Yes24BookCrawl> books = bookCrawlService.getAllBooks();
		model.addAttribute("books",books);
		return "/books";
	}

}
  1. html

책이름과 가격과 사진을 띄워줄 html을 templates에다가 만들어서 작성
컨트롤러에 주소와 이름이 일치하게 작성하면 됩니다
타임리프를 이용해서 정보를 보여줄거기 때문에 타임리프를 쓸 수 있는 코드를 추가해준 후에 간단한 테이블을 생성해줍니다

참고로 타임리프가 적용이 안될시에 properties에서 타임리프 설정이 되어있나 확인

# Thymeleaf 설정
spring.thymeleaf.prefix=classpath:/templates/
spring.thymeleaf.suffix=.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>BookList</title>
</head>
<body>
<h2>Book List</h2>

<table border="1">
    <thead>
    <tr>
        <th>Book Name</th>
        <th>Price</th>
        <th>Image</th>
    </tr>
    </thead>
    <tbody>
    <tr th:each="book : ${books}">
        <td th:text="${book.bookName}"></td>
        <td th:text="${book.bookPrice}"></td>
        <td>
            <!-- 이미지 표시 (이미지 경로는 예시로 넣어주세요) -->
            <img th:src="${book.imageName}" alt="Book Image" width="100">
        </td>
    </tr>
    </tbody>
</body>
</html>
  1. 결과

    이렇게 결과가 잘 도출된 것을 확인할 수 있습니다
profile
커피 잘 마시는 사람

0개의 댓글

관련 채용 정보