2024-03-13 (64일차) - 크롤링한 데이터 DB에 담기

·2024년 3월 13일

프로젝트

목록 보기
27/57

📅 2024-03-13, 64일차


크롤링한 데이터를 DB에 담기위해..

크롤링을 해온 데이터를 리스트에 담아 컨트롤러 -> 서비스 -> 레파지토리까지 넘겨주고 DB로 INSERT를 시도했다.

내가 마주한 문제상황들

1. 쿼리의 데이터 타입

  • 크롤링을 해서 DB에 넣는 과정에서 카페의 정보와 쿼리가 콘솔에 뜨면서 DB로 입력이 잘 되는가 싶더니 중간에 멈추는 에러 발생
  • 콘솔을 확인해보니 카페의 영업시간 정보의 내용이 너무 길었고 문득 든 생각은 데이터 타입의 크기가 작은가?
  • 몇몇 업체는 영업시간을 요일별로 라스트오더시간까지 매우 상세하게 적어 놓은 곳도 있다, 이런 업체의 데이터를 DB에 INSERT 할 때 CHAR(100)으로 모자랐다

해결방법

  • CHAR(100)에서 TEXT로 변경

2. 마지막 이미지url만 DB에 담는 문제

  • 이미지 URL이 1개 이상 필요했고 그걸 각각 cafe TABLE에 담아야했다.
    • cafe TABLE에 이미지URL을 담을 칼럼 5개 만들어 놓은 상태
  • 크롤링해온 카페 이미지url들을 콘솔에 출력 하기 위해선 for-each문을 사용했었는데 실제로 DB에는 맨 마지막으로 가지고 온 url만 5개의 칼럼에 들어갔다.
  • 일반 for문으로 5번 반복해서 이미지url리스트의 인덱스의 값을 cafe리스트에 담아보았다.

    ArrayIndexOutOfBoundsException

  • 5개의 인덱스를 지정해버렸으니, 이미지가 5개 이하인 업체를 크롤링해올때 문제가 일어 나는 것 같았다.

해결방법

  • 일단 vo와 DB 테이블에는 변수 cafeImgUrl2,cafeImgUrl2, cafeImgUrl3, cafeImgUrl4, cafeImgUrl5와 다섯개의 이미지 컬럼 존재하고 나는 이걸 바꾸고 싶지 않았다.
  • 반복문의 조건을 5번 반복이 아닌, 크롤링해오는 이미지url을 담은 리스트의 크기만큼 반복하라고 고쳤지만 반복문 밖에서 cafe리스트의 url을 담는 변수다섯개에 set을 못하면 또 에러가 날 것 같았다.
  • switch case문 추가했고 case안에 set을 하는 방식으로 했다!
    • "switch문을 작성할 때 case는 여러 개가 나와도 상관없다. break라는 것은 보조제어문 중 하나이다. 위에 설명한 것처럼 조건에 해당하는 실행문만 실행시키고 종료시키기 위해서 사용된다." - 구글링

수정 한 코드

			String imageUrl = "";
			List<WebElement> imageElements = driver.findElements(By.cssSelector("div.K0PDV._div"));
            
			// List<String> imageUrls = new ArrayList<>();
			for (int i = 0; i < imageElements.size(); i++) {
				WebElement imageElement = imageElements.get(i);

				String styleAttribute = imageElement.getAttribute("style");

			// 스타일 속성에서 URL 추출
				imageUrl = extractImageUrlFromStyleAttribute(styleAttribute);

			// imageUrls.add(i, imageUrl);

				switch (i) {
				case 0:
					cafe.setCafeImgUrl1(imageUrl);
					break;
				case 1:
					cafe.setCafeImgUrl2(imageUrl);
					break;
				case 2:
					cafe.setCafeImgUrl3(imageUrl);
					break;
				case 3:
					cafe.setCafeImgUrl4(imageUrl);
					break;
				case 4:
					cafe.setCafeImgUrl5(imageUrl);
					break;

				}
	            System.out.println("Image URL: " + imageUrl);
			}

			cafe.setName(key);
			cafe.setAddress(address);
			cafe.setPhoneNum(phoneNumber);
			cafe.setBusinessHours(businessHours);
			cafe.setFacilities(facilities);
//			cafe.setCafeImgUrl1(imageUrls.get(0)); // Cafe 객체에 이미지 URL 리스트 설정
//			cafe.setCafeImgUrl2(imageUrls.get(1));
//			cafe.setCafeImgUrl3(imageUrls.get(2));
//			cafe.setCafeImgUrl4(imageUrls.get(3));
//			cafe.setCafeImgUrl5(imageUrls.get(4));
			cafes.add(cafe);

3. 여러번 크롤링 시 이전에 크롤링해서 가져온 카페가 DB에 또 담기는 문제

  • 아직 나의 크롤링 로직은 크롤링 시 카페를 10개씩 밖에 못 긁어온다.
  • 많은 카페를 긁어오기 위해 크롤링 함수를 여러번 실행 하면 되는데..
    • 네이버에서 랜덤으로 10개를 지도에 보여주기 때문에, 크롤링해오다보면 이전 크롤링과정에서 가져온 카페A를 그 다음 크롤링에서 또 가져오는 경우가 발생
  • DB 테이블에 카페A가 중복입력 되는 문제가 생긴다.

해결방법

  • 레파지토리에서 INSERT하기 전에, 서비스에서 중복여부를 체크하고 레파지토리로 넘어가게 해주면 된다.
  • 크롤링해온 카페의 이름으로 DB에 몇개 들어가있는지 조회해본 후 COUNT의 값이 0이면 INSERT 진행해~ 라는 로직
    public void saveCafeDataFromWebCrawler(List<Cafe> cafes) {

        for (Cafe cafe : cafes) {
        	 // 크롤링으로 들어오는 카페 이름으로 DB에 몇개들어있는지 SELECT하고, 몇개인지 받아온다         
            int count = cafeRepository.countDuplicateCafeName(cafe.getName());
            
            // 만약 0개면 INSERT시키러 레파지토리로 데이터를 넘겨준다
            if (count == 0) {
            	 cafeRepository.insertCafe(cafe); 
            } 
        }
    }
	@Select("""
			SELECT COUNT(name)
			FROM cafe
			WHERE name = #{name}
			""")
	public int countDuplicateCafeName(String name);	
    
    
    	@Insert("""
	        INSERT INTO 
	        cafe SET
	        regDate = NOW(), 
	        updateDate = NOW(), 
	        name = #{name}, 
	        address = #{address}, 
	        businessHours = #{businessHours},
	        phoneNum = #{phoneNum}, 
	        facilities = #{facilities}, 
	        cafeImgUrl1 = #{cafeImgUrl1}, 
	        cafeImgUrl2 = #{cafeImgUrl2}, 
	        cafeImgUrl3 = #{cafeImgUrl3}, 
	        cafeImgUrl4 = #{cafeImgUrl4}, 
	        cafeImgUrl5 = #{cafeImgUrl5}, 
	        goodReactionPoint = #{goodReactionPoint}, 
	        reviewCount = #{reviewCount}, 
	        hashtag = #{hashtag}
	        """)

    public void insertCafe(Cafe cafe);
profile
hello world

0개의 댓글