본문 바로가기
생활 관련 정보

파이썬 크롤링 기초: 뷰티풀수프(BeautifulSoup) 사용법

by 나나's 2026. 10. 11.
반응형
PYTHON CRAWLING GUIDE

파이썬 크롤링은 웹페이지를 가져오는 과정과 필요한 데이터를 찾는 과정을 나누면 이해하기 쉽습니다

requests로 HTML을 받고 BeautifulSoup으로 제목·링크·목록을 추출하는 기본 흐름부터 익혀보세요

웹 크롤링을 처음 배우면 라이브러리와 HTML 선택자 때문에 복잡하게 느껴질 수 있습니다. 하지만 기본 구조는 요청 보내기, 응답 확인하기, HTML 분석하기, 원하는 요소 찾기, 데이터 정리하기의 반복입니다. BeautifulSoup은 이 가운데 HTML 문서를 탐색하고 원하는 요소를 찾는 작업에 주로 사용합니다.

🐍 Python 🌐 requests 🥣 BeautifulSoup 📁 CSV 저장
①
REQUEST
페이지 요청 HTML 받기
②
PARSE
HTML 분석 구조 읽기
③
SELECT
요소 선택 태그·CSS
④
SAVE
데이터 저장 CSV 등

파이썬 크롤링과 BeautifulSoup 기본 개념

웹 크롤링은 프로그램을 이용해 웹페이지에 접근하고 필요한 정보를 수집하는 작업을 의미합니다. 상품 목록이나 공개된 게시물 제목처럼 반복되는 정보를 사람이 하나씩 복사하는 대신 프로그램으로 읽어 정리하는 것이 대표적인 활용 사례입니다.

 

초보자가 먼저 구분해야 할 것은 페이지를 가져오는 작업과 HTML에서 정보를 찾는 작업입니다. BeautifulSoup 자체가 브라우저처럼 인터넷 주소에 접속해 페이지를 받아오는 역할을 하는 것은 아닙니다. 일반적인 기초 예제에서는 requests 같은 HTTP 라이브러리로 문서를 요청하고 BeautifulSoup으로 받은 HTML을 분석합니다.

 

예를 들어 웹페이지 안에 여러 개의 상품 이름이 각각 같은 class를 가진 태그에 들어 있다고 생각해 보겠습니다. BeautifulSoup으로 HTML을 파싱한 뒤 해당 class에 맞는 요소를 모두 선택하면 상품 이름을 반복해서 추출할 수 있습니다. 결국 HTML 구조를 읽는 능력이 크롤링의 기본이라고 볼 수 있습니다.

 

다만 웹브라우저에 보이는 모든 정보가 처음 받은 HTML에 포함되는 것은 아닙니다. 자바스크립트가 실행된 뒤 서버에서 데이터를 추가로 받아 화면을 구성하는 사이트도 많습니다. 이런 페이지에서는 requests와 BeautifulSoup만으로 원하는 내용이 나오지 않을 수 있으므로 먼저 HTML 응답에 실제 데이터가 들어 있는지 확인해야 합니다.

구성 주요 역할
requests 웹 서버에 HTTP 요청을 보내고 응답 받기
BeautifulSoup HTML을 분석하고 원하는 요소 탐색하기
HTML 웹페이지 내용과 구조를 표현하는 문서
CSS 선택자 태그·class·id 등의 조건으로 요소 선택

💡 처음에는 복잡한 사이트보다 연습용 HTML로 시작하세요: 실제 서비스는 로그인, 동적 로딩, 접근 제한, 복잡한 HTML 구조 때문에 초보자가 원인을 찾기 어려울 수 있습니다. 간단한 HTML 문서에서 태그 선택 방법부터 익힌 뒤 실제 페이지로 넘어가는 편이 이해하기 쉽습니다.

requests와 BeautifulSoup 설치 및 시작하기

파이썬 환경이 준비되어 있다면 필요한 라이브러리부터 설치합니다. BeautifulSoup을 사용할 때 설치하는 패키지 이름은 일반적으로 beautifulsoup4이며 HTTP 요청에는 requests를 많이 사용합니다. 가상환경을 사용하고 있다면 현재 활성화된 환경에 설치했는지도 확인해야 합니다.

pip install requests beautifulsoup4

설치 후에는 requests와 BeautifulSoup을 불러옵니다. 아래 구조는 크롤링을 처음 배울 때 자주 사용하는 기본 형태입니다. 예시 주소는 실제 수집 대상이 아니라 코드 구조를 설명하기 위한 주소이므로 자신이 접근 권한을 가진 연습 페이지에 맞게 변경해야 합니다.

import requests
from bs4 import BeautifulSoup

url = "https://example.com"
response = requests.get(url, timeout=10)

print(response.status_code)

soup = BeautifulSoup(response.text, "html.parser")
print(soup.title)

requests.get()은 지정한 주소에 GET 요청을 보냅니다. 응답 객체에는 상태 코드와 응답 본문 등이 들어 있습니다. 상태 코드가 정상적인 응답 범위인지 먼저 확인한 다음 HTML을 BeautifulSoup에 전달하는 습관을 들이는 것이 좋습니다.

 

BeautifulSoup의 두 번째 인수인 html.parser는 HTML을 어떤 방식으로 분석할지 지정하는 파서입니다. 기본 학습에서는 파이썬 표준 라이브러리의 HTML 파서를 사용할 수 있습니다. 프로젝트에 따라 다른 파서를 선택하기도 하지만 처음에는 하나의 방식으로 기본 탐색법을 익히는 것으로 충분합니다.

코드 의미 확인할 점
requests.get() GET 요청 전송 응답·시간초과
status_code HTTP 상태 확인 요청 성공 여부
response.text 문자열 응답 본문 인코딩 확인
BeautifulSoup() HTML 파싱 파서 지정
코드가 실행되지 않을 때 먼저 볼 세 가지
설치 환경 — 라이브러리를 설치한 파이썬과 코드를 실행하는 파이썬이 같은지 확인합니다.
HTTP 응답 — 바로 파싱하지 말고 상태 코드와 응답 내용을 먼저 확인합니다.
HTML 존재 여부 — 브라우저 화면의 데이터가 실제 응답 HTML에도 있는지 확인합니다.

💡 처음부터 데이터를 추출하지 말고 응답부터 출력해 보세요: 크롤링 오류의 상당수는 BeautifulSoup 선택자보다 그 이전 단계에서 발생합니다. 상태 코드와 응답 본문 일부를 확인하면 페이지를 제대로 받아왔는지 빠르게 판단할 수 있습니다.

find와 select로 원하는 데이터 찾기

BeautifulSoup으로 HTML을 파싱했다면 다음 단계는 필요한 요소를 찾는 것입니다. 초보 단계에서는 find(), find_all(), select_one(), select()의 차이를 익혀두면 대부분의 기본적인 HTML 탐색을 연습할 수 있습니다.

 

find()는 조건에 맞는 첫 번째 요소를 찾는 데 사용하고 find_all()은 조건에 맞는 요소들을 여러 개 찾을 때 사용합니다. select_one()과 select()는 CSS 선택자를 이용한다는 차이가 있습니다. 이미 브라우저 개발자 도구에서 CSS 구조를 살펴보는 데 익숙하다면 select 계열이 직관적으로 느껴질 수 있습니다.

html = """
<div class="products">
 <div class="item">
 <h2 class="name">상품 A</h2>
 <span class="price">12000</span>
 </div>
 <div class="item">
 <h2 class="name">상품 B</h2>
 <span class="price">18000</span>
 </div>
</div>
"""

soup = BeautifulSoup(html, "html.parser")

first_name = soup.find("h2", class_="name")
print(first_name.get_text(strip=True))

names = soup.find_all("h2", class_="name")

for name in names:
 print(name.get_text(strip=True))

class를 조건으로 지정할 때 파이썬의 class 예약어와 구분하기 위해 class_ 형태를 사용하는 부분도 처음에는 낯설 수 있습니다. find_all()의 결과는 여러 요소를 담고 있으므로 반복문을 이용해 하나씩 처리하는 경우가 많습니다.

 

같은 구조를 CSS 선택자로 찾으면 아래와 같이 작성할 수 있습니다. 점은 class를 의미하므로 .name은 name이라는 class를 가진 요소를 선택합니다. 부모와 자식 구조를 조합하면 특정 영역 안의 요소만 더 구체적으로 선택할 수도 있습니다.

first_name = soup.select_one(".name")
print(first_name.get_text(strip=True))

names = soup.select(".item .name")

for name in names:
 print(name.get_text(strip=True))
선택 방법을 외우는 간단한 기준
하나 찾기 → find() 또는 select_one() / 여러 개 찾기 → find_all() 또는 select()
둘 중 어느 계열만 사용해야 하는 것은 아닙니다. HTML 구조와 자신이 읽기 편한 방식에 따라 선택할 수 있습니다.
메서드 결과 활용
find() 첫 요소 태그·속성 기반 탐색
find_all() 여러 요소 목록 반복 처리
select_one() 첫 요소 CSS 선택자 사용
select() 여러 요소 복합 CSS 선택

💡 선택자는 짧고 안정적으로 만드세요: 부모 태그부터 모든 단계를 길게 연결한 선택자는 HTML 구조가 조금만 바뀌어도 작동하지 않을 수 있습니다. 의미 있는 id나 class가 있다면 필요한 범위만 지정하는 방식이 유지 관리에 유리합니다.

텍스트·링크·목록 데이터 추출하기

원하는 태그를 찾았다면 실제 값만 꺼내야 합니다. 화면에 표시되는 텍스트가 필요할 때는 get_text()를 사용할 수 있고 링크 주소처럼 태그의 속성값이 필요할 때는 해당 속성을 읽습니다. 태그 전체를 문자열로 저장하는 것과 실제 필요한 값만 저장하는 것은 다릅니다.

html = """
<a class="article" href="/post/123">
 파이썬 크롤링 시작하기
</a>
"""

soup = BeautifulSoup(html, "html.parser")
article = soup.select_one("a.article")

title = article.get_text(strip=True)
link = article.get("href")

print(title)
print(link)

get_text(strip=True)를 사용하면 요소 내부의 텍스트를 가져오면서 앞뒤의 불필요한 공백을 정리할 수 있습니다. href처럼 속성값을 읽을 때 get()을 사용하면 해당 속성이 없을 경우를 처리하기도 비교적 편합니다.

 

여러 항목을 수집할 때는 각 카드나 행을 먼저 선택한 뒤 그 내부에서 제목과 가격, 링크를 각각 찾는 방식이 안정적입니다. 제목 목록과 가격 목록을 별도로 추출해 순서대로 합치는 방법은 특정 항목에 가격이 빠지는 순간 서로 다른 상품의 값이 연결될 수 있습니다.

items = soup.select(".item")
results = []

for item in items:
 name_tag = item.select_one(".name")
 price_tag = item.select_one(".price")

 name = name_tag.get_text(strip=True) if name_tag else ""
 price = price_tag.get_text(strip=True) if price_tag else ""

 results.append({
 "name": name,
 "price": price
 })

print(results)

실전에서는 원하는 요소가 항상 존재한다고 가정하지 않는 것이 중요합니다. 광고 카드에는 가격이 없거나 품절된 항목에는 특정 요소가 빠질 수 있습니다. select_one()이 요소를 찾지 못하면 None이 반환될 수 있으므로 바로 get_text()를 호출하면 오류가 발생할 수 있습니다. 조건문으로 존재 여부를 확인하는 습관이 필요합니다.

데이터 추출에서 확인할 다섯 가지
① 텍스트 — 태그 자체보다 실제 표시되는 문자열이 필요한지 확인합니다.
② 속성 — 링크는 href, 이미지는 src 등 필요한 속성을 구분합니다.
③ 누락값 — 모든 항목에 같은 요소가 존재한다고 가정하지 않습니다.
④ 상대 주소 — href 값이 완전한 인터넷 주소가 아닐 수도 있습니다.
⑤ 데이터 구조 — 관련된 값은 같은 항목 단위에서 함께 추출합니다.
초보자가 자주 하는 실수 다섯 가지
❌ 브라우저에서 보인다는 이유로 response.text에도 있다고 생각하기
❌ select_one() 결과가 항상 존재한다고 가정하기
❌ 제목과 가격 목록을 따로 추출한 뒤 무조건 같은 순서라고 가정하기
❌ 페이지 구조가 바뀌어도 선택자가 계속 작동할 것이라고 생각하기
❌ 사이트의 이용 조건과 접근 정책을 확인하지 않고 대량 요청하기

💡 한 항목씩 완성한 뒤 반복문으로 확장하세요: 처음부터 수백 개 데이터를 수집하려고 하지 말고 첫 번째 카드에서 제목과 가격이 정확하게 추출되는지 확인하세요. 한 항목이 안정적으로 동작한 뒤 여러 항목으로 확장하면 오류 위치를 찾기가 훨씬 쉽습니다.

여러 페이지를 수집하고 CSV로 저장하기

한 페이지에서 원하는 값을 추출할 수 있게 되었다면 다음 단계는 여러 항목을 일정한 구조로 저장하는 것입니다. 수집 결과를 딕셔너리 형태로 만든 뒤 리스트에 추가하면 이후 CSV 파일이나 데이터 분석 도구로 옮기기 편합니다.

 

CSV는 표 형태의 데이터를 저장하는 간단한 파일 형식이라 크롤링 연습에 적합합니다. 파이썬 표준 라이브러리의 csv 모듈을 이용할 수도 있고 데이터 분석 작업까지 이어진다면 별도의 데이터 처리 라이브러리를 사용하는 방법도 있습니다. 기초 단계에서는 수집과 저장의 흐름을 이해하는 것이 먼저입니다.

import csv

results = [
 {"name": "상품 A", "price": "12000"},
 {"name": "상품 B", "price": "18000"}
]

with open("products.csv", "w", newline="", encoding="utf-8-sig") as f:
 writer = csv.DictWriter(
 f,
 fieldnames=["name", "price"]
 )

 writer.writeheader()
 writer.writerows(results)

여러 페이지가 page=1, page=2처럼 일정한 주소 규칙을 사용한다면 반복문으로 페이지 번호를 변경할 수 있습니다. 하지만 실제 사이트마다 페이지 이동 방식이 다르기 때문에 주소 규칙부터 확인해야 합니다. 다음 버튼의 링크를 따라가는 구조나 별도의 데이터 요청을 사용하는 경우도 있습니다.

import time
import requests
from bs4 import BeautifulSoup

for page in range(1, 4):
 url = f"https://example.com/list?page={page}"

 try:
 response = requests.get(url, timeout=10)
 response.raise_for_status()
 except requests.RequestException as e:
 print("요청 오류:", e)
 continue

 soup = BeautifulSoup(response.text, "html.parser")

 for item in soup.select(".item"):
 name_tag = item.select_one(".name")

 if name_tag:
 print(name_tag.get_text(strip=True))

 time.sleep(1)

예제의 대기 시간은 모든 사이트에 적용되는 정답이 아니라 요청 사이에 간격을 두는 개념을 보여주기 위한 것입니다. 실제 수집에서는 대상 서비스의 정책과 서버 부담을 고려해야 하며 요청 빈도를 무조건 빠르게 높이는 방식은 피해야 합니다. 공개 페이지라고 해서 자동화된 대량 수집이 언제나 허용되는 것도 아닙니다.

 

또한 네트워크 요청은 언제든 실패할 수 있습니다. 일시적인 연결 오류, 시간초과, 서버 오류가 발생할 수 있으므로 실제 프로그램에서는 예외 처리를 고려해야 합니다. response.raise_for_status()를 이용하면 성공 범위를 벗어난 HTTP 응답을 오류로 처리하는 기본적인 흐름을 만들 수 있습니다.

크롤링 프로그램을 만드는 네 단계
1한 페이지의 HTML을 확인합니다
원하는 데이터가 응답 본문에 실제로 포함되어 있는지 먼저 살펴봅니다.
2한 항목을 정확하게 추출합니다
제목·가격·링크 등의 선택자가 올바르게 작동하는지 확인합니다.
3목록과 페이지 단위로 확장합니다
반복문과 예외 처리를 추가하면서 요청 범위를 천천히 늘립니다.
4결과를 검증하고 저장합니다
누락·중복·잘못 연결된 값이 없는지 확인한 뒤 CSV 등에 저장합니다.

🚨 반복문부터 크게 돌리지 마세요: 선택자가 잘못된 상태에서 수백 페이지를 요청하면 원하는 데이터는 얻지 못하면서 대상 서버에는 불필요한 요청만 보낼 수 있습니다. 한 페이지와 소수 항목으로 정확성을 확인한 뒤 필요한 범위 안에서 확장하는 것이 좋습니다.

크롤링 오류와 주의사항

코드는 정상적으로 실행되는데 데이터가 하나도 나오지 않는다면 가장 먼저 응답 HTML을 확인해야 합니다. 브라우저 개발자 도구에서 보이는 최종 화면과 requests가 처음 받은 HTML은 다를 수 있습니다. 페이지가 자바스크립트로 데이터를 추가하는 구조라면 BeautifulSoup에 전달된 HTML에는 원하는 요소가 존재하지 않을 수 있습니다.

 

두 번째로 흔한 문제는 선택자 변경입니다. 웹사이트 운영자가 디자인을 수정하면서 class 이름이나 HTML 구조를 바꾸면 기존 코드는 갑자기 빈 결과를 반환할 수 있습니다. 따라서 실제 운영용 수집 프로그램이라면 결과 개수가 비정상적으로 줄었을 때 이를 알아차릴 수 있는 검증 과정이 필요합니다.

 

문자 인코딩 문제도 발생할 수 있습니다. 한글이 깨진다면 서버가 보내는 헤더와 실제 문서의 문자 인코딩이 일치하는지 확인해야 합니다. 무조건 특정 인코딩으로 고정하기보다 응답과 페이지의 설정을 확인한 뒤 처리하는 것이 좋습니다.

 

기술적으로 데이터를 읽을 수 있다는 것과 수집·저장·재배포해도 된다는 것은 별개의 문제입니다. 크롤링을 시작하기 전에는 사이트의 이용약관과 자동화된 접근에 관한 정책, robots.txt, 저작권과 개인정보 관련 조건을 확인할 필요가 있습니다. 로그인이 필요한 영역이나 개인정보, 접근이 제한된 데이터를 임의로 수집해서는 안 됩니다.

 

공식 API가 제공된다면 크롤링보다 API 이용을 우선 검토하는 것이 좋습니다. API는 데이터를 프로그램에서 사용할 수 있도록 정해진 형식과 규칙으로 제공하기 때문에 HTML 화면 구조가 변경될 때 생기는 유지보수 부담을 줄일 수 있습니다. 호출 한도와 인증 방식, 이용 조건은 해당 서비스의 안내를 따라야 합니다.

증상 가능한 원인 먼저 확인할 부분
결과 없음 선택자 오류 응답 HTML과 태그 구조
화면과 다름 동적 렌더링 response.text의 데이터 존재 여부
간헐적 실패 네트워크·서버 응답 상태 코드·시간초과·예외
글자 깨짐 인코딩 문제 응답 헤더와 문서 인코딩

💡 디버깅 순서를 정해두면 빠릅니다: 인터넷 연결 → HTTP 상태 → 응답 본문 → 원하는 데이터 존재 여부 → 선택자 → 텍스트 추출 → 저장 결과 순서로 확인하세요. 처음부터 반복문 전체를 고치는 것보다 어느 단계까지 정상인지 좁혀가는 것이 효율적입니다.

BeautifulSoup 사용법 Q&A

Q. BeautifulSoup만 설치하면 웹 크롤링을 할 수 있나요?

BeautifulSoup의 핵심 역할은 HTML이나 XML 같은 문서를 파싱하고 원하는 요소를 탐색하는 것입니다. 웹페이지의 HTML을 가져오는 단계에는 requests와 같은 HTTP 클라이언트를 함께 사용하는 경우가 많습니다. 따라서 페이지 요청과 문서 분석을 서로 다른 단계로 이해하는 것이 좋습니다.

Q. find와 select 중 무엇을 사용해야 하나요?

둘 다 기본적인 HTML 탐색에 사용할 수 있습니다. 태그와 속성을 기준으로 직접 찾는 방식이 편하다면 find 계열을 사용할 수 있고 CSS 선택자에 익숙하다면 select 계열이 편할 수 있습니다. 한 프로젝트 안에서는 팀이 읽기 쉬운 방식으로 일관성을 유지하는 것도 중요합니다.

Q. 브라우저에는 데이터가 있는데 BeautifulSoup에서는 안 나옵니다.

자바스크립트 실행 후 데이터가 추가되는 페이지일 가능성을 확인해 볼 수 있습니다. 먼저 response.text에 찾는 문자열이 실제로 존재하는지 살펴보세요. 없다면 선택자를 계속 수정해도 해결되지 않습니다. 데이터가 어떤 방식으로 제공되는지부터 확인해야 합니다.

Q. 크롤링할 때 User-Agent만 설정하면 차단을 피할 수 있나요?

차단을 우회하는 것을 목표로 접근하기보다 해당 서비스가 자동화된 수집을 허용하는지 먼저 확인해야 합니다. User-Agent는 HTTP 요청 정보를 구성하는 요소 중 하나일 뿐이며 사이트의 접근 정책을 무효화하는 수단이 아닙니다. 공식 API가 제공된다면 그 방식을 우선 검토하는 것이 좋습니다.

Q. 초보자는 어떤 순서로 연습하면 좋나요?

간단한 HTML 문자열에서 태그와 class를 찾는 연습부터 시작하는 것이 좋습니다. 이후 공개된 연습 페이지에서 HTTP 요청과 응답 확인, 한 항목 추출, 여러 항목 반복, 누락값 처리, CSV 저장 순서로 범위를 넓혀보세요. 마지막에 페이지 이동과 예외 처리를 추가하면 전체 흐름을 이해하기 쉽습니다.

파이썬 크롤링 핵심 체크리스트

확인 항목 핵심 내용
요청 requests 등으로 필요한 웹문서의 응답을 받기
응답 확인 상태 코드와 응답 본문을 파싱 전에 확인
파싱 BeautifulSoup으로 HTML 문서 구조 분석
선택 find 또는 CSS 선택자로 필요한 요소 탐색
추출 get_text와 속성값으로 실제 데이터 분리
예외 처리 누락된 태그·네트워크 실패·시간초과에 대비
저장 딕셔너리와 리스트로 정리한 뒤 CSV 등에 저장
동적 페이지 브라우저 화면과 최초 HTML 응답의 차이 확인
접근 원칙 이용 조건·자동 접근 정책·저작권·개인정보와 서버 부담 확인

BeautifulSoup을 이용한 파이썬 크롤링은 페이지 요청 → HTTP 응답 확인 → HTML 파싱 → 요소 선택 → 텍스트와 속성 추출 → 데이터 검증 → 저장이라는 흐름으로 이해하면 어렵지 않습니다. 초보 단계에서는 복잡한 실제 서비스에 바로 적용하기보다 간단한 HTML에서 find(), find_all(), select_one(), select() 사용법을 익히는 것이 좋습니다. 이후 한 페이지에서 정확하게 동작하는 코드를 만든 다음 여러 항목과 페이지로 범위를 넓히세요. 무엇보다 기술적으로 접근할 수 있다는 이유만으로 데이터를 무제한 수집하지 말고 대상 사이트의 이용 조건과 접근 정책을 확인하며, 공식 API가 제공되는 경우에는 이를 우선 검토하는 습관이 중요합니다.

반응형