[Python 100일 챌린지] Day 51 - requests 라이브러리 기초
Phase 6 시작! 🎉
requests.get('https://api.github.com')→.json()→ Python 딕셔너리로 변환! 😊날씨 API, 주식 API, 뉴스 API… 웹에서 데이터 가져오기는 이제 필수 기술! 3줄 코드로 전 세계 데이터를 내 프로그램으로 가져옵니다!
(40-50분 완독 ⭐⭐⭐)
🎯 오늘의 학습 목표
📚 사전 지식
- Day 43: JSON 데이터 처리 - API 응답은 대부분 JSON 형식
- Day 45: 예외 처리 기초 - 네트워크 오류 처리
🎯 학습 목표 1: HTTP와 API의 개념 이해하기
1.1 HTTP란?
💡 HTTP(HyperText Transfer Protocol)는 웹에서 데이터를 주고받는 약속입니다. 브라우저에서 웹사이트를 열면 HTTP로 서버에 “이 페이지 주세요”라고 요청하고, 서버가 HTML을 보내주는 것입니다.
HTTP 요청/응답 흐름:
1
2
3
4
5
[내 프로그램] --요청--> [서버]
"날씨 정보 주세요"
[내 프로그램] <--응답-- [서버]
"서울: 23°C, 맑음"
1.2 API란?
💡 API(Application Programming Interface)는 프로그램끼리 데이터를 주고받는 창구입니다. 날씨 앱이 기상청 서버에서 날씨를 가져오는 것, 카카오톡이 서버에서 메시지를 가져오는 것 모두 API를 통해 이루어집니다.
API 예시:
- 날씨 API: “서울 날씨 알려줘” →
{"temp": 23, "weather": "맑음"} - GitHub API: “torvalds 정보 알려줘” →
{"name": "Linus Torvalds", "repos": 7} - 환율 API: “달러-원 환율 알려줘” →
{"rate": 1350.5}
1.3 HTTP 메서드
| 메서드 | 용도 | 예시 |
|---|---|---|
| GET | 데이터 조회 | 날씨 조회, 사용자 정보 조회 |
| POST | 데이터 생성/전송 | 로그인, 글 작성 |
| PUT | 데이터 수정 | 프로필 수정 |
| DELETE | 데이터 삭제 | 게시글 삭제 |
💡 오늘은 가장 많이 사용하는 GET과 POST를 집중적으로 배웁니다!
1.4 HTTP 상태 코드
서버가 요청을 처리한 결과를 숫자로 알려줍니다:
| 코드 | 의미 | 설명 |
|---|---|---|
| 200 | OK | 성공! 요청이 정상 처리됨 |
| 201 | Created | 새로운 데이터가 생성됨 |
| 400 | Bad Request | 요청이 잘못됨 (내 실수) |
| 401 | Unauthorized | 인증 필요 (로그인 필요) |
| 404 | Not Found | 요청한 데이터가 없음 |
| 500 | Server Error | 서버 오류 (서버 문제) |
💡 외우기 팁: 2xx = 성공, 4xx = 내 실수, 5xx = 서버 문제
🎯 학습 목표 2: requests 라이브러리로 GET 요청 보내기
2.1 requests 설치
1
pip install requests
💡 왜 requests를 사용할까요? Python 기본 라이브러리
urllib도 있지만,requests가 훨씬 간단하고 직관적입니다. 전 세계 Python 개발자가 가장 많이 사용하는 HTTP 라이브러리입니다.
2.2 첫 번째 GET 요청
1
2
3
4
5
6
7
8
9
10
11
import requests
# GitHub API에 GET 요청
response = requests.get('https://api.github.com')
# 상태 코드 확인
print(f"상태 코드: {response.status_code}") # 200
# 응답 본문 (JSON → Python dict)
data = response.json()
print(f"현재 사용자 URL: {data['current_user_url']}")
출력:
1
2
상태 코드: 200
현재 사용자 URL: https://api.github.com/user
2.3 쿼리 파라미터 사용하기
💡 쿼리 파라미터는 URL 뒤에
?key=value형태로 추가 정보를 전달하는 방법입니다. 검색어, 페이지 번호 등을 전달할 때 사용합니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import requests
# 방법 1: URL에 직접 작성 (비추천)
response = requests.get('https://httpbin.org/get?name=Alice&age=25')
# 방법 2: params 사용 (권장!)
params = {
'name': 'Alice',
'age': 25,
'city': '서울' # 한글도 자동으로 인코딩됨
}
response = requests.get('https://httpbin.org/get', params=params)
# 실제 요청된 URL 확인
print(f"요청 URL: {response.url}")
# https://httpbin.org/get?name=Alice&age=25&city=%EC%84%9C%EC%9A%B8
# 응답 확인
data = response.json()
print(data['args']) # {'name': 'Alice', 'age': '25', 'city': '서울'}
2.4 실전: GitHub 사용자 정보 조회
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import requests
def get_github_user(username):
"""GitHub 사용자 정보 가져오기"""
url = f'https://api.github.com/users/{username}'
response = requests.get(url)
if response.status_code == 200:
user = response.json()
return {
'login': user['login'],
'name': user.get('name', '이름 없음'),
'bio': user.get('bio', '소개 없음'),
'public_repos': user['public_repos'],
'followers': user['followers']
}
elif response.status_code == 404:
print(f"❌ '{username}' 사용자를 찾을 수 없습니다.")
return None
else:
print(f"❌ 오류 발생: {response.status_code}")
return None
# 사용 예시
user = get_github_user('torvalds') # 리눅스 창시자
if user:
print(f"👤 {user['name']} (@{user['login']})")
print(f"📝 {user['bio']}")
print(f"📦 공개 저장소: {user['public_repos']}개")
print(f"👥 팔로워: {user['followers']}명")
출력:
1
2
3
4
👤 Linus Torvalds (@torvalds)
📝 None
📦 공개 저장소: 7개
👥 팔로워: 200000명
🎯 학습 목표 3: POST 요청과 데이터 전송하기
3.1 JSON 데이터 전송
💡 POST 요청은 서버에 데이터를 보낼 때 사용합니다. 로그인, 회원가입, 글 작성 등에 사용됩니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import requests
# 보낼 데이터
data = {
'name': 'Alice',
'email': 'alice@example.com',
'age': 25
}
# POST 요청 (json= 파라미터 사용)
response = requests.post(
'https://httpbin.org/post',
json=data # 자동으로 JSON 변환 + Content-Type 헤더 설정
)
print(f"상태 코드: {response.status_code}")
# 서버가 받은 데이터 확인
result = response.json()
print(f"서버가 받은 데이터: {result['json']}")
출력:
1
2
상태 코드: 200
서버가 받은 데이터: {'name': 'Alice', 'email': 'alice@example.com', 'age': 25}
3.2 폼 데이터 전송
💡 폼 데이터는 웹 브라우저에서 로그인 폼을 제출하는 것과 같은 형식입니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import requests
# 폼 데이터 (로그인 예시)
form_data = {
'username': 'alice',
'password': 'secret123'
}
# data= 파라미터 사용 (application/x-www-form-urlencoded)
response = requests.post(
'https://httpbin.org/post',
data=form_data
)
result = response.json()
print(f"전송된 폼 데이터: {result['form']}")
json= vs data= 비교: | 파라미터 | Content-Type | 용도 | |———-|————–|——| | json= | application/json | API 통신 | | data= | application/x-www-form-urlencoded | 웹 폼 전송 |
3.3 헤더 추가하기
💡 HTTP 헤더는 요청에 추가 정보를 담습니다. 인증 토큰, 클라이언트 정보 등을 전달합니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import requests
# 커스텀 헤더
headers = {
'User-Agent': 'MyPythonApp/1.0', # 내 앱 이름
'Accept': 'application/json', # JSON 응답 요청
'Authorization': 'Bearer YOUR_TOKEN' # 인증 토큰
}
response = requests.get(
'https://api.github.com/user',
headers=headers
)
print(f"상태 코드: {response.status_code}")
🎯 학습 목표 4: 응답 처리와 에러 핸들링
4.1 응답 객체 활용
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import requests
response = requests.get('https://api.github.com')
# 상태 확인
print(f"상태 코드: {response.status_code}") # 200
print(f"성공 여부: {response.ok}") # True (200-299면 True)
# 헤더 확인
print(f"Content-Type: {response.headers['Content-Type']}")
# 본문 가져오기
print(f"텍스트: {response.text[:100]}...") # 문자열
print(f"바이트: {response.content[:50]}...") # 바이트
data = response.json() # dict (JSON인 경우)
# 인코딩
print(f"인코딩: {response.encoding}") # utf-8
4.2 상태 코드 처리
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import requests
def safe_get(url):
"""안전한 GET 요청"""
response = requests.get(url)
if response.status_code == 200:
return response.json()
elif response.status_code == 404:
print("❌ 요청한 리소스를 찾을 수 없습니다.")
elif response.status_code == 401:
print("🔒 인증이 필요합니다.")
elif response.status_code >= 500:
print("⚠️ 서버에 문제가 발생했습니다.")
else:
print(f"❓ 알 수 없는 오류: {response.status_code}")
return None
# 테스트
data = safe_get('https://api.github.com/users/torvalds')
if data:
print(f"이름: {data['name']}")
4.3 예외 처리
💡 네트워크 요청은 언제든 실패할 수 있습니다! 인터넷 연결 끊김, 서버 다운, 타임아웃 등 다양한 오류가 발생할 수 있으므로 반드시 예외 처리를 해야 합니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import requests
def fetch_data(url):
"""예외 처리가 포함된 데이터 조회"""
try:
response = requests.get(url, timeout=5) # 5초 타임아웃
response.raise_for_status() # 4xx, 5xx 오류 시 예외 발생
return response.json()
except requests.exceptions.Timeout:
print("⏱️ 요청 시간이 초과되었습니다.")
except requests.exceptions.ConnectionError:
print("🔌 서버에 연결할 수 없습니다.")
except requests.exceptions.HTTPError as e:
print(f"🚫 HTTP 오류: {e}")
except requests.exceptions.RequestException as e:
print(f"❌ 요청 오류: {e}")
return None
# 사용
data = fetch_data('https://api.github.com/users/torvalds')
if data:
print(f"가져온 데이터: {data['name']}")
4.4 타임아웃 설정
1
2
3
4
5
6
7
8
9
10
import requests
# 단일 타임아웃 (전체 5초)
response = requests.get('https://api.example.com', timeout=5)
# 연결/읽기 타임아웃 분리
response = requests.get(
'https://api.example.com',
timeout=(3, 10) # 연결 3초, 읽기 10초
)
💡 타임아웃을 항상 설정하세요! 설정하지 않으면 서버가 응답하지 않을 때 프로그램이 영원히 멈춥니다.
🎯 학습 목표 5: 실전 API 활용하기
5.1 무료 공개 API로 연습하기
💡 httpbin.org는 HTTP 요청을 테스트할 수 있는 무료 서비스입니다. 보낸 데이터를 그대로 돌려주어 학습에 유용합니다.
1
2
3
4
5
6
7
8
import requests
# httpbin으로 요청 테스트
response = requests.get('https://httpbin.org/get', params={'name': 'Alice'})
print(response.json())
response = requests.post('https://httpbin.org/post', json={'message': 'Hello'})
print(response.json())
5.2 실전 예제: 환율 조회
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
import requests
def get_exchange_rate(base='USD', target='KRW'):
"""환율 조회 (무료 API)"""
url = f'https://api.exchangerate-api.com/v4/latest/{base}'
try:
response = requests.get(url, timeout=5)
response.raise_for_status()
data = response.json()
rate = data['rates'].get(target)
if rate:
return {
'base': base,
'target': target,
'rate': rate,
'date': data['date']
}
else:
print(f"❌ '{target}' 통화를 찾을 수 없습니다.")
return None
except requests.exceptions.RequestException as e:
print(f"❌ 오류: {e}")
return None
# 사용
result = get_exchange_rate('USD', 'KRW')
if result:
print(f"💱 1 {result['base']} = {result['rate']:,.2f} {result['target']}")
print(f"📅 기준일: {result['date']}")
출력:
1
2
💱 1 USD = 1,350.00 KRW
📅 기준일: 2025-04-20
5.3 실전 예제: 파일 다운로드
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
import requests
def download_file(url, filename):
"""파일 다운로드 (진행률 표시)"""
try:
response = requests.get(url, stream=True, timeout=30)
response.raise_for_status()
# 파일 크기 확인
total_size = int(response.headers.get('content-length', 0))
with open(filename, 'wb') as f:
downloaded = 0
for chunk in response.iter_content(chunk_size=1024):
if chunk:
f.write(chunk)
downloaded += len(chunk)
# 진행률 표시
if total_size:
percent = (downloaded / total_size) * 100
print(f"\r다운로드 중: {percent:.1f}%", end='')
print(f"\n✅ 다운로드 완료: {filename}")
return True
except requests.exceptions.RequestException as e:
print(f"\n❌ 다운로드 실패: {e}")
return False
# 사용 예시
# download_file('https://example.com/file.zip', 'downloaded.zip')
💡 실전 팁 & 주의사항
✅ 베스트 프랙티스
- 항상 타임아웃 설정
1
requests.get(url, timeout=5) # 필수!
- 예외 처리 필수
1 2 3 4 5
try: response = requests.get(url, timeout=5) response.raise_for_status() except requests.exceptions.RequestException as e: print(f"오류: {e}")
- 상태 코드 확인
1 2
if response.ok: # 200-299 data = response.json()
- API 키는 환경 변수로
1 2
import os api_key = os.environ.get('API_KEY')
⚠️ 주의사항
- API 호출 제한: 대부분의 API는 분당/시간당 호출 횟수 제한이 있음
- 인증 토큰 보안: 코드에 토큰을 직접 넣지 말고 환경 변수 사용
- 에러 응답 처리: 성공만 가정하지 말고 실패 케이스도 처리
🧪 연습 문제
문제 1: GitHub 저장소 목록 조회
GitHub API를 사용하여 특정 사용자의 공개 저장소 목록을 가져오는 함수를 작성하세요.
요구사항:
- 사용자 이름을 입력받아 저장소 목록 반환
- 각 저장소의 이름, 설명, 스타 수 출력
- 에러 처리 포함
해답 보기
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
import requests
def get_user_repos(username, max_repos=5):
"""GitHub 사용자의 저장소 목록 조회"""
url = f'https://api.github.com/users/{username}/repos'
params = {
'sort': 'updated', # 최근 업데이트순
'per_page': max_repos
}
try:
response = requests.get(url, params=params, timeout=5)
response.raise_for_status()
repos = response.json()
result = []
for repo in repos:
result.append({
'name': repo['name'],
'description': repo['description'] or '설명 없음',
'stars': repo['stargazers_count'],
'url': repo['html_url']
})
return result
except requests.exceptions.HTTPError:
if response.status_code == 404:
print(f"❌ '{username}' 사용자를 찾을 수 없습니다.")
else:
print(f"❌ HTTP 오류: {response.status_code}")
except requests.exceptions.RequestException as e:
print(f"❌ 요청 오류: {e}")
return None
# 테스트
repos = get_user_repos('torvalds')
if repos:
print(f"\n📦 저장소 목록:")
for repo in repos:
print(f" ⭐ {repo['stars']:,} - {repo['name']}")
print(f" {repo['description'][:50]}...")
문제 2: API 클라이언트 클래스
재사용 가능한 API 클라이언트 클래스를 작성하세요.
요구사항:
- base_url과 선택적 api_key를 받아 초기화
- get(), post() 메서드 구현
- 타임아웃과 에러 처리 포함
해답 보기
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
import requests
class APIClient:
"""재사용 가능한 API 클라이언트"""
def __init__(self, base_url, api_key=None, timeout=10):
self.base_url = base_url.rstrip('/')
self.timeout = timeout
self.headers = {
'User-Agent': 'PythonAPIClient/1.0',
'Accept': 'application/json'
}
if api_key:
self.headers['Authorization'] = f'Bearer {api_key}'
def get(self, endpoint, params=None):
"""GET 요청"""
url = f'{self.base_url}/{endpoint.lstrip("/")}'
try:
response = requests.get(
url,
params=params,
headers=self.headers,
timeout=self.timeout
)
response.raise_for_status()
return response.json()
except requests.exceptions.RequestException as e:
print(f"GET 오류 [{endpoint}]: {e}")
return None
def post(self, endpoint, data=None):
"""POST 요청"""
url = f'{self.base_url}/{endpoint.lstrip("/")}'
try:
response = requests.post(
url,
json=data,
headers=self.headers,
timeout=self.timeout
)
response.raise_for_status()
return response.json()
except requests.exceptions.RequestException as e:
print(f"POST 오류 [{endpoint}]: {e}")
return None
# 사용 예시
github = APIClient('https://api.github.com')
user = github.get('/users/torvalds')
if user:
print(f"이름: {user['name']}")
print(f"팔로워: {user['followers']}")
📝 오늘 배운 내용 정리
| 개념 | 설명 | 예시 |
|---|---|---|
| HTTP | 웹 데이터 전송 프로토콜 | GET, POST, PUT, DELETE |
| API | 프로그램 간 데이터 교환 창구 | GitHub API, 날씨 API |
| 상태 코드 | 요청 처리 결과 | 200(성공), 404(없음), 500(서버오류) |
| requests.get() | GET 요청 | requests.get(url, params=params) |
| requests.post() | POST 요청 | requests.post(url, json=data) |
| response.json() | JSON 응답 파싱 | 딕셔너리로 변환 |
| 타임아웃 | 요청 대기 시간 제한 | timeout=5 |
| 예외 처리 | 네트워크 오류 처리 | RequestException |
핵심 코드 패턴
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import requests
# 기본 GET 요청
response = requests.get('https://api.example.com/data', timeout=5)
if response.ok:
data = response.json()
# 파라미터와 함께 GET
params = {'key': 'value'}
response = requests.get(url, params=params, timeout=5)
# POST 요청 (JSON)
data = {'name': 'Alice'}
response = requests.post(url, json=data, timeout=5)
# 헤더 추가
headers = {'Authorization': 'Bearer TOKEN'}
response = requests.get(url, headers=headers, timeout=5)
# 안전한 요청 패턴
try:
response = requests.get(url, timeout=5)
response.raise_for_status()
return response.json()
except requests.exceptions.RequestException as e:
print(f"오류: {e}")
🔗 관련 자료
- requests 공식 문서
- HTTP 상태 코드 목록
- httpbin.org - HTTP 요청 테스트 서비스
- 공개 API 목록 - 무료 API 모음
📚 이전 학습
Day 50: 미니 프로젝트: 개인 가계부 ⭐⭐⭐⭐
Phase 5 마무리 프로젝트로 JSON, CSV, 예외 처리를 활용한 가계부 앱을 만들었습니다!
📚 다음 학습
Day 52: HTTP 심화 - PUT, DELETE와 인증
다음 시간에는 PUT/DELETE 메서드, 세션 관리, API 인증 방식을 배웁니다!
“늦었다고 생각할 때가 가장 빠른 시기입니다!” 🚀
Day 51/100 Phase 6: 웹 스크래핑과 API #100DaysOfPython
이제와서 시작하는 Python 마스터하기 - Day 51 완료! 🎉
