학습 내용
- requests 라이브러리 설치
- requests 사용법
- JSON 타입 처리
- requests 라이브러리 심층 분석
- 보안 인증이 필요한 요청 처리
서론
이전 시간에 API 테스트의 기초 이론과 패킷 캡처 도구, Postman을 활용한 수동 테스트 방법을 학습했습니다. 이제 Python 언어를 사용하여 API 요청을 자동으로 보내는 방법을 학습하겠습니다.
Python에서 HTTP 요청을 보내기 위해 주로 사용하는第三方 라이브러리는 requests입니다. Python 3의 내장 라이브러리인 http.client와 urllib.request도 HTTP 요청을 전송할 수 있지만, 사용 방법이 상대적으로 복잡합니다. requests 라이브러리는 요청 전송을 더욱 간단하게 만들어주며, 자동 인코딩/디코딩, 세션 유지, Keep-Alive 등을 지원합니다.
requests 라이브러리 설치
- Windows: 명령 프롬프트(cmd)를 열고
pip install requests입력 - Linux: 터미널에서
pip3 install requests입력 - Mac:
sudo python3 -m pip install requests입력
설치 확인
명령 프롬프트에서 python을 입력하고 Python 셸에서 import requests를 실행하여 오류가 발생하지 않으면 설치가 완료된 것입니다.
requests 사용법
기본 GET 요청
HTTP 요청은 다음 3단계로 구성됩니다:
- 요청 조립: URL, params, data, headers, cookies 등 포함 (최소 URL 필수)
- 요청 전송 및 응답 수신: GET, POST 등 다양한 메서드 지원
- 응답 처리: 응답 텍스트 출력
Pycharm에서 새 프로젝트를 생성하고 다음 코드를 입력해 보겠습니다:
# requests 패키지 임포트
import requests
# 1단계: 요청 조립
api_url = "http://httpbin.org/get"
# 2단계: 요청 전송 및 응답 수신
response = requests.get(api_url)
# 3단계: 응답 처리
print(response.text)
파라미터가 있는 GET 요청
import requests
api_url = "http://www.tuling123.com/openapi/api"
params_dict = {"key": "ec961279f453459b9248f0aeb6600bbe", "info": "안녕하세요"}
result = requests.get(url=api_url, params=params_dict)
print(result.text)
전통적인 폼 데이터 POST 요청 (x-www-form-urlencoded)
import requests
api_url = "http://httpbin.org/post"
form_data = {"username": "testuser", "age": 25}
response = requests.post(url=api_url, data=form_data)
print(response.text)
JSON 타입 POST 요청 (application/json)
import requests
import json
api_url = "http://httpbin.org/post"
json_payload = {
"username": "testuser",
"age": 25
}
headers_info = {"Content-Type": "application/json"}
response = requests.post(url=api_url, data=json.dumps(json_payload), headers=headers_info)
print(response.text)
참고: data 파라미터는 딕셔너리 또는 문자열 형식을 지원합니다. 딕셔너리 형식使用时, requests 메서드가 기본 폼 urlencoded 형식으로 변환합니다. 문자열 형식으로 전송할 때는 올바른 JSON 형식을 준수해야 합니다 (큰따옴표 사용, 소문자 true/false 등).
또는 json 파라미터를 직접 사용하여 자동으로 딕셔너리를 JSON으로 변환할 수 있습니다:
import requests
api_url = "http://openapi.tuling123.com/openapi/api/v2"
request_data = {
"reqType": 0,
"perception": {
"inputText": {
"text": "주변 호텔"
}
},
"userInfo": {
"apiKey": "ec961279f453459b9248f0aeb6600bbe",
"userId": "206379"
}
}
response = requests.post(url=api_url, json=request_data)
print(response.text)
연습 문제
- 위图灵聊天 API (GET)를 활용하여 Python input과 결합한 채팅 봇 프로그램을 작성하세요
- 위图灵 조회 API (POST)를 활용하여 주변 음식점을 검색하는 실용적인 메서드를 구현하세요
JSON 타입 처리
직렬화와 역직렬화
프로그램의 객체(딕셔너리, 리스트, 함수, 클래스 등)는 메모리에 저장되어 전원이 꺼지면 사라집니다. 전송이나 저장을 위해 메모리의 객체를 텍스트나 파일 형식으로 변환해야 합니다.
- 직렬화(Serialization): 메모리 객체 → 텍스트/파일
- 역직렬화(Deserialization): 텍스트 → 메모리 객체
HTTP 전송 과정
HTTP 프로토콜은 텍스트 또는 바이너리로 전송합니다. 따라서 요청은 텍스트로 변환되어 전송되고, 응답도 텍스트 형식으로 수신됩니다. JSON의 경우 텍스트를 다시 객체로 변환해야 합니다.
JSON과 Python 딕셔너리의 차이점
JSON(JavaScript Object Notation)은 JavaScript의 객체 형식으로, 범용적인 데이터 포맷입니다.
| 항목 | Python 딕셔너리 | JSON 형식 |
|---|---|---|
| 따옴표 | 작은따옴표, 큰따옴표 가능 | 큰따옴표만 가능 |
| 불리언 | True/False | true/false |
| 빈 값 | None | null |
JSON 처리 메서드
- 직렬화:
json.dumps()/json.dump() - 역직렬화:
json.loads()/json.load()
import json
sample_dict = {'name': '홍길동', 'password': '123456', "active": True, "balance": None}
json_string = json.dumps(sample_dict)
print(json_string)
출력: {"name": "\ud64d\uae38\ub3d9", "password": "123456", "active": true, "balance": null}
응답에서 JSON 파싱
import requests
import json
response = requests.post("http://www.tuling123.com/openapi/api?key=ec961279f453459b9248f0aeb6600bbe&info=안녕")
print(response.text)
parsed_data = response.json()
print(json.dumps(parsed_data, indent=2, ensure_ascii=False))
json.dumps() 파라미터 설명:
- indent: 들여쓰기 공백 수
- sort_keys: 키를 ASCII 순서로 정렬
- ensure_ascii: ASCII가 아닌 문자 이스케이프 방지
파일 입출력
쓰기:
import json
data_dict = {'name': '홍길동', 'age': 30}
file_handler = open("output.json", "w")
json.dump(data_dict, file_handler)
file_handler.close()
읽기:
import json
file_handler = open("output.json", "r", encoding="utf-8")
read_dict = json.load(file_handler)
print(read_dict['name'])
file_handler.close()
연습 문제
아래 JSON 파일을 읽고 요청을 보내 응답을 출력하세요:
demo.json:
{
"url": "http://www.tuling123.com/openapi/api",
"method": "get",
"params": {
"key": "ec961279f453459b9248f0aeb6600bbe",
"info": "안녕하세요"
}
}
참고: method가 없으면 data 있으면 POST, data 없으면 GET 요청입니다. type이 없으면 form 형식, json이면 JSON 형식으로 전송합니다.
requests 라이브러리 상세 분석
요청 메서드
- requests.get()
- requests.post()
- requests.put()
- requests.delete()
- requests.session(): 세션 유지에 사용
요청 파라미터
- url: 요청 주소
- params: URL 쿼리 파라미터 (딕셔너리)
- data: 요청 데이터 (딕셔너리 또는 문자열)
- headers: 요청 헤더 (딕셔너리)
- cookies: 쿠키 정보 (딕셔너리)
- files: 파일 업로드 (딕셔너리)
- auth: Basic Auth 인증 (튜플 형식)
- timeout: 요청 제한 시간 (초)
응답 처리
- response.status_code: HTTP 상태 코드
- response.reason: 상태 코드 의미
- response.text: 응답 텍스트 (디코딩 적용)
- response.content: 바이너리 응답
- response.encoding: 디코딩 방식
- response.apparent_encoding: 실제 인코딩 (chardet 제공)
- response.json(): JSON 객체로 변환
- response.headers: 응답 헤더
- response.cookies: 쿠키 Jar 객체
import requests
result = requests.get("https://www.baidu.com")
print(result.status_code, result.reason)
print(result.text)
print(result.content)
print(result.encoding)
print(result.apparent_encoding)
result.encoding = 'utf-8'
print(result.text)
print(result.cookies.items())
print(result.cookies.get("BDORZ"))
보안 인증이 필요한 요청
로그인이 필요한 요청 (Cookie/Session 인증)
예를 들어, 관리자 대시보드 페이지에 접근할 때 로그인이 필요합니다.
세션 유지 방식
import requests
session = requests.session()
session.post(url="https://demo.fastadmin.net/admin/index/login.html", data={"username": "admin", "password": "123456"})
dashboard = session.get("https://demo.fastadmin.net/admin/dashboard?ref=addtabs")
print(dashboard.text)
쿠키 직접 전달 방식
import requests
target_url = "https://demo.fastadmin.net/admin/dashboard?ref=addtabs"
session_cookie = {"PHPSESSID": "9bf6b19ddb09938cf73d55a094b36726"}
response = requests.get(url=target_url, cookies=session_cookie)
print(response.text)
비교:
- 세션 방식: 매번 요청 2개 전송, 효율 낮음
- 쿠키 방식: 수동 캡처 필요, 세션 만료 시 재수집
appid 또는 token 방식
- appid: 시스템이 부여한 고유 ID
- token: 암호화된 접근令牌, 일정 기간 유효
token vs session:
- session: 서버에 저장, 서버에서 검증
- token: 클라이언트가持有, 서버에서 복호화하여 검증
예시: Baidu AI 이미지 텍스트 인식
import requests
import json
app_key = 'your_app_key_here'
secret_key = 'your_secret_key_here'
image_url = '//example.com/image.png'
# 토큰 발급
token_url = f'https://aip.baidubce.com/oauth/2.0/token?grant_type=client_credentials&client_id={app_key}&client_secret={secret_key}'
token_response = requests.get(url=token_url).json()
access_token = token_response.get("access_token")
# OCR 요청
ocr_endpoint = f'https://aip.baidubce.com/rest/2.0/ocr/v1/general_basic?access_token={access_token}'
payload = {"url": image_url}
result = requests.post(url=ocr_endpoint, data=payload)
print(json.dumps(result.json(), indent=2, ensure_ascii=False))
오픈 프로토콜 인증
requests는 Basic Auth와 Digest Auth를 지원합니다:
import requests
import json
response = requests.get("https://api.github.com/user", auth=("username", "password"))
print(json.dumps(response.json(), indent=2, ensure_ascii=False))
디지털 서명
Cookie/Session이나 appid/token 방식은 신원 확인만 하고 파라미터 무결성은 검증하지 않습니다. 디지털 서명(sign)은 원본 파라미터를 암호화하여 생성한 문자열로, 요청 시 함께 전송하여 서버에서 서명 일치 여부를 검증합니다. 이를 통해 파라미터 조작을 방지할 수 있습니다.