ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [셀레니움 크롤링 예제] 다나와 리뷰 크롤링을 위한 지속가능한 코드 만들기 (2)
    직접 해보기/Python 2024. 6. 10. 22:45
    728x90
    반응형

     

    다나와 크롤링 예제 — 공기청정기 인기 제품리뷰 수집하기


    ✅  이전 단계가 궁금하다면, 

    (이전포스팅) 다나와 제품정보 크롤링하기!

     

    0️⃣  이전 포스팅에서 크롤링했던 제품정보들을 가지고 와서 살펴보겠습니다.

    • 피클파일로 저장했던 데이터프레임을 그대로 가져와서, dtypes로 데이터타입까지 잘 가져와졌는지 점검합니다.
      수치데이터가 들어간 칼럼들은 float 혹은 int로 잘 저장되어 왔네요.


    1️⃣  제품별 상세페이지로 들어간 다음, 리뷰 페이지를 넘기면서 크롤링해오려고 합니다.

    • 제품별로 상세페이지 URL을 수집했었죠! 이제 이 URL에 순서대로 접속해서 리뷰를 크롤링해오겠습니다.
    • 상세페이지 URL 접속 → 상품리뷰로 이동 → 리뷰 페이지 넘기면서 페이지 소스 가져오기 흐름으로 구성해볼게요.
    • 일단 맨 첫번째 제품으로 테스트를 해보겠습니다.

     

     

    • 리뷰 페이지 넘기면서 긁어오는 과정은 이전포스팅과 동일한 구성으로 진행하겠습니다.
      페이지 넘기고 클릭하는 동적인 부분은 Selenium이, 한 페이지에서 소스 긁어오는건 BeautifulSoup이 합니다.
    • while 반복문 돌리면서 다음 페이지를 클릭해줄거에요. 에러나지 않도록 한 페이지 끝날마다 time.sleep 대기도 필수입니다. 
    • 그리고 한 페이지마다 BeautifulSoup으로 리뷰를 전부 긁어올게요. 긁어온 소스들은 딕셔너리에 저장해두겠습니다.
    • 마찬가지로, 맨첫번째 제품 페이지에서 테스트 해보니 문제 없네요!
    # 페이지별 소스 저장할 빈 딕셔너리 생성
    soup_dict = {}
    
    page = 1
    
    # page에 1씩 더하면서, 전체 페이지 수와 같아지기 전까지 반복
    while page < num_pages:
    
    	# 페이지별 리뷰 전부 가져오기
        soup = BeautifulSoup(driver.page_source)
        reviews = soup.select('ul.rvw_list li.danawa-prodBlog-companyReview-clazz-more')
        time.sleep(1)
    
        # 페이지별 리뷰 딕셔너리에 저장해두기
        soup_dict[page] = reviews
        print(page, "페이지 완료!")
        
        page += 1
        
        # 11,21,31..번째 페이지인 경우 [다음 페이지] 버튼 클릭
        if page%10 == 1:
            driver.find_element(By.XPATH, 
                                '//*[@id="danawa-prodBlog-companyReview-content-list"]/div/div/a').click()
    
        # 나머지 경우는 page에 해당하는 페이지 클릭
        else:
            # 페이지 넘기는 부분 경로 통째로 가져와놓고
            page_nums = driver.find_element(By.XPATH, 
                                            '//*[@id="danawa-prodBlog-companyReview-content-list"]/div/div')
            # 다음 페이지 링크를 클릭
            page_nums.find_element(By.LINK_TEXT, str(page)).click()
    
        # 에러나지 않도록 한페이지마다 잠시 대기
        time.sleep(2)
    
    # 마지막 페이지 리뷰 가져오고 마무리
    soup = BeautifulSoup(driver.page_source)
    reviews = soup.select('ul.rvw_list li.danawa-prodBlog-companyReview-clazz-more')
    soup_dict[page] = reviews
    print(page, "페이지 완료!")
    
    print("<<< 테스트 크롤링 완료입니다! >>>")

     

     

     


    2️⃣  저장해둔 페이지 소스에서 쓸모있는 정보들만 가져와서, DataFrame에 정리해주겠습니다.

    • 별점, 구매처, 작성일, 리뷰내용 이렇게 하나씩 가져와보겠습니다.
      작성자ID는 이미 다나와에서 마스킹해둔 상태라.. 별 소용이 없을 것 같아 패스하려구요.
    • 위 코드에 이어서 맨첫번째 제품에서만 리뷰 크롤링을 테스트 해보겠습니다.
      저장해뒀던 딕셔너리에서 리뷰소스 하나씩 가져와서 데이터프레임을 생성했어요. 136개 리뷰 문제없이 저장됐습니다!
    # 모델이름, 별점, 구매처, 작성일, 리뷰내용
    name_list, star_list, mall_list, date_list, review_list = [], [], [], [], []
    
    ### 딕셔너리에 저장해둔 페이지 하나씩 돌리기 ###
    for key in range(len(soup_dict)):
        reviews = soup_dict[key+1]
        
        # 리뷰마다 돌리면서 모델이름 등등 가져오기
        for i in range(len(reviews)):
    
            # 모델이름
            try:
                name_list.append(str(df_for_review_crawling['제품모델명'][0]))
            except:
                name_list.append('')
    
            # 별점
            try:
                star_list.append(int
                	(reviews[i].select_one('span.star_mask').text.strip().replace('점',''))/20)
            except:
                star_list.append('')
    
            # 구매처
            try:
                mall_list.append(str(reviews[i].select_one('span.mall').text.strip()))
            except:
                mall_list.append('')
    
            # 작성일
            try:
                date_list.append(reviews[i].select_one('span.date').text.strip())
            except:
                date_list.append('')
    
            # 리뷰내용
            try:
                review_list.append(str(reviews[i].select_one('div.atc').text.strip()))
            except:
                review_list.append('')
            
    
    ### 크롤링 결과 저장 ###
    # 데이터프레임 생성
    df_result = pd.DataFrame({
        '제품모델명' : name_list, '구매처' : mall_list,
        '작성일' : date_list, '별점' : star_list, '리뷰' : review_list
    }); df_result

     

     


    3️⃣  최종함수를 만들어서, 공기청정기 인기제품 10개만 테스트로 크롤링해볼게요!

    • 이전포스팅에서 수집했던 LG전자, 삼성전자, 위닉스의 공기청정기 제품들 중 일부를 선정해서 리뷰크롤링 테스트를 해봅니다.
    • 다나와에서 검색되는 인기3사의 제품은 총 375개였는데요, 리뷰가 너무 적은 제품은 제외할게요.
      리뷰가 10개도 안쌓인 제품들은 제외하고, 리뷰 10개 이상인 제품으로 걸러보니 총 239개입니다.
    • 이걸 전부 크롤링하면 너무 오래 걸리겠어요..
      일부만 테스트해서 크롤링이 잘되는지 보고싶으니 이중에서 상위 10개 제품만 뽑아올게요.
    # 필요한 패키지 가져오기
    from selenium import webdriver
    from selenium.webdriver.chrome.options import Options
    from selenium.webdriver.common.keys import Keys
    from selenium.webdriver.common.by import By
    from selenium.webdriver.support.ui import WebDriverWait
    from selenium.webdriver.support import expected_conditions as EC
    
    from bs4 import BeautifulSoup
    import time
    from datetime import datetime
    import pandas as pd
    import math
    import pickle
    from tqdm import tqdm

     

     

    3️⃣-(1). [함수1] 크롬드라이버 실행부터 페이지 넘기면서 크롤링까지

    • 이제 위에서 작성했던 코드들을 함수 2개로 묶겠습니다.
    • crawling_danawa_reviews_get_sources: 제품 상세페이지에 접속해서, 리뷰페이지를 넘기면서 긁어오는 함수입니다.
      - 입력값은 이전포스팅에서 크롤링했던 제품정보 데이터프레임입니다.
      - 출력값은 크롤링 페이지 소스가 전부 저장된 딕셔너리 입니다. 이렇게 수집된 데이터는 함수2에서 정리될거에요.
    def crawling_danawa_reviews_get_sources(df_for_review_crawling): 
        # 함수 입력값: 제품정보 데이터프레임
        
        # webdirver 설정 및 실행
        my_options = webdriver.ChromeOptions()
        my_options.add_argument('headless')
        my_options.add_argument('--window-size=1920,1080')
        my_options.add_argument("disable-gpu")
        driver = webdriver.Chrome(options=my_options)
        driver.implicitly_wait(1)
    
        # 크롤링한 페이지소스 저장할 빈 딕셔너리 생성
        result_dict = {}
    
        ### 제품별로 돌아가면서 상세페이지 접속 ###
        for u in range(len(df_for_review_crawling)):
            driver.get(df_for_review_crawling['링크URL'][u])
            print(u+1, ".", df_for_review_crawling['제품모델명'][u], end=' ')
            time.sleep(3)
    
            # 제품별로 저장할 빈 딕셔너리 생성
            soup_dict = {}
    
            # 쇼핑몰 상품리뷰 개수 가져오기
            soup = BeautifulSoup(driver.page_source)
            num_reviews = soup.select_one('div.point_num span strong').text.strip().replace(',','')
            print('-', num_reviews, '개')
    
            # 리뷰 총 몇페이지인지 계산
            num_pages = math.ceil(int(num_reviews) / 10)
            
            ### 리뷰페이지 넘기면서 페이지 소스 크롤링 및 저장 ###
            # page에 1씩 더하면서, 전체 페이지 수와 같아지기 전까지 반복
            page = 1
            while page < num_pages:
            
                # 페이지별 리뷰 전부 가져오기
                soup = BeautifulSoup(driver.page_source)
                reviews = soup.select('ul.rvw_list li.danawa-prodBlog-companyReview-clazz-more')
                
                # 페이지별 리뷰 딕셔너리에 저장해두기
                soup_dict[page] = reviews
                
                page += 1
    
                # 페이지 넘기는 부분 경로 통째로 가져와놓고
                page_nums = driver.find_element(
                    By.XPATH, '//*[@id="danawa-prodBlog-companyReview-content-list"]/div/div')
                
                # 11,21,31..번째 페이지인 경우 [다음 페이지] 버튼 클릭
                if page%10 == 1:
                    page_nums.find_element(By.CLASS_NAME, 'point_arw_r').click()
            
                # 나머지 경우는 page에 해당하는 페이지 클릭
                else:
                    page_nums.find_element(By.LINK_TEXT, str(page)).click()
            
                # 에러나지 않도록 한페이지마다 잠시 대기
                print('.', end=' ')
                time.sleep(2)
            
            # 마지막 페이지 리뷰 가져오고 마무리
            soup = BeautifulSoup(driver.page_source)
            reviews = soup.select('ul.rvw_list li.danawa-prodBlog-companyReview-clazz-more')
            soup_dict[page] = reviews
            print("완료!")
            
            result_dict[u+1] = soup_dict
    
        # 함수 출력값: 제품별 리뷰페이지 소스 저장한 딕셔너리
        return result_dict

     

     

    3️⃣-(2). [함수2] 수집된 정보를 거르고 필터링해서 분석 가능한 데이터프레임으로!

    • crawling_danawa_reviews_result: 긁어온 데이터를 돌리면서 데이터프레임으로 정리하는 함수입니다.  
      - 입력값은 함수1에서 크롤링 페이지 소스를 저장했던 딕셔너리, 제품정보 데이터프레임, 그리고 저장할 엑셀파일의 이름입니다.
        제품정보 데이터프레임은 제품명을 가져와서 그대로 붙이기 위해 필요해요.

      - 출력값은 크롤링 결과가 정리된 데이터프레임, 그리고 저장된 엑셀파일입니다.
    def crawling_danawa_reviews_result(result_dict, df_for_review_crawling, excel_title): 
        # 함수 입력값: 리뷰소스 저장된 딕셔너리, 제품정보 데이터프레임, 결과 저장할 엑셀파일명
        
        # 제품명 풀네임, 제조사, 모델명
        name_list, brand_list, model_list = [], [], []
        # 별점, 구매처, 작성일, 리뷰내용
        star_list, mall_list, date_list, review_list = [], [], [], []
        
        # 제품 하나씩 돌리기
        for key_p in tqdm(range(len(result_dict))):
            soup_dict = result_dict[key_p+1]
    
            # 제품명 풀네임, 제조사, 모델명 가져오기
            name = df_for_review_crawling['제품모델명'][key_p]
            brand = name.split()[0]
            model = name.split()[-1]
    
            # 저장해둔 리뷰페이지 하나씩 돌리기
            for key_r in range(len(soup_dict)):
                reviews = soup_dict[key_r+1]
            
                # 리뷰마다 돌리면서 내용 가져오기
                for i in range(len(reviews)):
            
                    # 가져와둔 제품명 풀네임, 제조사, 모델명
                    name_list.append(str(name))
                    brand_list.append(str(brand))
                    model_list.append(str(model))
                    
                    # 별점
                    try:
                        star_list.append(int(
                            reviews[i].select_one('span.star_mask').text.strip().replace('점',''))/20)
                    except:
                        star_list.append('')
            
                    # 구매처
                    try:
                        mall_list.append(str(reviews[i].select_one('span.mall').text.strip()))
                    except:
                        mall_list.append('')
            
                    # 작성일
                    try:
                        date_list.append(reviews[i].select_one('span.date').text.strip())
                    except:
                        date_list.append('')
            
                    # 리뷰내용
                    try:
                        review_list.append(str(reviews[i].select_one('div.atc').text.strip()))
                    except:
                        review_list.append('')
                
        
        ### 크롤링 결과 저장 ###
        # 데이터프레임 생성
        df_result = pd.DataFrame({
            '제조사' : brand_list, '모델명' : model_list, '제품명' : name_list, 
            '구매처' : mall_list, '작성일' : date_list, '별점' : star_list, '리뷰' : review_list
        })
    
        # 데이터프레임을 엑셀파일로 저장
        with pd.ExcelWriter(excel_title) as writer:
            df_result.to_excel(writer)
    
        return df_result


     

    • 제품 10개 리뷰크롤링 테스트도 성공적으로 마쳤습니다! 10개만 선정했는데도 6천개에 가까운 리뷰가 수집됐네요.
      본격적으로 모든 제품의 리뷰를 수집하려면 시간이 좀 걸릴 것 같아요.
    • 다음에는 수집한 리뷰들을 khaiii 형태소분석기로 쪼개서 워드클라우드라도 만들어볼까 합니다!

     

     

     

     

    728x90
    반응형