본문 바로가기

데이터분석 및 프로젝트

크롤링과 코사인 유사도를 이용하여 영화추천 서비스를 만들어보자 :)

코사인 유사도를 이용하여 영화를 추천해보자.

코사인 유사도 이용 영화 추천 프로그램

In [1]:
from bs4 import BeautifulSoup  
import pandas as pd
from tqdm import tqdm_notebook
import nltk
import re
from urllib.request import urlopen

영화 추천 프로그램을 만들기 위해 먼저, 네이버영화를 통해 영화정보를 크롤링 합니다. 그러기위해 필요한 도구들을 불러오고 크롤링를 진행합니다. import해오는 도구들은 크롤링을 익히신 분들이라면 익히 아실거라 생각하고 진행하겠습니다. :)

In [2]:
domain='https://movie.naver.com'
story=[]
title=[]
genre=[]
for i in tqdm_notebook(range (1,11)):
    
    url="https://movie.naver.com/movie/sdb/rank/rmovie.nhn?sel=pnt&date=20191201&page="+str(i)
    html = urlopen(url)
    soup = BeautifulSoup(html,"html.parser")
    titles=soup.find_all('div',class_='tit5')
    hype=[]
    href=[]
  
    try:
        
        for each in titles:
           
                hype=each.find_all('a')
                for link in hype:
                    href.append(link['href'])
        for j in tqdm_notebook(range(len(href))):
                domain='https://movie.naver.com'
                domain=domain+href[j]
                html=urlopen(domain)
                soup=BeautifulSoup(html,"html.parser")
                story.append(soup.find('p',class_="con_tx").get_text())
            
                title_tag=soup.find('h3',class_='h_movie')
                title.append(title_tag.find('a').get_text())
            
                genre_tag=soup.find('p')
                genre.append(genre_tag.find('a').get_text())
    except:
        pass
                  

#스토리 정규화 처리
import re

for i in tqdm_notebook(range(len(story))):

    story[i] = re.sub('[-=+,#/\?:^$.@*\"※~&%ㆍ!』\\‘|\(\)\[\]\<\>`\'…》“”’]','',story[i] )
    story[i] = re.sub('\r\xa0','',story[i] )

저는 네이버 영화 평 랭킹을 평점순으로 10페이지까지만 불러오도록 하겠습니다. 하고나서 스토리에 의미없이 들어간 문자들 또한 빼주었습니다. tqdm_notebook으로 진행상황을 확인 하였을 때 중간중간 원활히 크롤링이 되지 않는 것이 보이는데 크롤링 파트가 아니니 그냥 넘어가겠습니다.나중에 이유 찾아서 글 추가로 올리도록 하겠습니다. :)

In [113]:
title[0]
Out[113]:
'그린 북'

크롤링이 잘 된 것을 확인합니다.(여러분은 그냥 title 전체를 출력해서 확인하세요~)

In [114]:
Nmovie=pd.DataFrame(data={'제목':title,'줄거리':story,'장르':genre})

크롤링해온 정보들을 하나의 데이터프레임 형태로 만들어 둡니다. 사실 꼭 해야하는 작업은 아니지만 보기 좋잖아요..

In [115]:
Nmovie.head()
Out[115]:
제목 줄거리 장르
0 그린 북 1962년 미국 입담과 주먹만 믿고 살아가던 토니 발레롱가비고 모텐슨는 교양과 우아... 드라마
1 가버나움 나를 세상에 태어나게 한 부모님을 고소하고 싶어요출생기록조차 없이 살아온 어쩌면 1... 드라마
2 베일리 어게인 귀여운 소년 이든의 단짝 반려견 베일리는 행복한 생을 마감한다하지만 눈을 떠보니 다... 모험
3 아일라 1950년 한국전쟁에 파병된 슐레이만은 칠흑 같은 어둠 속 홀로 남겨진 5살 소녀를... 드라마
4 주전장 일본의 인종차별 문제를 다룬 영상을 올린 후 우익들의 공격 대상이 된 일본계 미국인... 다큐멘터리

데이터 프레임 확인을 한 번 해줍니다. 그리고 나서 코사인 유사도를 이용하기 위해 모두 합쳐 줍니다! 그렇게 되면 장르와 제목도 줄거리 외에 들어가기 때문에 조금 더 유사도를 판단하기 좋겠죠. 또한 장르, 혹은 스토리 등에 곱하기로 비중을 높일 수도 있습니다. 그렇게 하면 같은 장르의 영화들의 유사도가 더 높게 나오겠죠.

In [118]:
Nmovie['합침'] = (Nmovie['제목']) + Nmovie['줄거리'] + (Nmovie['장르'])
In [119]:
Nmovie['합침'][0]
Out[119]:
'그린 북1962년 미국 입담과 주먹만 믿고 살아가던 토니 발레롱가비고 모텐슨는 교양과 우아함 그 자체인천재 피아니스트 돈 셜리마허샬라 알리 박사의 운전기사 면접을 보게 된다백악관에도 초청되는 등 미국 전역에서 콘서트 요청을 받으며 명성을 떨치고 있는 돈 셜리는위험하기로 소문난 미국 남부 투어 공연을 떠나기로 결심하고투어 기간 동안 자신의 보디가드 겸 운전기사로 토니를 고용한다거친 인생을 살아온 토니 발레롱가와 교양과 기품을 지키며 살아온 돈 셜리 박사생각 행동 말투 취향까지 달라도 너무 다른 두 사람은그들을 위한 여행안내서 그린북에 의존해 특별한 남부 투어를 시작하는데드라마'
In [120]:
from sklearn.feature_extraction.text import TfidfVectorizer
tfidf = TfidfVectorizer(stop_words='english')
Nmovie['합침'] = Nmovie['합침'].fillna('')

이제 sklearn을 이용하여 TF-IDF 방식으로 단어의 가중치를 조정한 벡터를 만들어 줍니다.

In [121]:
tfidf_matrix = tfidf.fit_transform(movie)
# overview에 대해서 tf-idf 수행
print(tfidf_matrix.shape)
(360, 21860)
In [122]:
#코사인유사도
from sklearn.metrics.pairwise import linear_kernel
cosine_sim = linear_kernel(tfidf_matrix, tfidf_matrix)

마찬가지로 sklearn을 이용하여 코사인 유사도를 구해줍니다. 코사인 유사도는 내적공간의 두 벡터간 각도의 코사인값을 이용하여 측정된 벡터간의 유사한 정도를 의미합니다. 그렇게 하기 위해 벡터를 만들어 준 것이죠.

In [123]:
indices = pd.Series(Nmovie.index, index=Nmovie['제목']).drop_duplicates()
print(indices.head())
#영화의 타이틀과 인덱스를 가진 테이블을 만듬
#영화 타이틀을 입력하면 인덱스를 리턴하려고 만듬
제목
그린 북       0
가버나움       1
베일리 어게인    2
아일라        3
주전장        4
dtype: int64

영화의 타이틀을 입력하면 인덱스를 리턴하는 영화의 타이틀과 인덱스를 가진 테이블을 만듭니다.

In [125]:
def get_recomm(title, cosine_sim=cosine_sim):
    choice = []
    # 선택한 영화의 타이틀로부터 해당되는 인덱스를 받아옵니다. 이제 선택한 영화를 가지고 연산할 수 있습니다.
    idx = indices[title]

    # 모든 영화에 대해서 해당 영화와의 유사도를 구합니다.
    sim_scores = list(enumerate(cosine_sim[idx]))

    # 유사도에 따라 영화들을 정렬합니다.
    sim_scores = sorted(sim_scores, key=lambda x: x[1], reverse=True)

    # 가장 유사한 10개의 영화를 받아옵니다.
    sim_scores = sim_scores[1:11]

    # 가장 유사한 10개의 영화의 인덱스를 받아옵니다.
    movie_indices = [i[0] for i in sim_scores]
    
    for i in range(10):
        choice.append(Nmovie['제목'][movie_indices[i]])
    # 가장 유사한 10개의 영화의 제목을 리턴합니다.
    print('***영화 추천 순위***')
    for i in range(10):
        print(str(i+1) + '순위 : ' + choice[i])
            

마지막으로 함수를 구성하여 원하는 값이 출력되도록 하겠습니다.

In [126]:
get_recomm('토이 스토리')
***영화 추천 순위***
1순위 : 토이 스토리 2
2순위 : 벅스 라이프
3순위 : 어네스트와 셀레스틴
4순위 : 모노노케 히메
5순위 : 가타카
6순위 : 사랑은 비를 타고
7순위 : 미쓰백
8순위 : 토이 스토리 4
9순위 : 캐스트 어웨이
10순위 : 언더독

결과가 나왔습니다. ㅎㅎ 스토리의 줄거리로 문자만 가지고 유사도를 측정하여 추천한 것이니 신뢰도가 그리 높은 프로그램은 아니지만, 결과를 보면 나름? 의미있는 값을 가지는 듯 합니다!! :)

In [ ]: