from bs4 import BeautifulSoup as bs
import requests
import pandas as pd
from urllib.parse import quote
headers = {'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.93 Safari/537.36'}
▶ 이번 강의에서는 'from urllib.parse import quote' 라는것을 처음 써본다
01 URL 분석
하나의 평점-리뷰를 클릭하면 이런 화면으로 이동하면서 빨간색 박스부분의 주소가 추가 된다
특히 'sword=208077' 부분이 변경이되면 영화가 바뀌면서 평점-리뷰도 바뀌는걸 알 수 있다
이렇게 세팅을하면 어떠한 숫자가 파라미터(mcode)로 들어가면 'str(mode)'에 반영되고 각 숫자에 맞는 영화를 찾아 올 수 있다
02 사이트 분석
들고 오고자하는 데이터를 분석하자
1차 코드
def get_movie_reviews(mcode, page_num=12):
url = "https://movie.naver.com/movie/point/af/list.naver?st=mcode&sword="+ str(mode) + "&target=after"
for _ in range(0, page_num): # 페이지를 위한 포문
movie_page = requests.get(url, headers=headers)
movie_page_soup = bs(movie_page.content, 'html.parser')
review_list = movie_page_soup.find_all("td", {"class","title"})
for review in review_list: #리뷰를 위한 포문
title = review.find('a', {'class':'movie color_b'}).get_text()
score = review.find('em').get_text()
페이지를 돌리기 위한 포문이 필요하고 이 페이지 포문을 통해서 'requests'와 'beautifulsoup'을 세팅 한다
리뷰-평점이 포함되어있는 전체 태그 <td class="title">를 들고오기 위한 find_all
find_all은 모든 데이터가 리스트 형식이니 포문을 돌리면서 <td> 태그안의 태그들을 들고오면 된다.
타이틀/평점은 직관적으로 나와있기때문에 find로 돌리면 된다
2차 코드 (리뷰가 어렵다)
강의에는 'report'를 찾아 진행이 가능하지만 현재 날짜로는 'onclick' 속성으로 변경 되었다. 다르게 처리해야한다.
<br>은 태그라고 볼 수 없다. <br>은 한칸 띄우기 용이기때문에 찾을 수 가 없다
그렇다면 저 내용이 들어가 있는 <a>태그를 들고와서 리뷰 내용 부분을 'split' 해줘야 한다
def get_movie_reviews(mcode, page_num=12):
url = "https://movie.naver.com/movie/point/af/list.naver?st=mcode&sword="+ str(mcode) + "&target=after"
for _ in range(0, page_num): # 페이지를 위한 포문
#DF만들기
movie_review_df = pd.DataFrame(columns=("Title", "Score", "Review"))
idx = 0 #DF 내용 인덱스
movie_page = requests.get(url, headers=headers)
movie_page_soup = bs(movie_page.content, 'html.parser')
review_list = movie_page_soup.find_all("td", {"class","title"})
for review in review_list: #리뷰를 위한 포문
title = review.find('a', {'class':'movie color_b'}).get_text()
score = review.find('em').get_text()
review_text = review.find_all('a')[1]['onclick']
review_text = review_text.replace("report(", "").split("', '")[2]
movie_review_df.loc[idx] = [title, score, review_text] #한칸씩 내려가면서 내용 채움
idx += 1 #칸 이동용 인덱스
print("#", end=" ") #진행사항 확인
'review_text'를 통해 <br>이 아닌 유의미한 정보를 가진 <a>태그 모두를 들고옴
그 중 [1]번째 <a> 태그를 들고 오면서 'onclick' 속성을 들고 옴
거기서 "report(" 부분을 공백으로 처리
다시 스플릿을 하면 다음으로 변경 됨 onclick="dlxo****', '1LKO8L8ZijUCDoiPeyD9pfXWBCjvQaia58AKt903mhQ=', '처음부터 함께해온 스파이더맨 다시 그 시작을 알리는 영화였다엔딩 크레딧 뒤에 쿠키 하나 더 있습니다', '17846032', 'point_after');" 0 1 [2] 부분을 들고 옴
참고로 nth로 하면 인덱싱[1]은 1부터 시작, 배열기준이면 [1]은 2번째를 가르킨다
3차 코드 (페이지 이동)
페이지 이동을 눌려보면 이런 형태로 반복 된다하지만 10페이지 이후 다음 페이지의 형태가 다르다&amp;amp;amp;amp;nbsp;
페이지 이동은 다음 버튼으로 하는게 가장 적절하다
또한 리뷰가 끝나는 지점이 오면 오류가 걸린다. 이에대한 처리도 필요하다
def get_movie_reviews(mcode, page_num=10):
movie_review_df = pd.DataFrame(columns=("Title", "Score", "Review"))
idx = 0 #DF 내용 인덱스
url = "https://movie.naver.com/movie/point/af/list.naver?st=mcode&sword="+ str(mcode) + "&target=after"
for _ in range(0, page_num): # 페이지를 위한 포문
#DF만들기
movie_page = requests.get(url, headers=headers)
movie_page_soup = bs(movie_page.content, 'html.parser')
review_list = movie_page_soup.find_all("td", {"class","title"})
# print(len(review_list))
for review in review_list: #리뷰를 위한 포문
title = review.find('a', {'class':'movie color_b'}).get_text()
score = review.find('em').get_text()
review_text = review.find_all('a')[1]['onclick']
review_text = review_text.replace("report(", "").split("', '")[2]
movie_review_df.loc[idx] = [title, score, review_text] #한칸씩 내려가면서 내용 채움
# print(movie_review_df.loc[idx])
idx += 1 #칸 이동용 인덱스
# print("#", end=" ") #진행사항 확인
try: #리뷰가 끝나는 지점 처리
url = "https://movie.naver.com" + movie_page_soup.find('a', {'class':'pg_next'}).get('href')
print(url)
except:
break
return movie_review_df
03 DF 만들기
*원하는 영화 번호를 넣으면 된다
movie_review_df = get_movie_reviews(208077, 1) *(영화번호, 페이징 수)
movie_review_df
※ 인덱싱 위치때문에 마지막에 해맸다. 인덱싱이 어디서 되는지 또 어디서 초기화가 되는지 잘 봐야 한다. 앞으로는 모든 문장 마다 실행하고 진행을 해야겠다!
04 현재 상영작으로 파싱 해보기
1) 페이지 분석
명확해 보인다. &amp;amp;amp;amp;lt;select&amp;amp;amp;amp;gt; + &amp;amp;amp;amp;lt;option&amp;amp;amp;amp;gt; 을 손보면 될 것 같다
▶ <option> 태그를 보면 영화의 번호와 영화 제목이 같이 들어 있는걸 볼 수 있다. 이를 이용 하자!
1차 코드
url = "https://movie.naver.com/movie/point/af/list.naver"
naver_movie = requests.get(url, headers=headers)
soup = bs(naver_movie.content, 'html.parser')
select = soup.find('select', {'id':'current_movie'})
movies = select.find_all('option')
# print(movies)
movies_dict = {}
for movie in movies[1:]:
movies_dict[movie.get('value')] = movie.get_text()
<select> 태그를 들고오면 버튼의 모든 내용이 들어 있다
딕셔너리 변수를 만든다 (키:밸류 로 쉽게 꺼내고 쓸 수 있도록)
[0]<option> 은 필요없으므로 포문을 [1]번 부터 돌도록 한다
'value' 가 영화 번호를 들고 있으므로 딕셔너리의 키로 들어가고 영화 타이틀인 텍스트는 딕셔너리의 밸류로 들어가도록 한다
2차 코드
url = "https://movie.naver.com/movie/point/af/list.naver"
naver_movie = requests.get(url, headers=headers)
soup = bs(naver_movie.content, 'html.parser')
select = soup.find('select', {'id':'current_movie'})
movies = select.find_all('option')
# print(movies)
movies_dict = {}
for movie in movies[1:]:
movies_dict[movie.get('value')] = movie.get_text()
movie_revie_df = pd.DataFrame(columns=("Title", "Score", "Review"))
for mcode in movies_dict:
df = get_movie_reviews(mcode, 1)
movie_review_df = pd.concat([movie_review_df, df])
movie_review_df
원래 만들어 놓았던 함수에 이제 대입만 하면 된다
데이터 프레임을 선언한 뒤
movies_dict에 mcode를 뺄수 있으니 그대로 빼와서 넣는다 (mcode, 페이징번호)
.concat을 활용하여 각기 다른 DF를 합쳐 준다
▶ 딕셔너리를 포문으로 쓸 경우 키값이 바로 온다는것을 기억 하자!
번외
- 이번에는 혼자서 네티즌 리뷰를 해볼 예정!
* 기본 세팅
from bs4 import BeautifulSoup as bs
import requests
import pandas as pd
from urllib.parse import quote
headers = {'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.93 Safari/537.36'}
1) 1차 영화 제목 뽑기
역시 강의만듣고 넘어가면 절대로 안된다. 직접해봐야 어디가 문제인지 나온다.
def get_movie_reviews(mcode, page_num=10):
url = "https://movie.naver.com/movie/board/review/list.naver?st=code&sword="+ str(mcode) + "&target=after"
for i in range(0, page_num):
movie_page = requests.get(url, headers=headers)
movie_page_soup = bs(movie_page.content, 'html.parser')
movie_list = movie_page_soup.find_all('div', {"class":"choice_movie_info"})
강의를 따라만 하니까 타이틀 들고 오는것 부터 막혔다 그 이유가 네티즌 리뷰 첫페이지의 타이틀 내용과 스크래핑시에 진행되는 타이틀 내용이 영화 번호를 받아서 바뀐다는거를 전혀 인지를 못했다. 만약 강의만 듣고 넘어갔으면 절대로 몰랐을 문제
즉 다시말해 아래 두 페이지는 다르다는것! 번호를 받아서 스크래핑이 이동하여 진행되는데 이때 가지고 있는 속성이 다르다. 이것때문에 계속해서 위의 사진 태그로 진행을하니 'none'을 내 뱉었다! 어디서 스크래핑이 진행되는지 알고 있어야 한다!▶ 빈칸이나 None 리턴시에는 url 이 정확한지 꼭 확인하자!
2) 타이틀
3) 작성자 및 날짜
- 굉장히 어려웠다. 특이 작성자 및 날짜 부분에 <br> 및 엄청난 공백때문에 초보자에게는 멘붕이었다
이렇게 나오는것을 볼 수 있다.
- 여기서 많이 해맸다. 아래와 같이 해결이 가능 하다.
for author in author_lists:
aa = author.find('a').get_text() # 작성자
bb = author.get_text().strip().split('\n')[-1].strip() #날짜
▶ 초보자에게는 정말 어려운 고난이도였다. 직접 해보는데 1주일이 걸렸다.. 글을 1주일 만에 다시 쓴다..
완성을 다 했는데도 불구하고 초보자인 내가 해결할 수 없는 부분에서 막혔다. 분명히 똑바로 소스를 다 가져왔는데도 페이지당 1개의 소스만 가져오고 바로 그다음 페이지로 넘어가는 오류가 계속 해서 났다
'table' 자체를 들고와버려서 뽑고자하는 소스가 한줄씩 돌지않고 한줄이 곧 한 페이지가 되도록 처리가 되버린것
▶ 완성 코드를 보면!
from bs4 import BeautifulSoup as bs
import requests
import pandas as pd
from urllib.parse import quote
headers = {'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.93 Safari/537.36'}
def get_movie_reviews(mcode, page_num=10):
movie_review_df = pd.DataFrame(columns = ("Movie Title", "Comment", "Writer", "Date", "Star", "Thums-Up", "Veiw"))
idx = 0
url = "https://movie.naver.com/movie/board/review/list.naver?st=code&sword="+ str(mcode) + "&target=after"
for _ in range(0, page_num):
movie_page = requests.get(url, headers=headers)
movie_page_soup = bs(movie_page.content, 'html.parser')
title = movie_page_soup.find('span',{"class":"choice_txt"}).get_text() #제목
info_list = movie_page_soup.find('table', {"class":"list_table_1 list_h48"}).find("tbody").findAll("tr", recursive=True)
# print(len(info_list))
for info in info_list: #리뷰 리스트
# print(info)
comment = info.find('td', {"class":"title"}).get_text().strip() #코멘트
author = info.find('td',{"class":"author num"}).find('a').get_text()
date = info.find('td', {"class":"author num"}).get_text().strip().split('\n')[-1].strip() #날짜
star_list = info.select("div.point_type_1 > div.mask > img")
star = star_list[0].attrs['alt'] #별점
recommend = info.find('td', {"class":"num c_ff4200"}).get_text().strip()#추천
view = info.find_all('td', {"class":"num"})[2].get_text()#조회.
print(view)
movie_review_df.loc[idx] = [title, comment, author, date, star, recommend, view]
idx += 1
print("#",end=" ")
try: #리뷰가 끝나는 지점 처리
url = "https://movie.naver.com" + movie_page_soup.find('a', {'class':'pg_next'}).get('href')
print(url)
except:
break
return movie_review_df
movie_review_df = get_movie_reviews(208077, 1)
movie_review_df
가장 중요 포인트는 'info_list = movie_page_soup.find('table', {"class":"list_table_1 list_h48"}).find("tbody").findAll("tr", recursive=True)' 이렇게 하위 태그들에 어떻게 접근하는지가 중요하다