Posts Beautiful Soup을 이용한 네이버 영화 평점 크롤링
Post
Cancel

Beautiful Soup을 이용한 네이버 영화 평점 크롤링

1. 네이버 영화 평점


1.1 네이버 영화 평점


1.2 URL 보기

1
'https://movie.naver.com/movie/sdb/rank/rmovie.nhn?sel=cur&date=20201021'
1
'https://movie.naver.com/movie/sdb/rank/rmovie.nhn?sel=cur&date=20201021'
  • URL 맨 뒤에 20201021은 date 형식으로 보임, 해당 날짜를 조금씩 바꾸면 다른 페이지에 접속이 가능
  • 이처럼 웹페이지 URL에는 많은 정보가 담겨있음


1.3 한페이지 보기 with Beautiful Soup

1
2
3
4
5
6
7
8
9
from bs4 import BeautifulSoup
from urllib.request import urlopen
import pandas as pd

url = 'https://movie.naver.com/movie/sdb/rank/rmovie.nhn?sel=cur&date=20201021'
page = urlopen(url)

soup = BeautifulSoup(page, 'html.parser')
soup
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
<!DOCTYPE html>

<html lang="ko">
<head>
<meta content="text/html; charset=utf-8" http-equiv="Content-Type"/>
<meta content="IE=edge" http-equiv="X-UA-Compatible"/>
<meta content="http://imgmovie.naver.com/today/naverme/naverme_profile.jpg" property="me2:image">
<meta content="네이버영화 " property="me2:post_tag">
<meta content="네이버영화" property="me2:category1"/>
<meta content="" property="me2:category2"/>
<meta content="랭킹 : 네이버 영화" property="og:title"/>
<meta content="영화, 영화인, 예매, 박스오피스 랭킹 정보 제공" property="og:description"/>
<meta content="article" property="og:type"/>
<meta content="https://movie.naver.com/movie/sdb/rank/rmovie.nhn?sel=cur&amp;date=20201021" property="og:url"/>
<meta content="http://static.naver.net/m/movie/icons/OG_270_270.png" property="og:image"/><!-- http://static.naver.net/m/movie/im/navermovie.jpg -->
<meta content="http://imgmovie.naver.com/today/naverme/naverme_profile.jpg" property="og:article:thumbnailUrl"/>
<meta content="네이버 영화" property="og:article:author"/>
<meta content="https://movie.naver.com/" property="og:article:author:url"/>
<link href="https://ssl.pstatic.net/static/m/movie/icons/naver_movie_favicon.ico" rel="shortcut icon" type="image/x-icon"/>
<title>랭킹 : 네이버 영화</title>
<link href="/common/css/movie_tablet.css?20201015140005" rel="stylesheet" type="text/css"/>
...

window.addEventListener('pageshow', function(event) { lcs_do(); });

document.addEventListener('click', function (event) {
	var welSource = event.srcElement;	// jindo.$Element(oEvent.element);
	if (!document.getElementById("gnb").contains(welSource)) {
		gnbAllLayerClose();
	}
});
</script>
<!-- //Footer -->
</div>
</body>
</html> 
  • html형식을 beautifulsoup을 사용하여 가져옴


  • 위의 내용 중 영화제목과 평점을 가져옴


1.4 영화제목 가져오기

  • 크롬 개발자 도구로 확인해 본 결과, div 태그 tit5 클래스가 영화 제목
1
soup.find_all('div', 'tit5')[0].a.string
1
'소년시절의 너'
  • BeautifulSoup의 find_all 명령어로 제목을 모두 찾을수 있음.
  • 그중 첫번째 ([0])인 내용만 가져왔고, string 형식인 제목만 가져옴


1
2
movie_name = [i.a.string for i in soup.find_all('div', 'tit5')]
movie_name
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
['소년시절의 너',
 '브레이크 더 사일런스: 더 무비',
 '울지마 톤즈',
 '다시 태어나도 우리',
 '언더독',
 '그대, 고맙소 : 김호중 생애 첫 팬미팅 무비',
 '우리들',
 '스파이더맨: 뉴 유니버스',
 '톰보이',
 '사랑과 영혼',
 '파수꾼',
 '제리 맥과이어',
 '삼진그룹 영어토익반',
 '공범자들',
 '타오르는 여인의 초상',
 '박하사탕',
 '인생 후르츠',
 '남매의 여름밤',
 '윤희에게',
 '비투스',
 '아웃포스트',
 '담보',
 '너의 이름은.',
 '소공녀',
 '벌새',
 '마미',
 '브리짓 존스의 일기',
 '찬실이는 복도 많지',
 '69세',
 '라라랜드',
 '기생충',
 '아무르',
 '로렌스 애니웨이 ',
 '3:10 투 유마',
 '검객',
 '주디',
 '리스본행 야간열차',
 '테넷',
 '경계선',
 '프란시스 하',
 '신문기자',
 '위크엔드 인 파리',
 '블레이드 러너 2049',
 '페이트 스테이 나이트 헤븐즈필 제2장 로스트 버터플라이',
 '환상의 빛',
 '날씨의 아이',
 '라붐',
 '21 브릿지: 테러 셧다운',
 '한여름의 판타지아',
 '다만 악에서 구하소서']
  • 리스트 컴프리행션을 사용하여 한 페이지의 영화 제목을 모두 가져옴


1.4 평점 가져오기

  • td 태그에 point 클래스가 영화 평점


1
soup.find_all('td', 'point')[0].string
1
'9.39'
  • 영화 제목 가져왔을떄랑 똑같이 find_all과 string을 사용하여 가져옴


1
2
movie_point = [i.string for i in soup.find_all('td', 'point')]
movie_point
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
['9.39',
 '9.36',
 '9.35',
 '9.34',
 '9.30',
 '9.29',
 '9.26',
 '9.20',
 '9.20',
 '9.19',
 '9.18',
 '9.16',
 '9.15',
 '9.10',
 '9.06',
 '9.03',
 '9.02',
 '9.02',
 '8.98',
 '8.94',
 '8.93',
 '8.86',
 '8.78',
 '8.77',
 '8.76',
 '8.69',
 '8.68',
 '8.67',
 '8.63',
 '8.61',
 '8.49',
 '8.48',
 '8.44',
 '8.41',
 '8.36',
 '8.35',
 '8.31',
 '8.27',
 '8.17',
 '8.14',
 '8.10',
 '8.06',
 '7.99',
 '7.98',
 '7.98',
 '7.97',
 '7.93',
 '7.91',
 '7.81',
 '7.64']
  • 한 페이지의 평점을 가져옴


1.5 날짜 만들기

1
'https://movie.naver.com/movie/sdb/rank/rmovie.nhn?sel=cur&date=20201021'
1
'https://movie.naver.com/movie/sdb/rank/rmovie.nhn?sel=cur&date=20201021'
  • 한페이지에서 데이터를 얻게됨
  • 아까전에 이야기한 date를 바꾸면 웹 페이지가 바뀌기떄문에, 날짜를 바꿔가며 url을 변경함


1
2
date = pd.date_range('2020.09.01', periods=60, freq='D')
date
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
DatetimeIndex(['2020-09-01', '2020-09-02', '2020-09-03', '2020-09-04',
               '2020-09-05', '2020-09-06', '2020-09-07', '2020-09-08',
               '2020-09-09', '2020-09-10', '2020-09-11', '2020-09-12',
               '2020-09-13', '2020-09-14', '2020-09-15', '2020-09-16',
               '2020-09-17', '2020-09-18', '2020-09-19', '2020-09-20',
               '2020-09-21', '2020-09-22', '2020-09-23', '2020-09-24',
               '2020-09-25', '2020-09-26', '2020-09-27', '2020-09-28',
               '2020-09-29', '2020-09-30', '2020-10-01', '2020-10-02',
               '2020-10-03', '2020-10-04', '2020-10-05', '2020-10-06',
               '2020-10-07', '2020-10-08', '2020-10-09', '2020-10-10',
               '2020-10-11', '2020-10-12', '2020-10-13', '2020-10-14',
               '2020-10-15', '2020-10-16', '2020-10-17', '2020-10-18',
               '2020-10-19', '2020-10-20', '2020-10-21', '2020-10-22',
               '2020-10-23', '2020-10-24', '2020-10-25', '2020-10-26',
               '2020-10-27', '2020-10-28', '2020-10-29', '2020-10-30'],
              dtype='datetime64[ns]', freq='D')
  • Pandas의 date_range를 사용하여 날짜를 생성
  • 시작날짜를 적어주고, 만들고 싶은 날짜 갯수와 날짜 형태를 적으면 됨


1
2
3
4
print(date[0].strftime('%y-%m-%d'))
print(date[0].strftime('%y.%m.%d'))
print(date[0].strftime('%y%m%d'))
print(date[0].strftime('%Y%m%d'))
1
2
3
4
20-09-01
20.09.01
200901
20200901
  • 날짜형 데이터는 strftime 명령으로 원하는 형태의 문자열로 만들수 있음
  • URL에서 필요한 형식은 맨 아래 20200901 형식임


1.6 여러날짜에서 영화제목과 평점가져오기

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import time

movie_date = []
movie_name = []
movie_point = []
date = pd.date_range('2020.09.01', periods=45, freq='D')

for today in date:
    html = 'https://movie.naver.com/movie/sdb/rank/rmovie.nhn?sel=cur&date={date}'
    response = urlopen(html.format(date = today.strftime('%Y%m%d')))
    soup = BeautifulSoup(response, 'html.parser')
    
    movie_date.extend([today] * len(soup.find_all('td', 'point')))
    movie_name.extend([i.a.string for i in soup.find_all('div', 'tit5')])
    movie_point.extend([i.string for i in soup.find_all('td', 'point')])
    
    print(str(today))
    time.sleep(0.5)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
2020-09-01 00:00:00
2020-09-02 00:00:00
2020-09-03 00:00:00
2020-09-04 00:00:00
2020-09-05 00:00:00
2020-09-06 00:00:00
2020-09-07 00:00:00
2020-09-08 00:00:00
2020-09-09 00:00:00
2020-09-10 00:00:00
2020-09-11 00:00:00
2020-09-12 00:00:00
2020-09-13 00:00:00
2020-09-14 00:00:00
2020-09-15 00:00:00
2020-09-16 00:00:00
2020-09-17 00:00:00
2020-09-18 00:00:00
2020-09-19 00:00:00
2020-09-20 00:00:00
2020-09-21 00:00:00
2020-09-22 00:00:00
2020-09-23 00:00:00
2020-09-24 00:00:00
2020-09-25 00:00:00
2020-09-26 00:00:00
2020-09-27 00:00:00
2020-09-28 00:00:00
2020-09-29 00:00:00
2020-09-30 00:00:00
2020-10-01 00:00:00
2020-10-02 00:00:00
2020-10-03 00:00:00
2020-10-04 00:00:00
2020-10-05 00:00:00
2020-10-06 00:00:00
2020-10-07 00:00:00
2020-10-08 00:00:00
2020-10-09 00:00:00
2020-10-10 00:00:00
2020-10-11 00:00:00
2020-10-12 00:00:00
2020-10-13 00:00:00
2020-10-14 00:00:00
2020-10-15 00:00:00
  • 일단 20년 9월 1일 ~ 10월 15일까지의 영화 평점을 가져옴
  • 한페이지를 크롤링하고 또 바로 크롤링하면 과부화 및 접속 차단등이 걸릴수 있으니 time.sleep을 걸어주지


1
len(movie_date), len(movie_name), len(movie_point)
1
(2158, 2158, 2158)
  • 총 2158개의 영화 평점을 가져옴


1.7 데이터 프레임 생성

1
2
movie = pd.DataFrame({'date' : movie_date, 'name': movie_name, 'point' : movie_point})
movie.tail()
datenamepoint
21532020-10-15언힌지드6.69
21542020-10-15죽지않는 인간들의 밤6.62
21552020-10-15강철비2: 정상회담5.01
21562020-10-15국제수사4.87
21572020-10-15뮬란4.20


1
movie.info()
1
2
3
4
5
6
7
8
9
10
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 2158 entries, 0 to 2157
Data columns (total 3 columns):
 #   Column  Non-Null Count  Dtype         
---  ------  --------------  -----         
 0   date    2158 non-null   datetime64[ns]
 1   name    2158 non-null   object        
 2   point   2158 non-null   object        
dtypes: datetime64[ns](1), object(2)
memory usage: 50.7+ KB
  • 평점은 float으로 변경


1
2
movie['point'] = movie['point'].astype(float)
movie.info()
1
2
3
4
5
6
7
8
9
10
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 2158 entries, 0 to 2157
Data columns (total 3 columns):
 #   Column  Non-Null Count  Dtype         
---  ------  --------------  -----         
 0   date    2158 non-null   datetime64[ns]
 1   name    2158 non-null   object        
 2   point   2158 non-null   float64       
dtypes: datetime64[ns](1), float64(1), object(1)
memory usage: 50.7+ KB
  • 평점타입 변경 완료


1
movie.to_csv('./data/naver_movie_points_20201022.csv', sep = ',', encoding = 'utf-8')


2. 데이터 Preprocessing


2.1 Data Load

1
2
3
4
5
import numpy as np
import pandas as pd

movie = pd.read_csv('https://raw.githubusercontent.com/hmkim312/datas/main/navermoviepoints/naver_movie_points_20201022.csv', index_col = 0)
movie.tail()
datenamepoint
21532020-10-15언힌지드6.69
21542020-10-15죽지않는 인간들의 밤6.62
21552020-10-15강철비2: 정상회담5.01
21562020-10-15국제수사4.87
21572020-10-15뮬란4.20
  • index_col = 0 옵션을 넣어서 인덱스를 불러오지 않도록 함


2.2 평점 합산

1
2
movie_unique = pd.pivot_table(movie, index=['name'], aggfunc=np.sum)
movie_unique.sort_values('point', ascending = False).head(10)
point
name
소년시절의 너422.21
사랑과 영혼413.48
제리 맥과이어412.20
극장판 짱구는 못말려: 신혼여행 허리케인~ 사라진 아빠!393.58
브리짓 존스의 일기390.57
69세388.31
500일의 썸머378.90
라라랜드378.84
테넷372.53
타오르는 여인의 초상371.08
  • 영화 이름으로 인덱스를 잡고, 점수를 합산 후 내림차순 10개를 출력함, best 10


2.3 DataFrame Query

1
movie.query('name == ["테넷"]')
datenamepoint
322020-09-01테넷8.37
782020-09-02테넷8.36
1292020-09-03테넷8.34
1792020-09-04테넷8.33
2252020-09-05테넷8.34
2772020-09-06테넷8.31
3262020-09-07테넷8.29
3732020-09-08테넷8.28
4182020-09-09테넷8.28
4632020-09-10테넷8.28
5092020-09-11테넷8.27
5602020-09-12테넷8.27
6102020-09-13테넷8.27
6562020-09-14테넷8.27
7062020-09-15테넷8.27
7502020-09-16테넷8.27
7942020-09-17테넷8.27
8412020-09-18테넷8.28
8872020-09-19테넷8.27
9322020-09-20테넷8.27
9752020-09-21테넷8.27
10182020-09-22테넷8.26
10552020-09-23테넷8.26
10962020-09-24테넷8.27
11462020-09-25테넷8.27
11962020-09-26테넷8.26
12472020-09-27테넷8.26
12932020-09-28테넷8.26
13402020-09-29테넷8.26
13922020-09-30테넷8.26
14422020-10-01테넷8.26
14922020-10-02테넷8.26
15422020-10-03테넷8.27
15892020-10-04테넷8.26
16432020-10-05테넷8.26
16982020-10-06테넷8.27
17472020-10-07테넷8.27
18002020-10-08테넷8.27
18492020-10-09테넷8.27
18992020-10-10테넷8.27
19452020-10-11테넷8.27
19902020-10-12테넷8.27
20382020-10-13테넷8.27
20882020-10-14테넷8.27
21382020-10-15테넷8.27
  • DataFrame Query로 검색을 해볼수 있음


2.4 날짜별 영화 평점 변화 그리기

1
2
3
4
5
6
7
8
9
import matplotlib.pyplot as plt

plt.figure(figsize=(24, 8))
plt.plot(movie.query('name == ["테넷"]')['date'],
         movie.query('name == ["테넷"]')['point'])
plt.legend(labels = ['point'])
plt.xticks(rotation=45)
plt.grid()
plt.show()

  • 영화 테넷의 평점 변화를 봄
  • x 축의 길이가 너무 길어서 rotation을 45로 해줌
  • 그래프의 곡선이 많이 움직이는것 같지만, 사실 y축을 보면 그렇게 크지 않음 최고점과 최저점이 0.1점 차이


2.5 영화 정리

1
2
3
movie_pivot = movie.pivot_table(movie, index = ['date'], columns = ['name'])
movie_pivot.columns = movie_pivot.columns.droplevel([0])
movie_pivot.tail()
name500일의 썸머69세가버나움감쪽같은 그녀강철비2: 정상회담검객경계선국제수사그대, 고맙소 : 김호중 생애 첫 팬미팅 무비그래비티...포드 V 페라리폭스캐처프란시스 하피아노피아니스트피아니스트의 전설피터와 드래곤하녀항거:유관순 이야기홀리 모터스
date
2020-10-118.428.63NaNNaNNaN8.368.17NaN9.478.29...9.49NaN8.14NaN9.32NaN8.25NaNNaNNaN
2020-10-128.428.63NaNNaNNaN8.368.17NaN9.45NaN...9.49NaN8.14NaN9.32NaNNaNNaNNaN7.52
2020-10-138.428.63NaNNaN5.018.358.174.899.46NaN...NaNNaN8.14NaN9.32NaNNaNNaNNaN7.52
2020-10-148.428.63NaNNaN5.018.368.174.889.44NaN...NaNNaN8.14NaN9.32NaNNaNNaNNaN7.52
2020-10-158.428.63NaNNaN5.018.368.174.879.43NaN...9.49NaN8.14NaN9.32NaNNaNNaNNaN7.52

5 rows × 132 columns

  • 크롤링한 영화를 컬럼으로 하게 pivot함


2.6 보고싶은 영화들의 평점들 시각화

1
movie_pivot.columns
1
2
3
4
5
6
Index(['500일의 썸머', '69세', '가버나움', '감쪽같은 그녀', '강철비2: 정상회담', '검객', '경계선', '국제수사',
       '그대, 고맙소 : 김호중 생애 첫 팬미팅 무비', '그래비티',
       ...
       '포드 V 페라리', '폭스캐처', '프란시스 하', '피아노', '피아니스트', '피아니스트의 전설', '피터와 드래곤',
       '하녀', '항거:유관순 이야기', '홀리 모터스'],
      dtype='object', name='name', length=132)
1
2
3
4
5
6
7
targer_col = ['극장판 짱구는 못말려: 신혼여행 허리케인~ 사라진 아빠!', '테넷', '라라랜드', '동주']

plt.figure(figsize=(12,8))
plt.plot(movie_pivot[targer_col])
plt.legend(targer_col, loc = 'best')
plt.tick_params(bottom = False, labelbottom = False)
plt.show()

  • 보고싶은 영화의 평점만 골라서 비교해볼수 있음
  • 중간에 선이 끊긴것은 평점 데이터가 없는것


2.7 엑셀로 저장

1
movie_pivot.to_excel('./data/naver_movie_points_pivot_20201022.xlsx')

  • to_excel을 사용하여 저장
  • 이런식으로 저장됨
This post is licensed under CC BY 4.0 by the author.