Stage2 - 파이썬으로 데이터 수집하기

두번째 스테이지에서는 앞에서 설치한 requests와 BeautifulSoup4 패키지를 활용해서 직접 데이터수집기를 만들어 보겠습니다.

네이버TV TOP 100(https://tv.naver.com/r)

첫번째 시간에 선택자찾는 연습을 했던 네이버TV TOP 100(https://tv.naver.com/r)에서 1 ~ 100위까지 영상의 제목/채널명/재생 수/좋아요 수를 수집하려고합니다.

파이썬 데이터수집 시작하기

오픈소스 패키지 가져오기

새로만든 week3 디렉토리 안에 week3_1.py를 만들어 데이터수집기 만들기를 시작합니다.

week3_1.py
import requests
from bs4 import BeautifulSoup

데이터수집기를 만들기 위해서는 첫번째 스테이지에서 설치한 requestsBeautifulSoup4 패키지를 사용해야 합니다. 설치한 패키지를 사용하기 위해서는 위처럼 import 명령어를 사용합니다. BeautifulSoup4의 경우는 내부적으로 bs4라는 디렉토리 안에 저장되어있기 때문에 from 명령어를 사용해서 bs4 디렉토리 안에 있는 BeautifulSoup이라는 기능을 가져와줍니다.

각 패키지의 기본적인 사용법(import 방법/기초 함수 사용법)은 주로 정식 Document에 설명되어있습니다. ex) BeautifulSoup4: https://www.crummy.com/software/BeautifulSoup/bs4/doc/

import패키지이름 or from패키지이름import하위모듈

다운로드한 패키지를 사용하기 위해서는 import 명령어를 사용해야합니다.

설치한 패키지에 있는 모든 내용을 가져옵니다.

특정한 기능(모듈)만을 가지고오고 싶다면 from 명령어와 함께 사용합니다.

처음 파이참에 위 코드를 적으면 위와 같이 import ~ 부분이 회색으로 되어있습니다. 설치가 잘못되거나 문법 오류가 아니라 아직 외부패키지를 사용하지 않았기 때문에 "너 이거 불러와놓고 안썼어!" 라는 안내의 의미입니다.

웹에서 데이터 가져오기 - requests 사용하기

먼저 웹에서 데이터를 가져오기 위하여 requests패키지를 사용해보겠습니다. 아래와 같이 코드를 적고 실행해봅시다.

week3_1.py
import requests
from bs4 import BeautifulSoup
raw = requests.get("https://tv.naver.com/r")
print(raw)

실행결과 <response[200]>

코드를 먼저 살펴보면 requests.get("https://tv.naver.com/r")부분은 reqeusts패키지가 가지고있는 get이라는 함수를 사용하는 부분입니다. 추가한 패키지 안에 있는 함수를 사용하려면 import한 패키지 이름 옆에 .을 찍어줘야합니다.

get함수는 괄호 안의 URL주소에 접속을 요청하여 여러가지 데이터를 받아오는 기능을 합니다. 실행결과에서 보이는 <response[200]>은 정상적으로 응답했다는 뜻의 코드입니다. response 안의 숫자는 응답 상태를 의미합니다. 인터넷 서핑을 하다가 가끔 보게되는 404:Page not found의 경우는 <response[404]>로 표현됩니다.

하지만 우리가 데이터수집을 하기위해서 필요한 데이터는 응답성공여부가 아니라 페이지의 HTML코드입니다. 아래와 같이 코드를 수정해서 실행해봅니다.

week3_1.py
import requests
from bs4 import BeautifulSoup
raw = requests.get("https://tv.naver.com/r")
print(raw.text)

실행결과 <!DOCTYPE html> <html lang="ko"> <head> <title>네이버TV</title> ...(생략)

실행결과를 살펴보면 소스코드가 출력된 것을 알 수 있습니다. get함수를 사용하면 응답성공여부 뿐만 아니라 응답시간, 쿠키, 응답시간 등 여러가지 데이터를 가져와서 저장합니다. 기본적으로는 응답성공여부를 보여주지만 .text를 입력하면 해당하는 HTML코드를 확인할 수 있습니다.

변수 = requests.get("URL주소")

requests의 get함수는 해당 URL주소에 접속을 요청하여 여러가지 데이터를 가져옵니다. (응답실패여부, 소스코드, url주소, 쿠키… 등)

변수(기본) - 응답성공여부 // <response[200]>

변수.text - 접속한 URL의 소스코드(HTML) // <head></head>...

변수.elapsed - 페이지가 응답하는데 걸리는 시간 // 0:00:00.04452

HTML소스코드 파싱하기 - BeautifulSoup 사용하기

이번에는 BeautifulSoup을 사용해 봅시다. 아래와 같이 소스코드를 수정해서 실행해봅니다.

week3_1.py
import requests
from bs4 import BeautifulSoup
raw = requests.get("https://tv.naver.com/r")
# print(raw.text)
# 소스코드 출력부분을 주석처리해줍시다!
html = BeautifulSoup(raw.text, "html.parser")
print(html)

실행결과 <!DOCTYPE html> <html lang="ko"> <head> <title>네이버TV</title> ...(생략)

html이라는 변수에 BeautifulSoup의 괄호 안에는 소스코드(raw.text)와, "html.parser"가 들어갑니다. 그리고 html을 출력해보면 raw.text를 출력했을 때와 동일하게 소스코드가 출력된 것을 확인할 수 있습니다.

그럼 BeautifulSoup이 하는 역할은 무엇일까요?

raw.text에 저장되어있는 소스코드
BeautifulSoup을 통해 파싱한 소스코드

BeautifulSoup함수는 html소스코드를 태그 기준으로 파싱해주는 일을 합니다. 쉽게 말하면 BeautifulSoup을 사용해줘야 선택자를 사용할 수 있습니다.

raw.text에 저장된 소스코드는 단순한 문자열일뿐이므로 컴퓨터에게 class가 name인 h3 태그를 찾아줘!!라는 명령을 해도 이해할 수 없습니다. BeautifulSoup을 씌워주면 html소스코드를 태그기준으로 잘라주어 class가 name인 h3 태그를 찾아줘!!라는 명령을 했을 때 김코끼리라는 데이터를 찾을 수 있게됩니다.

requests.get을 사용해서 소스코드를 저장하고, BeautifulSoup을 사용해서 html 파싱까지 완료하면 본격적으로 데이터를 수집할 수 있습니다.

데이터수집 과정 이해하기(1-3위)

파이썬 프로그래밍을 통해 네이버TV TOP100 영상의 데이터를 수집하기 전에 데이터수집 과정에 대해서 한 번 살펴봅시다. 데이터를 선택하기 위한 선택자를 찾는 방법은 1주차 강의자료를 참고해주세요. 네이버TV TOP100 선택자 찾기

#1. 컨테이너 수집

1주차에서 잠시 다루었던 것처럼 네이버TV TOP100과 같이 한페이지에 여러가지 반복되는 데이터를 수집하기 위해서는 컨테이너를 먼저 수집해야합니다.

컨테이너는 수집하려는 데이터를 모두 포함하고 있는 영역입니다. 1-3위 영상 수집의 경우 3개 영상을 수집해야하므로 수집하는 컨테이너의 수 역시 3개가 되야합니다.

컨테이너 수집을 통해서 향후에 세부 데이터(제목, 채널명, 재생수, 좋아요수)의 연결성을 확보할 수 있습니다.

1-3위 영상 컨테이너를 선택하는 선택자: div.inner

#2. 영상(컨테이너)별 데이터 수집

컨테이너를 수집한 후에는 우리가 결과적으로 수집하려고하는 컨테이너 내부의 세부데이터(제목, 채널명, 재생수, 좋아요수)를 수집합니다.

컨테이너 안의 세부 데이터 제목: dt.title 채널명: dd.chn 재생수: span.hit 좋아요수: span.like

#3. 수집반복(컨테이너)

#2. 영상별 데이터수집 과정을 #1. 컨테이너 수집에서 수집한 컨테이너에 대해서 모두 반복합니다. 이 과정을 통해서 원하는 모든 영상에 대한 세부 데이터를 수집할 수 있습니다.

파이썬으로 데이터 수집하기

#1. 컨테이너 수집

데이터 수집의 첫번째 단계인 컨테이너 수집을 코드로 구현해봅니다.

week3_1.py
import requests
from bs4 import BeautifulSoup
raw = requests.get("https://tv.naver.com/r")
html = BeautifulSoup(raw.text, "html.parser")
clips = html.select("div.inner")
print(clips[0])

실행결과 <div class="inner"> <a class="box" ...> ...(생략)

코드를 살펴보면 선택자를 사용해서 원하는 데이터를 선택하기 위해서 select함수를 사용합니다. select함수의 괄호 안에 "선택자"를 넣어주면 선택자가 가리키는 모든 데이터를 선택하여 리스트형식으로 저장해주는 함수입니다.

네이버TV 1-3위 영상을 가리키는 선택자 div.inner를 넣어주면 clips에 1-3위 영상에 대한 데이터가 리스트 형태로 저장되게됩니다. 위 코드처럼 clips[0]을 출력해주면, 1위 영상에 대한 html소스코드 데이터를 출력해줍니다.

*select함수는 BeautifulSoup4에 포함된 함수이기 때문에 html로 파싱한 소스코드가 들어있는 변수에 사용해줘야합니다.

변수 = BS.select("선택자")

괄호 안의 선택자가 가리키는 데이터를 모두 가져옵니다.

가져온 데이터는 리스트 형식으로 저장됩니다.

*BS: html.parser로 파싱된 소스코드

#2. 영상별 데이터 수집

#1에서 수집한 1위 영상의 컨테이너에서 제목 데이터를 수집해봅시다.

week3_1.py
import requests
from bs4 import BeautifulSoup
raw = requests.get("https://tv.naver.com/r")
html = BeautifulSoup(raw.text, "html.parser")
clips = html.select("div.inner")
# print(clips[0])
# 컨테이너 소스코드는 주석처리해줍니다.
title = clips[0].select_one("dt.title")
print(title)

실행결과 <dt class="title"> <a href="https://tv.naver.com/v/5172177" ... ...> <strong class="tit"><span>눈물 쏙 들어가게 만드는 ... </span></strong> </a> </dt>

select함수가 괄호 안의 선택자에 해당하는 모든 데이터를 저장해줬다면, select_one은 괄호 안의 선택자에 해당하는 데이터 중 첫번째 데이터 하나만 저장하는 함수입니다. 데이터를 수집하려는 소스코드 안에 데이터가 하나만 있다면 select함수를 통해 데이터를 수집(리스트)하는 것보다 select_one함수를 통해 데이터를 수집(값)하는 것이 더욱 효율적입니다.(인덱싱을 해줄 필요가 없습니다.)

1위 영상 소스코드에 제목은 하나밖에 없으므로 select_one함수를 통해서 데이터를 선택해줍니다.

변수 = BS.select_one("dt.title")

괄호 안의 선택자가 가리키는 데이터 중 첫번째 데이터만 가져옵니다.

가져온 데이터는 값으로 저장됩니다.

*BS: html.parser로 파싱된 소스코드

그런데 위 소스코드의 실행결과를 살펴보면 우리가 원하는 데이터(제목 텍스트)가 아니라 제목에 해당하는 소스코드 전체(태그 포함)가 출력되어 있습니다. 아래와 같이 소스코드를 수정하여 텍스트 데이터만 추출합니다.

week3_1.py
import requests
from bs4 import BeautifulSoup
raw = requests.get("https://tv.naver.com/r")
html = BeautifulSoup(raw.text, "html.parser")
clips = html.select("div.inner")
# print(clips[0])
# 컨테이너 소스코드는 주석처리해줍니다.
title = clips[0].select_one("dt.title")
print(title.text)

실행결과 눈물 쏙 들어가게 만드는 뉴 하우스를 소개합니다☆

제목 데이터가 저장되어있는 title변수 뒤에 .text를 붙여주면 소스코드에서 태그가 삭제되고 텍스트 데이터만 추출할 수 있습니다.

그런데 실행결과를 살펴보면 제목 데이터 앞뒤에 공백이 함께 출력된 것을 알 수 있습니다. 이 공백은 2주차에 배운 문자열 strip()함수를 통해 쉽게 제거할 수 있습니다.

같은 방법으로 채널명, 재생수, 좋아요수를 수집하여 공백까지 삭제할 수 있습니다.

week3_1.py
import requests
from bs4 import BeautifulSoup
raw = requests.get("https://tv.naver.com/r")
html = BeautifulSoup(raw.text, "html.parser")
clips = html.select("div.inner")
# print(clips[0])
# 컨테이너 소스코드는 주석처리해줍니다.
# 데이터 수집부분
title = clips[0].select_one("dt.title")
chn = clips[0].select_one("dd.chn")
hit = clips[0].select_one("span.hit")
like = clips[0].select_one("span.like")
# 수집결과 출력부분
print(title.text.strip())
print(chn.text.strip())
print(hit.text.strip())
print(like.text.strip())

실행결과 눈물 쏙 들어가게 만드는 뉴 하우스를 소개합니다☆ 나 혼자 산다 재생 수477,082 좋아요 수2,339

#3. 수집반복(컨테이너)

#2까지의 코드는 1위 영상에 대한 세부데이터를 수집한 코드입니다. 1-3위 영상에 대한 데이터를 모두 수집하기 위해서는 1-3위 영상의 컨테이너를 반복하며 실행해야합니다. 2주차에서 다룬 반복문을 활용하면 쉽게 컨테이너를 반복시킬 수 있습니다.

범위(range())를 통해 반복하는 경우

week3_1.py
import requests
from bs4 import BeautifulSoup
raw = requests.get("https://tv.naver.com/r")
html = BeautifulSoup(raw.text, "html.parser")
clips = html.select("div.inner")
# print(clips[0])
# 컨테이너 소스코드는 주석처리해줍니다.
for i in range(len(clips)):
title = clips[i].select_one("dt.title")
chn = clips[i].select_one("dd.chn")
hit = clips[i].select_one("span.hit")
like = clips[i].select_one("span.like")
print(title.text.strip())
print(chn.text.strip())
print(hit.text.strip())
print(like.text.strip())

리스트(clips)를 통해 반복하는 경우

week3_1.py
import requests
from bs4 import BeautifulSoup
raw = requests.get("https://tv.naver.com/r")
html = BeautifulSoup(raw.text, "html.parser")
clips = html.select("div.inner")
# print(clips[0])
# 컨테이너 소스코드는 주석처리해줍니다.
for cl in clips:
title = cl.select_one("dt.title")
chn = cl.select_one("dd.chn")
hit = cl.select_one("span.hit")
like = cl.select_one("span.like")
print(title.text.strip())
print(chn.text.strip())
print(hit.text.strip())
print(like.text.strip())

범위와 리스트 편한 방법으로 컨테이너를 반복시키며 데이터를 수집하면 1-3위 모든 영상에 대한 데이터를 수집할 수 있습니다.

파이참 사용 TIP)

들여쓰기를 하려는 줄을 모두 선택(드래그)한 후 tab키를 누르면 선택된 줄이 한 번에 들여쓰기 됩니다. 반대는 shift+tab