이 튜토리얼에서는 다음을 다룹니다:
- 왜 Yelp를 스크래핑할까요?
- Yelp 스크래핑 라이브러리 및 도구
- Beautiful Soup을 이용한 Yelp 비즈니스 데이터 스크래핑
왜 Yelp를 스크래핑해야 할까?
기업이 Yelp를 스크래핑하는 데는 여러 이유가 있습니다. 그 중에는 다음과 같은 것들이 포함됩니다:
- 포괄적인 비즈니스 데이터 접근: 리뷰, 평점, 연락처 정보 등 지역 비즈니스에 관한 풍부한 정보를 제공합니다.
- 고객 피드백에 대한 통찰력 확보: 사용자 리뷰로 유명한 플랫폼으로, 고객 의견과 경험에 대한 귀중한 통찰력을 제공합니다.
- 경쟁사 분석 및 벤치마킹 수행: 경쟁사의 성과, 강점, 약점, 고객 감정에 대한 가치 있는 통찰력을 제공합니다.
비슷한 플랫폼도 있지만, 데이터 스크래핑에 Yelp가 선호되는 이유는 다음과 같습니다:
유사한 플랫폼도 있지만, Yelp는 다음과 같은 이유로 데이터 스크래핑에 선호되는 선택지입니다:
- 광범위한 사용자 기반
- 다양한 비즈니스 카테고리
- 확고한 평판
Yelp에서 수집한 데이터는 시장 조사, 경쟁사 분석, 감성 분석 및 의사 결정에 유용할 수 있습니다. 이러한 정보는 또한 개선이 필요한 부분을 파악하고, 서비스를 세밀하게 조정하며, 경쟁에서 앞서 나갈 수 있도록 도와줍니다.
Yelp 스크래핑 라이브러리 및 도구
파이썬은 사용자 친화적인 특성, 직관적인 구문, 그리고 방대한 라이브러리 지원 덕분에 웹 스크래핑에 탁월한 언어로 널리 인정받고 있습니다. 따라서 Yelp 스크래핑에 권장되는 프로그래밍 언어입니다. 자세한 내용은 파이썬을 활용한 웹 스크래핑 방법에 대한 심층 가이드를 참고하세요.
다음 단계는 다양한 옵션 중에서 적합한 스크래핑 라이브러리를 선택하는 것입니다. 정보에 기반한 결정을 내리기 위해 먼저 웹 브라우저에서 플랫폼을 살펴보세요. 웹 페이지가 수행하는
다음 단계는 다양한 옵션 중에서 적합한 스크래핑 라이브러리를 선택하는 것입니다. 정보에 기반한 결정을 내리기 위해 먼저 웹 브라우저에서 플랫폼을 탐색해 보세요. 웹 페이지가 수행하는 AJAX 호출을 분석하면 대부분의 데이터가 서버에서 가져온 HTML 문서 내에 내장되어 있음을 발견할 수 있습니다.
이는 서버에 요청을 보내는 간단한 HTTP 클라이언트와 HTML 파서만으로도 작업에 충분함을 의미합니다. 다음은 이를 선택해야 하는 이유입니다:
- Requests: 파이썬에서 가장 널리 사용되는 HTTP 클라이언트 라이브러리입니다. HTTP 요청 전송 및 해당 응답 처리 과정을 간소화합니다.
- Beautiful Soup: 웹 스크래핑에 광범위하게 활용되는 포괄적인 HTML 및 XML 파싱 라이브러리입니다. DOM 내 탐색 및 데이터 추출을 위한 강력한 메서드를 제공합니다.
Requests와 Beautiful Soup 덕분에 Python으로 Yelp를 효과적으로 스크래핑할 수 있습니다. 이제 이 작업을 수행하는 방법에 대해 자세히 알아보겠습니다!
Beautiful Soup을 활용한 Yelp 비즈니스 데이터 스크래핑
이 단계별 튜토리얼을 따라 Yelp 스크레이퍼를 구축하는 방법을 배워보세요.
1단계: Python 프로젝트 설정
시작하기 전에 먼저 다음이 준비되어 있는지 확인하세요:
- 컴퓨터에 Python 3 이상 설치: 설치 프로그램을 다운로드하여 실행하고 안내에 따라 설치하세요.
- 원하는 Python IDE: Python 확장 프로그램이 설치된 Visual Studio Code 또는 PyCharm Community Edition 모두 사용 가능합니다.
먼저 yelp-scraper 폴더를 생성하고 가상 환경을 포함한 Python 프로젝트로 초기화합니다:
mkdir yelp-scraper
cd yelp-scraper
python -m venv env
Windows에서는 아래 명령어로 환경을 활성화하세요:
envScriptsactivate.ps1
Linux 또는 macOS에서는:
env/bin/activate
다음으로 프로젝트 폴더에 아래 코드가 포함된 scraper.py 파일을 추가합니다:
print('Hello, World!')
이것이 가장 간단한 Python 스크립트입니다. 현재는 “Hello, World!”만 출력하지만, 곧 Yelp를 스크래핑하는 로직이 포함될 것입니다.
스크레이퍼는 다음 명령어로 실행할 수 있습니다:
python scraper.py
터미널에 다음과 같이 출력됩니다:
Hello, World!
예상한 대로 정확히 출력됩니다. 이제 모든 것이 정상적으로 작동함을 확인했으니, Python IDE에서 프로젝트 폴더를 열어 보세요.
좋아요, 이제 파이썬 코드를 작성할 준비를 하세요!
2단계: 스크래핑 라이브러리 설치
이제 웹 스크래핑을 수행하는 데 필요한 라이브러리를 프로젝트의 종속성에 추가해야 합니다. 활성화된 가상 환경에서 다음 명령어를 실행하여 Beautiful Soup 과 Requests를 설치하세요:
pip install beautifulsoup4 requests
scraper.py 파일을 비운 후 패키지를 임포트하기 위해 다음 줄을 추가하세요:
import requests
from bs4 import BeautifulSoup
# 스크래핑 로직...
Python IDE에서 오류가 발생하지 않는지 확인하세요. 사용되지 않은 임포트 때문에 경고가 표시될 수 있지만 무시해도 됩니다. 이제 이 스크래핑 라이브러리를 사용하여 Yelp에서 데이터를 추출할 예정입니다.
3단계: 대상 페이지 식별 및 다운로드
Yelp를 탐색하여 스크래핑할 페이지를 확인하세요. 본 가이드에서는 뉴욕 최고 평점 이탈리아 레스토랑 목록에서 데이터를 추출하는 방법을 살펴보겠습니다:

대상 페이지의 URL을 변수에 할당합니다:
url = 'https://www.yelp.com/search?find_desc=Italian&find_loc=New+York%2C+NY'
다음으로 requests.get()을 사용하여 해당 URL에 HTTP GET 요청을 수행합니다:
page = requests.get(url)
이제 변수 page에는 서버에서 생성된 응답이 포함됩니다.
특히 page.text에는 대상 웹페이지와 연관된 HTML 문서가 저장됩니다. 이를 확인하려면 로그를 출력하세요:
print(page.text)
다음과 같이 출력됩니다:
<!DOCTYPE html><html lang="en-US" prefix="og: http://ogp.me/ns#" style="margin: 0;padding: 0; border: 0; font-size: 100%; font: inherit; vertical-align: baseline;"><head><script>document.documentElement.className=document.documentElement.className.replace(no-j/,"js");</script><meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /><meta http-equiv="Content-Language" content="en-US" /><meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"><link rel="mask-icon" sizes="any" href="https://s3-media0.fl.yelpcdn.com/assets/srv0/yelp_large_assets/b2bb2fb0ec9c/assets/img/logos/yelp_burst.svg" content="#FF1A1A"><link rel="shortcut icon" href="https://s3-media0.fl.yelpcdn.com/assets/srv0/yelp_large_assets/dcfe403147fc/assets/img/logos/favicon.ico"><script> window.ga=window.ga||function(){(ga.q=ga.q||[]).push(arguments)};ga.l=+new Date;window.ygaPageStartTime=new Date().getTime();</script><script>
<!-- 간결함을 위해 생략... -->
완벽합니다! 이제 데이터를 추출하기 위해 이를 파싱하는 방법을 배워보겠습니다.
4단계: HTML 콘텐츠 파싱하기
서버에서 가져온 HTML 콘텐츠를 BeautifulSoup() 생성자에 전달하여 파싱합니다:
soup = BeautifulSoup(page.text, 'html.parser')
이 함수는 두 개의 인수를 받습니다:
- HTML을 포함하는 문자열.
- BeautifulSoup가 콘텐츠를 처리하는 데 사용할 파서입니다.
“html.parser“는 Python 내장 HTML 파서의 이름입니다.
BeautifulSoup()은 파싱된 내용을 탐색 가능한 트리 구조로 반환합니다. 특히 soup 변수는 DOM 트리에서 요소를 선택하는 데 유용한 메서드를 제공합니다. 가장 널리 사용되는 메서드는 다음과 같습니다:
- find(): 매개변수로 전달된 선택기 전략과 일치하는 첫 번째 HTML 요소를 반환합니다.
- find_all(): 입력된 선택기 전략과 일치하는 HTML 요소들의 목록을 반환합니다.
- select_one(): 매개변수로 전달된 CSS 선택자와 일치하는 첫 번째 HTML 요소를 반환합니다.
- select(): 입력된 CSS 선택자와 일치하는 HTML 요소 목록을 반환합니다.
훌륭합니다! 곧 이들을 사용하여 Yelp에서 원하는 데이터를 추출하게 될 것입니다.
5단계: 페이지에 익숙해지기
효과적인 선택 전략을 수립하려면 먼저 대상 웹페이지의 구조를 숙지해야 합니다. 브라우저에서 해당 페이지를 열고 탐색을 시작하세요.
페이지의 HTML 요소를 마우스 오른쪽 버튼으로 클릭하고 “검사”를 선택하여 개발자 도구(DevTools)를 엽니다:

사이트가 빌드 시점에 무작위로 생성된 것으로 보이는 CSS 클래스에 의존하고 있음을 즉시 확인할 수 있습니다. 배포 시마다 변경될 수 있으므로 CSS 선택자를 이 클래스에 기반해서는 안 됩니다. 효과적인 스크래퍼를 구축하기 위해 반드시 알아야 할 핵심 정보입니다.
DOM을 자세히 살펴보면 가장 중요한 요소들이 독특한 HTML 속성을 가지고 있음을 확인할 수 있습니다. 따라서 선택기 전략은 이러한 속성에 기반해야 합니다.
Python으로 스크래핑할 준비가 될 때까지 개발자 도구에서 계속 페이지를 검사하세요!
6단계: 핵심 데이터 추출
여기서의 목표는 페이지의 각 카드에서 비즈니스 정보를 추출하는 것입니다. 이 데이터를 추적하려면 저장할 데이터 구조가 필요합니다:
items = []
먼저 카드 HTML 요소를 살펴봅니다:

다음과 같이 모두 선택할 수 있습니다:
html_item_cards = soup.select('[data-testid="serp-ia-card"]')
이들을 반복 처리하고 스크립트를 준비하여:
- 각 카드에서 데이터를 추출합니다.
- Python 사전 항목에 저장합니다.
- items에 추가합니다.
for html_item_card in html_item_cards:
item = {}
# 스크래핑 로직...
items.append(item)
스크래핑 로직을 구현할 시간입니다!
이미지 요소를 검사합니다:

비즈니스 이미지의 URL을 다음과 같이 가져옵니다:
image = html_item_card.select_one('[data-lcp-target-id="SCROLLABLE_PHOTO_BOX"] img').attrs['src']
select_one()으로 요소를 가져온 후 attrs 멤버를 통해 해당 HTML 속성에 접근할 수 있습니다.
추가로 가져올 유용한 정보로는 비즈니스 상세 페이지의 제목과 URL이 있습니다:

보시다시피, h3 a 노드에서 두 데이터 필드를 모두 얻을 수 있습니다:
name = html_item_card.select_one('h3 a').text
url = 'https://www.yelp.com' + html_item_card.select_one('h3 a').attrs['href']
text 속성은 현재 요소와 그 모든 자식 요소 내의 텍스트 콘텐츠를 반환합니다. 일부 링크는 상대 경로이므로, 완전한 URL을 만들기 위해 기본 URL을 추가해야 할 수 있습니다.
Yelp에서 가장 중요한 데이터 중 하나는 사용자 리뷰 비율입니다:

이 경우 이를 쉽게 얻는 방법은 없지만, 다음을 통해 목표를 달성할 수 있습니다:
html_stars_element = html_item_card.select_one('[class^="five-stars"]')
stars = html_stars_element.attrs['aria-label'].replace(' star rating', '')
reviews = html_stars_element.parent.parent.next_sibling.text
문자열을 정리하고 관련 데이터만 추출하기 위해 Python의 replace() 함수를 사용한 점에 유의하세요.
태그와 가격 범위 요소도 함께 검사하세요:

모든 태그 문자열을 수집하려면 모두 선택한 후 반복 처리해야 합니다:
tags = []
html_tag_elements = html_item_card.select('[class^="priceCategory"] button')
for html_tag_element in html_tag_elements:
tag = html_tag_element.text
tags.append(tag)
반면 선택적 가격대 표시는 훨씬 쉽게 가져올 수 있습니다:
price_range_html = html_item_card.select_one('[class^="priceRange"]')
# 가격 범위 정보는 선택 사항이므로
if price_range_html is not None:
price_range = price_range_html.text
마지막으로, 레스토랑이 제공하는 서비스도 스크래핑해야 합니다:

다시 한번, 모든 노드를 반복 처리해야 합니다:
services = []
html_service_elements = html_item_card.select('[data-testid="services-actions-component"] p[class^="tagText"]')
for html_service_element in html_service_elements:
service = html_service_element.text
services.append(service)
잘하셨습니다! 방금 스크래핑 로직을 구현하셨습니다.
스크래핑된 데이터 변수를 사전(dictionary)에 추가하세요:
item['name'] = name
item['image'] = image
item['url'] = url
item['stars'] = stars
item['reviews'] = reviews
item['tags'] = tags
item['price_range'] = price_range
item['services'] = services
print(item)을 사용하여 데이터 추출 과정이 원하는 대로 작동하는지 확인하세요. 첫 번째 카드에서는 다음과 같은 결과가 나올 것입니다:
{'name': 'Olio e Più', 'image': 'https://s3-media0.fl.yelpcdn.com/bphoto/CUpPgz_Q4QBHxxxxDJJTTA/348s.jpg', 'url': 'https://www.yelp.com/biz/olio-e-pi%C3%B9-new-york-7?osq=Italian', 'stars': '4.5', 'reviews': '4588', 'tags': ['Pizza', 'Italian', 'Cocktail Bars'], 'price_range': '$$', 'services': ['Outdoor seating', 'Delivery', 'Takeout']}
대단해요! 목표에 한 걸음 더 가까워졌어요!
7단계: 크롤링 로직 구현
사업체 정보가 사용자에게 페이지별로 표시된다는 점을 잊지 마세요. 지금까지는 단일 페이지를 스크래핑하는 방법을 살펴봤지만, 모든 데이터를 가져오고 싶다면 어떻게 해야 할까요? 이를 위해 Yelp 데이터 스크래퍼에 웹 크롤링 기능을 통합해야 합니다.
먼저 스크립트 상단에 지원 데이터 구조를 정의하세요:
visited_pages = []
pages_to_scrape = ['https://www.yelp.com/search?find_desc=Italian&find_loc=New+York%2C+NY']
visited_pages에는 스크래핑된 페이지의 URL이 저장되고, pages_to_scrape에는 다음에 방문할 페이지 목록이 저장됩니다.
스크랩할 페이지가 더 이상 없거나 특정 횟수(반복) 이후에 종료되는 while 루프를 생성하세요:
limit = 5 # 실제 운영 환경에서는 제거 가능
i = 0
while len(pages_to_scrape) != 0 and i < limit:
# 배열에서 첫 번째 페이지 추출
url = pages_to_scrape.pop(0)
# 방문한 페이지로 표시
visited_pages.append(url)
# 페이지 다운로드 및 파싱
page = requests.get(url)
soup = BeautifulSoup(page.text, 'html.parser')
# 스크래핑 로직...
# 크롤링 로직...
# 페이지 카운터 증가
i += 1
각 반복은 목록에서 한 페이지를 제거하고, 스크래핑하며, 새로운 페이지를 발견하여 대기열에 추가하는 작업을 처리합니다. limit은 스크래퍼가 무한정 실행되는 것을 방지하기 위한 것입니다.
이제 크롤링 로직만 구현하면 됩니다. HTML 페이지네이션 요소를 살펴보세요:

여러 개의 링크로 구성됩니다. 모두 수집하고 새로 발견된 링크를 pages_to_visit에 추가합니다:
pagination_link_elements = soup.select('[class^="pagination-links"] a')
for pagination_link_element in pagination_link_elements:
pagination_url = pagination_link_element.attrs['href']
# 발견된 URL이 새로운 경우
if pagination_url not in visited_pages and pagination_url not in pages_to_scrape:
pages_to_scrape.append(pagination_url)
훌륭합니다! 이제 스크레이퍼가 모든 페이지네이션 페이지를 자동으로 처리할 것입니다.
8단계: 스크랩한 데이터를 CSV로 내보내기
수집한 데이터를 공유하고 읽기 쉽게 만드는 마지막 단계입니다. 가장 좋은 방법은 CSV와 같은 사람이 읽을 수 있는 형식으로 내보내는 것입니다:
import csv
# ...
# .csv 출력 파일 초기화
with open('restaurants.csv', 'w', newline='', encoding='utf-8') as csv_file:
writer = csv.DictWriter(csv_file, fieldnames=headers, quoting=csv.QUOTE_ALL)
writer.writeheader()
# CSV 파일 채우기
for item in items:
# 배열 필드를 "['element1', 'element2', ...]" 형식에서
# "element1; element2; ..." 형식으로 변환
csv_item = {}
for key, value in item.items():
if isinstance(value, list):
csv_item[key] = '; '.join(str(e) for e in value)
else:
csv_item[key] = value
# 새 레코드 추가
writer.writerow(csv_item)
open()으로 restaurants.csv 파일을 생성합니다. 그런 다음 DictWriter와 사용자 정의 로직을 사용하여 데이터를 채웁니다. csv 패키지는 Python 표준 라이브러리에서 제공되므로 추가 의존성을 설치할 필요가 없습니다.
훌륭합니다! 웹페이지에 포함된 원시 데이터에서 시작하여 반구조화된 CSV 데이터를 얻었습니다. 이제 전체 Yelp Python 스크레이퍼를 살펴볼 때입니다.
9단계: 모든 것을 통합하기
다음은 완성된 scraper.py 스크립트의 모습입니다:
import requests
from bs4 import BeautifulSoup
import csv
# 크롤링 로직 구현을 위한
# 데이터 구조 지원
visited_pages = []
pages_to_scrape = ['https://www.yelp.com/search?find_desc=Italian&find_loc=New+York%2C+NY']
# 스크랩된 데이터 저장용
items = []
# Yelp 서버에 과도한 요청이 발생하지 않도록
limit = 5
i = 0
# 모든 페이지네이션 페이지가 방문될 때까지
# 또는 페이지 제한에 도달할 때까지
while len(pages_to_scrape) != 0 and i < limit:
# 배열에서 첫 번째 페이지 추출
url = pages_to_scrape.pop(0)
# "방문됨"으로 표시
visited_pages.append(url)
# 페이지 다운로드 및 파싱
page = requests.get(url)
soup = BeautifulSoup(page.text, 'html.parser')
# 모든 아이템 카드 선택
html_item_cards = soup.select('[data-testid="serp-ia-card"]')
for html_item_card in html_item_cards:
# 스크래핑 로직
item = {}
image = html_item_card.select_one('[data-lcp-target-id="SCROLLABLE_PHOTO_BOX"] img').attrs['src']
name = html_item_card.select_one('h3 a').text
url = 'https://www.yelp.com' + html_item_card.select_one('h3 a').attrs['href']
html_stars_element = html_item_card.select_one('[class^="five-stars"]')
stars = html_stars_element.attrs['aria-label'].replace(' star rating', '')
reviews = html_stars_element.parent.parent.next_sibling.text
tags = []
html_tag_elements = html_item_card.select('[class^="priceCategory"] button')
for html_tag_element in html_tag_elements:
tag = html_tag_element.text
태그.append(태그)
가격_범위_html = html_item_card.select_one('[class^="priceRange"]')
# 이 HTML 요소는 선택 사항입니다
if 가격_범위_html is not None:
가격_범위 = 가격_범위_html.text
서비스 = []
html_service_elements = html_item_card.select('[data-testid="services-actions-component"] p[class^="tagText"]')
for html_service_element in html_service_elements:
service = html_service_element.text
services.append(service)
# 추출한 데이터를 객체에 추가
# 객체를 배열에 추가
item['name'] = name
item['image'] = image
item['url'] = url
item['stars'] = stars
item['reviews'] = reviews
item['tags'] = tags
item['price_range'] = price_range
item['services'] = services
items.append(item)
# 새로운 페이지네이션 페이지를 발견하고 큐에 추가
pagination_link_elements = soup.select('[class^="pagination-links"] a')
for pagination_link_element in pagination_link_elements:
pagination_url = pagination_link_element.attrs['href']
# 발견된 URL이 새로운 경우
if pagination_url not in visited_pages and pagination_url not in pages_to_scrape:
pages_to_scrape.append(pagination_url)
# 페이지 카운터 증가
i += 1
# 배열의 첫 번째 객체에서 키 추출
# CSV 헤더로 사용하기 위해
headers = items[0].keys()
# .csv 출력 파일 초기화
with open('restaurants.csv', 'w', newline='', encoding='utf-8') as csv_file:
writer = csv.DictWriter(csv_file, fieldnames=headers, quoting=csv.QUOTE_ALL)
writer.writeheader()
# CSV 파일 작성
for item in items:
# 배열 필드를 "['element1', 'element2', ...]" 형식에서
# "element1; element2; ..." 형식으로 변환
csv_item = {}
for key, value in item.items():
if isinstance(value, list):
csv_item[key] = '; '.join(str(e) for e in value)
else:
csv_item[key] = value
# 새 레코드 추가
writer.writerow(csv_item)
약 100줄의 코드로 Yelp에서 비즈니스 데이터를 추출하는 웹 크롤러를 구축할 수 있습니다.
스크레이퍼 실행 명령어:
python scraper.py
실행이 완료될 때까지 기다리면 프로젝트 루트 폴더 아래에 restaurants.csv 파일이 생성됩니다:

축하합니다! 방금 Python으로 Yelp 스크래핑하는 법을 배웠습니다!
결론
이 단계별 가이드를 통해, 지역 비즈니스에 대한 사용자 데이터를 얻기 위한 최고의 스크래핑 대상 중 하나인 Yelp의 이유를 이해하셨습니다. 구체적으로, Yelp 데이터를 가져올 수 있는 Python 스크래퍼를 구축하는 방법을 배웠습니다. 여기서 보여드린 것처럼, 단 몇 줄의 코드만으로 가능합니다.
동시에 사이트들은 끊임없이 변화하는 사용자의 기대에 맞춰 UI와 구조를 계속 발전시키고 적응합니다. 여기서 구축한 스크레이퍼는 오늘은 작동하지만 내일은 더 이상 효과적이지 않을 수 있습니다. 유지보수에 시간과 비용을 낭비하지 마시고, 저희 Yelp 스크레이퍼를 사용해 보세요!
또한 대부분의 사이트가 자바스크립트에 크게 의존한다는 점을 명심하세요. 이러한 경우 HTML 파서를 기반으로 한 전통적인 접근 방식은 작동하지 않습니다. 대신 자바스크립트를 렌더링하고 지문 인식, CAPTCHA, 자동 재시도를 처리해 주는 도구를 사용해야 합니다. 바로 이것이 저희의 새로운 스크래핑 브라우저 솔루션이 제공하는 핵심 기능입니다!
웹 스크래핑을 직접 다루기보다 Yelp 데이터를 바로 원하시나요? Yelp 데이터셋을 구매하세요
참고: 본 가이드는 작성 당시 당사 팀에서 철저히 테스트되었으나, 웹사이트가 코드와 구조를 자주 업데이트함에 따라 일부 단계가 예상대로 작동하지 않을 수 있습니다.