본문 바로가기

Data Handling/Data Collecting

beautifulsoup - 부킹닷컴 호텔 이름, 평점 크롤링

순서

  1. url 준비
  2. requests를 사용하여 url로부터 정보 가져오기
  3. beautifulsoup을 사용하여 html 형식으로 파싱하기
  4. 원하는 정보  지정해서 담기
  5. 문자열만 추출

 

 


 

 

0. 라이브러리 

import requests # url에서 정보 요구!!
from bs4 import BeautifulSoup # 이걸로 가져온 정보를 정리할거야

 

 

1. url 준비

url = '찾고자 하는 정보가 포함된 페이지 url'

 

 

2. requests를 사용하여 url로부터 정보 가져오기

content = requests.get(url).content # url로부터 정보(content)를 가져와

url에 담긴 정보가 이렇게나 많습니다.

이걸 컴퓨터가 이해하기 쉽게 정리하는 작업을 beautifulsoup이 도와줍니다.

 

 

3. beautifulsoup을 사용하여 html 형식으로 파싱하기

컴퓨터가 이해하기 쉬운 형태(html)로 변환(parsing)하는 개념. 
soup = BeautifulSoup(content, 'html.parser')

훨씬 깔끔해졌죠?

이 많은 정보 중에서 제가 원하는 정보만 가져오겠습니다.

 

 

4. 원하는 정보 지정해서 담기

# 가장 먼저 나오는 하나만 찾아라 : find()
names = soup.find_all(attrs={'class' : 'sr-hotel__name'}) # 호텔 이름
rates = soup.find_all(attrs={'class' : 'bui-review-score__badge'}) # 호텔 평점

names
rates

 

잠깐!

'sr-hotel__name' 이게 호텔 이름을 뜻하는 클래스인지 어떻게 알았을까요?

 

부킹닷컴 화면으로 돌아가서 F12를 눌러보시면 개발자도구 창이 생성됩니다.

지금 보여지는 화면이 어떤 구조로 되어있는지 확인할 수 있는 공간인데요,

개발자 도구창의 왼쪽 모서리에 마우스 커서 모양을 클릭하시고 크롤링하고자 하는 정보에 마우스를 가져가면

해당 정보를 정의하고 있는 class 값 혹은 id 값을 찾을 수 있습니다.

 

이렇게 말이죠 !!

 

 

5. 문자열만 추출

마지막으로 필요한 문자열만 추출하면 끝입니다.

print(soup.find_all(attrs={'class':'sr-hotel__name'})[0].text)
print(soup.find_all(attrs={'class':'bui-review-score__badge'})[0].text)

예시로 리스트의 첫번째 요소만 추출했습니다.

이제 for문을 사용해서 모든 정보를 가져오는건 식은죽 먹기죠?

 

 


 

결론

 

이번 포스팅은 beautifulsoup이 어떻게 작동하는지, 간단한 뼈대를 집중해서 분석해보았습니다.

하지만 실제 유의미한 데이터로 사용하기 위해서는 

체크인/아웃 날짜, 호텔이름, 평점, 인원 수, 객실 유형, 가격, 할인율 등 더 많은 정보를 필요로 합니다.

 

이에 따라 객실이 매진되어 가격정보가 사라지는 경우, 

주말에는 2박 이상만 판매하는 호텔의 경우 등

다양한 예외사항에 대해서 고민하고 분석해야 할 필요가 있습니다.

 

실제로 제가 게스트하우스에서 일을 하며 경쟁사들의 객실 가격을 실시간으로 파악하기 위해 코드를 짰었는데,

에러가 발생할때마다 수정해주며 정말 많이 보완해주었던 기억이 있습니다.

특히 가격 정보는 beautifulsoup으로 크롤링되지 않았었는데, 그 이유를 찾는데 30시간 이상 투자한 것 같습니다.

이것과 관련해서는 selenium을 활용한 크롤링 편에서 설명해보겠습니다.

 

 

 

 


 

 

 

전체 코드 입니다.

import requests
from bs4 import BeautifulSoup
import pandas as pd

url = 'https://www.booking.com/searchresults.ko.html?label=gen173nr-1FCAEoggI46AdIM1gEaH2IAQGYARe4ARfIAQzYAQHoAQH4AQuIAgGoAgO4AsLN3PYFwAIB0gIkZTkwMGZhZWMtY2YzZS00M2UwLTkxMDMtNDFkYjQzOTVjNzZh2AIG4AIB&sid=fcf64615c790e8ee11e9baa9f0175b1c&sb=1&sb_lp=1&src=index&src_elem=sb&error_url=https%3A%2F%2Fwww.booking.com%2Findex.ko.html%3Flabel%3Dgen173nr-1FCAEoggI46AdIM1gEaH2IAQGYARe4ARfIAQzYAQHoAQH4AQuIAgGoAgO4AsLN3PYFwAIB0gIkZTkwMGZhZWMtY2YzZS00M2UwLTkxMDMtNDFkYjQzOTVjNzZh2AIG4AIB%3Bsid%3Dfcf64615c790e8ee11e9baa9f0175b1c%3Bsb_price_type%3Dtotal%26%3B&ss=%EB%AA%85%EB%8F%99%2C+%EC%84%9C%EC%9A%B8%2C+%EB%8C%80%ED%95%9C%EB%AF%BC%EA%B5%AD&is_ski_area=&checkin_year=2020&checkin_month=7&checkin_monthday=23&checkout_year=2020&checkout_month=7&checkout_monthday=24&group_adults=2&group_children=0&no_rooms=1&map=1&b_h4u_keep_filters=&from_sf=1&ss_raw=%EB%AA%85%EB%8F%99&ac_position=0&ac_langcode=ko&ac_click_type=b&dest_id=2000&dest_type=district&place_id_lat=37.5607331089608&place_id_lon=126.985566718492&search_pageview_id=13161f61126b001a&search_selected=true&search_pageview_id=13161f61126b001a&ac_suggestion_list_length=5&ac_suggestion_theme_list_length=0#map_closed'
content = requests.get(url).content
soup = BeautifulSoup(content, 'html.parser')

names = soup.find_all(attrs={'class':'sr-hotel__name'})
rates = soup.find_all(attrs={'class':'bui-review-score__badge'})

containers = []
for i in range(len(rates)):
    container = []
    container.append(names[i].text[1:-1])
    container.append(rates[i].text)
    
    containers.append(container)
    
df = pd.DataFrame(data = containers, columns = ['name','rate'])
df