파이썬 크롤링 기초 - requests와 BeautifulSoup으로 웹 데이터 자동 수집하기
매일 아침 경쟁사 사이트 들어가서 가격 확인하고 엑셀에 복붙하고, 공고 페이지 새로고침하면서 새 글 올라왔나 보고.
하루 30분이면 별거 아닌 것 같은데, 한 달이면 10시간이에요. 딱 하루치 근무시간이 마우스 드래그로 사라지는 거죠.
15까지 다룬 건 전부 이미 내 컴퓨터에 있는 데이터였어요. 엑셀, PDF, Word, 메일. 오늘부터는 방향이 바뀝니다. 밖에서 데이터를 구해오는 이야기예요.
파이썬 크롤링, 사실 별거 아니에요
주소창에 주소를 치면 브라우저가 서버한테 "이 페이지 좀 주세요" 하고 요청을 보내요. 서버는 HTML이라는 글자 덩어리를 답장으로 보내주고, 브라우저는 그걸 읽어서 우리 눈에 보기 좋게 그려주는 거예요.
파이썬이 하는 일은 앞의 두 단계까지예요. 예쁘게 그리는 대신, 받아온 글자 덩어리에서 필요한 부분만 쏙 뽑아내는 거죠.
비유하자면 requests는 도서관에서 책 한 권을 통째로 복사해오는 도구예요. BeautifulSoup은 그 복사본에서 필요한 문장에만 형광펜을 긋는 도구고요. 복사를 안 했으면 형광펜 그을 종이도 없잖아요. 그래서 둘은 늘 짝으로 다닙니다.
크롤링보다 API가 먼저입니다
어떤 사이트는 아예 데이터를 정식으로 내주는 창구를 열어둬요. API라고 부르는 건데, 네이버나 공공데이터포털이 대표적이에요.
안내데스크에서 정리된 서류를 받는 것과, 게시판에 붙은 종이를 직접 옮겨 적는 것의 차이예요. 크롤링은 정문이 없을 때 쓰는 방법이라는 것만 기억해두시면 됩니다.
웹페이지를 통째로 가져오는 requests
일단 받아오는 것부터 해볼게요.
import requests # 웹페이지를 요청하는 도구
url = "https://example.com" # 연습용으로 쓰기 좋은 사이트예요
response = requests.get(url) # 이 주소로 "페이지 주세요" 요청
print(response.status_code) # 200이면 성공
print(response.text[:300]) # 받아온 HTML 앞부분 300글자만
# 실행 결과:
# 200
# <!doctype html>
# <html>
# <head>
# <title>Example Domain</title>
# ...
status_code가 200이면 성공이에요. 403은 거부당한 거고, 404는 그런 페이지가 없다는 뜻, 500은 상대 서버 쪽 문제고요.
그리고 response.text를 찍어보면... 화면 가득 이상한 글자들이 쏟아지죠. 저도 처음엔 "이게 다 뭐야" 싶었어요. 그런데 정상이에요. 저 안에 원하는 게 분명히 들어 있거든요.
HTML, 딱 이만큼만 알면 됩니다
여기서 제일 겁먹으시는데, HTML을 작성할 줄 알 필요는 전혀 없어요. 읽고 위치만 찾으면 됩니다.
태그는 여는 것과 닫는 것 사이에 내용이 들어가는 구조예요. <a>는 링크, <h1>~<h3>은 제목, <li>는 목록 한 줄, <div>는 큰 상자예요.
class와 id는 태그에 붙는 이름표고요. class는 같은 종류끼리 나눠 쓰는 거라 여러 개 있을 수 있고, id는 페이지에 딱 하나뿐이에요. href엔 링크가 가리키는 주소가 담깁니다.
<a href="https://news.com/1" class="title">오늘의 뉴스 제목</a>
태그는 a, class는 title, 링크 주소는 href 안에, 우리가 원하는 글자는 태그 사이에. 딱 이 구조만 눈에 익히시면 돼요.
python 웹 스크래핑, 개발자 도구부터 열어보세요
여기가 이 글에서 제일 중요한 부분이에요. 솔직히 문법보다 훨씬 중요해요. 이걸 못 하면 어떤 사이트도 못 긁거든요. 순서대로 해보실게요.
- 크롬으로 긁고 싶은 페이지를 엽니다
- 원하는 글자(예: 뉴스 제목) 위에서 마우스 우클릭 → "검사" 를 누릅니다 (F12를 눌러도 돼요)
- 오른쪽에 창이 열리면서, 방금 우클릭한 부분의 HTML이 파란색으로 하이라이트되어 있을 거예요
- 그 줄에서 세 가지를 확인합니다. 태그 이름이 뭔지(
a인지h3인지span인지), class나 id가 뭔지, 내가 원하는 게 글자인지 링크인지 - 바로 아래 두 번째 항목도 똑같이 검사해서 같은 class를 쓰는지 확인합니다. 같으면 한 번에 전부 수집할 수 있어요
개발자 도구는 웹페이지의 엑스레이 같은 거예요. 겉으로는 예쁜 화면이지만, F12를 누르면 그 안의 뼈대가 다 보이거든요.
팁 하나. 하이라이트된 줄에서 우클릭 → Copy → Copy selector 를 누르면 CSS 선택자를 그대로 복사할 수 있어요. 다만 복사된 선택자가 너무 길면 잘 깨지니까, 익숙해지면 class 하나로 줄여 쓰는 게 좋아요.
그리고 이 말씀은 꼭 드리고 싶어요.
사이트마다 구조가 전부 달라요. 정답 코드라는 건 없고, 매번 F12로 확인하는 게 정상이에요.
남의 블로그 코드를 그대로 복붙했는데 안 된다고 자책하실 필요 없어요. 원래 안 되는 게 맞아요.
BeautifulSoup으로 원하는 것만 골라내기
이제 형광펜을 들 차례예요. 설치부터 할게요.
pip install beautifulsoup4
여기서 함정 하나. 설치는 beautifulsoup4인데, 코드에서 불러올 땐 bs4예요.
from bs4 import BeautifulSoup # 설치명이랑 import명이 다릅니다
12에서 pip install python-docx 해놓고 import docx 했던 거 기억나세요? 또 나왔죠. 외우려 하지 마시고, 그럴 수 있다는 것만 알아두세요.
받아온 HTML을 BeautifulSoup에 넘겨주면, 뒤져볼 수 있는 형태로 바뀝니다.
import requests
from bs4 import BeautifulSoup
url = "https://example.com"
response = requests.get(url)
soup = BeautifulSoup(response.text, "html.parser") # HTML을 뜯어볼 수 있게 변환
title = soup.find("h1") # 첫 번째 h1 태그를 찾기
print(title.text) # 태그 사이의 글자만 꺼내기
# 실행 결과:
# Example Domain
"html.parser"는 "파이썬에 기본으로 들어있는 해석기를 쓰겠다"는 뜻이에요. 지금은 항상 같이 써주는 주문이라고 보셔도 됩니다.
find는 하나, find_all은 전부
soup.find("h2") # 조건에 맞는 첫 번째 하나만
soup.find(class_="title") # class가 title인 첫 요소
soup.find("div", id="content") # id가 content인 div
soup.find_all("a") # 조건에 맞는 전부 (리스트로 나옴)
soup.select("div.news-list a.title") # CSS 선택자 방식
주의점이 두 가지 있어요.
첫째, class가 아니라 class_예요. 밑줄이 붙어 있죠. class가 파이썬에서 이미 다른 용도로 쓰이는 예약어라 어쩔 수 없이 밑줄을 붙인 거예요.
둘째, find_all의 결과는 리스트예요. 그래서 .text를 바로 못 붙여요.
items = soup.find_all("a")
print(items.text) # 에러! 리스트에는 .text가 없어요
for item in items: # 이렇게 하나씩 꺼내서
print(item.text) # 각각의 글자를 출력해야 맞습니다
처음에 이거 정말 많이 틀리시더라고요. 저도 그랬고요.
수집한 데이터를 엑셀 파일로 저장하기
여기가 오늘의 하이라이트예요. 05에서 배운 openpyxl을 붙이면 화면 출력으로 끝나지 않고 바로 엑셀 파일이 나와요. 복붙하던 30분이 3초가 되는 지점입니다.
두 가지만 미리 알려드릴게요. item["href"]처럼 대괄호를 쓰면 태그의 속성값을 꺼낼 수 있어요(이미지면 item["src"]). 그리고 링크가 /news/1 처럼 반쪽만 나오는 경우가 있는데, 상대경로라고 해서 앞에 사이트 주소를 붙여줘야 완전해져요.
import requests
import time
from bs4 import BeautifulSoup
from openpyxl import Workbook # 05에서 배운 엑셀 도구
url = "https://예시사이트.com/news" # 실제 주소로 바꿔주세요
headers = {"User-Agent": "Mozilla/5.0"} # 차단 방지용 명찰 (아래에서 설명)
response = requests.get(url, headers=headers) # 페이지 요청
response.encoding = "utf-8" # 한글 깨짐 방지
soup = BeautifulSoup(response.text, "html.parser")
wb = Workbook() # 새 엑셀 파일 만들기
ws = wb.active # 첫 번째 시트 선택
ws.append(["제목", "링크"]) # 맨 윗줄에 제목 행 넣기
for item in soup.select("a.title"): # F12로 찾은 선택자를 여기에!
title = item.get_text(strip=True) # 제목 글자
link = item["href"] # 링크 주소
if not link.startswith("http"): # 반쪽 주소라면
link = "https://예시사이트.com" + link # 앞에 도메인 붙이기
ws.append([title, link]) # 엑셀에 한 줄 추가
wb.save("뉴스목록.xlsx") # 파일로 저장
print("저장 완료")
time.sleep(1) # 한 박자 쉬기 (서버에 대한 예의)
# 실행 결과:
# 저장 완료
URL이랑 a.title 부분은 예시예요. 그대로 돌리면 당연히 안 돼요. F12로 직접 확인한 주소와 선택자를 넣으셔야 합니다.
크롤링 전에 지켜야 할 최소한의 예의
겁주려는 게 아니라, 알고 있으면 오히려 마음이 편해지는 것들이에요.
robots.txt를 한번 보세요. 사이트 주소 뒤에 /robots.txt를 붙이면 "여긴 긁지 마세요" 하는 안내가 적혀 있어요. 이용약관에 크롤링 금지를 써둔 곳도 있고요.
너무 빠르게 요청하지 마세요. 초당 수십 번씩 두드리면 서버에 부담이 되고, 심하면 업무방해가 될 수 있어요. time.sleep(1) 한 줄이면 됩니다.
개인정보와 저작물은 건드리지 마세요. 이름이나 연락처는 공개돼 있어도 수집·활용에 법적 제약이 있고, 기사 본문을 통째로 복제해 재배포하는 것도 저작권 문제가 될 수 있어요.
한 줄로 줄이면 이거예요. 공개된 페이지를 사람처럼, 천천히, 개인적인 용도로 보는 것.
처음에 꼭 만나게 되는 에러들
ModuleNotFoundError: No module named 'bs4'
설치를 안 하셨거나 pip install bs4로 잘못 설치하신 거예요. pip install beautifulsoup4가 맞습니다.
status_code가 403으로 나올 때
서버가 "너 파이썬이지?" 하고 알아채서 문을 닫은 거예요. 명찰을 하나 달아주면 됩니다.
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
}
response = requests.get(url, headers=headers) # 명찰 달고 요청
"저 파이썬 아니고 그냥 크롬 쓰는 사람이에요"라고 말해주는 셈이죠.
AttributeError: 'NoneType' object has no attribute 'text'
찾으려던 걸 못 찾아 None이 나왔는데 거기다 .text를 붙인 상황이에요. 십중팔구 class 이름 오타예요. 이렇게 방어해두면 마음이 편해요.
title = soup.find("h1")
if title: # 찾았을 때만
print(title.text)
else:
print("못 찾았어요. F12로 태그를 다시 확인해 보세요.")
한글이 ìëíì¸ì 처럼 깨질 때
인코딩 자동 감지가 실패한 거예요. response.encoding = "utf-8" 한 줄이면 대부분 해결됩니다.
오늘 직접 해보실 것
- 자주 들어가는 사이트를 하나 골라서, F12로 제목이 어떤 태그와 class로 되어 있는지 찾아 메모해보세요. 코드는 안 쓰셔도 돼요. 이것만 해도 절반은 온 거예요.
- 그 사이트를
requests.get()으로 받아와서status_code가 200인지 확인해보세요. 403이 나오면 User-Agent 헤더를 붙여보시고요. find_all로 목록 제목 5개만 출력해보세요. 힌트는for item in items[:5]:입니다.- 심화 과제. 제목과 링크를 엑셀 파일로 저장해보세요.
첫 시도에 안 되는 게 정상이에요. 원인은 거의 둘 중 하나거든요. class 이름 오타이거나, 아니면 지금부터 말씀드릴 그 경우거나.
분명히 보이는데 안 잡힐 때가 있어요
브라우저에는 뉴스 제목이 멀쩡히 보이는데 print(soup) 해보면 그 글자가 어디에도 없는 경우가 있어요. 선택자를 아무리 바꿔도 결과가 계속 빈 리스트 []로 나오고요.
이거 실패하신 게 아니에요.
그 페이지가 처음엔 빈 껍데기만 보내주고, 나중에 자바스크립트로 내용을 채워 넣는 방식이라 그래요. requests는 "처음 받은 HTML"만 볼 수 있으니 당연히 못 찾는 거죠.
이럴 땐 파이썬이 브라우저를 직접 켜서, 화면이 다 그려질 때까지 기다렸다 보게 만들어야 해요. 그게 다음 편에서 다룰 Selenium이에요.
제 글이 도움이 되셨다면 댓글 & 공감 부탁드려요 😀
'Application > Python' 카테고리의 다른 글
| [Python] 18. 네이버·구글 검색 결과, 파이썬으로 자동 수집하는 가장 쉬운 방법 (0) | 2026.07.30 |
|---|---|
| [Python] 17. requests로 안 되던 사이트, 파이썬 Selenium으로 긁어보기 (0) | 2026.07.27 |
| [Python] 15. 파이썬으로 양식 일괄 발송 시스템 만들기 (엑셀 명단 → 개인 문서 → 자동 메일) (0) | 2026.07.22 |
| [Python] 14. 파이썬으로 첨부파일과 HTML 메일 보내기 (0) | 2026.07.21 |
| [Python] 13. 파이썬 이메일 자동 발송: smtplib 기초 (0) | 2026.07.10 |