코딩헤딩

[웹 크롤링(web crawling)] 영화데이터수집 본문

크롤링(crawling) | 시각화

[웹 크롤링(web crawling)] 영화데이터수집

멈머이 2023. 12. 4. 22:49
728x90

크롤링(crawling)

웹상에 존재하는 데이터들을 수집하는 작업 

(프로그래밍으로 자동화)

  1. 웹 페이지의 html 코드를 가져와서, HTML/CSS 등을 필요한 데이터만 추출하는 기법
  2. Open API(Rest API)를 제공하는 서비스에 Open API를 호출해서, 받은 데이터 중 필요한 데이터만 추출하는 기법
  3. Selenium 등 브라우저를 프로그래밍으로 조작해서, 필요한 데이터만 추출하는 기법

 

<다음 영화 사이트 웹크롤링>

 - URL : https://movie.daum.net
 - 다음영화 > 랭킹 > 박스오피스 > 월간 위치의 데이터 수집
 - 수집데이터 : 영화제목, 평점, 댓글
 - 생성할 데이터 : 긍정/부정

 - 고려할 상황 : 평점에서 한 화면에서 보여주는 평점의 수가 10이다.

                           10 이상은 "평점 더 보기" 버튼을 눌러주어야 한다.

 


<웹크롤링 라이브러리>


  * 정적페이지                                                                                 * 동적페이지

                   BeautifulSoup                                                                               selenium                  

              하나의 페이지에 보이는                                                         클릭과 같은 이벤트 등 페이지

               부분만 수집할 때 사용                                                           전환을 하면서 수집할 때 사용


# 동적 웹페이지 처리를 위한 라이브러리
from selenium import webdriver

# 웹페이지 내에 데이터 추출을 위한 라이브러리
from selenium.webdriver.common.by import By

# 시간 라이브러리 추가 브라우저가 로딩되는시간동안 시간을 기다려주기 위해
import time

 - 설치 필요 : pip install selenium

try:
    ### 크롬브라우저 띄우기
    # - 브라우저 컨트롤
    driver = webdriver.Chrome()
    
    ### url을 이용하여 페이지 접근
    # - get() : 페이지에 접근 후 해당 html코드 읽어 들이기
    # - driver 객체가 모든정보를 가지고 있음
    driver.get("https://movie.daum.net/ranking/boxoffice/monthly")

    
    ### 제목이 있는 부분의 html태그 경로(패스) 추출하기
    # - 크롬브라우저 -> f12 -> 영화제목마우스우클륵 -> [검사]클릭 -> 
    #               a태그에 마우스 위치 후 우클릭 -> copy -> copy selector 클릭
    # - 해당 제목의 위치에 저장 됨
    
    ### 영화제목이 있는 a태그 위치 경로
    movie_path = "#mainContent > div > div.box_boxoffice > ol > li > div > div.thumb_cont > strong > a"
    
    ### 크롬브라우저에 보이는 영화제목 모두 추출하기
    # - find_element() : 한건조회, find_elements : 여러건 조회(리스트 타입으로 반환)
    # - by.css_selector : css스타일로 경로를 인식할 수 있도록 지정
    movie_elements = driver.find_elements(By.CSS_SELECTOR, movie_path)

    ### ----------------------------------------------------------------------------------
    
    ### 수집데이터를 txt파일로 저장시키기
    f = open("./data/movie_reviews.txt", "w", encoding="UTF-8")

    # ------------------------------------------------------------------------------------
    ### 제목 10개만 추출하기
    for i in range(10):        
        title = movie_elements[i].text.strip() # strip() : 공백제거
        print(f"title[{i}] >>>> title[{title}]")
        ### 제목을 클릭시켜서 상세페이지로 접근
        # - click() 이벤트 발생
        movie_elements[i].click()
        ### 상세 페이지로 접근했다라는 정보를 받아오기
        # - 실제 상세페이지에 접근
        # - window_handles : 페이지가 열릴때 마다 리스트 타입으로 윈도우 정보를 순서대로 가지고 있는 객체
        #                    -1은 마지막 접근한 페이지를 의미함
        movie_handle = driver.window_handles[-1]
        # - 새로열린 페이지로 전환하기
        driver.switch_to.window(movie_handle)

        # 페이지 로딩 및 코드 읽어들이는 시간을 벌어주기
        time.sleep(1)
     
        ### -----------------------------------------------
        
        ###[평점] 탭 클릭 이벤트 발생새키기
        tab_score_path="#mainContent > div > div.box_detailinfo > div.tabmenu_wrap > ul > li:nth-child(4) > a"
        ### a태그 정보 가져오기
        tab_score_element = driver.find_element(By.CSS_SELECTOR, tab_score_path)
        ### [평점] 탭, 즉 a태그 클릭이벤트 발생시키기
        tab_score_element.click()
        
        tab_score_handle = driver.window_handles[-1]
        # - 새로열린 페이지로 전환하기
        driver.switch_to.window(tab_score_handle)

        # 페이지 로딩 및 코드 읽어들이는 시간을 벌어주기
        time.sleep(1)

        ### -------------------------------------
        
        ### [평점] 더보기 보튼을 클릭하여 모두 펼치기
        # - 펼친 갯수 확인 변수
        more_view_cnt = 0

        ### 모두 펼치기(더보기) 수행
        while True:
            try:                 
                more_view_path= "#alex-area > div > div > div > div.cmt_box > div.alex_more > button"
                more_view_element = driver.find_element(By.CSS_SELECTOR, more_view_path)
                more_view_element.click()
        
                ### 상세 페이지로 접근했다라는 정보를 받아오기
                more_view_handle = driver.window_handles[-1]
                # - 새로열린 페이지로 전환하기
                driver.switch_to.window(more_view_handle)
                
                # 페이지 로딩 및 코드 읽어들이는 시간을 벌어주기
                time.sleep(1)
                ### 더보기 클릿 횟수 확인을 위해 1씩 증가
                more_view_cnt +=1
        
            except Exception as e:
                ### 더이상 더보기 버튼이 보이지 않으면 오류발생
                # - 오류발생 시점이 더보기 버튼이 끝나는 시점
                break

        ### 더보기 클릭횟수 확인하기
        print(f"더보기 클릭 횟수 : [{more_view_cnt}]")
        
        ### --------------------------------------------------------------------
        
        ### 모든 평정데이터 주줄하기
        score_path = "ul.list_comment div.ratings"
        score_lists = driver.find_elements(By.CSS_SELECTOR, score_path)
        
        ### 모든 리뷰데이터 추출하기
        comment_path = "ul.list_comment p.desc_txt"
        comment_lists = driver.find_elements(By.CSS_SELECTOR, comment_path)

        ### ---------------------------------------------------------------------
        
        ### 평점, 리뷰 추출하기
        ### 평점을 이용하여 긍정/부정값 생성하기4

        ### 평점 또는 리뷰데이터가 없을 수 있기에 두개 리스트의 갯수 중 작은 값을 사용
        ### 평점 또는 리뷰가 없으면, 수집에서 제외
        for_cnt = 0
        if len(score_lists) < len(comment_lists):
            for_cnt = len(score_lists)
        elif len(score_lists) > len(comment_lists):
            for_cnt = len(comment_lists)
        else:
            for_cnt = len(score_lists)
                          
        
        for j in range(for_cnt):
            ### 평점추출하기
            score = score_lists[j].text.strip()

            ### 리뷰 추출하기
            comment = comment_lists[j].text.strip().replace("\n", "")

            ### 평점을 이용하여 긍정 / 부정 데이터 생성
            # - 긍정 : 평점이 8이상인 경우로, 긍정값은 1사용
            # - 부정 : 평점이 4이하인 경우로 , 부정값은 0사용
            # - 기타 : 나머지, 기타값은 2 사용
            lable = 0
            if int(score) >= 8:
                    lable = 1
            elif int(score) <= 4:
                    lable = 0
            else:
                    lable = 2

            
            ### 각 영화별 확인하기
            print(f"{title} \t{score} \t{comment} \t{lable} \n")

            ### 파일에 쓰기
            f.write(f"{title}\t{score}\t{comment}\t{lable}\n")
        # tab으로 구분한 이유 : 댓글안에 , 가 있으면 

### --------------------------------------------------------------------------------

        ### 영화 한편에 대한 정보 수집이 끝나면 다시 메인으로 이용
        driver.execute_script("window.history.go(-2)")
        time.sleep(1)
        
        
except Exception as e:
    print(e)

    ### 파일 자원 닫기
    f.close()

    ### 웹크롤링 처리가 모두 왼료되면, driver를 꼭 종료해야 한다.
    driver.quit()

finally:
    ### 파일 자원 닫기
    f.close()
    
    ### 웹크롤링 처리가 모두 왼료되면, driver를 꼭 종료해야 한다.
    driver.quit()

결과 :

.

.

.

728x90