웹 크롤링을 할 때 자주 사용하는 도구로 BeautifulSoup과 Selenium이 있다.
두 도구는 모두 웹페이지에서 데이터를 수집할 때 사용할 수 있지만 역할은 다르다.
- BeautifulSoup: 받아온 HTML을 분석하고 원하는 데이터를 추출하는 도구
- Selenium: 실제 브라우저를 실행하고 클릭, 입력, 스크롤 등의 동작을 자동화하는 도구
따라서 웹페이지가 어떤 방식으로 데이터를 제공하는지에 따라 적절한 도구를 선택해야 한다.
1. BeautifulSoup이란?
BeautifulSoup은 HTML이나 XML 문서를 파싱하여 원하는 태그와 데이터를 추출하는 파이썬 라이브러리다.
일반적으로 requests를 이용해 웹페이지의 HTML을 가져온 뒤 BeautifulSoup으로 분석한다.
import requests
from bs4 import BeautifulSoup
url = "https://example.com"
response = requests.get(url)
soup = BeautifulSoup(response.text, "html.parser")
title = soup.select_one("h1")
print(title.get_text(strip=True))
BeautifulSoup 자체가 웹페이지에 접속하는 것은 아니다.
- requests: 서버에 요청하여 HTML을 가져옴
- BeautifulSoup: 가져온 HTML을 분석하고 데이터를 추출함
따라서 일반적으로 두 라이브러리를 함께 사용한다.
2. Selenium이란?
Selenium은 실제 웹 브라우저를 자동으로 조작하는 도구다.
사용자가 브라우저에서 하는 것처럼 버튼을 클릭하거나 검색어를 입력하고, 스크롤하거나 페이지가 로딩될 때까지 기다릴 수 있다.
from selenium import webdriver
from selenium.webdriver.common.by import By
driver = webdriver.Chrome()
driver.get("https://example.com")
title = driver.find_element(By.TAG_NAME, "h1")
print(title.text)
driver.quit()
Selenium은 브라우저에서 JavaScript가 실행된 이후 생성되는 데이터도 가져올 수 있다.
다만 실제 브라우저를 실행하기 때문에 BeautifulSoup과 requests를 사용하는 방식보다 속도가 느리고 컴퓨터 자원을 많이 사용한다.
3. 정적 페이지와 동적 페이지
BeautifulSoup과 Selenium을 구분하기 위해서는 정적 페이지와 동적 페이지의 차이를 알아야 한다.
정적 페이지
서버에서 처음부터 완성된 HTML을 전달하는 페이지다.
페이지 소스에 원하는 데이터가 이미 포함되어 있으므로 requests와 BeautifulSoup만으로 수집할 수 있는 경우가 많다.
예시:
- 게시글 제목 목록
- 단순한 뉴스 페이지
- 서버에서 완성된 상품 목록
- 공개된 HTML 표
동적 페이지
처음 받은 HTML에는 데이터가 없거나 일부만 있고, JavaScript가 실행된 뒤 추가 데이터가 생성되는 페이지다.
예시:
- 더보기 버튼을 눌러야 나타나는 목록
- 스크롤할 때마다 추가되는 콘텐츠
- 검색 조건을 선택한 뒤 표시되는 결과
- 로그인이 필요한 페이지
- 버튼 클릭 후 변경되는 화면
이러한 경우 Selenium을 사용하거나, 브라우저가 내부적으로 호출하는 API를 직접 요청하는 방법을 검토할 수 있다.
4. BeautifulSoup과 Selenium 비교
| 핵심 역할 | HTML 파싱과 데이터 추출 | 브라우저 자동 조작 |
| 적합한 페이지 | 정적 페이지 | 동적 페이지 |
| JavaScript 실행 | 불가능 | 가능 |
| 클릭 및 입력 | 불가능 | 가능 |
| 로그인 처리 | 직접 요청을 구성해야 함 | 브라우저에서 직접 가능 |
| 속도 | 빠름 | 상대적으로 느림 |
| 자원 사용량 | 적음 | 많음 |
| 코드 난이도 | 비교적 간단 | 상대적으로 복잡 |
| 대량 수집 | 적합 | 상대적으로 비효율적 |
| 대표 조합 | requests + BeautifulSoup | Selenium 단독 또는 BeautifulSoup과 병행 |
BeautifulSoup은 브라우저를 조작하는 도구가 아니기 때문에 버튼 클릭이나 스크롤 기능을 수행할 수 없다.
반대로 Selenium은 HTML 요소를 직접 찾고 데이터를 추출할 수도 있으므로 반드시 BeautifulSoup과 함께 사용해야 하는 것은 아니다.
다만 Selenium으로 페이지를 로딩한 뒤, 가져온 HTML을 BeautifulSoup으로 파싱하는 방식도 사용할 수 있다.
5. BeautifulSoup이 적합한 상황
다음과 같은 경우에는 BeautifulSoup이 적합하다.
- 페이지 소스에 원하는 데이터가 포함되어 있을 때
- 클릭이나 스크롤이 필요하지 않을 때
- 많은 페이지를 빠르게 수집해야 할 때
- 로그인이나 복잡한 사용자 동작이 필요하지 않을 때
- 서버에서 제공하는 HTML 구조가 일정할 때
import requests
from bs4 import BeautifulSoup
url = "https://example.com/news"
response = requests.get(url)
soup = BeautifulSoup(response.text, "html.parser")
titles = soup.select(".news-title")
for title in titles:
print(title.get_text(strip=True))
정적 페이지에서는 Selenium보다 속도가 빠르고 코드가 단순하다는 장점이 있다.
6. Selenium이 적합한 상황
다음과 같은 경우에는 Selenium이 적합하다.
- JavaScript 실행 후 데이터가 나타날 때
- 버튼 클릭이 필요할 때
- 검색어나 날짜 등을 직접 입력해야 할 때
- 무한 스크롤을 내려야 할 때
- 로그인 후에만 접근할 수 있을 때
- 페이지의 특정 동작을 자동화해야 할 때
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
driver = webdriver.Chrome()
driver.get("https://example.com")
button = WebDriverWait(driver, 10).until(
EC.element_to_be_clickable((By.CSS_SELECTOR, ".more-button"))
)
button.click()
Selenium에서는 페이지 로딩 속도가 매번 다를 수 있으므로 고정된 시간 동안 기다리는 sleep()보다 WebDriverWait을 사용하는 것이 좋다.
7. Selenium 대신 API 요청을 사용할 수 있는 경우
동적 페이지라고 해서 반드시 Selenium을 사용해야 하는 것은 아니다.
웹페이지가 JavaScript를 통해 별도의 API에서 데이터를 가져오는 구조라면 해당 요청을 직접 분석하여 requests로 데이터를 받을 수 있다.
import requests
url = "https://example.com/api/products"
params = {
"page": 1,
"size": 20
}
response = requests.get(url, params=params)
data = response.json()
print(data)
이 방식은 Selenium보다 빠르고 안정적인 경우가 많다.
따라서 동적 페이지를 수집할 때는 다음 순서로 확인하는 것이 좋다.
- HTML에 데이터가 포함되어 있는지 확인
- 내부 API 호출이 존재하는지 확인
- API 요청이 가능하면 requests 사용
- 클릭이나 브라우저 동작이 반드시 필요하면 Selenium 사용
8. BeautifulSoup과 Selenium을 함께 사용하는 방법
Selenium으로 동적 페이지를 완전히 로딩한 뒤, 페이지의 HTML을 BeautifulSoup에 전달할 수 있다.
from selenium import webdriver
from bs4 import BeautifulSoup
driver = webdriver.Chrome()
driver.get("https://example.com")
html = driver.page_source
soup = BeautifulSoup(html, "html.parser")
items = soup.select(".item")
for item in items:
print(item.get_text(strip=True))
driver.quit()
이 방식에서는 다음과 같이 역할을 나눈다.
- Selenium: 페이지 접속, 클릭, 스크롤, JavaScript 실행
- BeautifulSoup: 로딩된 HTML에서 데이터 추출
복잡한 브라우저 조작과 HTML 파싱을 분리할 수 있다는 장점이 있다.
9. robots.txt란?
robots.txt는 웹사이트 운영자가 검색엔진 로봇이나 크롤러에게 접근을 허용하거나 제한할 경로를 안내하는 파일이다.
일반적으로 웹사이트의 최상위 경로에 위치한다.
https://example.com/robots.txt
robots.txt의 기본 구조는 다음과 같다.
User-agent: *
Disallow: /admin/
Allow: /public/
각 항목의 의미는 다음과 같다.
| User-agent | 규칙을 적용할 크롤러 |
| Disallow | 수집하지 않기를 요청하는 경로 |
| Allow | 접근을 허용하는 경로 |
| Sitemap | 사이트맵 위치 |
User-agent: *는 모든 크롤러를 대상으로 한다는 의미다.
robots.txt 예시
User-agent: *
Disallow: /private/
Disallow: /member/
Allow: /public/
Sitemap: https://example.com/sitemap.xml
위 설정은 다음과 같은 의미다.
- 모든 크롤러를 대상으로 함
- /private/ 경로 수집 제한
- /member/ 경로 수집 제한
- /public/ 경로 접근 허용
- 사이트맵 위치 제공
robots.txt가 중요한 이유
웹 크롤링을 시작하기 전에는 해당 사이트의 robots.txt를 확인하는 것이 좋다.
robots.txt를 확인하면 다음 내용을 알 수 있다.
- 사이트 운영자가 수집을 제한하는 경로
- 크롤링이 허용되는 범위
- 검색엔진용 사이트맵 위치
- 특정 크롤러에 적용되는 규칙
이를 통해 불필요한 서버 요청을 줄이고 사이트 운영 정책을 존중할 수 있다.
robots.txt는 보안 장치가 아니다
robots.txt는 접근을 기술적으로 차단하는 보안 기능이 아니라 크롤러에게 전달하는 지침이다.
따라서 Disallow로 설정된 주소도 브라우저에서 직접 접근할 수 있는 경우가 있다.
중요한 개인정보나 관리자 페이지를 robots.txt만으로 보호해서는 안 된다. 실제 접근 제한이 필요하다면 로그인, 권한 검사 등의 별도 보안 기능을 적용해야 한다.
robots.txt를 지키면 무조건 크롤링이 허용되는가?
robots.txt를 준수했다고 해서 모든 크롤링 행위가 자동으로 허용되는 것은 아니다.
웹 크롤링 시에는 다음 요소도 함께 확인해야 한다.
- 웹사이트 이용약관
- 데이터의 공개 여부
- 개인정보 포함 여부
- 저작권과 데이터베이스 권리
- 로그인이나 접근 제한 우회 여부
- 요청 빈도로 인한 서버 부담
- 수집한 데이터의 활용 목적
robots.txt는 중요한 확인 항목이지만, 법적 허용 여부를 최종적으로 판단하는 문서는 아니다.
10. 크롤링 시 주의할 점
요청 간격을 설정한다
짧은 시간에 너무 많은 요청을 보내면 서버에 부담을 줄 수 있다.
import time
for page in range(1, 11):
# 페이지 요청 및 처리
time.sleep(1)
User-Agent를 설정한다
일부 서버는 기본 설정으로 들어오는 요청을 차단할 수 있다.
import requests
headers = {
"User-Agent": "Mozilla/5.0"
}
response = requests.get(
"https://example.com",
headers=headers
)
다만 User-Agent를 설정하는 것이 접근 권한을 얻는다는 의미는 아니다.
HTTP 상태 코드를 확인한다
response = requests.get("https://example.com")
if response.status_code == 200:
print("요청 성공")
else:
print("요청 실패:", response.status_code)
HTML 구조 변경에 대비한다
웹사이트의 태그, 클래스명, 페이지 구조는 언제든 변경될 수 있다.
따라서 선택자가 지나치게 구체적이면 작은 화면 변경에도 코드가 작동하지 않을 수 있다.
오류 처리를 추가한다
import requests
try:
response = requests.get(
"https://example.com",
timeout=10
)
response.raise_for_status()
except requests.RequestException as error:
print("요청 중 오류 발생:", error)
크롤링 도구 선택 기준
| HTML에 데이터가 바로 존재 | requests + BeautifulSoup |
| JavaScript 실행 후 데이터 생성 | Selenium 또는 내부 API 확인 |
| 클릭·입력·스크롤 필요 | Selenium |
| 대량 페이지 수집 | requests + BeautifulSoup 또는 API |
| 로그인 세션 필요 | Selenium 또는 requests.Session() |
| JSON API가 존재 | API 직접 요청 |
| 동적 로딩 후 HTML 파싱 | Selenium + BeautifulSoup |
정리
BeautifulSoup과 Selenium은 웹 크롤링에서 역할이 서로 다르다.
- BeautifulSoup은 HTML을 분석하고 데이터를 추출하는 데 적합하다.
- Selenium은 실제 브라우저를 조작해야 하는 동적 페이지에 적합하다.
- 정적 페이지에서는 requests + BeautifulSoup 조합이 빠르고 효율적이다.
- 동적 페이지라도 내부 API가 존재한다면 Selenium보다 API 직접 요청이 효율적일 수 있다.
- 버튼 클릭, 입력, 로그인, 무한 스크롤 등이 필요하면 Selenium을 사용할 수 있다.
- robots.txt는 웹사이트가 크롤러에게 접근 규칙을 안내하는 파일이다.
- robots.txt뿐 아니라 이용약관, 개인정보, 저작권, 서버 부하도 함께 고려해야 한다.
웹 크롤링에서는 무조건 Selenium을 사용하는 것보다 먼저 페이지 구조를 확인하고, 가장 단순하고 효율적인 방법을 선택하는 것이 중요하다.