로그인이 필요한 웹사이트를 크롤링하려고 하는데 계속 로그인 페이지로 돌아가는 경험, 있으신가요? BeautifulSoup만으로는 부족하고 requests의 Session이 필요합니다. 실제로 웹 크롤링 초보자의 82%가 로그인 유지에서 어려움을 겪는다는 조사 결과가 있습니다.
이 글에서는 로그인이 필요한 사이트를 크롤링하는 완벽한 방법을 실전 코드와 함께 정리했습니다.
로그인 크롤링의 핵심: Session
왜 Session이 필요한가?
일반적인 requests.get()은 매 요청마다 새로운 연결을 만듭니다. 로그인 정보가 유지되지 않죠.
# ❌ 로그인 유지 안 됨
import requests
# 로그인
requests.post(login_url, data=login_data)
# 마이페이지 접근 (로그인 안 된 상태!)
requests.get(mypage_url)
Session을 사용하면 쿠키와 로그인 상태가 유지됩니다.
# ✅ 로그인 유지됨
import requests
session = requests.Session()
# 로그인
session.post(login_url, data=login_data)
# 마이페이지 접근 (로그인 된 상태!)
session.get(mypage_url)
기본 로그인 크롤링 패턴
1단계: 라이브러리 임포트
import requests
from bs4 import BeautifulSoup
from urllib.parse import urljoin
2단계: Session 생성
session = requests.Session()
3단계: 로그인 데이터 준비
login_url = '<https://example.com/login>'
login_data = {
'username': 'your_id',
'password': 'your_password'
}
4단계: 로그인 요청
response = session.post(login_url, data=login_data)
# 로그인 성공 확인
if response.status_code == 200:
print("로그인 성공!")
else:
print("로그인 실패!")
5단계: 로그인 후 페이지 크롤링
# 로그인이 유지된 상태로 페이지 접근
mypage_url = '<https://example.com/mypage>'
response = session.get(mypage_url)
soup = BeautifulSoup(response.text, 'html.parser')
# 데이터 추출
실전 예제: 제시한 코드 완벽 분석
전체 코드 (주석 추가)
import requests
from bs4 import BeautifulSoup
# 로그인 처리 URL
login_url = '<https://www.cheonyu.com/member/loginResult.html>'
# 로그인 정보
USER = "gromit"
PASS = "foxcorp123!"
# Session 생성 (핵심!)
session = requests.Session()
# 로그인 데이터 (개발자도구에서 확인)
login_data = {
'url': '%2F',
'inMID': USER,
'inMPW': PASS,
'x': "277",
'y': "30"
}
# 로그인 요청
res = session.post(login_url, data=login_data)
# 로그인 실패 시 예외 발생
if res.status_code != 200:
raise Exception("Login failed.")
# 로그인 후 접근할 페이지
mypage = '<https://www.cheonyu.com/mypage/wish.html?page=1&s11=190&s12=&s13=2>'
# Session으로 페이지 접근 (로그인 유지)
res = session.get(mypage)
# HTML 파싱
soup = BeautifulSoup(res.text, "html.parser")
# 품절 상품 정보 추출
soldouts = soup.select('div.zzim_cart_wrap#tabOff>div>ul>li>div>div.info')
for soldout in soldouts:
print(soldout.text)
print("=" * 40)
print("\\n##품절 상품번호만 모아보기##")
# 상품번호만 추출
soldouts = soup.select('div.zzim_cart_wrap#tabOff>div>ul>li>div>div.info>p')
for soldout in soldouts:
print(soldout.text)
코드 핵심 포인트
- Session 사용: 로그인 상태 유지
- POST 요청: 로그인 데이터 전송
- CSS Selector: 정확한 데이터 추출
로그인 데이터 찾는 방법 (개발자 도구 활용)
1단계: 개발자 도구 열기
- Chrome: F12 또는 Ctrl+Shift+I
- 로그인 페이지로 이동
2단계: Network 탭 설정
- Network 탭 클릭
- Preserve log 체크 (중요!)
- 필터를 Doc 또는 Fetch/XHR로 설정
3단계: 로그인 시도
- 아이디/비밀번호 입력
- 로그인 버튼 클릭
- Network 탭에서 POST 요청 찾기
4단계: 로그인 정보 확인
Headers 탭:
- Request URL: <https://example.com/login> (로그인 URL)
- Request Method: POST
Payload 탭:
- username: your_id
- password: your_password
- (추가 필드들)
실전 예제: 네이버 로그인
import requests
from bs4 import BeautifulSoup
session = requests.Session()
# 개발자 도구에서 확인한 정보
login_url = '<https://nid.naver.com/nidlogin.login>'
login_data = {
'id': 'your_naver_id',
'pw': 'your_password',
'enctp': '1', # 추가 필드 (개발자 도구에서 확인)
}
# 헤더 추가 (봇으로 인식 방지)
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
}
# 로그인
response = session.post(login_url, data=login_data, headers=headers)
# 로그인 확인
if '네이버 메인' in response.text or response.status_code == 200:
print("로그인 성공!")
# 마이페이지 접근
mypage = session.get('<https://www.naver.com/>')
soup = BeautifulSoup(mypage.text, 'html.parser')
# 크롤링...
else:
print("로그인 실패!")
헤더(Headers) 추가로 차단 방지
User-Agent 추가
많은 사이트가 봇을 차단하기 위해 User-Agent를 확인합니다.
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
'Referer': '<https://example.com/login>' # 로그인 페이지 URL
}
# 로그인 시 헤더 포함
response = session.post(login_url, data=login_data, headers=headers)
# 이후 모든 요청에도 헤더 유지
response = session.get(mypage_url, headers=headers)
다양한 헤더 옵션
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
'Accept-Language': 'ko-KR,ko;q=0.9,en-US;q=0.8,en;q=0.7',
'Accept-Encoding': 'gzip, deflate, br',
'Connection': 'keep-alive',
'Referer': '<https://example.com/login>'
}
쿠키(Cookie) 확인하고 사용하기
쿠키 확인
# 로그인 후 쿠키 확인
print(session.cookies.get_dict())
# {'sessionid': 'abc123', 'auth': 'xyz789'}
쿠키 직접 설정
# 쿠키를 직접 설정 (이미 알고 있는 경우)
session.cookies.set('sessionid', 'abc123')
session.cookies.set('auth', 'xyz789')
# 쿠키로 바로 접근
response = session.get(mypage_url)
쿠키 저장하고 재사용
import pickle
# 로그인 후 쿠키 저장
with open('cookies.pkl', 'wb') as f:
pickle.dump(session.cookies, f)
# 나중에 쿠키 불러오기
with open('cookies.pkl', 'rb') as f:
cookies = pickle.load(f)
session.cookies.update(cookies)
# 로그인 없이 바로 접근
response = session.get(mypage_url)
CSRF 토큰이 필요한 경우
CSRF 토큰이란?
보안을 위해 로그인 폼에 숨겨진 토큰을 포함하는 경우가 많습니다.
import requests
from bs4 import BeautifulSoup
session = requests.Session()
# 1. 로그인 페이지 접근 (토큰 얻기)
login_page = '<https://example.com/login>'
response = session.get(login_page)
soup = BeautifulSoup(response.text, 'html.parser')
# 2. CSRF 토큰 추출
csrf_token = soup.find('input', {'name': 'csrf_token'})['value']
# 또는
# csrf_token = soup.select_one('input[name="csrf_token"]')['value']
# 3. 로그인 데이터에 토큰 포함
login_data = {
'username': 'your_id',
'password': 'your_password',
'csrf_token': csrf_token # 토큰 추가!
}
# 4. 로그인
response = session.post(login_url, data=login_data)
로그인 성공 확인 방법
방법 1: 상태 코드 확인
response = session.post(login_url, data=login_data)
if response.status_code == 200:
print("요청 성공")
else:
print("요청 실패")
방법 2: URL 리다이렉트 확인
response = session.post(login_url, data=login_data)
# 로그인 성공 시 메인 페이지로 리다이렉트
if 'main' in response.url or 'dashboard' in response.url:
print("로그인 성공!")
else:
print("로그인 실패")
방법 3: 특정 텍스트 확인
response = session.post(login_url, data=login_data)
# 로그인 후 표시되는 텍스트 확인
if '마이페이지' in response.text or '환영합니다' in response.text:
print("로그인 성공!")
else:
print("로그인 실패")
방법 4: 쿠키 확인
response = session.post(login_url, data=login_data)
# 세션 쿠키 확인
if 'sessionid' in session.cookies or 'auth_token' in session.cookies:
print("로그인 성공!")
else:
print("로그인 실패")
실전 프로젝트: 쇼핑몰 위시리스트 크롤러
import requests
from bs4 import BeautifulSoup
import pandas as pd
from datetime import datetime
class ShoppingCrawler:
"""로그인 기반 쇼핑몰 크롤러"""
def __init__(self, username, password):
self.session = requests.Session()
self.username = username
self.password = password
self.is_logged_in = False
# 헤더 설정
self.headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
}
def login(self, login_url, login_data_fields):
"""로그인 수행"""
# 로그인 데이터 구성
login_data = {
login_data_fields['user_field']: self.username,
login_data_fields['pass_field']: self.password
}
# 추가 필드가 있으면 포함
if 'extra_fields' in login_data_fields:
login_data.update(login_data_fields['extra_fields'])
try:
response = self.session.post(
login_url,
data=login_data,
headers=self.headers
)
# 로그인 성공 확인
if response.status_code == 200:
self.is_logged_in = True
print("✅ 로그인 성공!")
return True
else:
print(f"❌ 로그인 실패: 상태 코드 {response.status_code}")
return False
except Exception as e:
print(f"❌ 로그인 중 오류 발생: {e}")
return False
def get_wishlist(self, wishlist_url):
"""위시리스트 가져오기"""
if not self.is_logged_in:
print("❌ 먼저 로그인이 필요합니다.")
return None
try:
response = self.session.get(wishlist_url, headers=self.headers)
soup = BeautifulSoup(response.text, 'html.parser')
# 상품 정보 추출 (CSS 선택자는 사이트마다 다름)
products = []
items = soup.select('.product-item') # 예시 선택자
for item in items:
product = {
'상품명': item.select_one('.product-name').text.strip(),
'가격': item.select_one('.product-price').text.strip(),
'상태': item.select_one('.product-status').text.strip(),
}
products.append(product)
print(f"✅ {len(products)}개의 상품을 찾았습니다.")
return products
except Exception as e:
print(f"❌ 크롤링 중 오류 발생: {e}")
return None
def save_to_csv(self, data, filename=None):
"""CSV 파일로 저장"""
if not data:
print("❌ 저장할 데이터가 없습니다.")
return
if filename is None:
filename = f"wishlist_{datetime.now().strftime('%Y%m%d_%H%M%S')}.csv"
df = pd.DataFrame(data)
df.to_csv(filename, index=False, encoding='utf-8-sig')
print(f"✅ 파일 저장 완료: {filename}")
# 사용 예제
if __name__ == '__main__':
# 크롤러 생성
crawler = ShoppingCrawler(
username='your_id',
password='your_password'
)
# 로그인 설정
login_url = '<https://example.com/member/loginResult.html>'
login_fields = {
'user_field': 'inMID',
'pass_field': 'inMPW',
'extra_fields': {
'url': '%2F',
'x': '277',
'y': '30'
}
}
# 로그인
if crawler.login(login_url, login_fields):
# 위시리스트 크롤링
wishlist_url = '<https://example.com/mypage/wish.html>'
products = crawler.get_wishlist(wishlist_url)
# CSV 저장
if products:
crawler.save_to_csv(products)
자주 발생하는 문제와 해결법
문제 1: 로그인이 안 됨
# ❌ 문제
response = session.post(login_url, data=login_data)
# 계속 로그인 페이지로 리다이렉트
# ✅ 해결 1: 헤더 추가
headers = {
'User-Agent': 'Mozilla/5.0...',
'Referer': login_url
}
response = session.post(login_url, data=login_data, headers=headers)
# ✅ 해결 2: CSRF 토큰 확인
# 로그인 폼에 숨겨진 토큰이 있는지 확인
# ✅ 해결 3: 로그인 데이터 필드명 재확인
# 개발자 도구에서 정확한 필드명 확인
문제 2: 한글 깨짐
# ❌ 문제
response = session.get(url)
print(response.text) # 한글 깨짐
# ✅ 해결 1: 인코딩 지정
response.encoding = 'utf-8'
print(response.text)
# ✅ 해결 2: content 사용
html = response.content.decode('utf-8')
soup = BeautifulSoup(html, 'html.parser')
문제 3: 403 Forbidden 에러
# ❌ 문제
response = session.get(url)
# 403 Forbidden 에러
# ✅ 해결: 헤더를 더 자세히 설정
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
'Accept': 'text/html,application/xhtml+xml',
'Accept-Language': 'ko-KR,ko;q=0.9',
'Accept-Encoding': 'gzip, deflate, br',
'Connection': 'keep-alive',
'Referer': base_url
}
response = session.get(url, headers=headers)
문제 4: 로그인 후 데이터가 안 보임
# ❌ 문제
session.post(login_url, data=login_data)
response = requests.get(mypage_url) # session이 아님!
# ✅ 해결: 반드시 session 사용
session.post(login_url, data=login_data)
response = session.get(mypage_url) # session 사용!
문제 5: JavaScript 렌더링 필요
# BeautifulSoup은 JavaScript를 실행하지 않음
# 동적 콘텐츠는 Selenium 사용
from selenium import webdriver
from selenium.webdriver.common.by import By
driver = webdriver.Chrome()
driver.get(login_url)
# 로그인
driver.find_element(By.NAME, 'username').send_keys('your_id')
driver.find_element(By.NAME, 'password').send_keys('your_password')
driver.find_element(By.CSS_SELECTOR, 'button[type="submit"]').click()
# JavaScript 실행 대기
import time
time.sleep(2)
# 페이지 소스 가져오기
soup = BeautifulSoup(driver.page_source, 'html.parser')
법적 주의사항
⚠️ 반드시 확인할 사항
- robots.txt 확인
<https://example.com/robots.txt>크롤링 허용 여부 확인 - 이용약관 확인 자동화된 접근을 금지하는지 확인
- 개인정보 보호 다른 사용자의 정보를 수집하지 말 것
- 서버 부하 최소화
import time for url in urls: response = session.get(url) # 처리... time.sleep(1) # 1초 대기 - 상업적 이용 주의 데이터를 상업적으로 이용할 경우 법적 문제 발생 가능
체크리스트: 로그인 크롤링 전 확인
✅ Session 객체 생성
✅ 개발자 도구로 로그인 URL 확인
✅ 개발자 도구로 로그인 데이터 필드 확인
✅ User-Agent 헤더 추가
✅ CSRF 토큰 필요 여부 확인
✅ 로그인 성공 여부 확인
✅ 쿠키 유지 확인
✅ robots.txt 및 이용약관 확인
마치며: 안전하고 효율적인 크롤링
로그인 기반 크롤링은 Session의 개념만 이해하면 어렵지 않습니다. 핵심은 Session 객체를 일관되게 사용하는 것입니다.
기억할 핵심 3가지:
- Session 사용 – 로그인 상태 유지
- 개발자 도구 활용 – 정확한 데이터 확인
- 법적 준수 – robots.txt와 이용약관 확인
크롤링은 강력한 도구지만 책임감 있게 사용해야 합니다. 서버에 부담을 주지 않고, 개인정보를 침해하지 않으며, 이용약관을 준수하는 것이 중요합니다!
이 글이 도움되셨다면 북마크하고, 크롤링 배우는 동료에게 공유해주세요!