🚀
고급 (Advanced)
requests · BeautifulSoup · session · robots.txt
7주차 — 웹 스크래핑
requests 로 HTTP 요청, BeautifulSoup 로 HTML 파싱, 세션·쿠키 관리, 그리고 robots.txt 와 매너까지 — 윤리적 스크래핑의 기본을 익힙니다.
requestsBeautifulSoupscrapingethics
소요 시간
⏱ 2시간
난이도
📊 고급
선수 조건
🎯 고급 6주차
결과물
정중하고 견고한 스크래퍼 작성
이 강의에서 배우는 것
- 1requests 로 HTTP GET/POST 요청을 한다
- 2BeautifulSoup 로 HTML 을 파싱한다
- 3헤더·세션·쿠키를 다룬다
- 4robots.txt 와 매너를 지킨다
1. requests
python
import requests
r = requests.get("https://example.com")
print(r.status_code) # 200
print(r.text[:200])
print(r.headers["Content-Type"])
# JSON API
r = requests.get("https://api.github.com/users/python")
data = r.json()
print(data["public_repos"])2. POST 요청
python
r = requests.post("https://httpbin.org/post",
data={"key": "value"},
headers={"User-Agent": "MyBot 1.0"},
)3. BeautifulSoup
bash
pip install beautifulsoup4 lxmlpython
from bs4 import BeautifulSoup
html = """<html><body>
<h1>제목</h1>
<ul>
<li class="item">A</li>
<li class="item">B</li>
</ul>
</body></html>"""
soup = BeautifulSoup(html, "html.parser")
print(soup.h1.text)
print([li.text for li in soup.find_all("li")])
print([li.text for li in soup.select("li.item")]) # CSS 선택자4. 세션과 쿠키
python
session = requests.Session()
session.headers.update({"User-Agent": "Mozilla/5.0"})
# 로그인
session.post("https://example.com/login", data={"user": "x", "pass": "y"})
# 같은 세션 → 쿠키 유지
r = session.get("https://example.com/me")5. 매너와 윤리
- robots.txt 확인: https://example.com/robots.txt
- 요청 간격: time.sleep(1) 정도 권장
- 사용자 에이전트: 정직하게 표시
- 저작권·약관 준수
⚠️
스크래핑은 합법성과 윤리가 중요한 영역입니다. 사이트의 이용 약관과 robots.txt 를 반드시 확인하세요.
6. 실전 패턴
python
import requests
from bs4 import BeautifulSoup
URL = "https://news.ycombinator.com"
HEADERS = {"User-Agent": "Mozilla/5.0 (study)"}
def fetch_titles():
r = requests.get(URL, headers=HEADERS, timeout=10)
r.raise_for_status()
soup = BeautifulSoup(r.text, "html.parser")
titles = [a.get_text() for a in soup.select(".titleline > a")]
return titles[:10]
for i, title in enumerate(fetch_titles(), 1):
print(f"{i}. {title}")자주 하는 실수
- timeout 누락 — 무한 대기. 항상 timeout=10
- raise_for_status() 안 함 — 404 못 알아챔
- HTML 파서 미지정 — 명시적으로 'html.parser'
- 선택자 깨지기 쉬움 — id/class 변동에 취약
- 차단당함 — 너무 빠른 요청. time.sleep 으로 간격
FAQ
Q1. JS 렌더링 페이지? — Selenium, Playwright 사용.
Q2. 동적 사이트? — 보통 내부 API 직접 호출(개발자 도구 → 네트워크 탭).
Q3. 로그인 후 페이지? — Session 사용. CSRF 토큰까지.
💻 예제 (examples)
실제로 실행해 결과를 확인할 수 있는 예제 코드입니다.
01_requests_basic.py— GET, JSON
CODE
import requests
r = requests.get("https://httpbin.org/get", params={"q": "python"}, timeout=10)
print(r.status_code)
print(r.json()["args"])▶ 실행 결과
200
{'q': 'python'}02_bs4_parse.py— HTML 파싱
CODE
from bs4 import BeautifulSoup
html = """<html><body>
<h1>제목</h1>
<ul>
<li class="item">A</li>
<li class="item">B</li>
<li class="item">C</li>
</ul>
</body></html>"""
soup = BeautifulSoup(html, "html.parser")
print(soup.h1.text)
print([li.text for li in soup.select("li.item")])▶ 실행 결과
제목
['A', 'B', 'C']03_session.py— 세션 + 쿠키
CODE
import requests
s = requests.Session()
s.headers.update({"User-Agent": "study/1.0"})
# httpbin이 보낸 쿠키를 받아서 그대로 사용
s.get("https://httpbin.org/cookies/set/sessionid/abc123")
r = s.get("https://httpbin.org/cookies")
print(r.json())▶ 실행 결과
{'cookies': {'sessionid': 'abc123'}}04_scrape_demo.py— 실제 스크래핑 (httpbin)
CODE
import requests
from bs4 import BeautifulSoup
r = requests.get("https://httpbin.org/html", timeout=10)
r.raise_for_status()
soup = BeautifulSoup(r.text, "html.parser")
print("h1:", soup.h1.get_text(strip=True))
print("p 개수:", len(soup.find_all("p")))▶ 실행 결과
h1: Herman Melville - Moby-Dick
p 개수: 1📝 과제 (exercises)
직접 풀어보고, 막힐 때 정답을 펼쳐 비교해보세요.
과제 1
JSON API 호출
목표: GitHub API 로 사용자 저장소 정보를 가져온다.
요구사항
- username 입력 받기
- public_repos 개수 출력
- 예외 처리 (timeout, raise_for_status)
💡 힌트
https://api.github.com/users/{username}
입출력 예시
사용자: torvalds
공개 저장소: 7채점
- · timeout 사용
- · raise_for_status 사용
▶정답 코드 펼치기 / 접기
SOLUTION
import requests
def info(username):
url = f"https://api.github.com/users/{username}"
r = requests.get(url, timeout=10)
r.raise_for_status()
data = r.json()
return data.get("public_repos", 0)
name = "torvalds"
print(f"사용자: {name}")
print(f"공개 저장소: {info(name)}")▶ 실행 결과
사용자: torvalds
공개 저장소: 7과제 2
HTML 헤딩 추출
목표: 주어진 URL 의 모든 h1, h2 텍스트를 추출한다.
요구사항
- BeautifulSoup 사용
- h1, h2 모두
💡 힌트
soup.find_all('h1')
입출력 예시
H1: ['Herman Melville - Moby-Dick']
H2: []채점
- · 파서 명시
- · 결과 list
▶정답 코드 펼치기 / 접기
SOLUTION
import requests
from bs4 import BeautifulSoup
r = requests.get("https://httpbin.org/html", timeout=10)
soup = BeautifulSoup(r.text, "html.parser")
h1s = [t.get_text(strip=True) for t in soup.find_all("h1")]
h2s = [t.get_text(strip=True) for t in soup.find_all("h2")]
print(f"H1: {h1s}")
print(f"H2: {h2s}")▶ 실행 결과
H1: ['Herman Melville - Moby-Dick']
H2: []과제 3
robots.txt 확인
목표: 주어진 도메인의 robots.txt 를 가져와 출력한다.
요구사항
- https://{domain}/robots.txt
- 200 이 아니면 안내 메시지
💡 힌트
status_code 확인
입출력 예시
User-agent: *
Disallow: ...채점
- · timeout 사용
- · 비정상 응답 처리
▶정답 코드 펼치기 / 접기
SOLUTION
import requests
def show_robots(domain):
url = f"https://{domain}/robots.txt"
r = requests.get(url, timeout=10)
if r.status_code != 200:
print(f"robots.txt 없음 (status {r.status_code})")
return
print(r.text[:200])
show_robots("github.com")▶ 실행 결과
# If you would like to crawl GitHub contact us via https://support.github.com/contact?tags=dotcom-robots
# We also provide an extensive API: https://docs.github.com
User-agent: *
Disallow: /*/pulls
Disallow: /*/pull/