파이썬으로 크롤링하기

파이썬으로 크롤링하기

크롤링이란?

사전적 의미로는 기어다니는 것을 뜻하는데, 웹 상을 돌아다니며 데이터를 수집하고 분류하는 것을 의미한다. 주로 인터넷 상의 HTML 페이지를 수집해서 분류하고 저장하는 것을 의미한다.

흔히 스크랩핑이라는 용어와 혼용해서 쓰는데 정확히 말하자면 웹 스크랩핑이라는 단어가 알맞다.

웹 크롤러는 "인터넷에 있는 웹페이지를 방문하여 데이터를 수집하는 일을 하는 프로그램"을 말한다.

하지만 요즘은 이것을 통틀어서 크롤링이라고 다들 부르는 것 같다. 파이썬을 이용하면 쉽게 크롤링을 할 수 있다.

크롤링 Tool

어떤 툴을 사용하든지 크롤링 행위 자체는 변함이 없다. 본인의 필요에 맞는 툴을 선택하여 사용하면 된다.

가장 일반적으로는 아래에서 사용해 볼 Beautiful soup, 자바 언어로 사용하는 JSoup, 브라우저를 이용하는 Selenium 등이 있다.

실습 예제

다음 예제를 통하여 실습해보자.

파이썬에서 크롤링을 하기 위해서는 requests라는 라이브러리와 BeautifulSoup이라는 라이브러리를 설치해주어야 한다.

웹 사이트는 HTML 형식으로 쓰여진 문서이다. 따라서 문서에 담긴 내용을 가져오도록 request, 즉 요청을 해야 한다. 파이썬에서는 이것을 쉽게 사용할 수 있게 하기 위해 requests라는 패키지를 제공해서 편리하게 사용 가능하다.

requests 패키지를 사용하여

브라우저를 켜지 않고 requests 요청으로 코드를 가져오고

beautifulsoup 패키지를 사용하여

가져온 html 코드 중 원하는 데이터를 추출하는 것이다.

파이참 기준으로 File -> Settings -> Python Interpreter에 들어간다.

사진에 보이는 + 버튼을 눌러 requests와 bs4 패키지를 검색하여 설치해준다.

설치 완료 후 ok 버튼을 눌러 설정을 저장해주면 크롤링을 할 준비는 다됬다.

네이버 영화 순위 페이지를 크롤링 해볼 것이다.

크롤링을 하기 위해 필수적으로 작성해야 하는 코드는 일단 아래와 같다.

import requests from bs4 import BeautifulSoup # 브라우저에서 엔터친 것 처럼 효과를 내주는것 headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36'} # 데이터 요청 후 받아오기 data = requests.get('https://movie.naver.com/movie/sdb/rank/rmovie.nhn?sel=pnt&date;=20200303', headers=headers) soup = BeautifulSoup(data.text, 'html.parser')

headers 부분의 코드는 기본적인 요청을 막아둔 웹 사이트들이 많아서 브라우저에서 엔터를 친 것 처럼 효과를 내어주는 코드이다.

requests.get('해당 부분에 데이터를 수집할 페이지 주소를 넣는다 ', headers = headers)

BeautifulSoup 함수

select : 여러 개 리스트

select_one : 하나만 가져오기

둘 다 사용해보자

해당 페이지에 접속해보자.

다음과 같은 화면이 뜰 것이다. 여기서 영화의 제목을 하나만 가져와보자.

영화 제목에 커서를 대고 마우스 우클릭 -> 검사 에 들어간다.

html 문서가 보이면서 한 부분에 파랗게 표시가 뜰 것이다. 해당 부분이 영화 제목에 해당하는 코드이다.

이제 이 코드의 선택자를 복사하는데 선택자는 쉽게 이해하려면 그냥 이 코드가 어느 위치에 있는지 알려주는 것이라 생각하면 된다. 복사해서 빈 곳에 붙여 넣어보자

#old_content > table > tbody > tr:nth-child(2) > td.title > div > a

요렇게 뜰 것이다. 이것을 출력해보자.

import requests from bs4 import BeautifulSoup # 브라우저에서 엔터친 것 처럼 효과를 내주는것 headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36'} # 데이터 요청 후 받아오기 data = requests.get('https://movie.naver.com/movie/sdb/rank/rmovie.nhn?sel=pnt&date;=20200303', headers=headers) soup = BeautifulSoup(data.text, 'html.parser') #old_content > table > tbody > tr:nth-child(2) > td.title > div > a title = soup.select_one('#old_content > table > tbody > tr:nth-child(2) > td.title > div > a') print(title)

이렇게 그린 북에 해당하는 html 구문이 잘 가져와 진 것을 볼 수 있다. 여기서 "그린 북"만 출력하려면 어떻게 해야 할까? 방법은 간단하다.

print(title.text)

.text 를 해주면 해당 텍스트만 가져오게 된다.

이제 여러 개를 가져와보자. 여러 개를 가져오기 위해 먼저 규칙성을 찾아보자. 2순위의 영화제목의 선택자를 복사해서 가져오자

#1순위 그린북 #old_content > table > tbody > tr:nth-child(2) > td.title > div > a #2순위 가버나움 #old_content > table > tbody > tr:nth-child(3) > td.title > div > a

규칙을 찾는 것이 중요한데, 보면 tr부터 인덱스만 달라지며 같은 것을 알 수 있다. 그럼 위치를 알았으니 select를 이용해서 리스트로 가져와보자.

import requests from bs4 import BeautifulSoup # 브라우저에서 엔터친 것 처럼 효과를 내주는것 headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36'} # 데이터 요청 후 받아오기 data = requests.get('https://movie.naver.com/movie/sdb/rank/rmovie.nhn?sel=pnt&date;=20200303', headers=headers) soup = BeautifulSoup(data.text, 'html.parser') #1순위 그린북 #old_content > table > tbody > tr:nth-child(2) > td.title > div > a #2순위 가버나움 #old_content > table > tbody > tr:nth-child(3) > td.title > div > a title_list = soup.select('#old_content > table > tbody > tr') print(title_list)

경로를 tr까지만 적어주면 이 하위에 있는 것들을 리스트로 가져올 수 있다.

출력으로 확인해보면

하위에 있는 코드들을 가져왔더니 영화 제목과 함께 평점 등 다른 것들도 끼어있다. 이것은 반복문을 이용하여 해결할 수 있다. 많은 하위 코드들 중 제목에 해당하는 것은 뭘까? 다시 위에 그린 북과 가버나움의 선택자를 보자.

#1순위 그린북 #old_content > table > tbody > tr:nth-child(2) > td.title > div > a #2순위 가버나움 #old_content > table > tbody > tr:nth-child(3) > td.title > div > a

nth-child(index)여기만 다르고 뒷 부분의 td.title > div > a 부분은 같은 것을 확인할 수 있다. 여기가 제목이구나 짐작할 수 있는 것이다.

그럼 반복문을 돌면서 영화 제목에 해당하는 "td.title > div > a"이 부분들만 가져오면 해결 가능하다.

import requests from bs4 import BeautifulSoup # 브라우저에서 엔터친 것 처럼 효과를 내주는것 headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36'} # 데이터 요청 후 받아오기 data = requests.get('https://movie.naver.com/movie/sdb/rank/rmovie.nhn?sel=pnt&date;=20200303', headers=headers) soup = BeautifulSoup(data.text, 'html.parser') #1순위 그린북 #old_content > table > tbody > tr:nth-child(2) > td.title > div > a #2순위 가버나움 #old_content > table > tbody > tr:nth-child(3) > td.title > div > a title_list = soup.select('#old_content > table > tbody > tr') for title in title_list: a_tag = title.select_one('td.title > div > a') print(a_tag.text)

이렇게 코드를 작성해볼 수 있다. 하지만 이 코드를 돌리면 에러가 날 것이다.

NoneType 객체에서는 text를 가져올 수 없다.. 뭐 이런 내용인데 사실 생각해보면 당연하다. title_list의 코드 중에서는 td.title > div > a 선택자에 맞지 않는 코드들도 많을 것이다. 해당 선택자를 찾지 못할 시 select_one 함수는 None을 반환할 것이고 None에서 text를 추출하려고 하니 에러가 날 수 밖에..

이것도 간단하게 해결 가능하다.

import requests from bs4 import BeautifulSoup # 브라우저에서 엔터친 것 처럼 효과를 내주는것 headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36'} # 데이터 요청 후 받아오기 data = requests.get('https://movie.naver.com/movie/sdb/rank/rmovie.nhn?sel=pnt&date;=20200303', headers=headers) soup = BeautifulSoup(data.text, 'html.parser') #1순위 그린북 #old_content > table > tbody > tr:nth-child(2) > td.title > div > a #2순위 가버나움 #old_content > table > tbody > tr:nth-child(3) > td.title > div > a title_list = soup.select('#old_content > table > tbody > tr') for title in title_list: a_tag = title.select_one('td.title > div > a') if a_tag is not None: print(a_tag.text)

그냥 a_tag변수가 None이 아닐때만 출력을 하도록 코드를 작성해주면 간단하게 해결 가능하다.

50위 울지마 톤즈까지 제목이 잘 출력 되는 것을 볼 수 있다.

조금 더 응용해보자면...

import requests from bs4 import BeautifulSoup # 브라우저에서 엔터친 것 처럼 효과를 내주는것 headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36'} # 데이터 요청 후 받아오기 data = requests.get('https://movie.naver.com/movie/sdb/rank/rmovie.nhn?sel=pnt&date;=20200303', headers=headers) soup = BeautifulSoup(data.text, 'html.parser') # 영화제목 - 그린북 #old_content > table > tbody > tr:nth-child(2) > td.title > div > a # 순위 - 그린북 #old_content > table > tbody > tr:nth-child(2) > td:nth-child(1) > img # 평점 - 그린북 #old_content > table > tbody > tr:nth-child(2) > td.point title_list = soup.select('#old_content > table > tbody > tr') for title in title_list: rank = title.select_one('td:nth-child(1) > img') a_tag = title.select_one('td.title > div > a') a_point = title.select_one('td.point') if a_point is not None and a_tag is not None: print(rank['alt'], a_tag.text, a_point.text)

이렇게 순위랑 평점까지 같이 추출해볼 수 있다. 여기서 rank는 왜 rank['alt']일까?

# 순위 - 그린북 #old_content > table > tbody > tr:nth-child(2) > td:nth-child(1) > img title_list = soup.select('#old_content > table > tbody > tr') for title in title_list: rank = title.select_one('td:nth-child(1) > img') print(rank)

보면 img 태그의 alt 속성에 해당하기 때문에.. alt속성의 값을 불러오려면 rank['alt']를 해주면 순위가 불러와진다! 이런 식으로 크롤링은 규칙을 잘 찾아서 적절하게 데이터를 추출하면 된다.

from http://diddl.tistory.com/10 by ccl(A) rewrite - 2021-10-11 00:27:50