https://github.com/miintto/crawling
https://github.com/miintto/crawling
Last synced: 3 days ago
JSON representation
- Host: GitHub
- URL: https://github.com/miintto/crawling
- Owner: miintto
- Created: 2018-03-29T06:09:23.000Z (about 7 years ago)
- Default Branch: master
- Last Pushed: 2018-04-05T12:05:47.000Z (about 7 years ago)
- Last Synced: 2025-03-24T07:55:43.991Z (3 months ago)
- Language: Jupyter Notebook
- Size: 61.5 KB
- Stars: 1
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
README
[네이버 영화 평점 크롤링 (feat. Python 3)]
==========# 1. 사이트 구조 파악
## 1.1 URL 분석영화 '신과 함께'의 평점을 크롤링한다고 가정해보자. 가장 먼저 필요한건 **크롤링하고자 하는 페이지의 url**이다. 웹페이지 크롤링의 원리가 해당 페이지의 html 소스에서 원하는 내용을 긁어오는 방식이기 때문에 url의 규칙을 잘 파악해두면 응용하는데 꽤 많은 도움이 된다.
영화 신과함께 평점 창의 url은 다음과 같다.
https://movie.naver.com/movie/bi/mi/pointWriteFormList.nhn?code=85579&type=after&isActualPointWriteExecute=false&isMileageSubscriptionAlready=false&isMileageSubscriptionReject=false&page=1
>중간에 붙은 85579가 영화의 고유번호라고 생각하면 쉽다.
>저 번호를 변경해주면 다른 영화의 페이지로 이동하게 된다.
> ex) '라라랜드' 는 134963, '범죄도시'는 161242 같은 식으로...
>개인적으로는 시간순으로 번호를 붙이지 않았나 생각했는데, 딱히 규칙은 없는둣하다.
>
>제일 마지막에 있는 번호는 페이지 번호다.
>한 페이지당 평점 10개씩 있고, for문으로 저걸 넘겨주는 코드를 짜면 한번에 대량의 평점을 가져올 수 있다.네이버 영화 사이트의 구조를 살펴보면 중심이 되는 큰 틀 안에 여러 프레임이 들어있는 형태로 구성되어있는데 각각 url이 분리되어 있다.
따라서 메인 창에서도 클릭 몇번으로 평점을 확인할 수 있지만, 정작 평점에 대한 데이터는 다른 프레임 안에 저장되어 있다.
그 프레임의 주소가 위와 같다.
또한 주어진 url로 접속하게 되면 평점 창으로만 들어가게 된다.----------
## 1.2 html 소스 분석
자, 이제 평점과 코멘트 내용을 가져와야 한다.
해당 창에서 마우스 오른쪽 버튼을 눌러 '페이지 소스 보기'를 클릭하면 해당 페이지의 소스를 볼 수 있다.
메인 창에서 불러오고 싶다면 '프레임 소스 보기'를 클릭하면 된다.다음과 같은 소스가 쭈욱 나온다.
네이버 영화
...필자는 html에 대해 무지하지만 html 구조가 여는 태그 <>, 닫는 태그 >가 반복되면서 구성된다는건 익히 들어 알고 있다.
이정도 개념만 알고있어도 크롤링엔 크게 문제될건 없다고 본다.좀 더 스크롤을 내려 영화 평점이 담겨있는 부분을 확인하자.
....
10
...
\
더 자세히 살펴보면 평점은 \
긴 문장이 아니라서 발견하기 힘들수도 있다....
또한 코멘트는 \
\
사이에 위치한다.**이런식으로 내가 원하는 텍스트가 어느 위치에 있는지 파악하는게 매우 중요하다!**
----------
----------
# 2. 크롤링
## 2.1 코드 구조 분석
이젠 파이썬을 이용해 크롤링을 해보도록 하자.
우선 필요한 라이브러리는 다음과 같다.
from bs4 import BeautifulSoup
import urllib.request as req
import math
**BeautifulSoup**는 html 데이터를 파싱 및 탐색하는 함수를 포함하고 있다.
**urllib**는 웹에서 html 소스를 불러올때 사용된다.
둘 다 필요하니 꼭 설치해두자.
이제 해당 url에서 html소스를 뽑아올 차례이다.
url = 'https://movie.naver.com/movie/bi/mi/pointWriteFormList.nhn?code=85579&type=after&isActualPointWriteExecute=false&isMileageSubscriptionAlready=false&isMileageSubscriptionReject=false&page=1'
response = req.urlopen(url)
soup = BeautifulSoup(response, 'html.parser')
먼저 url변수에 영화 평점 페이지의 주소를 넣어주었다.
urlopen()은 해당 페이지에 대한 정보를 가져오는 역할을 한다.
따라서 response에는 지정한 주소의 데이터가 들어있는데, BeautifulSoup()로 html데이터를 파싱하여 안에 담긴 페이지 소스를 뽑아올 수 있다.
**용어가 어려우면 크롤링하고자 하는 html 페이지의 소스를 가져오는 코드라고 생각하면 편하다.**
>soup를 직접 출력해보면 위에서 확인했었던 페이지 소스와 동일한 소스가 출력됨을 알 수 있다.
이제 저 방대한 양의 소스에서 필요한 부분만 가져오도록 하자.
소스를 잘 살펴보면 평점들은 \
평점1
평점2
평점3
....
>간단히 설명하자면 대략 이런식의 구조라고 할수 있다.
이제 저 부분에서 \
class="score_result" 는 해당 \
score_result = soup.find('div', class_ = 'score_result')
lis = score_result.find_all('li')
find()는 조건에 부합하는 부분을 찾아주는 함수다. 안의 내용은 'div' 중에서 class가 score_result인 부분만을 찾도록 하는 명령이다.
규칙만 알고 있으면 다용도로 변형이 가능하다.
find_all()은 주어진 조건에 알맞은 부분을 모두 찾아 벡터 형태로 묶어 내보내준다.
결과적으로 보면 score_result안에는 \
>lis = [\
>이런 식으로 저장되어있다.
>위에서 \
이젠 해당 소스에서 텍스트만 출력해보자.
위에서 확인했듯이 각각 \
다음 코드를 이용해보면.
score = lis[0].select_one('div.star_score > em')
reple = lis[0].select_one('div.score_reple > p')
select_one()의 구조도 보면 알겠지만 해당 div 안에 em이나 p 안의 부분을 불러오는 명령이다.
이 상태로 출력을 하게 되면 다음과 같다.
>\10\
>\
\BEST\\관람객\이정재가 홍보 따라다닌 이유를 알겠음 ㅋㅋㅋ 그리고 관심병사 연기 도랐다 \
코드가 거의 다 완성되어간다.
하지만 아직 태그가 제거되지 않아 이렇게 나타나게 된다.
이럴땐 get_text()함수를 이용하여 텍스트만 뽑아줄 수 있다.
적용해보면 다음과 같다.
print('평점 :', score.get_text())
print('리뷰 :', reple.get_text())
>평점 : 10
>리뷰 : BEST관람객이정재가 홍보 따라다닌 이유를 알겠음 ㅋㅋㅋ 그리고 관심병사 연기 도랐다
이런식으로 가능하다.
이를 좀더 응용하면
url = 'https://movie.naver.com/movie/bi/mi/pointWriteFormList.nhn?code='85579&type=after&isActualPointWriteExecute=false&isMileageSubscriptionAlready=false&isMileageSubscriptionReject=false&page=1
response = req.urlopen(url)
print(response)soup = BeautifulSoup(response, 'html.parser')
score_result = soup.find('div', class_ = 'score_result')
lis = score_result.find_all('li')
for li in lis:
score = li.select_one('div.star_score > em')
reple = li.select_one('div.score_reple > p')
print('평점 :', score.get_text())
print('리뷰 :', reple.get_text())
>평점 : 10
>리뷰 : BEST관람객이정재가 홍보 따라다닌 이유를 알겠음 ㅋㅋㅋ 그리고 관심병사 연기 도랐다
>평점 : 10
>리뷰 : BEST관람객나 죽어서 지옥가면 어떡하냐 ㄷㄷㄷ 앞으로 착하게 살아야지
>평점 : 10
>리뷰 : BEST관람객남자 셋이서 안 울라고…주먹으로 입틀막함 ㅋㅋㅋㅋ
>평점 : 10
>리뷰 : BEST관람객여보, 이번 겨울에… 부모님댁에 밥솥 놔드려야겠어요
>평점 : 10
>리뷰 : BEST관람객가족이랑 봤는데… 나… 우리 아빠 우는 거 처음 봤음 ㅜㅜㅜㅜ
>평점 : 8
>리뷰 : 관람객오늘도 열심히 사는 대한민국의 자홍,수홍씨들을 응원합니다 ㅠㅠ
>평점 : 10
>리뷰 : 야 이거 생각한 것 보다 잘 나왔잖아 ?
>평점 : 10
>리뷰 : 관람객오늘 화장 안하고 신과함께를 본 것은 신의한수였음.
>평점 : 10
>리뷰 : 원작팬이에요. 솔직히 기대 내려놓고 봤지만 볼거리, 스토리, 캐릭터 케미 모두 기대 이상이었어요. 이정도 영화 없다고 봅니다. 많이 감동 받았어요
>평점 : 10
>리뷰 : 관람 내내 지옥행 롤러코스터 타고 온 기분이었음 ㅋㅋㅋㅋ
와 같이 크롤링이 가능하다.
----------
## 2.2 모든 페이지의 평점 크롤링
좀더 응용해서 모든 페이지의 댓글을 크롤링해보자.
우선 마지막 페이지 번호를 가져와보자.
**총 댓글 개수를 가져와 10으로 나누어주고 소숫점은 올려주는 방식으로 페이지의 개수를 계산하려고 한다.**
자신이 필요한 평점의 갯수가 1000개 정도로 정해져 있거나 특정 영화만 분석할 경우에는 굳이 이런 코드가 필요가 없겠지만,
여러 영화의 평점을 동시에 가져오는 경우라면 이런 코드가 휠씬 편리할 것이다.
마찬가지로 첫 페이지의 소스를 가져온다.
url = 'https://movie.naver.com/movie/bi/mi/pointWriteFormList.nhn?code=85579&type=after&isActualPointWriteExecute=false&isMileageSubscriptionAlready=false&isMileageSubscriptionReject=false&page=1'
response = req.urlopen(url)
soup = BeautifulSoup(response, 'html.parser')
이제 댓글 전체 개수를 가져와야 하는데 아래 소스와 같이 \ 안에서 두번째 \에 위치하게 된다.
...
140자 평|총55,536건
....
따라서 같은 방식으로 전체 개수를 가져오게 되면
score_total = soup.find('div', class_ = 'score_total')
Count = score_total.find_all('em')[1]
print(Count.get_text())
>55,536
위와 같이 출력이 된다.
여기서 주의할 점은 출력된 숫자는 숫자형이 아니라 문자형이기 때문에 나눗셈과 같은 사칙연산이 불가능하다. 따라서 다음과 같은 과정을 통해 좀 더 가공이 필요하다.
import math
page_limit = int(Count.get_text().replace(',', ''))
page_limit = math.ceil(page_limit/10)
print(page_limit)
>5554
math 라이브러리는 소숫점 올림 연산을 하기위해 불러왔다.
replace()함수를 이용해 콤마를 제거해준다. 이 과정이 없으면 숫자로 변형이 안된다.
그리고 숫자형으로 불러온뒤 10으로 나누어 준다. ceil()함수는 math 라이브러리 안에 있는 소숫점 올림 연산을 해주는 함수이다.
이제 마지막 페이지도 알아냈으니 해당 범위 안에서 평점들을 모두 뽑아내기만 하면 된다.
그러기 위해서는 **url을 두 개로 나누어주어야 한다.**
url1 = 'https://movie.naver.com/movie/bi/mi/pointWriteFormList.nhn?code=85579&type=after&isActualPointWriteExecute=false&isMileageSubscriptionAlready=false&isMileageSubscriptionReject=false&page='
num = i
url = url1+str(num)
위와 같은 방식으로 나누어준 뒤 num 변수를 1부터 위에서 계산한 마지막까지 돌려주기만 하면 된다.
url1 = 'https://movie.naver.com/movie/bi/mi/pointWriteFormList.nhn?code='85579&type=after&isActualPointWriteExecute=false&isMileageSubscriptionAlready=false&isMileageSubscriptionReject=false&page='
for num in range(1, page_limit+1):
response = req.urlopen(url1+str(num))
soup = BeautifulSoup(response, 'html.parser')
score_result = soup.find('div', class_ = 'score_result')
lis = score_result.find_all('li')
for li in lis:
score = li.select_one('div.star_score > em')
reple = li.select_one('div.score_reple > p')
print(score.get_text(), ' , ', reple.get_text(), '\n')
>10 , BEST관람객이정재가 홍보 따라다닌 이유를 알겠음 ㅋㅋㅋ 그리고 관심병사 연기 도랐다
>10 , BEST관람객나 죽어서 지옥가면 어떡하냐 ㄷㄷㄷ 앞으로 착하게 살아야지
>10 , BEST관람객남자 셋이서 안 울라고…주먹으로 입틀막함 ㅋㅋㅋㅋ
>10 , BEST관람객여보, 이번 겨울에… 부모님댁에 밥솥 놔드려야겠어요
>10 , BEST관람객가족이랑 봤는데… 나… 우리 아빠 우는 거 처음 봤음 ㅜㅜㅜㅜ
>8 , 관람객오늘도 열심히 사는 대한민국의 자홍,수홍씨들을 응원합니다 ㅠㅠ
>10 , 야 이거 생각한 것 보다 잘 나왔잖아 ?
>10 , 관람객오늘 화장 안하고 신과함께를 본 것은 신의한수였음.
>
> ...
나중에 데이터 처리하기 편하도록 평점과 리뷰사이는 콤마로 나누어 출력했다.
----------
## 2.3 텍스트 파일로 내보내기
이제는 가져온 자료들을 텍스트 파일로 내보낼 차례이다.
f = open('Movie_Comment.txt', 'w')
open() 함수는 텍스트 파일을 불러오는 함수다.
뒤에 붙은 w는 명령어인데 텅 빈 턱스트 파일을 불러오고 텍스트를 입력하게 해준다.
단순히 입력된 데이터를 가져오는 경우라면 w대신 r을 넣어주면 된다.
**종합하자면, Movie_Comment.txt 라는 텍스트 파일을 생성하고 f에 불러와서 내용을 입력하게 해주는 내용이다.**
그 다음 진행할 내용은 전 단계와 비슷하다.
url1 = 'https://movie.naver.com/movie/bi/mi/pointWriteFormList.nhn?code='85579&type=after&isActualPointWriteExecute=false&isMileageSubscriptionAlready=false&isMileageSubscriptionReject=false&page='
response = req.urlopen(url1+'1')
soup = BeautifulSoup(response, 'html.parser')
score_total = soup.find('div', class_ = 'score_total')
Count = score_total.find_all('em')[1]
page_limit = int(Count.get_text().replace(',', ''))
page_limit = math.ceil(page_limit/10)
for num in range(1, page_limit+1):
response = req.urlopen(url1+str(num))
print(response)soup = BeautifulSoup(response, 'html.parser')
score_result = soup.find('div', class_ = 'score_result')
lis = score_result.find_all('li')
for li in lis:
score = li.select_one('div.star_score > em')
reple = li.select_one('div.score_reple > p')
f.write(score.get_text()+' , '+reple.get_text()+'\n')
f.close
**달라진 부분은 print() 부분이 그대로 f.write()로 바뀌어졌다는것 뿐이다.** 그러면 원래 출력될 부분이 그대로 텍스트 파일로 들어가게 된다.
마지막에 f.close로 입력을 끝내준다.
----------
## 2.4 다수의 영화 평점 크롤링
이젠 다른 영화의 평점도 함께 출력해보자.
예시로 5개의 영화와 영화 코드번호를 가져왔다. 개인적으로 더 돌리고 싶으면 더 돌려도 된다. (대신 시간이 좀 오래걸린다....)
movies = [85579, 123519, 134963, 137008, 137326, 158885]
신과함께, 아가씨, 라라랜드, 리얼, 블랙팬서, 범죄도시
각 영화마다 텍스트 파일을 따로 분류해서 저장하려고 한다.
이번에는**url주소를 4개의 조각으로 나누어야 한다.**
url1 = 'https://movie.naver.com/movie/bi/mi/pointWriteFormList.nhn?code='
num1 = movies
url2 = '&type=after&isActualPointWriteExecute=false&isMileageSubscriptionAlready=false&isMileageSubscriptionReject=false&page='
num2 = i
텍스트 파일 이름은 Movie_85579.txt 같은 식으로 영화마다 다르게 지정해 주었다.
텍스트 파일마다 제일 처음에 제목을 내보내고, 그 다음부터 평점과 리뷰를 넣으려고 한다.
for num1 in movies:
f = open('Movie_'+str(num1)+'.txt', 'w')
response = req.urlopen('https://movie.naver.com/movie/bi/mi/point.nhn?code='+str(num1))
soup = BeautifulSoup(response, 'html.parser')
title = soup.select_one('title').get_text()
f.write(title+'\n\n') ### 영화 제목 출력
response = req.urlopen(url1+str(num1)+url2+'1')
soup = BeautifulSoup(response, 'html.parser')
score_total = soup.find('div', class_ = 'score_total')
Count = score_total.find_all('em')[1]
page_limit = int(Count.get_text.replace(',', ''))
page_limit = math.ceil(page_limit/10)
for num2 in range(1, page_limit+1):
response = req.urlopen(url1+str(num1)+url2+str(num2))
soup = BeautifulSoup(response, 'html.parser')
score_result = soup.find('div', class_ = 'score_result')
lis = score_result.find_all('li')
for li in lis:
score = li.select_one('div.star_score > em')
reple = li.select_one('div.score_reple > p')
f.write(score.get_text()+' , '+reple.get_text()+'\n')
f.close
제목을 출력하는 코드는 따로 설명하지 않았지만 여기까지 내용을 이해했다면 크게 문제없을거라고 생각한다.
**실행이 다 끝나면 텍스트 파일을 열어서 빠짐 없이 내보내졌는지 꼼꼼히 확인해보자!**