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);