파이썬이 인터넷과 대화하는 방법
날씨 API, 주식 데이터, OpenAI API 모두 HTTP를 통해 데이터를 주고받습니다.
flowchart LR
PY["파이썬 코드"] -->|"HTTP 요청\n(GET/POST/PUT/DELETE)"| API["외부 API 서버"]
API -->|"JSON 응답"| PY
requests 설치와 기본 사용
pip install requests
import requests
# GET 요청: 데이터 조회
response = requests.get("https://api.github.com/users/octocat")
print(response.status_code) # 200 (성공)
print(response.headers["content-type"])
# JSON 응답 파싱
data = response.json()
print(data["name"]) # The Octocat
print(data["bio"]) # 프로필 소개
HTTP 상태 코드
| 코드 | 의미 |
|---|---|
| 200 | 성공 |
| 201 | 생성 성공 |
| 400 | 잘못된 요청 |
| 401 | 인증 필요 |
| 403 | 권한 없음 |
| 404 | 찾을 수 없음 |
| 429 | 요청 한도 초과 |
| 500 | 서버 오류 |
GET 요청: 쿼리 파라미터
import requests
# URL에 직접 쿼리스트링 쓰는 대신 params 사용
response = requests.get(
"https://api.example.com/search",
params={
"query": "파이썬",
"limit": 10,
"lang": "ko"
}
)
# 실제 요청: https://api.example.com/search?query=파이썬&limit=10&lang=ko
print(response.url) # 완성된 URL 확인
data = response.json()
POST 요청: 데이터 전송
import requests
# JSON 데이터 전송
response = requests.post(
"https://api.example.com/users",
json={ # json= 파라미터: 자동으로 Content-Type: application/json
"name": "김철수",
"email": "kim@example.com"
}
)
if response.status_code == 201:
created = response.json()
print(f"생성됨: {created['id']}")
헤더와 인증
import requests
import os
API_KEY = os.getenv("MY_API_KEY")
headers = {
"Authorization": f"Bearer {API_KEY}",
"Content-Type": "application/json",
"User-Agent": "MyApp/1.0"
}
response = requests.get(
"https://api.example.com/protected",
headers=headers
)
에러 처리
import requests
from requests.exceptions import RequestException, Timeout, ConnectionError
def fetch_data(url: str) -> dict | None:
try:
response = requests.get(url, timeout=10) # 10초 타임아웃
response.raise_for_status() # 4xx, 5xx면 예외 발생
return response.json()
except Timeout:
print(f"요청 시간 초과: {url}")
except ConnectionError:
print("네트워크 연결 실패")
except requests.HTTPError as e:
print(f"HTTP 오류 {e.response.status_code}: {url}")
except RequestException as e:
print(f"요청 실패: {e}")
return None
data = fetch_data("https://api.github.com/users/octocat")
if data:
print(data["name"])
Session: 연결 재사용
같은 서버에 여러 번 요청할 때 Session을 쓰면 효율적입니다.
import requests
session = requests.Session()
session.headers.update({
"Authorization": f"Bearer {API_KEY}",
"Accept": "application/json"
})
# 헤더를 매번 설정하지 않아도 됨
r1 = session.get("https://api.example.com/users")
r2 = session.get("https://api.example.com/products")
r3 = session.post("https://api.example.com/orders", json={"item_id": 1})
session.close() # 또는 with 구문 사용
httpx: 비동기 HTTP 클라이언트
FastAPI 등 비동기 환경에서는 httpx를 씁니다.
pip install httpx
import httpx
import asyncio
async def fetch_multiple():
async with httpx.AsyncClient() as client:
# 동시에 여러 요청
tasks = [
client.get(f"https://api.example.com/user/{i}")
for i in range(1, 6)
]
responses = await asyncio.gather(*tasks)
return [r.json() for r in responses]
results = asyncio.run(fetch_multiple())
실전: 날씨 API 연동
import requests
import os
def get_weather(city: str) -> dict:
"""OpenWeatherMap API로 날씨 조회"""
API_KEY = os.getenv("OPENWEATHER_API_KEY")
response = requests.get(
"https://api.openweathermap.org/data/2.5/weather",
params={
"q": city,
"appid": API_KEY,
"units": "metric", # 섭씨
"lang": "kr"
},
timeout=5
)
response.raise_for_status()
data = response.json()
return {
"city": data["name"],
"temp": data["main"]["temp"],
"feels_like": data["main"]["feels_like"],
"description": data["weather"][0]["description"],
"humidity": data["main"]["humidity"]
}
try:
weather = get_weather("Seoul")
print(f"{weather['city']}: {weather['temp']}°C ({weather['description']})")
except requests.HTTPError as e:
print(f"API 오류: {e}")
실전: GitHub API로 레포지토리 목록 조회
import requests
def get_repos(username: str, token: str = None) -> list:
headers = {}
if token:
headers["Authorization"] = f"token {token}"
all_repos = []
page = 1
while True:
response = requests.get(
f"https://api.github.com/users/{username}/repos",
headers=headers,
params={"per_page": 100, "page": page}
)
response.raise_for_status()
repos = response.json()
if not repos:
break
all_repos.extend(repos)
page += 1
return sorted(all_repos, key=lambda r: r["stargazers_count"], reverse=True)
repos = get_repos("torvalds")
for repo in repos[:5]:
print(f"⭐ {repo['stargazers_count']:,} | {repo['name']}")
정리
| 메서드 | 용도 | 파라미터 |
|---|---|---|
requests.get(url) | 데이터 조회 | params={} |
requests.post(url) | 데이터 생성 | json={} |
requests.put(url) | 데이터 수정 | json={} |
requests.delete(url) | 데이터 삭제 | - |
response.json() | JSON 파싱 | - |
response.raise_for_status() | 오류 상태 자동 예외 | - |
httpx.AsyncClient | 비동기 HTTP | async with |
다음 편에서는 최종 프로젝트 — 지금까지 배운 파이썬 개념을 모두 활용해 실용적인 도구를 만듭니다.