관리 메뉴

솜씨좋은장씨

[Python]네이버 영화 데이터 크롤링하기 본문

Programming/Python

[Python]네이버 영화 데이터 크롤링하기

솜씨좋은장씨 2019. 9. 27. 01:02
728x90
반응형

 과거의 영화 줄거리, 평점, 장르 데이터를 가지고 새로운 영화의 평점을 예측하는 모델을 만들 때 학습데이터로 사용하기 위한 데이터들을 크롤링해오기 위한 코드를 짜 보았습니다.

 

 데이터를 수집하기 위한 크롤러를 제작하는데에는 Selenium, BeautifulSoup 그리고 requests를 사용했습니다.

네이버 영화에서 데이터를 수집해오기는 해야하나 순차적으로 데이터를 가져올 수 있도록 링크를 어디서 가져올까 고민하던 중

네이버 시리즈에 18812개의 영화 링크가 있는 페이지를 찾았습니다. 

 

저는 이 페이지에서 판매순으로 가져오기로 했습니다.

from bs4 import BeautifulSoup
import requests
from selenium import webdriver as wd
from selenium.webdriver.common.keys import Keys
import time
import re
import pandas as pd

먼저 필요한 라이브러리들을 import 해줍니다.

def getPageLinks(pageRange):
    links = []
    
    for pageNo in range(pageRange):
        url = "https://series.naver.com/movie/recentList.nhn?orderType=sale&sortingType=&tagCode=&page=" + str(pageNo + 1) 
        req = requests.get(url)
        soup = BeautifulSoup(req.text, 'lxml')
        movielinks = soup.select('div.lst_thum_wrap ul li a[href]')

        for movielink in movielinks:
            link = str(movielink.get('href'))
            links.append("https://series.naver.com"+link)        
    return links

원하는 페이지까지 링크들을 쭉 받아오는 함수를 받아옵니다.

def getPageLinksWantRange(startPageNo, lastPageNo):
    links = []
    return_links = []
    
    for pageNo in range(startPageNo-1, lastPageNo):
        url = "https://series.naver.com/movie/recentList.nhn?orderType=sale&sortingType=&tagCode=&page=" + str(pageNo + 1) 
        req = requests.get(url)
        soup = BeautifulSoup(req.text, 'lxml')
        movielinks = soup.select('div.lst_thum_wrap ul li a[href]')

        for movielink in movielinks:
            link = str(movielink.get('href'))
            links.append("https://series.naver.com"+link)
        
    return links

여러대의 컴퓨터로 분산 작업하기위해서 원하는 시작페이지부터 끝나는 페이지 사이의 링크를 받아오는 함수를 만들었습니다.

 

원래는 리뷰데이터와 평점정보도 받아오려고 했기에 해당 링크를 들어가면 아래와 같은 페이지를 볼수있고 그 페이지에서

영화정보라는 버튼을 찾고 그 버튼의 태그 속 href속성값을 가져오는 방식으로 링크가 걸려있는 주소를 가져옵니다.

 

해당 링크의 주소를 받아와 그 주소를 가지고 requests와 BeautifulSoup을 사용하여 데이터를 가져옵니다.

 

그런데 한 가지 문제점이 있었습니다.

 

성인영화의 주소를 셀레니움이 입력하고 접근하려하면 네이버 로그인 창이 나왔고

그 로그인창에 아이디와 비밀번호를 입력하고 버튼을 누르는 코드로는 해결이 되지않았습니다.

 

결국 처음 셀레니움으로 크롬을 띄우고 나서 30초 대기시간을 주는 그 잠깐의 찰나에 사람이 로그인을 하고 그 후에 그 창을 재사용하면서 로그인이 유지되도록 하였습니다.

 

하지만!

 

10번 20번 하다보니 아예 사람이 로그인해도 아이디와 비밀번호를 입력하라고 하며 로그인이 되지 않는 현상이 발생했습니다.

그래서 그냥 성인영화를 제외하기로 하였고 받아온 html코드에서 초반의 10자를 비교하여 성인영화인지 아닌지를 파악 후 성인영화가 아닌 영화에서만 데이터를 가져올 수 있도록 하였습니다.

 

아래의 코드는 받아온 데이터를 바탕으로 DataFrame을 만들고 중복값을 제거한 후 return 해주는 함수입니다.

def getMovieDataFromNaverSeries(links):
    title_infos = []
    content_infos = []
    genre_infos = []
    score_infos = []
    date_infos = []


    url2 = "https://www.naver.com"

    driver = wd.Chrome(executable_path="chromedriver.exe")
    driver.get(url2)
    time.sleep(3.0) # 30

    driver.find_element_by_css_selector('body').send_keys(Keys.CONTROL + "t")
    
    for link in links:
        driver.switch_to.window(driver.window_handles[-1])
        time.sleep(0.1)
        driver.get(link)
        time.sleep(0.1)
        driver.switch_to.window(driver.window_handles[0])
        time.sleep(0.3)

        html_source = driver.page_source

        html_soup = BeautifulSoup(html_source, 'lxml')

        flag = html_soup.text[0:10]

        newflag = "".join(flag)
        newflag = newflag.replace('\n', '')

        if newflag == '네이버':
            time.sleep(1.0)
            
            
            score = driver.find_element_by_css_selector('div.score_area > em ')

            score = float(score.text)
            score = int(score)
            score_infos.append(score)

            
            genre = driver.find_element_by_css_selector('li.info_lst > ul > li:nth-child(4)').get_attribute('textContent')
            genre = genre.replace('장르','')
            genre = genre.split('/')
            genre_infos.append(genre)

            text =  driver.find_element_by_css_selector('span.al_r > a')  #.click()
            
#             review_url = text.get_attribute('href')
#             review_url = review_url.replace('basic', 'pointWriteFormList')
            
#             review_url = review_url + '&type=after&onlyActualPointYn=N&order=newest&page=1'
           
            
            
            
            movieInfoUrl = text.get_attribute('href')

            movie_req = requests.get(movieInfoUrl)

            movie_soup = BeautifulSoup(movie_req.text, 'lxml')

            titles = movie_soup.select('div.mv_info > h3.h_movie > a')
            
            temp_titles = []
            
            for title in titles:
                temp = title.text
                temp = temp.replace('상영중', '')
                temp = temp.replace('\n', '')
                temp_titles.append(temp)

            if '' in temp_titles or ' ' in temp_titles:
                temp_titles.remove('')
            
            temp_titles = set(temp_titles)
            temp_titles = list(temp_titles)
            temp_titles = [x for x in temp_titles if x is not '']
            title_infos.append(list(temp_titles)[0])


            contents_texts = movie_soup.select('div.story_area > p.con_tx')
    
            if len(contents_texts) == 0:
                content_infos.append("줄거리 오류")
            else:
                for contents in contents_texts:
                    temp = contents.text
                    temp = temp.replace('\r', '')
                    temp = temp.replace('\xa0', '') 
                    content_infos.append(temp)


        elif newflag == '네이버 :':
            adult_movies.append(link)
    print(len(score_infos), len(genre_infos), len(content_infos))

    driver.close()
    
    movie_dic = {"평점":score_infos,"장르":genre_infos, "줄거리":content_infos}
    
    movie_df = pd.DataFrame(movie_dic, index=title_infos)
    
    movie_df2 = movie_df.drop_duplicates("줄거리", keep='first')
    
    return movie_df2

 

받아온 데이터를 저장해 두어야 오래오래 두고 사용할 수 있으니 dataframe.to_csv 메소드를 활용하여 csv파일로 저장해줍니다.

def dftoCsv(movie_df, num):
    try:
        movie_df.to_csv(('movie_data'+ str(num) +'.csv'),  sep=',', na_rep='NaN', encoding='euc-kr')
    except:
        print("Error")

위의 코드로 csv파일로 변환하여 저장합니다.

 

 

3. 만들고 나서 느낀점

1. 만들고 나서 생각해보니 리뷰를 받아오지 않을 것이었으면 Selenium을 사용하지 않고 할 수 있었을 것이라는 생각이 들었습니다.

 

2. 추후 병렬처리(?)를 통하여 한번에 여러개를 돌릴수 있는 Scrapy도있다고하는데 사용해보고 싶습니다. 제가 네이버를 크롤링할때는 3개의 컴퓨터를 가지고 했었는데 Scrapy라고 하면 한대의 컴퓨터로도 많은 양의 데이터를 가져올 수 있을 것 같습니다.

 

추 후 더 공부를해서 여기 올라와있는 코드를 리팩토링해보고 다른 라이브러리로도 구현해보겠습니다.

Comments