자동적 웹페이지 크롤링 하기
1. chromedriver을 깐다.
2.build.gradle에 라이브러리를 임포트 한다
(maven repo를 창에 치고 들어가서 selenium을 치고 사용자가 많은 것을 선택후 그레이들을 선택하여 붙여넣기하세요)
// https://mvnrepository.com/artifact/org.seleniumhq.selenium/selenium-java
implementation group: 'org.seleniumhq.selenium', name: 'selenium-java', version: '3.141.59'
bean으로 스프링에 주입
@Bean
public CommandLineRunner crawlArticles(ArticleService articleService) {
return (args -> {
Path path = Paths.get(System.getProperty("user.dir"), ("src/main/resources/chromedriver.exe"));
webdriver를 사용하기 위해 c드라이브에 넣고 메인에서 경로 설정을 해줍니다.
// WebDriver 경로 설정
System.setProperty("webdriver.chrome.driver", path.toString());
// WebDriver 옵션 설정
ChromeOptions options = new ChromeOptions();
options.addArguments("headless");
options.addArguments("--start-maximized"); // 전체화면으로 실행
options.addArguments("--disable-popup-blocking"); // 팝업 무시
options.addArguments("--disable-default-apps"); // 기본앱 사용안함
//주의:팝업창 안뜨게 만들기 잊지말기!!! 중요함...
창을 띄울때 원하는 조건에 맞춰서 창을 띄우기 위해 설정을 합니다.
// WebDriver 객체 생성
ChromeDriver driver = new ChromeDriver(options);
// 빈 탭 생성
driver.executeScript("window.open('about:blank','_blank');");
// 탭 목록 가져오기
List<String> tabs = new ArrayList<String>(driver.getWindowHandles());
//첫번째 탭으로 전환
driver.switchTo().window(tabs.get(0));
//웹페이지 요청
driver.get("https://newneek.co/");
이번에 크롤링한 웹페이지는 sbslr이라는 동적 웹페이지를 크롤링 하였기 때문에
스피너가 돌아가서 시간을 주고 loadmore라는 아이디가 나올때 까지 기다리게 하였습니다.
*기다리지 않으면 에러가 나서 크롤링을 할 수 없었습니다.
WebDriverWait wait = new WebDriverWait(driver, 20);
wait.until(ExpectedConditions.visibilityOfElementLocated(By.className("loadmore")));
뉴닉 메인펭지에 있는 더보기를 15번 정도를 눌러 페이지를 열고 크롤링을 할 생각이어서
for문을 사용하여 15번을 눌렀습니다. 하지만 연속적으로 버튼이 15번 눌리기 때문에 wait을 주어 시간을 준뒤 다시 버튼을 누르도록 설정해주었습니다.
for (int i = 0; i < 15; i++) {
driver.findElementByClassName("loadmore").sendKeys(Keys.ENTER);
String waitTargetSelector = String.format(".card:nth-child(%d)", 12 + 12 * (i + 1));
wait.until(ExpectedConditions.visibilityOfElementLocated(By.cssSelector(waitTargetSelector)));
}
크롬드라이버가 카드로 들어갔을 때 크롤링 하려는 특정 태그의 위치를 Xpath로 찾아 기다렸다가
그 태그로 들어가서 크롤링하게 했습니다.
//각 카드 href로 들어가기
List<WebElement> webElements = driver.findElementsByXPath("//*[@id=\"root\"]/div/section/div/a");
List<String> detailsUrlList = new ArrayList<>();
for (int i = 0; i < webElements.size(); i++) {
String url = webElements.get(i).getAttribute("href");
detailsUrlList.add(url);
}
각 페이지에 원하는 태그들을 크롤링 해오기 위해 태그들이 로딩 될때까지 기다리게 만들었다.
//각 url에 들어가서 제목, 카테고리, 내용 가지고 오기
for (int i = 0; i < detailsUrlList.size(); i++) {
driver.get(detailsUrlList.get(i));
final String TITLE_SELECTOR = "#root > div > section > div > header > h2";
final String CATEGORY_SELECTOR = "#root > div > section > div > header > a";
final String CONTENTS_SELECTOR = "#root > div > section > div > div";
final String DATE_SELECTOR = "#root > div > section > div > header > time";
final String IMAGE_SELECTOR = "#root > div > section > div > div > div.post-body-thumb > img";
wait.until(ExpectedConditions.visibilityOfElementLocated(By.cssSelector(TITLE_SELECTOR)));
wait.until(ExpectedConditions.visibilityOfElementLocated(By.cssSelector(CATEGORY_SELECTOR)));
wait.until(ExpectedConditions.visibilityOfElementLocated(By.cssSelector(CONTENTS_SELECTOR)));
try&catch로 이미지가 없는 부분을 예외처리 해주었고
ArticleRequestDto에 저장 할 수 있도록 밖에서 String으로 변수를 만들었다
String title = "";
String categoryName = "";
String image = "";
String contents = "";
LocalDate date;
//제목가지고 오기
WebElement postWebElement = driver.findElementByCssSelector(TITLE_SELECTOR);
title = postWebElement.getText();
//카테고리 가지고 오기
WebElement cateWebElement = driver.findElementByCssSelector(CATEGORY_SELECTOR);
categoryName = cateWebElement.getText();
//날짜 가지고 오기
WebElement dateWebElement = driver.findElementByCssSelector(DATE_SELECTOR);
String dateText = dateWebElement.getText();
date = LocalDate.parse(dateText, DateTimeFormatter.ofPattern("yyyy/MM/dd"));
//이미지 가지고 오기 /이미지가 없을 때를 대비
try {
WebElement imageWebElement = driver.findElementByCssSelector(IMAGE_SELECTOR);
image = imageWebElement.getAttribute("src");
} catch (NoSuchElementException e) {
}
//내용 가지고 오기
WebElement contentWebElement = driver.findElementByCssSelector(CONTENTS_SELECTOR);
contents = contentWebElement.getAttribute("innerHTML");
크롤링한 값들을 ArticleRequestDto에 저장했다.
ArticleRequestDto requestDto = new ArticleRequestDto(title, image, contents, categoryName);
ArticleCrawlRequestDto crawlRequestDto = new ArticleCrawlRequestDto(requestDto, date);
articleService.create(crawlRequestDto);
}
그리고 크롬 브라우저 창 닫기
driver.close();
driver.quit();
});
}
'항해99' 카테고리의 다른 글
[항해99 - chapter01] 미니 웹 프로젝트(Starting Assignment) (0) | 2021.03.01 |
---|