PythonPython 기초 · 9입문

HTTP 요청 — 외부 API 호출하기

PythonHTTPAPIrequestsJSONREST

파이썬이 인터넷과 대화하는 방법

날씨 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비동기 HTTPasync with

다음 편에서는 최종 프로젝트 — 지금까지 배운 파이썬 개념을 모두 활용해 실용적인 도구를 만듭니다.

궁금한 점이 있으신가요?

협업·의뢰는 아래로, 가벼운 소통은 인스타그램 @bluefox._.hi도 환영이에요.