Skip to main content

HTTP Requests

Learn how to communicate with web APIs using Python's requests library.

What is requests?โ€‹

requests is the most popular library in Python for sending HTTP requests. With its simple and intuitive API, you can easily communicate with web servers.

Key Featuresโ€‹

  • Simple and intuitive API
  • Automatic JSON encoding/decoding
  • Session management and cookie handling
  • File upload support
  • Configurable timeouts

Installationโ€‹

pip install requests

Basic GET Requestโ€‹

Let's start with the most basic GET request.

import requests

# ๊ธฐ๋ณธ GET ์š”์ฒญ
response = requests.get('https://api.github.com')

# ์‘๋‹ต ํ™•์ธ
print(response.status_code) # 200
print(response.text) # ์‘๋‹ต ๋ณธ๋ฌธ
print(response.json()) # JSON ํ˜•์‹์œผ๋กœ ํŒŒ์‹ฑ

Adding Query Parametersโ€‹

# URL์— ์ง์ ‘ ์ž‘์„ฑ
response = requests.get('https://api.github.com/search/repositories?q=python')

# params ๋”•์…”๋„ˆ๋ฆฌ ์‚ฌ์šฉ (๊ถŒ์žฅ)
params = {
'q': 'python',
'sort': 'stars',
'order': 'desc'
}
response = requests.get('https://api.github.com/search/repositories', params=params)

print(response.url) # ์‹ค์ œ ์š”์ฒญ๋œ URL ํ™•์ธ

POST Requestsโ€‹

Use POST requests when sending data to the server.

# Form ๋ฐ์ดํ„ฐ ์ „์†ก
data = {
'username': 'john',
'password': 'secret123'
}
response = requests.post('https://httpbin.org/post', data=data)

# JSON ๋ฐ์ดํ„ฐ ์ „์†ก
json_data = {
'name': 'John Doe',
'email': 'john@example.com',
'age': 30
}
response = requests.post('https://httpbin.org/post', json=json_data)

print(response.json())

File Uploadโ€‹

# ๋‹จ์ผ ํŒŒ์ผ ์—…๋กœ๋“œ
files = {'file': open('report.pdf', 'rb')}
response = requests.post('https://httpbin.org/post', files=files)

# ํŒŒ์ผ๋ช… ์ง€์ •
files = {
'file': ('report.pdf', open('report.pdf', 'rb'), 'application/pdf')
}
response = requests.post('https://httpbin.org/post', files=files)

# ์—ฌ๋Ÿฌ ํŒŒ์ผ ์—…๋กœ๋“œ
files = {
'file1': open('image1.jpg', 'rb'),
'file2': open('image2.jpg', 'rb')
}
response = requests.post('https://httpbin.org/post', files=files)

PUT and DELETE Requestsโ€‹

Use these to update or delete resources.

# PUT ์š”์ฒญ (์ „์ฒด ์—…๋ฐ์ดํŠธ)
update_data = {
'id': 1,
'title': 'Updated Title',
'completed': True
}
response = requests.put('https://jsonplaceholder.typicode.com/todos/1', json=update_data)

# PATCH ์š”์ฒญ (๋ถ€๋ถ„ ์—…๋ฐ์ดํŠธ)
partial_data = {'completed': True}
response = requests.patch('https://jsonplaceholder.typicode.com/todos/1', json=partial_data)

# DELETE ์š”์ฒญ
response = requests.delete('https://jsonplaceholder.typicode.com/todos/1')
print(response.status_code) # 200 ๋˜๋Š” 204

Setting Headersโ€‹

You can pass additional information through request headers.

# ๊ธฐ๋ณธ ํ—ค๋” ์„ค์ •
headers = {
'User-Agent': 'MyApp/1.0',
'Accept': 'application/json',
'Content-Type': 'application/json'
}
response = requests.get('https://api.github.com', headers=headers)

# API ํ‚ค ์ธ์ฆ
headers = {
'Authorization': 'Bearer YOUR_API_KEY',
'X-API-Key': 'YOUR_API_KEY'
}
response = requests.get('https://api.example.com/data', headers=headers)

Authenticationโ€‹

Various authentication methods are supported.

from requests.auth import HTTPBasicAuth, HTTPDigestAuth

# Basic ์ธ์ฆ
response = requests.get(
'https://api.example.com/protected',
auth=HTTPBasicAuth('username', 'password')
)

# ๊ฐ„๋‹จํ•œ ๋ฐฉ๋ฒ•
response = requests.get(
'https://api.example.com/protected',
auth=('username', 'password')
)

# Bearer Token ์ธ์ฆ
headers = {'Authorization': 'Bearer YOUR_ACCESS_TOKEN'}
response = requests.get('https://api.example.com/data', headers=headers)

# OAuth 2.0
from requests_oauthlib import OAuth2Session

oauth = OAuth2Session(client_id, token=token)
response = oauth.get('https://api.example.com/protected')

JSON Processingโ€‹

You can easily handle JSON data.

# JSON ์‘๋‹ต ํŒŒ์‹ฑ
response = requests.get('https://api.github.com/users/github')
data = response.json()

print(data['name'])
print(data['public_repos'])

# JSON ์š”์ฒญ ์ „์†ก
payload = {
'name': 'John',
'email': 'john@example.com',
'settings': {
'notifications': True,
'theme': 'dark'
}
}
response = requests.post('https://api.example.com/users', json=payload)

# ์ปค์Šคํ…€ JSON ์ธ์ฝ”๋”
import json

class CustomEncoder(json.JSONEncoder):
def default(self, obj):
if isinstance(obj, datetime):
return obj.isoformat()
return super().default(obj)

response = requests.post(
'https://api.example.com/data',
data=json.dumps(payload, cls=CustomEncoder),
headers={'Content-Type': 'application/json'}
)

Timeout Settingsโ€‹

Prevent indefinite waiting due to network issues.

# ์—ฐ๊ฒฐ ํƒ€์ž„์•„์›ƒ 5์ดˆ, ์ฝ๊ธฐ ํƒ€์ž„์•„์›ƒ 10์ดˆ
try:
response = requests.get('https://api.example.com', timeout=(5, 10))
except requests.exceptions.Timeout:
print("์š”์ฒญ ์‹œ๊ฐ„์ด ์ดˆ๊ณผ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.")

# ์ „์ฒด ํƒ€์ž„์•„์›ƒ 10์ดˆ
response = requests.get('https://api.example.com', timeout=10)

# ํƒ€์ž„์•„์›ƒ ์—†์Œ (๊ถŒ์žฅํ•˜์ง€ ์•Š์Œ)
response = requests.get('https://api.example.com', timeout=None)

Error Handlingโ€‹

Different HTTP errors should be handled appropriately.

import requests
from requests.exceptions import (
ConnectionError,
Timeout,
HTTPError,
RequestException
)

try:
response = requests.get('https://api.example.com/data', timeout=5)
response.raise_for_status() # 4xx, 5xx ์—๋Ÿฌ ๋ฐœ์ƒ

data = response.json()
print(data)

except ConnectionError:
print("์—ฐ๊ฒฐ ์‹คํŒจ: ๋„คํŠธ์›Œํฌ๋ฅผ ํ™•์ธํ•˜์„ธ์š”.")
except Timeout:
print("์‹œ๊ฐ„ ์ดˆ๊ณผ: ์„œ๋ฒ„๊ฐ€ ์‘๋‹ตํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.")
except HTTPError as e:
print(f"HTTP ์—๋Ÿฌ: {e.response.status_code}")
if e.response.status_code == 404:
print("๋ฆฌ์†Œ์Šค๋ฅผ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.")
elif e.response.status_code == 401:
print("์ธ์ฆ์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.")
elif e.response.status_code == 500:
print("์„œ๋ฒ„ ๋‚ด๋ถ€ ์˜ค๋ฅ˜์ž…๋‹ˆ๋‹ค.")
except RequestException as e:
print(f"์š”์ฒญ ์‹คํŒจ: {str(e)}")

Using Sessionsโ€‹

When making multiple requests, using sessions is more efficient.

# ์„ธ์…˜ ์ƒ์„ฑ
session = requests.Session()

# ๋ชจ๋“  ์š”์ฒญ์— ์ ์šฉ๋  ํ—ค๋” ์„ค์ •
session.headers.update({
'User-Agent': 'MyApp/1.0',
'Authorization': 'Bearer YOUR_TOKEN'
})

# ์„ธ์…˜์œผ๋กœ ์—ฌ๋Ÿฌ ์š”์ฒญ
response1 = session.get('https://api.example.com/users')
response2 = session.get('https://api.example.com/posts')
response3 = session.post('https://api.example.com/comments', json={'text': 'Hello'})

# ์ฟ ํ‚ค ์ž๋™ ๊ด€๋ฆฌ
login_data = {'username': 'john', 'password': 'secret'}
session.post('https://example.com/login', data=login_data)

# ๋กœ๊ทธ์ธ ํ›„ ์ธ์ฆ๋œ ์š”์ฒญ
response = session.get('https://example.com/dashboard')

# ์„ธ์…˜ ์ข…๋ฃŒ
session.close()

# Context Manager ์‚ฌ์šฉ (๊ถŒ์žฅ)
with requests.Session() as session:
session.headers.update({'Authorization': 'Bearer TOKEN'})
response = session.get('https://api.example.com/data')

Retry Settingsโ€‹

Automatic retries can be performed in case of network issues.

from requests.adapters import HTTPAdapter
from requests.packages.urllib3.util.retry import Retry

# ์žฌ์‹œ๋„ ์ „๋žต ์„ค์ •
retry_strategy = Retry(
total=3, # ์ตœ๋Œ€ 3๋ฒˆ ์žฌ์‹œ๋„
backoff_factor=1, # 1์ดˆ, 2์ดˆ, 4์ดˆ ๋Œ€๊ธฐ
status_forcelist=[429, 500, 502, 503, 504],
method_whitelist=["HEAD", "GET", "OPTIONS", "POST"]
)

adapter = HTTPAdapter(max_retries=retry_strategy)
session = requests.Session()
session.mount("https://", adapter)
session.mount("http://", adapter)

# ์ž๋™ ์žฌ์‹œ๋„ ์ ์šฉ
response = session.get('https://api.example.com/data')

Practical Examplesโ€‹

Example 1: Getting Weather Information with OpenWeather APIโ€‹

import requests

def get_weather(city, api_key):
"""ํŠน์ • ๋„์‹œ์˜ ๋‚ ์”จ ์ •๋ณด๋ฅผ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค."""
base_url = "http://api.openweathermap.org/data/2.5/weather"

params = {
'q': city,
'appid': api_key,
'units': 'metric', # ์„ญ์”จ ์˜จ๋„
'lang': 'kr' # ํ•œ๊ตญ์–ด
}

try:
response = requests.get(base_url, params=params, timeout=10)
response.raise_for_status()

data = response.json()

weather_info = {
'city': data['name'],
'temperature': data['main']['temp'],
'feels_like': data['main']['feels_like'],
'humidity': data['main']['humidity'],
'description': data['weather'][0]['description'],
'wind_speed': data['wind']['speed']
}

return weather_info

except requests.exceptions.RequestException as e:
print(f"๋‚ ์”จ ์ •๋ณด๋ฅผ ๊ฐ€์ ธ์˜ค๋Š”๋ฐ ์‹คํŒจํ–ˆ์Šต๋‹ˆ๋‹ค: {e}")
return None

# ์‚ฌ์šฉ ์˜ˆ์‹œ
api_key = "YOUR_API_KEY"
weather = get_weather("Seoul", api_key)

if weather:
print(f"๋„์‹œ: {weather['city']}")
print(f"์˜จ๋„: {weather['temperature']}ยฐC")
print(f"์ฒด๊ฐ์˜จ๋„: {weather['feels_like']}ยฐC")
print(f"์Šต๋„: {weather['humidity']}%")
print(f"๋‚ ์”จ: {weather['description']}")
print(f"ํ’์†: {weather['wind_speed']} m/s")

Example 2: Retrieving Repository Information with GitHub APIโ€‹

import requests
from typing import List, Dict

class GitHubAPI:
def __init__(self, token=None):
self.base_url = "https://api.github.com"
self.session = requests.Session()

if token:
self.session.headers.update({
'Authorization': f'token {token}',
'Accept': 'application/vnd.github.v3+json'
})

def get_user_info(self, username: str) -> Dict:
"""์‚ฌ์šฉ์ž ์ •๋ณด๋ฅผ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค."""
url = f"{self.base_url}/users/{username}"
response = self.session.get(url)
response.raise_for_status()
return response.json()

def get_user_repos(self, username: str) -> List[Dict]:
"""์‚ฌ์šฉ์ž์˜ ์ €์žฅ์†Œ ๋ชฉ๋ก์„ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค."""
url = f"{self.base_url}/users/{username}/repos"
params = {
'sort': 'updated',
'per_page': 10
}
response = self.session.get(url, params=params)
response.raise_for_status()
return response.json()

def search_repositories(self, query: str, language=None) -> List[Dict]:
"""์ €์žฅ์†Œ๋ฅผ ๊ฒ€์ƒ‰ํ•ฉ๋‹ˆ๋‹ค."""
url = f"{self.base_url}/search/repositories"

search_query = query
if language:
search_query += f" language:{language}"

params = {
'q': search_query,
'sort': 'stars',
'order': 'desc',
'per_page': 10
}

response = self.session.get(url, params=params)
response.raise_for_status()
return response.json()['items']

# ์‚ฌ์šฉ ์˜ˆ์‹œ
github = GitHubAPI()

# ์‚ฌ์šฉ์ž ์ •๋ณด ์กฐํšŒ
user = github.get_user_info('torvalds')
print(f"์ด๋ฆ„: {user['name']}")
print(f"ํŒ”๋กœ์›Œ: {user['followers']}")
print(f"๊ณต๊ฐœ ์ €์žฅ์†Œ: {user['public_repos']}")

# ์ €์žฅ์†Œ ๊ฒ€์ƒ‰
repos = github.search_repositories('machine learning', language='python')
for repo in repos[:5]:
print(f"\n{repo['full_name']}")
print(f"โญ {repo['stargazers_count']} | ๐Ÿด {repo['forks_count']}")
print(f"์„ค๋ช…: {repo['description']}")

Example 3: Creating a REST API Clientโ€‹

import requests
from typing import Optional, Dict, Any
import json

class APIClient:
"""๋ฒ”์šฉ REST API ํด๋ผ์ด์–ธํŠธ"""

def __init__(self, base_url: str, api_key: Optional[str] = None):
self.base_url = base_url.rstrip('/')
self.session = requests.Session()

# ๊ธฐ๋ณธ ํ—ค๋” ์„ค์ •
self.session.headers.update({
'Content-Type': 'application/json',
'User-Agent': 'Python-API-Client/1.0'
})

# API ํ‚ค๊ฐ€ ์žˆ์œผ๋ฉด ํ—ค๋”์— ์ถ”๊ฐ€
if api_key:
self.session.headers.update({'Authorization': f'Bearer {api_key}'})

def _make_request(
self,
method: str,
endpoint: str,
**kwargs
) -> requests.Response:
"""HTTP ์š”์ฒญ์„ ๋ณด๋ƒ…๋‹ˆ๋‹ค."""
url = f"{self.base_url}/{endpoint.lstrip('/')}"

try:
response = self.session.request(method, url, timeout=30, **kwargs)
response.raise_for_status()
return response
except requests.exceptions.HTTPError as e:
print(f"HTTP Error: {e.response.status_code}")
print(f"Response: {e.response.text}")
raise
except requests.exceptions.RequestException as e:
print(f"Request Error: {str(e)}")
raise

def get(self, endpoint: str, params: Optional[Dict] = None) -> Dict[str, Any]:
"""GET ์š”์ฒญ"""
response = self._make_request('GET', endpoint, params=params)
return response.json()

def post(self, endpoint: str, data: Optional[Dict] = None) -> Dict[str, Any]:
"""POST ์š”์ฒญ"""
response = self._make_request('POST', endpoint, json=data)
return response.json()

def put(self, endpoint: str, data: Optional[Dict] = None) -> Dict[str, Any]:
"""PUT ์š”์ฒญ"""
response = self._make_request('PUT', endpoint, json=data)
return response.json()

def patch(self, endpoint: str, data: Optional[Dict] = None) -> Dict[str, Any]:
"""PATCH ์š”์ฒญ"""
response = self._make_request('PATCH', endpoint, json=data)
return response.json()

def delete(self, endpoint: str) -> bool:
"""DELETE ์š”์ฒญ"""
response = self._make_request('DELETE', endpoint)
return response.status_code in [200, 204]

# ์‚ฌ์šฉ ์˜ˆ์‹œ
client = APIClient('https://jsonplaceholder.typicode.com')

# GET ์š”์ฒญ
posts = client.get('/posts', params={'userId': 1})
print(f"๊ฒŒ์‹œ๊ธ€ ์ˆ˜: {len(posts)}")

# POST ์š”์ฒญ
new_post = client.post('/posts', data={
'title': 'New Post',
'body': 'This is a new post',
'userId': 1
})
print(f"์ƒ์„ฑ๋œ ๊ฒŒ์‹œ๊ธ€ ID: {new_post['id']}")

# PUT ์š”์ฒญ
updated_post = client.put('/posts/1', data={
'id': 1,
'title': 'Updated Title',
'body': 'Updated body',
'userId': 1
})

# DELETE ์š”์ฒญ
success = client.delete('/posts/1')
print(f"์‚ญ์ œ ์„ฑ๊ณต: {success}")

Example 4: Displaying Download Progressโ€‹

import requests
from tqdm import tqdm

def download_file(url: str, filename: str):
"""ํŒŒ์ผ์„ ๋‹ค์šด๋กœ๋“œํ•˜๊ณ  ์ง„ํ–‰๋ฅ ์„ ํ‘œ์‹œํ•ฉ๋‹ˆ๋‹ค."""
response = requests.get(url, stream=True)
response.raise_for_status()

total_size = int(response.headers.get('content-length', 0))

with open(filename, 'wb') as file, tqdm(
desc=filename,
total=total_size,
unit='B',
unit_scale=True,
unit_divisor=1024,
) as progress_bar:
for chunk in response.iter_content(chunk_size=8192):
file.write(chunk)
progress_bar.update(len(chunk))

print(f"๋‹ค์šด๋กœ๋“œ ์™„๋ฃŒ: {filename}")

# ์‚ฌ์šฉ ์˜ˆ์‹œ
url = "https://example.com/large-file.zip"
download_file(url, "downloaded-file.zip")

Frequently Asked Questionsโ€‹

Q1. What's the difference between requests and urllib?โ€‹

A: requests is a more user-friendly library built on top of urllib. requests provides convenient features such as simple API, automatic JSON processing, and session management.

# urllib (๋ณต์žกํ•จ)
import urllib.request
import json

request = urllib.request.Request(
'https://api.github.com',
headers={'User-Agent': 'Python'}
)
response = urllib.request.urlopen(request)
data = json.loads(response.read().decode())

# requests (๊ฐ„๋‹จํ•จ)
import requests

response = requests.get('https://api.github.com')
data = response.json()

Q2. How do you download large files?โ€‹

A: Use the stream=True option to download in chunks.

response = requests.get(url, stream=True)
with open('large_file.zip', 'wb') as f:
for chunk in response.iter_content(chunk_size=8192):
if chunk:
f.write(chunk)

Q3. Getting an SSL certificate error.โ€‹

A: In development environments, you can use verify=False, but this is not recommended in production.

# ๊ฐœ๋ฐœ ํ™˜๊ฒฝ์—์„œ๋งŒ ์‚ฌ์šฉ
response = requests.get(url, verify=False)

# ํ”„๋กœ๋•์…˜: ์ธ์ฆ์„œ ๊ฒฝ๋กœ ์ง€์ •
response = requests.get(url, verify='/path/to/certfile')

Q4. How do you configure a proxy?โ€‹

A: Use the proxies parameter.

proxies = {
'http': 'http://10.10.10.10:8000',
'https': 'http://10.10.10.10:8000',
}

response = requests.get('https://api.example.com', proxies=proxies)

Q5. What if the response isn't JSON?โ€‹

A: Use the appropriate attribute depending on the response type.

response = requests.get(url)

# JSON
data = response.json()

# ํ…์ŠคํŠธ
text = response.text

# ๋ฐ”์ด๋„ˆ๋ฆฌ
binary = response.content

# ์ž๋™ ๊ฐ์ง€
if 'application/json' in response.headers.get('Content-Type', ''):
data = response.json()
else:
text = response.text

Next Stepsโ€‹

If you've mastered HTTP requests with requests, learn these topics:

  1. Web Scraping - Extract data from web pages with BeautifulSoup
  2. Asynchronous Requests - Process multiple requests simultaneously with aiohttp
  3. API Development - Create your own REST API with FastAPI
  4. Authentication - Advanced authentication methods like OAuth 2.0, JWT

Referencesโ€‹