웹 크롤러의 종류
범용 크롤러는 검색 엔진처럼 모든 웹사이트를 무차별적으로 수집하여 키워드를 추출하고 인덱스를 생성한다. 반면 특화(포커스) 크롤러는 특정 도메인에 한정된 데이터만 수집하도록 설계된다.
Robots 프로토콜
robots.txt 파일은 크롤러가 접근 가능한 경로를 명시하는 규약이다. 예: taobao.com/robots.txt. 이는 법적 구속력 없이 자율적으로 따르는 '신사 협정'이다.
HTTP 요청과 Python urllib 모듈
웹 크롤링은 HTTP 프로토콜을 통해 브라우저 동작을 코드로 자동화하는 과정이다. Python 표준 라이브러리 urllib은 다음 하위 모듈로 구성된다:
urllib.request: URL 열기 및 데이터 읽기urllib.error: 요청 관련 예외 처리urllib.parse: URL 파싱 및 인코딩urllib.robotparser: robots.txt 분석
urllib.request로 GET 요청 보내기
urlopen() 함수는 기본적인 HTTP 요청을 수행한다. 반환값은 파일 객체와 유사한 HTTPResponse 객체다.
from urllib.request import urlopen
with urlopen('http://www.bing.com') as resp:
print(f"상태 코드: {resp.status}") # 200
print(f"응답 URL: {resp.geturl()}") # 리디렉션 후 실제 URL
print(f"헤더 정보:\n{resp.info()}")
content = resp.read() # 바이트 데이터
기본 User-Agent는 Python-urllib/3.x로, 많은 사이트에서 이를 차단한다. 따라서 브라우저 흉내를 내기 위해 사용자 에이전트를 변경해야 한다.
Request 객체로 요청 헤더 조작
Request 클래스를 사용하면 요청 헤더를 자유롭게 설정할 수 있다.
from urllib.request import Request, urlopen
import random
user_agents = [
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_0) AppleWebKit/537.36"
]
url = "http://www.bing.com/"
req = Request(url)
req.add_header("User-Agent", random.choice(user_agents))
with urlopen(req, timeout=20) as response:
html = response.read()
print(f"요청한 User-Agent: {req.get_header('User-agent')}")
URL 인코딩과 디코딩
urllib.parse.urlencode()은 GET/POST 파라미터를 URL-safe 형식으로 변환한다. 특수문자(/, &, = 등)와 비ASCII 문자(예: 한글)는 퍼센트 인코딩된다.
from urllib.parse import urlencode, unquote
# 인코딩 예시
params = {"q": "马哥教育"}
encoded = urlencode(params) # q=%E9%A9%AC%E5%93%A5%E6%95%99%E8%82%B2
# 디코딩 예시
decoded = unquote(encoded) # q=马哥教育
실제 활용: Bing 검색 결과 저장
키워드로 Bing 검색을 수행하고 결과를 HTML 파일로 저장하는 예제:
from urllib.parse import urlencode
from urllib.request import Request, urlopen
base = "http://cn.bing.com/search"
query = urlencode({"q": "马哥教育"})
full_url = f"{base}?{query}"
req = Request(full_url, headers={"User-Agent": "Mozilla/5.0..."})
with urlopen(req) as res, open("bing_result.html", "wb") as f:
f.write(res.read())
POST 요청 테스트
httpbin.org 서비스를 이용해 POST 데이터 전송을 검증할 수 있다:
from urllib.request import Request, urlopen
from urllib.parse import urlencode
import json
data = urlencode({"name": "张三!@#$%", "age": "6"}).encode()
req = Request("http://httpbin.org/post", data=data)
req.add_header("User-Agent", "Mozilla/5.0...")
with urlopen(req) as res:
result = json.loads(res.read())
print(result["form"]) # 전송된 폼 데이터 확인