[2주차_데이터분석] 개발일지 (크롤링 연습, 워드 클라우드 만들기-1)

Coastby·2022년 7월 5일
0

[네이버 영화 줄거리로 워드클라우드 만들기]
네이버 영화 페이지에서 줄거리를 크롤링하여 워드 클라우드를 만들어본다.
1. 👉네이버 영화 줄거리 크롤링👈
2. 워드 클라우드 만들기

[목차] : 네이버 영화 줄거리 크롤링

  • URL, HTML 구조 분석
  • 크롤링 기본 구조
  • 크롤링을 위한 함수
  • csv 파일로 저장

👉 URL, HTML 구조 분석


○ URL 구조 분석

# URL 
https://movie.naver.com/movie/bi/mi/basic.naver?code=191559

위의 URL에서 마지막 code=191559에서 숫자를 수정해보면 다른 영화가 나온다. code 뒤쪽 부분을 바꾸면서 영화를 크롤링 해 올 수 있을 것이다.


○ HTML 구조 분석

  • 영화 상세 페이지에서 개발자 도구를 이용하여 각각 요소 (제목, 장르, 줄거리)의 복사-selector 복사를 실행하여 본다.

👉 크롤링의 기본 구조


○ 크롤링 시작 코드

패키지들을 임포트하고 해당 url에서 code를 변수로 설정하고 html을 가져온다.

import requests # requests라는 패키지를 임포트
import pandas as pd # pandas라는 패키지를 임포트하는데 앞으로 pd로 부르겠음
from bs4 import BeautifulSoup # bs4라는 패키지로부터 BeautifulSoup라는 모듈을 임포트

# 네이버의 크롤링 방지 장치를 우회하기 위함
headers = {'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.114 Safari/537.36'}

movie_code = 191559 # 211097까지 존재, 191559는 앞서 봤던 영화 '듄'의 코드
movies = requests.get('https://movie.naver.com/movie/bi/mi/basic.naver?code='+str(movie_code), headers=headers)

# html 분석
soup = BeautifulSoup(movies.content, 'html.parser')

○ 요소들을 변수에 담기

html 구조 파악에서 selector 복사한 것을 변수에 담아준다.
이때 nth-childnth-of-type으로 바꿔줘야 코랩에서 원활하게 작동한다. 같은 의미이나 버전차이라고 한다. 아래와 같은 순서로 장르를 찾아낸다. 그러나 가져온 결과가 비어있거나 다르면 괄호한의 숫자를 하나하나 바꿔보면서 맞는 것을 찾아야 한다.

# copy selector에서 나오는 것
movie_genre = soup.select('#content > div.article > div.mv_info_area > div.mv_info > dl > dd:nth-child(2) > p > span:nth-child(1) > a:nth-child(1)')

# nth-child를 nth-of-type으로 바꿔줌
# 마지막 a의 :nth-child는 다 가져올 거기 때문에 삭제
movie_genre = soup.select('#content > div.article > div.mv_info_area > div.mv_info > dl > dd:nth-of-type(2) > p > span:nth-of-type(1) > a')

# 하지만 결과는 []으로 비어있음. 괄호안에 숫자를 바꿔가면 맞는 것 찾아내기
movie_genre = soup.select('#content > div.article > div.mv_info_area > div.mv_info > dl > dd:nth-of-type(1) > p > span:nth-of-type(1) > a')

TypeError
TypeError: 'NoneType' object is not callable
이 과정은 혼자하다가 에러가 계속 났는데 알고보니 select 오타^^
(selcet 이라 적어놓고 selector만 바꾸고 있었음)

movie_title = soup.select_one('#content > div.article > div.mv_info_area > div.mv_info > h3 > a').text
movie_genre = soup.select('#content > div.article > div.mv_info_area > div.mv_info > dl > dd:nth-of-type(1) > p > span:nth-of-type(1) > a')
movie_content = soup.select_one('#content > div.article > div.section_group.section_group_frst > div:nth-of-type(1) > div > div > p').text

print(movie_title, movie_genre, movie_content)

#Result[<a href="/movie/sdb/browsing/bmovie.naver?genre=6">모험</a>, <a href="/movie/sdb/browsing/bmovie.naver?genre=1">드라마</a>, <a href="/movie/sdb/browsing/bmovie.naver?genre=18">SF</a>] 10191, 아트레이데스 가문의 후계자인 폴(티모시 샬라메)은 시공을 초월한 존재이자 전 우주를 구원할 예지된 자의 운명을 타고났다. 그리고 어떤 계시처럼 매일 꿈에서 아라키스 행성에 있는 한 여인을 만난다. 모래언덕을 뜻하는 '듄'이라 불리는 아라키스는 물 한 방울 없는 사막이지만 우주에서 가장 비싼 물질인 신성한 환각제 스파이스의 유일한 생산지로 이를 차지하기 위한 전쟁이 치열하다. 황제의 명령으로 폴과 아트레이데스 가문은 죽음이 기다리는 아라키스로 향하는데…  위대한 자는 부름에 응답한다, 두려움에 맞서라, 이것은 위대한 시작이다!

결과값에서 장르는 여러개라 리스트 형식으로 출력되며, 태그가 붙어있으므로 for문을 이용하여 이를 정리해준다.

movie_genres = []
for genre in movie_genre:
    each_genre = genre.text
    movie_genres.append(each_genre)

#Result
['모험', '드라마', 'SF']

어려웠던 점
movie_genres = []를 for문 안에다가 넣어놔서 마지막 장르만 나옴


👉 크롤링을 위한 함수 만들기


앞서 작성한 코드를 활용하여 크롤링을 위한 함수를 만들어본다.

def movie_crawl (movie_code):
  movies = requests.get('https://movie.naver.com/movie/bi/mi/basic.naver?code='+str(movie_code), headers=headers)
  soup = BeautifulSoup(movies.content, 'html.parser')

  movie_title = soup.select_one('#content > div.article > div.mv_info_area > div.mv_info > h3 > a').text
  movie_content = soup.select_one('#content > div.article > div.section_group.section_group_frst > div:nth-of-type(1) > div > div > p').text
  movie_genre = soup.select('#content > div.article > div.mv_info_area > div.mv_info > dl > dd:nth-of-type(1) > p > span:nth-of-type(1) > a')
  movie_genres = []
  for genre in movie_genre:
    each_genre = genre.text
    movie_genres.append(each_genre)
  movie_info = [movie_title, movie_genres, movie_content]

  return movie_info

영화 [듄]의 코드를 넣어서 출력해본다.

print(movie_crawl(191559))

#Result
['듄', ['모험', '드라마', 'SF'], "10191년, 아트레이데스 가문의 후계자인 폴(티모시 샬라메)은 시공을 초월한 존재이자\xa0전 우주를 구원할 예지된 자의 운명을 타고났다.\xa0그리고 어떤 계시처럼 매일 꿈에서 아라키스 행성에 있는 한 여인을 만난다.\xa0모래언덕을 뜻하는 '듄'이라 불리는 아라키스는 물 한 방울 없는 사막이지만\xa0우주에서 가장 비싼 물질인 신성한 환각제 스파이스의 유일한 생산지로 이를 차지하기 위한 전쟁이 치열하다.\xa0황제의 명령으로 폴과 아트레이데스 가문은 죽음이 기다리는 아라키스로 향하는데…\xa0\xa0위대한 자는 부름에 응답한다, 두려움에 맞서라, 이것은 위대한 시작이다!"]

dataframe 생성하기

방법1) 열별로 만들기

pd.DataFrame(
    {"컬럼명1":[데이터 입력],
     "컬럼명2":[데이터 입력]
     ,,,,
     "컬럼명n":[데이터 입력]},  
     index = 리스트 형태[ ])

방법2) 행별로 만들기

pd.DataFrame(
    [[1], ,,, ,[막행]],
    index = [],
    columns = [컬럼명])

실제로 크롤링을 하는 함수를 작성한다. 지금까지는 열별로 데이터프레임을 만들었지만 이번에는 행별로 데이터프레임을 만든다. movie_crawl함수를 사용하면 하나의 리스트에 title, genre, content가 순서대로 들어간다. 그리고 이 리스트를 다시 리스트로 묶으면 데이터 프레임을 만들 수 있을 것이다.

또한, 영화 코드에 따라서 존재하지 않는 페이지도 있다. 따라서 이러한 페이지는 오류를 만들 수 있으므로 try-except문으로 예외처리를 해준다.

# for문으로 크롤링하는 함수 만들기
def crawl_all():
  movie_list = []
  for movie_code in range(10000, 10100):
    try:
      movie_info = movie_crawl(movie_code)
      movie_list.append(movie_info)
    except:
      continue
  return movie_list


print (crawl_all())

👉 csv 파일로 저장


movie_list_all = crawl_all()
# 데이터프레임으로 만들기
df = pd.DataFrame(movie_list_all, columns=['Title', 'Genre', 'Content'])
print(df)

# 데이터프레임으로 만든 후, csv 파일 형태로 저장
df.to_csv('naver_movies.csv')
profile
훈이야 화이팅

0개의 댓글