이 튜토리얼에서는 다음을 확인할 수 있습니다:
- 예약 스크레이퍼의 정의
- 이를 통해 추출 가능한 데이터
- Python으로 Booking.com 스크래핑 스크립트 구축 방법
자, 시작해 보겠습니다!
예약 스크래퍼란 무엇인가요?
Booking.com 스크레이퍼는 Booking.com 페이지에서 데이터를 자동으로 추출하는 도구입니다. 이를 통해 호텔 이름, 가격, 리뷰, 평점, 편의시설, 예약 가능 여부 등 숙소 목록 페이지의 정보를 가져올 수 있습니다. 이 데이터는 시장 분석, 가격 비교, 여행 관련 데이터셋 구축 등 다양한 목적으로 활용될 수 있습니다.
Booking.com에서 스크래핑할 수 있는 데이터
아래는 Booking.com에서 추출할 수 있는 데이터 항목 목록입니다:
- 숙소 상세 정보: 호텔명, 주소, 주요 지점(예: 시내 중심가, 다운타운 등)까지의 거리
- 가격 정보: 정상 가격, 할인 가격(해당 시)
- 리뷰 및 평점: 리뷰 점수, 리뷰 수, 게스트 피드백
- 예약 가능 여부: 이용 가능한 객실 유형, 예약 옵션(예: 무료 취소, 조식 포함), 예약 가능한 날짜
- 미디어: 숙소 이미지, 객실 이미지
- 편의시설: 제공 시설(예: Wi-Fi, 주차장, 수영장), 객실별 편의시설
- 프로모션: 특별 할인 또는 혜택, 기간 한정 특가
- 정책: 취소 정책, 체크인 및 체크아웃 시간
- 추가 정보: 숙소 설명, 인근 관광지, 특정 날짜에 이용 가능한 객실 수
Python으로 Booking.com 스크래핑하기: 단계별 가이드
이 가이드 섹션에서는 Booking.com 스크레이퍼를 구축하는 방법을 배웁니다.
목표는 숙소 목록 페이지에서 데이터를 자동으로 수집하는 Python 스크립트를 만드는 것입니다:

아래 단계를 따르세요!
1단계: 프로젝트 설정
시작하기 전에 컴퓨터에 Python 3이 설치되어 있는지 확인하세요. 설치되어 있지 않다면 다운로드하여 실행 파일을 실행하고 설치 마법사를 따라 설치하세요.
이제 아래 명령어를 사용하여 프로젝트 폴더를 생성하세요:
mkdir booking-scraper
booking-scraper 디렉터리는 Python Booking.com 스크래핑 스크립트의 프로젝트 폴더를 나타냅니다.
이 디렉터리로 이동하여 가상 환경을 초기화하세요:
cd booking-scraper
python -m venv env
선호하는 Python IDE에서 프로젝트 폴더를 로드하세요. Python 확장 프로그램이 설치된 Visual Studio Code와 PyCharm Community Edition 모두 훌륭한 선택입니다.
프로젝트 폴더 내에 scraper.py 파일을 생성하세요. 이 파일은 다음과 같은 구조를 가져야 합니다:

scraper.py는 현재 빈 Python 스크립트이지만 곧 스크래핑 로직이 포함될 것입니다.
IDE 터미널에서 가상 환경을 활성화하세요. Linux 또는 macOS에서는 다음 명령어를 실행합니다:
./env/bin/activate
Windows에서는 다음과 같이 실행하세요:
env/Scripts/activate
이제 웹 스크래핑을 위한 Python 환경이 준비되었습니다!
2단계: 스크래핑 라이브러리 선택
Booking.com이 정적 사이트인지 동적 사이트인지 판단하고 그에 맞는 스크래핑 라이브러리를 선택할 차례입니다. 사이트의 동작을 관찰하여 확인할 수 있습니다. 브라우저에서 Booking.com을 열고 검색을 수행한 후 숙소 페이지로 이동하세요:

스크롤을 내릴수록 페이지가 동적으로 새 데이터를 불러오는 것을 확인할 수 있습니다:

이 패턴은 무한 스크롤링으로 알려져 있으며 동적 사이트의 특징입니다. 동적 사이트에서 웹 스크래핑을 수행하는 방법에 대해 자세히 알아보세요.
서버가 반환한 문서의 HTML 코드를 분석하거나 개발자 도구(DevTools)의 네트워크 탭을 확인하는(사이트가 정적인지 판단하는 두 가지 일반적인 단계) 과정 없이도 Booking.com이 동적 사이트임을 이미 결론지을 수 있습니다.
동적 콘텐츠 사이트를 스크래핑하는 최선의 방법은 브라우저 자동화 도구를 사용하는 것입니다. 이러한 솔루션은 브라우저를 제어하고 페이지에서 특정 상호작용을 수행하여 데이터를 효과적으로 추출할 수 있게 합니다.
Python용 가장 강력한 브라우저 자동화 도구 중 하나인 Selenium은 Booking.com 스크래핑에 탁월한 선택입니다. 이 작업의 핵심 라이브러리가 될 것이므로 설치 준비를 하세요!
3단계: Selenium 설치 및 구성
파이썬에서는 Selenium을 selenium pip 패키지를 통해 사용할 수 있습니다. 활성화된 파이썬 가상 환경에서 다음 명령어로 설치하세요:
pip install selenium
도구 사용법에 대한 안내는 Selenium을 활용한 웹 스크래핑 튜토리얼을 참고하세요.
scraper.py 파일에 Selenium을 임포트하고 Chrome 인스턴스를 제어할 WebDriver 객체를 초기화하세요:
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
# Chrome 웹 드라이버 인스턴스 생성
driver = webdriver.Chrome(service=Service())
위 코드는 Chrome 브라우저를 제어하기 위해 Chrome WebDriver 인스턴스를 초기화합니다. Booking.com은 헤드리스 브라우저를 차단하는 안티 스크래핑 기술을 사용하는 것으로 보입니다. 따라서 --headless 플래그 설정을 피하십시오. 대안으로 Playwright Stealth 가이드를 참조하세요.
스크레이퍼의 마지막 줄에서 웹 드라이버를 반드시 종료하세요:
driver.quit()
훌륭합니다! 이제 Booking.com 스크래핑을 시작할 준비가 완전히 완료되었습니다.
4단계: 대상 페이지 방문
Booking.com 페이지에는 검색을 세분화할 수 있는 다양한 상호작용 기능이 제공됩니다:

Selenium으로 이러한 모든 상호작용을 프로그래밍 방식으로 시뮬레이션하는 것은 복잡하고 시간이 많이 소요됩니다. 따라서 작업을 단순화하고 속도를 높이기 위해 먼저 브라우저에서 수동으로 상호작용을 수행하세요.
관심 있는 검색 쿼리를 구성한 후, 브라우저 주소창에서 결과 페이지 URL을 복사하세요.
예를 들어, 위 URL은 11월 18일부터 12월 18일까지 성인 2명이 뉴욕에서 숙박할 아파트를 검색한 결과입니다.
이 URL을 복사하여 Selenium의 get() 메서드에 전달하세요:
driver.get("https://www.booking.com/searchresults.html?ss=New+York&ssne=New+York&ssne_untouched=New+York&label=gen173nr-1FCAEoggI46AdIM1gEaHGIAQGYATG4ARfIAQzYAQHoAQH4AQKIAgGoAgO4Aof767kGwAIB0gIkNGE2MTI1MjgtZjJlNC00YWM4LWFlMmQtOGIxZjM3NWIyNDlm2AIF4AIB&sid=b91524e727f20006ae00489afb379d3a&aid=304142&lang=en-us&sb=1&src_elem=sb&src=index&dest_id=20088325&dest_type=city&checkin=2024-11-18&checkout=2024-12-18&group_adults=2&no_rooms=1&group_children=0")
스크래핑 스크립트가 원하는 Booking.com 페이지에 자동으로 연결됩니다.
scraper.py 파일에는 이제 다음과 같은 코드 줄이 포함됩니다:
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
# Chrome 웹 드라이버 인스턴스 생성
driver = webdriver.Chrome(service=Service())
# 대상 페이지에 연결
driver.get("https://www.booking.com/searchresults.html?ss=New+York&ssne=New+York&ssne_untouched=New+York&label=gen173nr-1FCAEoggI46AdIM1gEaHGIAQGYATG4ARfIAQzYAQHoAQH4AQKIAgGoAgO4Aof767kGwAIB0gIkNGE2MTI1MjgtZjJlNC00YWM4LWFlMmQtOGIxZjM3NWIyNDlm2AIF4AIB&sid=b91524e727f20006ae00489afb379d3a&aid=304142&lang=en-us&sb=1&src_elem=sb&src=index&dest_id=20088325&dest_type=city&checkin=2024-11-18&checkout=2024-12-18&group_adults=2&no_rooms=1&group_children=0")
# 스크래핑 로직...
# 웹 드라이버 종료 및 리소스 해제
driver.quit()
마지막 줄에 디버깅 중단점을 설정하고 스크립트를 실행하세요. 아래와 같은 결과가 표시되어야 합니다:

“Chrome이 자동화된 테스트 소프트웨어에 의해 제어되고 있습니다.”라는 메시지는 Selenium이 Chrome에서 원하는 대로 작동하고 있음을 확인해 줍니다. 잘하셨습니다!
5단계: 로그인 알림 처리
브라우저에서 Booking.com을 처음 방문하면 사이트가 종종 20초 이내에 로그인 알림을 표시합니다. 이 알림은 페이지 콘텐츠 접근을 차단하여 웹 스크래핑을 더 어렵게 만듭니다:

이 경고와 상호작용하기 전까지는 해당 페이지의 콘텐츠에 접근할 수 없습니다.
경고를 처리하려면 Selenium을 사용하여 닫으세요. 닫기 버튼을 마우스 오른쪽 버튼으로 클릭하고 컨텍스트 메뉴에서 “검사” 옵션을 선택하세요:

다음 CSS 선택자로 버튼을 선택하여 모달을 닫을 수 있습니다:
[role="dialog"] button[aria-label="Dismiss sign-in info."]
이제 Selenium이 알림이 나타날 때까지 최대 10초 동안 대기하도록 지시하세요. 알림이 표시되면 닫기 버튼을 클릭하여 닫습니다. 모달이 항상 나타나지 않을 수 있으므로 이 로직을 try...except 블록으로 감싸는 것이 좋습니다:
try:
# 로그인 알림이 나타날 때까지 최대 20초 대기
close_button = WebDriverWait(driver, 20).until(
EC.presence_of_element_located((By.CSS_SELECTOR, "[role="dialog"] button[aria-label="Dismiss sign-in info."]"))
)
# 닫기 버튼 클릭
close_button.click()
except TimeoutException:
print("로그인 모달이 나타나지 않았습니다. 계속 진행합니다...")
WebDriverWait는 페이지에서 지정된 조건이 충족될 때까지 스크립트를 일시 중지하는 Selenium의 특수 클래스입니다. 위 예시에서는 페이지에 알림 닫기 버튼이 나타날 때까지 최대 10초 동안 대기합니다.
경고창이 나타나지 않으면 Selenium은 TimeoutException 예외를 발생시킵니다. 아래와 같이 WebDriverWait, EC, By와 함께 임포트하세요:
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
from selenium.common.exceptions import TimeoutException
좋습니다! 로그인 알림창은 더 이상 문제가 되지 않습니다.
6단계: Booking.com 항목 선택
스크래핑할 Booking.com 페이지에는 여러 항목이 포함되어 있습니다. 모든 항목을 스크래핑하려면 스크래핑된 데이터를 저장할 배열을 초기화하세요:
items = []
이제 해당 항목들과 연관된 HTML 요소를 선택하는 방법을 이해해야 합니다. 브라우저에서 Booking.com을 열고 검색을 수행한 후, 숙소 항목 중 하나를 검사해 보세요:

HTML 요소의 클래스가 무작위로 생성된 것처럼 보인다는 점에 유의하세요. 이는 사이트 배포 시마다 변경될 가능성이 높다는 뜻으로, 요소 선택에 신뢰할 수 없습니다. 대신 data-testid와 같은 더 안정적인 속성에 집중하세요.
data-* 속성은 웹 스크래핑에 탁월한 대상입니다.
Selenium의 find_elements() 메서드를 사용하여 페이지에 CSS 선택자를 적용하고 관심 있는 요소를 선택하세요:
property_items = driver.find_elements(By.CSS_SELECTOR, "[data-testid="property-card"]")
속성 항목을 반복 처리하고 Booking.com 스크레이퍼를 준비하여 데이터를 추출하세요:
for property_item in property_items:
# 스크래핑 로직...
훌륭합니다! 다음 단계는 이 요소들에서 데이터를 스크래핑하는 것입니다.
7단계: Booking.com 항목 스크래핑
페이지의 숙소 항목을 살펴보면 포함된 요소들이 일관되지 않음을 알 수 있습니다:

일부는 리뷰 점수가 있는 반면, 다른 일부는 없습니다. 마찬가지로 일부는 할인 가격이 있는 반면, 다른 일부는 없습니다.
이러한 차이점 때문에 모든 숙소 항목에 대해 일관된 스크래핑 로직을 작성하기 어렵습니다. 페이지에 존재하지 않는 요소를 선택하려고 하면 Selenium이 NoSuchElementException 예외를 발생시킵니다. 따라서 해당 시나리오를 처리하는 함수를 정의하는 것이 합리적입니다:
def handle_no_such_element_exception(data_extraction_task):
try:
return data_extraction_task()
except NoSuchElementException as e:
return None
위 함수는 람다 함수를 인수로 받아 실행을 시도합니다. NoSuchElementException이 발생하면 예외를 잡아 None을 반환합니다. 이를 통해 Booking.com 스크래핑 스크립트가 중단 없이 계속 진행될 수 있습니다.
NoSuchElementException 임포트:
from selenium.common import NoSuchElementException
모든 요소(리뷰 점수, 할인 가격 등)를 포함하는 속성 항목 검사:

다음과 같이 추출할 수 있습니다:
a[data-testid="property-card-desktop-single-image"]에서 속성 링크 추출img[data-testid=image]에서 속성 이미지 추출
for 루프 내에서 해당 요소들을 선택하고 데이터를 추출하는 현재 로직을 적용합니다:
url = handle_no_such_element_exception(lambda: property_item.find_element(By.CSS_SELECTOR, "a[data-testid="property-card-desktop-single-image"]").get_attribute("href"))
image = handle_no_such_element_exception(lambda: property_item.find_element(By.CSS_SELECTOR, "img[data-testid="image"]").get_attribute("src"))
find_element() 는 페이지에서 단일 노드를 선택하는 반면, get_attribute()는 지정된 HTML 속성 내부의 콘텐츠를 가져옵니다. 데이터 추출 지침은 NoSuchElementException을처리하기 위해 handle_no_such_element_exception으로 감싸져 있음을 유의하십시오.
마찬가지로, 제목 섹션과 그 바로 아래의 정보에 주목하세요:

여기서 다음을 얻을 수 있습니다:
[data-testid="title"]속성에서 제목[data-testid="address"]에서 address 속성[data-testid="distance"]속성에서 distance 속성
다음으로 모두 스크래핑하세요:
title = handle_no_such_element_exception(lambda: property_item.find_element(By.CSS_SELECTOR, "[data-testid="title"]").text)
address = handle_no_such_element_exception(lambda: property_item.find_element(By.CSS_SELECTOR, "[data-testid="address"]").text)
distance = handle_no_such_element_exception(lambda: property_item.find_element(By.CSS_SELECTOR, "[data-testid="distance"]").text)
텍스트 속성은 선택된 요소 내부의 텍스트를 포함합니다.
다음으로 리뷰 점수 노드에 집중합니다:

data-testid="review-score" 로 선택하고 텍스트를 추출합니다. 텍스트는 다음과 같은 특별한 형식을 가질 수 있습니다:
'평점 8.4n8.4n매우 좋음n120개 리뷰'
사용자 정의 로직을 통해 리뷰 점수와 리뷰 수를 추출할 수 있습니다:
review_score = None
review_count = None
review_text = handle_no_such_element_exception(lambda: property_item.find_element(By.CSS_SELECTOR, "[data-testid="review-score"]").text)
if review_text is not None:
# 리뷰 문자열을 n으로 분할
parts = review_text.split("n")
# 각 부분 처리
for part in parts:
part = part.strip()
# 이 부분이 숫자인지 확인 (잠재적 리뷰 점수)
if part.replace(".", "", 1).isdigit():
review_score = float(part)
# "reviews" 문자열 포함 여부 확인
elif "reviews" in part:
# "reviews" 앞의 숫자 추출
review_count = int(part.split(" ")[0].replace(",", ""))
설명 요소 타겟팅:

data-testid="recommended-units" 로 선택하고 설명을 추출:
description = handle_no_such_element_exception(lambda: property_item.find_element(By.CSS_SELECTOR, "[data-testid="recommended-units"]").text)
마지막으로 가격 요소에 집중:

data-testid="availability-rate-information" 요소에서 선택:
aria-hidden="true"속성이 있고data-testid속성이 없는 노드의 원래 가격data-testid="price-and-discounted-price"속성을 가진 요소의 할인/현재 가격
가격 추출 로직은 아래와 같이 작성합니다:
price_element = handle_no_such_element_exception(lambda: (property_item.find_element(By.CSS_SELECTOR, "[data-testid="availability-rate-information"]")))
if price_element is not None:
original_price = handle_no_such_element_exception(lambda: (
price_element.find_element(By.CSS_SELECTOR, "[aria-hidden="true"]:not([data-testid])").text.replace(",", "")
))
price = handle_no_such_element_exception(lambda: (
price_element.find_element(By.CSS_SELECTOR, "[data-testid="price-and-discounted-price"]").text.replace(",", "")
))
와! Booking.com 스크래핑 로직이 거의 완성되었습니다.
7단계: 스크래핑된 데이터 수집
이제 스크래핑된 데이터가 for 루프 내 여러 변수에 분산되어 있습니다. 새 item 객체를 생성하고 해당 데이터로 채운 후 items 배열에 추가하세요:
item = {
"url": url,
"image": image,
"title": title,
"address": address,
"distance": distance,
"review_score": review_score,
"review_count": review_count,
"description": description,
"original_price": original_price,
"price": price
}
items.append(item)
for 루프가 끝날 때 items에는 모든 스크래핑 데이터가 포함됩니다. items를 출력하여 확인하세요 :
print(items)
다음과 같은 출력이 생성됩니다:
[{'url': 'https://www.booking.com/hotel/us/murray-hill-east-manhattan.html?label=gen173nr-1FCAEoggI46AdIM1gEaHGIAQGYATG4ARfIAQzYAQHoAQH4AQKIAgGoAgO4Aof767kGwAIB0gIkNGE2MTI1MjgtZjJlNC00YWM4LWFlMmQtOGIxZjM3NWIyNDlm2AIF4AIB&sid=b91524e727f20006ae00489afb379d3a&aid=304142&ucfs=1&arphpl=1&checkin=2024-11-18&checkout=2024-12-18&dest_id=20088325&dest_type=city&group_adults=2&req_adults=2&no_rooms=1&group_children=0&req_children=0&hpos=1&hapos=1&sr_order=popularity&srpvid=c6926559ebaa0862&srepoch=1731939905&all_sr_blocks=5604802_204869446_2_0_0&highlighted_blocks=5604802_204869446_2_0_0&matching_block_id=5604802_204869446_2_0_0&sr_pri_blocks=5604802_204869446_2_0_0__523000&from=searchresults', 'image': 'https://cf.bstatic.com/xdata/images/hotel/square600/84564452.webp?k=ff50b7387e08e01ba7a400effa788e668f894cabe4a295f60d6cd018ec9ac4d0&o=', 'title': 'Murray Hill East Suites', 'address': 'Murray Hill, New York', 'distance': '다운타운에서 1.3마일', 'review_score': 8.2, 'review_count': 54, 'description': '스튜디오전체 스튜디오 • 욕실 1개 • 주방 1개 • 398 평방피트다양한 침대 유형', 'original_price': None, 'price': '$5230'},
# 생략...
, {'url': 'https://www.booking.com/hotel/us/renaissance-times-square.html?label=gen173nr-1FCAEoggI46AdIM1gEaHGIAQGYATG4ARfIAQzYAQHoAQH4AQKIAgGoAgO4Aof767kGwAIB0gIkNGE2MTI1MjgtZjJlNC00YWM4LWFlMmQtOGIxZjM3NWIyNDlm2AIF4AIB&sid=b91524e727f20006ae00489afb379d3a&aid=304142&ucfs=1&arphpl=1&checkin=2024-11-18&checkout=2024-12-18&dest_id=20088325&dest_type=city&group_adults=2&req_adults=2&no_rooms=1&group_children=0&req_children=0&hpos=12&hapos=12&sr_order=popularity&srpvid=c6926559ebaa0862&srepoch=1731939905&all_sr_blocks=2315604_274565698_0_2_0&highlighted_blocks=2315604_274565698_0_2_0&matching_block_id=2315604_274565698_0_2_0&sr_pri_blocks=2315604_274565698_0_2_0__1805400&from_sustainable_property_sr=1&from=searchresults', 'image': 'https://cf.bstatic.com/xdata/images/hotel/square600/437371642.webp?k=d1a06036e365573e326e6b0f1b045f8f43b6ad0d18e119cfb92d92cc81fa5c88&o=', 'title': 'Renaissance New York Times Square by Marriott', 'address': '맨해튼, 뉴욕', 'distance': '다운타운에서 0.6마일', 'review_score': 8.4, 'review_count': 2209, 'description': '킹룸(킹 사이즈 침대 1개)', 'original_price': '$20060', 'price': '$18054'}]
훌륭합니다! 이제 이 정보를 CSV와 같은 사람이 읽을 수 있는 파일로 내보내기만 하면 됩니다.
8단계: CSV로 내보내기
파이썬 표준 라이브러리에서 csv 패키지를 임포트합니다:
import csv
그런 다음, 이를 사용하여 항목들을 CSV 파일로 내보냅니다:
# 출력 CSV 파일 이름 지정
output_file = "properties.csv"
# 항목 목록을 CSV 파일로 내보내기
with open(output_file, mode="w", newline="", encoding="utf-8") as file:
# CSV 라이터 객체 생성
writer = csv.DictWriter(file, fieldnames=["url", "image", "title", "address", "distance", "review_score", "review_count", "description", "original_price", "price"])
# 헤더 행 작성
writer.writeheader()
# 각 항목을 CSV 행으로 작성
writer.writerows(items)
이 코드 조각은 items 배열의 데이터를 사용하여 properties.csv라는 CSV 파일을 채웁니다. 위에서 사용된 주요 함수는 다음과 같습니다:
open(): 지정된 파일을 UTF-8 인코딩으로 쓰기 모드에서 엽니다.csv.DictWriter(): 지정된 필드명으로 CSV 라이터 생성.writeheader(): 지정된 필드 이름을 기반으로 CSV 파일에 헤더 행을 작성합니다.writer.writerow(): 각 사전 항목을 CSV의 행으로 작성합니다.
9단계: 모든 것을 통합하기
scraper.py 파일에는 이제 다음 줄이 포함되어야 합니다:
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
from selenium.common.exceptions import TimeoutException
from selenium.common import NoSuchElementException
import csv
def handle_no_such_element_exception(data_extraction_task):
try:
return data_extraction_task()
except NoSuchElementException as e:
return None
# Chrome 웹 드라이버 인스턴스 생성
driver = webdriver.Chrome(service=Service())
# 대상 페이지 연결
driver.get("https://www.booking.com/searchresults.html?ss=New+York&ssne=New+York&ssne_untouched=New+York&label=gen173nr-1FCAEoggI46AdIM1gEaHGIAQGYATG4ARfIAQzYAQHoAQH4AQKIAgGoAgO4Aof767kGwAIB0gIkNGE2MTI1MjgtZjJlNC00YWM4LWFlMmQtOGIxZjM3NWIyNDlm2AIF4AIB&sid=b91524e727f20006ae00489afb379d3a&aid=304142&lang=en-us&sb=1&src_elem=sb&src=index&dest_id=20088325&dest_type=city&checkin=2024-11-18&checkout=2024-12-18&group_adults=2&no_rooms=1&group_children=0")
# 로그인 알림 처리
try:
# 로그인 알림이 나타날 때까지 최대 20초 대기
close_button = WebDriverWait(driver, 20).until(
EC.presence_of_element_located((By.CSS_SELECTOR, "[role="dialog"] button[aria-label="Dismiss sign-in info."]"))
)
# 닫기 버튼 클릭
close_button.click()
except e:
print("로그인 모달이 나타나지 않음, 계속 진행...")
# 스크랩된 데이터 저장 위치
items = []
# 페이지의 모든 부동산 항목 선택
property_items = driver.find_elements(By.CSS_SELECTOR, "[data-testid="property-card"]")
# 부동산 항목 반복 처리 및
# 데이터 추출
for property_item in property_items:
# 스크래핑 로직...
url = handle_no_such_element_exception(lambda: property_item.find_element(By.CSS_SELECTOR, "a[data-testid="property-card-desktop-single-image"]").get_attribute("href"))
image = handle_no_such_element_exception(lambda: property_item.find_element(By.CSS_SELECTOR, "img[data-testid="image"]").get_attribute("src"))
title = handle_no_such_element_exception(lambda: property_item.find_element(By.CSS_SELECTOR, "[data-testid="title"]").text)
address = handle_no_such_element_exception(lambda: property_item.find_element(By.CSS_SELECTOR, "[data-testid="address"]").text)
distance = handle_no_such_element_exception(lambda: property_item.find_element(By.CSS_SELECTOR, "[data-testid="distance"]").text)
review_score = None
review_count = None
review_text = handle_no_such_element_exception(lambda: property_item.find_element(By.CSS_SELECTOR, "[data-testid="review-score"]").text)
if review_text is not None:
# 리뷰 문자열을 n으로 분할
parts = review_text.split("n")
# 각 부분 처리
for part in parts:
part = part.strip()
# 이 부분이 숫자인지 확인 (잠재적 리뷰 점수)
if part.replace(".", "", 1).isdigit():
review_score = float(part)
# "reviews" 문자열이 포함되어 있는지 확인
elif "reviews" in part:
# "reviews" 앞의 숫자 추출
review_count = int(part.split(" ")[0].replace(",", ""))
decription = handle_no_such_element_exception(lambda: property_item.find_element(By.CSS_SELECTOR, "[data-testid="recommended-units"]").text)
price_element = handle_no_such_element_exception(lambda: (property_item.find_element(By.CSS_SELECTOR, "[data-testid="availability-rate-information"]")))
if price_element is not None:
original_price = handle_no_such_element_exception(lambda: (
price_element.find_element(By.CSS_SELECTOR, "[aria-hidden="true"]:not([data-testid])").text.replace(",", "")
))
price = handle_no_such_element_exception(lambda: (
price_element.find_element(By.CSS_SELECTOR, "[data-testid="price-and-discounted-price"]").text.replace(",", "")
))
# 스크랩된 데이터로 새 항목 생성
item = {
"url": url,
"image": image,
"title": title,
"address": address,
"distance": distance,
"review_score": review_score,
"review_count": review_count,
"decription": decription,
"original_price": original_price,
"price": price
}
# 새로 생성된 항목을 스크랩된 항목 목록에 추가
items.append(item)
# 출력 CSV 파일 이름 지정
output_file = "properties.csv"
# 항목 목록을 CSV 파일로 내보내기
with open(output_file, mode="w", newline="", encoding="utf-8") as file:
# CSV 라이터 객체 생성
writer = csv.DictWriter(file, fieldnames=["url", "image", "title", "address", "distance", "review_score", "review_count", "decription", "original_price", "price"])
# 헤더 행 작성
writer.writeheader()
# 각 항목을 CSV 행으로 작성
writer.writerows(items)
# 웹 드라이버 종료 및 리소스 해제
driver.quit()
믿을 수 있나요? 고작 110줄 정도로 Python Booking.com 스크래퍼를 만들었습니다.
스크래핑 스크립트를 실행하여 작동 여부를 확인하세요. Windows에서는 다음 명령어로 스크래퍼를 실행합니다:
python scraper.py
Linux 또는 macOS에서는 다음과 같이 실행하세요:
python3 scraper.py
스크립트 실행이 완료될 때까지 기다리세요. 프로젝트 루트 디렉터리에 properties.csv 파일이 생성됩니다. 추출된 데이터를 확인하려면 파일을 열어보세요:

축하합니다, 미션 완료!
결론
이 튜토리얼에서는 Booking.com 스크레이퍼가 무엇인지, 그리고 Python을 사용하여 이를 구축하는 방법을 배웠습니다. 보여드린 바와 같이, Booking.com에서 데이터를 자동으로 가져오는 기본 스크립트를 만드는 데는 몇 줄의 코드만 필요합니다.
그러나 여기 제시된 예시는 Booking.com 스크래핑 시 발생할 수 있는 많은 문제점들을 다루지 않았습니다. 헤드리스 브라우저 방지 조치, 검색 결과 생성을 위한 사용자 상호작용 처리, 무한 스크롤링 대응 등과 같은 문제들은 스크래핑 작업을 빠르게 복잡하게 만들 수 있습니다.
더 쉽고, 모든 기능을 갖춘 강력한 스크래핑 솔루션을 찾고 계신가요? Bright Data의 Booking Scraper API를 사용해 보세요!
Booking Scraper API는 공개 호텔 데이터, 리뷰, 평점 등을 스크래핑할 수 있는 강력한 엔드포인트를 제공합니다. 간단한 API 호출만으로 JSON 또는 HTML 형식의 데이터를 가져올 수 있습니다.
사전 구축된 솔루션을 선호하시나요? Bright Data는 바로 사용 가능한 Booking.com 데이터셋도 제공합니다!
스크레이퍼 API를 사용해 보거나 데이터셋을 살펴보려면 지금 바로 무료 Bright Data 계정을 생성하세요.