이전에 크롤링 하는 법을 정리해놨는데, 이걸 통해서 크롤링 할 수 있는 api를 구현했다.
영화 정보 크롤링
@Service
@RequiredArgsConstructor
public class MovieCrawlingService {
private static final String weekRankingUrl = "http://www.cine21.com/rank/boxoffice/domestic";
private static String WEB_DRIVER_ID = "webdriver.chrome.driver";
private static String WEB_DRIVER_PATH = "./chromedriver.exe";
private final MovieRepository movieRepository;
private final ActorRepository actorRepository;
private WebDriver startDrive(String url) {
System.setProperty(WEB_DRIVER_ID, WEB_DRIVER_PATH);
ChromeOptions options = new ChromeOptions().addArguments(
"--disable-popup-blocking", // 팝업 안띄움
"headless", // 브라우저 안띄움
"--disable-gpu", // gpu 비활성화
"--blink-settings=imagesEnabled=false", // 이미지 다운 안받음
"--remote-allow-origins=*"); //없으면 접근 안됨
WebDriver driver = new ChromeDriver(options);
driver.get(url);
return driver;
}
@Transactional
public void getWeekRankingList() {
List<MovieWeekRankingDto> movieDtoList = getWeekRankingDtoList();
for (MovieWeekRankingDto dto : movieDtoList) {
Movie movie = movieRepository.save(MovieCrawlingConverter.toMovie(dto));
System.out.println(dto.getActor());
actorRepository.saveAll(MovieCrawlingConverter.toActors(movie, dto.getActor()));
}
}
public List<MovieWeekRankingDto> getWeekRankingDtoList() {
WebDriver driver = startDrive(weekRankingUrl);
List<MovieWeekRankingDto> movieDtoList = new ArrayList<>();
try {
List<WebElement> elements = driver.findElements(By.cssSelector("li.boxoffice_li > a"));
List<String> detailList = elements.stream().map(item -> item.getAttribute("href")).toList();
for (int i = 0; i < Math.min(10, detailList.size()); i++) { //갯수 지정
driver.navigate().to(detailList.get(i));
WebElement element = driver.findElement(By.cssSelector("div.mov_info"));
WebElement imgAttr = driver.findElement(By.cssSelector("div.mov_poster > img"));
movieDtoList.add(
MovieWeekRankingDto.builder()
.movieCode(detailList.get(i).split("=")[1])
.imgLink(imgAttr.getAttribute("src") != null ? imgAttr.getAttribute("src") : "")
.summary(findElementByCssSelector(driver, "div.story_area > div"))
.title(findElementByCssSelector(element, "p.tit"))
.nation(findElementByCssSelector(element, "p:nth-child(3) > span:nth-child(2)"))
.genre(findElementByCssSelector(element, "p:nth-child(4) > span:nth-child(1)"))
.director(findElementByCssSelector(element, "p:nth-child(6) > a"))
.runningTime(
findElementByCssSelector(element, "p:nth-child(4) > span:nth-child(2)"))
.actor(findElementByCssSelector(element, "p:nth-child(7)"))
.build());
}
driver.quit();
} catch (Exception e) {
driver.quit();
}
return movieDtoList;
}
private String findElementByCssSelector(WebDriver element, String cssSelector) {
try {
WebElement attribute = element.findElement(By.cssSelector(cssSelector));
return attribute.getText() != null ? attribute.getText() : "";
} catch (Exception e) {
return "";
}
}
private String findElementByCssSelector(WebElement element, String cssSelector) {
try {
WebElement attribute = element.findElement(By.cssSelector(cssSelector));
return attribute.getText() != null ? attribute.getText() : "";
} catch (Exception e) {
return "";
}
}
드라이브와 관련된 생성부분을 분리했고, 중간에 어떤 오류로 인해 강제종료가 되는 상황을 위해서 try-catch문으로 에러를 방지했다.
그리고 원하는 정보가 없거나 크롤링할 웹사이트의 구조가 바뀌었을 수도 있으니 문자열이 null이 되는 상황도 없애주었다.
일단 동작은 잘 되는데 무지성으로 크롤링할 경우 영화의 갯수가 너무 많아서 렉이 너무 심해지는 현상이 있었다.
그래서 search 부분을 추가해 갯수에 제한을 두는 기능을 추가해야 될 것 같다.
'토이프로젝트 > 리뷰어(영화 리뷰 사이트)' 카테고리의 다른 글
백엔드 cicd 구축하기2 (0) | 2023.07.21 |
---|---|
백엔드 cicd 구축하기 (0) | 2023.06.02 |
영화 정보 크롤링 하기 (0) | 2023.05.25 |
16일차 - JPA N+1 문제 (0) | 2022.11.07 |
14일차 - 영화 상세정보 조회 api 구현 (0) | 2022.11.04 |