Project

[Crawling] 닌텐도 온라인 스토어 크롤링하기

콩스프 2022. 12. 28. 20:40

Introduce

주제

닌텐도 온라인 스토어 사이트의 게임 타이틀, 발매일 및 가격 크롤링

 

 

개발환경

  • IDE : Eclipse EE
  • JDK : 14.0.1
  • Java 라이브러리 : jsoup-1.14.3.jar

 

타겟 사이트

https://store.nintendo.co.kr/games?game_category=15%2C23%2C24%2C19%2C43&publisher=14

 

 

타겟 데이터

  • 타이틀
  • 발매일
  • 가격

 

게임 정보 크롤링

1) Jsoup의 connect() 메서드를 이용해 Connection 객체를 얻은 후 get() 메서드로 Document 타입의 doc에 저장

// 타겟 사이트
final String URL = "https://store.nintendo.co.kr/games?game_category=15%2C23%2C24%2C19%2C43&publisher=14";
Document doc = null;
try {
	doc = Jsoup.connect(URL).get();
} catch (IOException e) {
	e.printStackTrace();
}

 

2) Document의 select(”[html 선택자]”) 메서드를 이용해 크롤링할 데이터를 가져와 각각 Elements 타입의 변수에 저장

// 타이틀
Elements eles = doc.select("a.category-product-item-title-link");
Iterator<Element> itr = eles.iterator();

// 발매일
Elements eles2 = doc.select("div.category-product-item-released");
Iterator<Element> itr2 = eles2.iterator();

// 가격
Elements eles3 = doc.select("span.price");
Iterator<Element> itr3 = eles3.iterator();
  • 타이틀

 

  • 발매일

 

  • 가격

 

 

3) while문을 사용해 데이터를 가공하여 속성값을 넣어준다

while (itr.hasNext()) {
	GameVO gvo = new GameVO();
	// 타이틀 저장
	String title = itr.next().text();
	gvo.setTitle(title);

	// ex) "발매 22.11.22"
	// "발매", " " > ""
	// 발매와 공백을 ""로 대체 후 발매일 저장 
	String date = itr2.next().text().replace("발매", "").trim(); // "22.11.22"
	// 4자리의 연도로 초기화하기 위해 "20" 연결
	int year = Integer.parseInt("20" + date.split("\\.")[0]); // 연도: 2022
	int month = Integer.parseInt(date.split("\\.")[1]); // 월: 11
	int day = Integer.parseInt(date.split("\\.")[2]); // 일: 22
	LocalDate gameDate = LocalDate.of(year, month, day); // LocalDate.of(2022, 11, 22)
	gvo.setDate(gameDate);

	// ex) "₩100,000"
	// "₩", "," > ""
	// 원 기호와 쉼표를 ""로 대체 후 가격 저장
	String price = itr3.next().text().replace("₩", "").replace(",", ""); // "100000"
	gvo.setPrice(Integer.parseInt(price)); // 문자열 형변환

	// games에 추가
	// 4번에서 설명
	gdao.addGame(gvo);
}

 

 

4) 타겟 데이터인 게임 타이틀, 발매일, 가격을 저장한 객체를 인자로 받는 GameDAO의 addGame() 메서드를 호출

public class GameDAO {
	private int PK;
	ArrayList<GameVO> games;
	Time time;

	public GameDAO() {
		PK = 101;
		games = new ArrayList<GameVO>();
	}

	// 게임 추가(크롤링) C
	public boolean addGame(GameVO gvo) {	
		try {
			gvo.setNum(PK++);
			games.add(gvo);
			System.out.println("\t로그 : 게임 추가 성공");
		} catch (Exception e) {
			System.out.println("\t로그 : 게임 추가 실패");
			return false;
		}
		return true;
	}
}

 

 

데이터 가공

가공 전

String title = itr.next().text();
System.out.println("게임 타이틀 - " + title);

String date = itr2.next().text();
System.out.println("발매일 - " + date);

String price = itr3.next().text();
System.out.println("가격 - " + price);

 

발매일 데이터 가공

  • 데이터 가공 전 - “발매”와 공백이 함께 크롤링 된다.

1. “발매” 제거

String date = itr2.next().text().replace("발매", "");
System.out.println("발매일-" + date);
  • 두 번째 인자로 첫 번째 인자를 바꿔주는 String의 replace() 메서드를 사용하여 ”발매”를 “”로 변경

  • “발매”는 제거하였지만 공백이 발생한다는 문제가 생김

 

2. 공백 제거

String date = itr2.next().text().replace("발매", "").trim();
System.out.println("발매일-" + date);
  • String의 앞 뒤 공백을 없애주는 trim() 메서드를 사용하여 공백제거

 

3. LocalDate 형으로 가공하기

int year = Integer.parseInt("20" + date.split("\\.")[0]); // 2022
int month = Integer.parseInt(date.split("\\.")[1]); // 11
int day = Integer.parseInt(date.split("\\.")[2]); // 22
LocalDate gameDate = LocalDate.of(year, month, day); // LocalDate.of(2022, 11, 22)
  • 추후에 LocalDate의 메서드를 사용하여 발매일을 비교하기 위해 String형이 아닌 LocalDate로 가공해줘야 했다.

 

int year = Integer.parseInt("20" + date.split("\\.")[0]); // 2022
  • split() 메서드를 통해 .을 기준으로 String을 분리하였다.
    ➡ split() 메서드를 사용하여 문자열을 분리하면 String타입의 배열로 반환된다.

  • split() 메서드는 파라미터로 정규식을 입력 받는데 정규식에서 마침표(.)는 임의의 한 문자를 의미한다. 따라서 문자열을 마침표(.)로 구분하고 싶다면 (”\\.”) 과 같이 사용해야 한다.

 

* 정규식에서 역슬래시(\) 다음에 마침표와 같은 특수문자 (즉, 정규식에서 특정한 의미를 가지는 문자)가 오면   
   역슬래시(\) 다음에 오는 문자를 일반 문자로 취급함  ➡ 정규식에서 '\.'는 일반문자 마침표를 의미

*Java의 문자열에서 역슬래시(\)를 표현하기 위해서는 앞에 \를 붙여서 escape 처리를 해야함 
  split()의 파라미터로 "\\."를 전달하여, 문자열을 마침표 단위로 자르도록 하였음

 

 

LocalDate gameDate = LocalDate.of(year, month, day); // LocalDate.of(2022, 11, 22)
System.out.println("발매일 - " + LocalDate);
  • LocalDate 클래스는 4자리 연도를 사용하므로 20을 붙여주었다.
  • LocalDate의 static 메서드인 of()를 사용하여 LocalDate 객체를 생성해주었다.
  • of : int 타입 인자 3개 (year, month, dayOfMonth)를 입력받아 LocalDate의 인스턴스를 반환하는 함수

 

 

 

 

가격 데이터 가공

  • 데이터 가공 전 - “₩”와 “,”가 함께 크롤링 된다.

  • int형으로 형변환을 해주기위해 문자를 제거하는 작업이 필요했다.

 

 

” 제거

String price = itr3.next().text().replace("₩", "");
System.out.println("가격-" + price);
  • 두 번째 인자로 첫 번째 인자를 바꿔주는 String의 replace() 메서드를 사용하여 ”₩”를 “”로 변경

 

“,” 제거

String price = itr3.next().text().replace("₩", "").replace(",", "");
System.out.println("가격-" + price);
  • String의 두 번재 인자로 첫 번째 인자를 바꿔주는 replace() 메서드를 사용하여 ”,”를 “”로 변경

 

 

Integer 형으로 가공하기

String price = itr3.next().text().replace("₩", "").replace(",", "");
System.out.println("가격 - " + price);
gvo.setPrice(Integer.parseInt(price));
  • Integer(Wrapper 클래스)의 parseInt 메서드를 통해 String 타입인 price를 int 타입으로 변경

 

 

가공 후

//타이틀
String title = itr.next().text();
System.out.println("게임 타이틀 - " + title);

//발매일
String date = itr2.next().text().replace("발매", "").trim(); // "22.11.22"
int year = Integer.parseInt("20" + date.split("\\.")[0]); // 2022
int month = Integer.parseInt(date.split("\\.")[1]); // 11
int day = Integer.parseInt(date.split("\\.")[2]); // 22
LocalDate gameDate = LocalDate.of(year, month, day); // LocalDate.of(2022, 11, 22)
System.out.println("발매일 - "+gameDate);

//가격
String price = itr3.next().text().replace("₩", "").replace(",", "");
System.out.println("가격 - " + price);