크롤링이란?
- 웹 페이지의 Contents를 수집하고 필요한 데이터를 추출하는 것
BeautifulSoup
- HTML 및 XML 문서를 구문 분석하기 위한 Python 패키지
- 페이지에 대한 트리 작성
- 트리에서 필요한 정보를 추출하여 사용
목표
- 밥대생(대학교 식단 제공 사이트)에서 식단 정보를 수집 후 출력
본 포스트에서는 공주대학교 천안캠퍼스 학생생활관 식단을 기준으로 크롤링을 진행한다.
위의 웹페이지에서 파란색 박스에 있는 날짜 및 식단 정보를 수집해보자.
요소 추출 방법
추출을 원하는 영역에 마우스 커서를 올리고 오른쪽 버튼 클릭 후 검사 클릭
<div data-v-45a7895a="" class="card-title"> 북어해장국 치킨너겟 명엽채볶음 참나물무침 식혜/깍두기</div>
현재 지정한 영역을 살펴보면 div 태그로 둘러쌓여있으며 card-title 이라는 class 에 포함되어 있는 것을 확인할 수 있다.
해당 영역의 정보를 BeautifulSoup를 이용하여 얻어보자.
페이지 정보 얻어오기
가장 먼저 페이지에 있는 HTML 요소를 얻어오기 위하여 url에 request를 보내서 정보를 얻어온다.
from bs4 import BeautifulSoup as bs
import requests
#Get elemnets from url
url = "https://bds.bablabs.com/restaurants/MTA5NDAwMjU=?campus_id=wqNWwIvBVE"
page = requests.get(url)
data = page.text
soup = bs(data, 'html.parser')
실행 결과 정보를 url에 대하여 정보를 받아온 것을 확인할 수 있다.
이 데이터 중에서 우리가 필요한 것을 추출해보도록 하자.
특정 데이터 추출
위에서 언급하였던 것처럼 우리는 class 를 기준으로 요소를 추출할 것이다.
필요한 데이터는 3가지이다.
- 날짜 (20xx년 x월 x일)
- 시간 (아침 / 점심 / 저녁)
- 식단
아래는 각각의 데이터에 대하여 태그를 검사하여 필터링 기준을 결정한 것이다.
날짜 - class=”data-title” 시간 - span 태그 식단 - class=”card-title”
아래는 위 기준을 이용하여 정보를 추출하는 코드이다.
#Get elements with matching class name
def match_class(target):
def do_match(tag):
classes = tag.get('class', [])
return all(c in classes for c in target)
return do_match
#get date and food list
dates = soup.find_all(match_class(["date-title"]))
times = soup.find_all("span")
times = times[3:]
foods = soup.find_all(match_class(["card-title"]))[1:]
시간 같은 경우 모든 span 태그를 가져오기 때문에 필요없는 부분을 버리는 작업을 거쳤다.
일치하는 class명을 가지는 요소를 찾기 위하여 match_class 함수를 정의하였다.
이후 find_all 함수를 이용하여 해당 클래스를 가지고 있는 모든 요소를 추출하였으며 시간(times)은 span 태그를 가지는 모든 요소를 추출하였다.
실행 결과를 확인하면 정보뿐만이 아니라 여러가지 태그가 섞여있는 것을 확인할 수 있다.
정보 가공 및 출력하기
위처럼 복잡한 문자열에서 필요한 부분만 뽑아내보도록 하자.
각각의 문자열을 살펴보면 아래와 같은 구조로 이루어져있는 것을 확인할 수 있다.
<div class="date-title" data-v-de55ce24="">9월 17일 (오늘, 화)</div>
자세하게 살펴보면 div 태그로 둘러쌓여 있기 때문에 “>”와 “<” 문자를 기준으로 나눈다면 그 사이에 우리가 원하는 문자열이 위치해있다는 것을 알 수 있다.
이제 “>”와 “<”을 기준으로 그 사이에 있는 문자열만 추출한다면 원하는 정보를 얻을 수 있다는 사실을 알 수 있다.
data = str(data).split(">")[1].split("<")[0]
이와 같은 코드를 작성 후 실행해보면
정상적으로 동작하는 것을 확인할 수 있다.
이를 이용하여 아래와 같은 코드를 완성하였다.
#Get elements with matching class name
def match_class(target):
def do_match(tag):
classes = tag.get('class', [])
return all(c in classes for c in target)
return do_match
from bs4 import BeautifulSoup as bs
import requests
#Get elemnets from url
url = "https://bds.bablabs.com/restaurants/MTA5NDAwMjU=?campus_id=wqNWwIvBVE"
page = requests.get(url)
data = page.text
soup = bs(data, 'html.parser')
#get date and food list
dates = soup.find_all(match_class(["date-title"]))
times = soup.find_all("span")
times = times[3:]
foods = soup.find_all(match_class(["card-title"]))[1:]
#Print element of dates and foods
for i in range(len(foods)):
if i%3 == 0:
print()
date = str(dates[int(i/3)]).split(">")[1].split("<")[0]
print(date)
print(str(times[i]).split(">")[1].split("<")[0])
food = str(foods[i]).split(">")[1].split("<")[0]
food = food.replace("&", "&")
print(food)
출력 결과
추후에 이를 이용한 카카오톡 챗봇 제작 or 라즈베리 파이를 이용한 웹서버를 구상중에 있다.