이 가이드에서는 다음을 살펴보게 됩니다:
- AIOHTTP의 정의 및 주요 기능
- 웹 스크래핑을 위한 AIOHTTP 사용 단계별 안내
- AIOHTTP를 활용한 웹 스크래핑 고급 기법
- 자동화된 요청 처리를 위한 AIOHTTP 대 Requests 비교
자, 시작해 보겠습니다!
AIOHTTP란 무엇인가?
AIOHTTP는 Python의 asyncio를 기반으로 구축된 비동기 클라이언트/서버 HTTP 프레임워크입니다. 기존 HTTP 클라이언트와 달리 AIOHTTP는 클라이언트 세션을 사용하여 여러 요청에 걸쳐 연결을 유지합니다. 이로 인해 고동시성 세션 기반 작업에 효율적인 선택이 됩니다.
⚙️ 주요 기능
- HTTP 프로토콜의 클라이언트 및 서버 측 모두 지원합니다.
- 웹소켓(클라이언트 및 서버 양쪽)에 대한 네이티브 지원 제공.
- 웹 서버용 미들웨어 및 플러그인 가능한 라우팅을 제공합니다.
- 대용량 데이터 스트리밍을 효율적으로 처리합니다.
- 클라이언트 세션 지속성을 포함하여 연결 재사용을 가능하게 하고 다중 요청에 대한 오버헤드를 줄입니다.
AIOHTTP를 사용한 스크래핑: 단계별 튜토리얼
웹 스크래핑의 맥락에서 AIOHTTP는 페이지의 원시 HTML 콘텐츠를 가져오는 HTTP 클라이언트에 불과합니다. 그 HTML에서 데이터를 파싱하고 추출하려면 BeautifulSoup과 같은 HTML 파서가 필요합니다.
이 섹션을 따라 BeautifulSoup을 사용한 웹 스크래핑을 위해 AIOHTTP를 사용하는 방법을 배워보세요!
주의: AIOHTTP는 주로 프로세스 초기 단계에서 사용되지만, 전체 스크래핑 워크플로를 안내해 드리겠습니다. 보다 고급 AIOHTTP 웹 스크래핑 기법에 관심이 있으시다면, 3단계 이후 다음 장으로 건너뛰셔도 됩니다.
1단계: 스크래핑 프로젝트 설정
컴퓨터에 Python 3 이상이 설치되어 있는지 확인하세요. 설치되어 있지 않다면 공식 사이트에서 다운로드하여 설치 지침을 따르세요.
다음으로, 다음 명령어를 사용하여 AIOHTTP 스크래핑 프로젝트용 디렉터리를 생성하세요:
mkdir aiohttp-scraper
해당 디렉터리로 이동하여 가상 환경을 설정하세요:
cd aiohttp-scraper
python -m venv env
선호하는 Python IDE에서 프로젝트 폴더를 엽니다. Python 확장 프로그램이 설치된 Visual Studio Code 나 PyCharm Community Edition을 사용할 수 있습니다.
이제 프로젝트 폴더 내에 scraper.py 파일을 생성하세요. 처음에는 비어 있지만 곧 스크래핑 로직을 추가하게 됩니다.
IDE 터미널에서 가상 환경을 활성화하세요. Linux 또는 macOS에서는 다음 명령어를 사용합니다:
./env/bin/activate
Windows에서는 동일하게 다음 명령어를 실행하세요:
env/Scripts/activate
좋습니다! 이제 모든 준비가 완료되었습니다.
2단계: 스크래핑 라이브러리 설정
가상 환경을 활성화한 상태에서 아래 명령어로 AIOHTTP와 BeautifulSoup을 설치하세요:
pip install aiohttp beautifulsoup4
이렇게 하면 aiohttp와 beautifulsoup4가 프로젝트의 종속성에 추가됩니다.
스크레이퍼 스크립트 (scraper.py )에 임포트하세요:
import asyncio
import aiohttp
from bs4 import BeautifulSoup
aiohttp는 작동하기 위해 asyncio가 필요합니다.
이제 스크래퍼.py 파일에 다음 비동기 함수 워크플로를 추가하세요:
async def scrape_quotes():
# 스크래핑 로직...
# 비동기 함수 실행
asyncio.run(scrape_quotes())
scrape_quotes() 는 스크래핑 로직이 차단 없이 동시 실행되는 비동기 함수를 정의합니다. 마지막으로 asyncio.run(scrape_quotes()) 는 비동기 함수를 시작하고 실행합니다.
훌륭합니다! 스크래핑 워크플로의 다음 단계로 진행할 수 있습니다.
3단계: 대상 페이지의 HTML 가져오기
이 예제에서는 “Quotes to Scrape” 사이트에서 데이터를 스크래핑하는 방법을 살펴보겠습니다:

Requests나 AIOHTTP 같은 라이브러리를 사용하면 단순히 GET 요청을 보내 페이지의 HTML 콘텐츠를 직접 받을 수 있습니다. 그러나 AIOHTTP는 다른 요청 라이프사이클을 따릅니다.
AIOHTTP의 핵심 구성 요소는 ClientSession으로, 연결 풀을 관리하며 기본적으로 Keep-Alive를 지원합니다. 요청마다 새 연결을 열지 않고 기존 연결을 재사용하여 성능을 향상시킵니다.
요청을 수행할 때 일반적으로 다음 세 단계가 포함됩니다:
ClientSession()을 통해 세션 열기.session.get()을 사용하여 비동기적으로 GET 요청을 전송합니다.await response.text()와 같은 메서드로 응답 데이터에 접근합니다.
이러한 설계로 이벤트 루프는 작업 간에 서로 다른 컨텍스트를 사용하면서도 차단되지 않아, 높은 동시성 작업에 이상적입니다.
이를 바탕으로 AIOHTTP를 사용하여 다음과 같은 로직으로 홈페이지 HTML을 가져올 수 있습니다:
async with aiohttp.ClientSession() as session:
async with session.get("http://quotes.toscrape.com") as response:
# 대상 페이지의 HTML 접근
html = await response.text()
배경에서 AIOHTTP는 서버에 요청을 전송하고 페이지 HTML이 포함된 응답을 기다립니다. 응답을 수신하면 await response.text() 가 HTML 콘텐츠를 문자열로 추출합니다.
html 변수를 출력하면 다음과 같이 표시됩니다:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>스크래핑할 인용문</title>
<link rel="stylesheet" href="/static/bootstrap.min.css">
<link rel="stylesheet" href="/static/main.css">
</head>
<body>
<!-- 생략... -->
</body>
</html>
잘하셨습니다! 대상 페이지의 HTML 콘텐츠를 성공적으로 가져왔습니다. 이제 이 콘텐츠를 파싱하고 필요한 데이터를 추출할 차례입니다.
4단계: HTML 파싱하기
HTML 콘텐츠를 BeautifulSoup 생성자에 전달하여 파싱합니다:
# BeautifulSoup을 사용해 HTML 콘텐츠 파싱
soup = BeautifulSoup(html, "html.parser")
html.parser는 콘텐츠 처리에 사용되는 Python의 기본 HTML 파서입니다.
soup 객체는 파싱된 HTML을 포함하며 필요한 데이터를 추출하는 메서드를 제공합니다.
AIOHTTP가 HTML 가져오기를 처리했으며, 이제 BeautifulSoup을 사용한 일반적인 데이터 파싱 단계로 넘어갑니다. 자세한 내용은 BeautifulSoup 웹 스크래핑 튜토리얼을 참조하세요.
5단계: 데이터 추출 로직 작성
다음 코드를 사용하여 페이지에서 인용문 데이터를 스크래핑할 수 있습니다:
# 추출된 데이터 저장 위치
quotes = []
# 페이지에서 모든 인용문 추출
quote_elements = soup.find_all("div", class_="quote")
# 명언을 순회하며 텍스트, 저자, 태그 추출
for quote_element in quote_elements:
text = quote_element.find("span", class_="text").get_text().get_text().replace("“", "").replace("”", "")
author = quote_element.find("small", class_="author")
tags = [tag.get_text() for tag in quote_element.find_all("a", class_="tag")]
# 추출한 데이터 저장
quotes.append({
"text": text,
"author": author,
"tags": tags
})
이 코드 조각은 수집된 데이터를 저장할 quotes라는 리스트를 초기화합니다. 이후 모든 인용문 HTML 요소를 식별하고, 이를 순회하며 인용문 텍스트, 저자, 태그를 추출합니다. 추출된 각 인용문은 quotes 리스트에 사전 형태로 저장되어, 추후 사용이나 내보내기를 위해 데이터를 체계화합니다.
좋아요! 스크래핑 로직이 구현되었습니다.
6단계: 스크랩된 데이터 내보내기
다음 코드 줄을 사용하여 스크랩된 데이터를 CSV 파일로 내보냅니다:
# 내보낼 파일 열기
with open("quotes.csv", mode="w", newline="", encoding="utf-8") as file:
writer = csv.DictWriter(file, fieldnames=["text", "author", "tags"])
# 헤더 행 작성
writer.writeheader()
# 스크래핑된 명언 데이터 작성
writer.writerows(quotes)
위 코드 조각은 quotes.csv 파일을 쓰기 모드로 엽니다. 그런 다음 열 헤더(text, author, tags)를 설정하고 헤더를 작성한 후 quotes 리스트의 각 사전(dictionary)을 CSV 파일에 작성합니다.
csv.DictWriter는 데이터 형식을 단순화하여 구조화된 데이터를 저장하기 쉽게 합니다. 사용하려면 Python 표준 라이브러리에서 csv를 임포트해야 합니다:
import csv
7단계: 모든 것을 통합하기
최종 AIOHTTP 웹 스크래핑 스크립트는 다음과 같아야 합니다:
import asyncio
import aiohttp
from bs4 import BeautifulSoup
import csv
# HTTP GET 요청을 수행하는 비동기 함수 정의
async def scrape_quotes():
async with aiohttp.ClientSession() as session:
async with session.get("http://quotes.toscrape.com") as response:
# 대상 페이지의 HTML에 접근
html = await response.text()
# BeautifulSoup을 사용하여 HTML 콘텐츠 파싱
soup = BeautifulSoup(html, "html.parser")
# 스크래핑된 데이터를 저장할 리스트
quotes = []
# 페이지에서 모든 명언 추출
quote_elements = soup.find_all("div", class_="quote")
# 각 인용문을 순회하며 텍스트, 저자, 태그 추출
for quote_element in quote_elements:
text = quote_element.find("span", class_="text").get_text().replace("“", "").replace("”", "")
author = quote_element.find("small", class_="author").get_text()
tags = [tag.get_text() for tag in quote_element.find_all("a", class_="tag")]
# 스크랩한 데이터 저장
quotes.append({
"text": text,
"author": author,
"tags": tags
})
# 내보내기용 파일 열기
with open("quotes.csv", mode="w", newline="", encoding="utf-8") as file:
writer = csv.DictWriter(file, fieldnames=["text", "author", "tags"])
# 헤더 행 작성
writer.writeheader()
# 스크랩된 명언 데이터 작성
writer.writerows(quotes)
# 비동기 함수 실행
asyncio.run(scrape_quotes())
다음 명령어로 실행할 수 있습니다:
python scraper.py
또는 Linux/macOS에서는:
python3 scraper.py
프로젝트 루트 폴더에 quotes.csv 파일이 생성됩니다. 파일을 열면 다음과 같은 내용을 확인할 수 있습니다:

자, 이제 AIOHTTP와 BeautifulSoup을 사용한 웹 스크래핑 방법을 배웠습니다.
웹 스크래핑을 위한 AIOHTTP: 고급 기능과 기법
기본적인 웹 스크래핑에 AIOHTTP를 사용하는 방법을 이해하셨으니, 이제 더 고급 시나리오를 살펴볼 차례입니다.
다음 예시에서는 HTTPBin.io의 /anything 엔드포인트를 대상으로 합니다. 이 API는 요청자가 보낸 IP 주소, 헤더 및 기타 데이터를 반환하는 편리한 도구입니다.
웹 스크래핑을 위한 AIOHTTP를 마스터할 준비를 하세요!
사용자 정의 헤더 설정
헤더 인자( headers )를 사용하여 AIOHTTP 요청에 사용자 정의 헤더를 지정할 수 있습니다:
import aiohttp
import asyncio
async def fetch_with_custom_headers():
# 요청에 사용할 커스텀 헤더
headers = {
"Accept": "application/json",
"Accept-Language": "en-US,en;q=0.9,fr-FR;q=0.8,fr;q=0.7,es-US;q=0.6,es;q=0.5,it-IT;q=0.4,it;q=0.3"
}
async with aiohttp.ClientSession() as session:
# 사용자 정의 헤더로 GET 요청 수행
async with session.get("https://httpbin.io/anything", headers=headers) as response:
data = await response.json()
# 응답 처리...
print(data)
# 이벤트 루프 실행
asyncio.run(fetch_with_custom_headers())
이렇게 하면 AIOHTTP가 Accept 및 Accept-Language 헤더를 설정하여 GET HTTP 요청을 수행합니다.
사용자 정의 User Agent 설정
User-Agent는 웹 스크래핑에 있어 가장 중요한 HTTP 헤더 중 하나입니다. 기본적으로 AIOHTTP는 다음과 같은 User-Agent를 사용합니다:
Python/<PYTHON_VERSION> aiohttp/<AIOHTTP_VERSION>
위의 기본값은 요청이 자동화된 스크립트에서 온 것임을 쉽게 노출시킬 수 있습니다. 이는 대상 사이트에 의해 차단될 위험을 증가시킵니다.
탐지될 가능성을 줄이려면 다음과 같이 실제 환경의 사용자 에이전트를 커스텀 설정할 수 있습니다:
import aiohttp
import asyncio
async def fetch_with_custom_user_agent():
# Chrome과 유사한 사용자 정의 User-Agent 정의
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.0.0 Safari/537.36"
}
async with aiohttp.ClientSession(headers=headers) as session:
# 사용자 정의 User-Agent로 GET 요청 수행
async with session.get("https://httpbin.io/anything") as response:
data = await response.text()
# 응답 처리...
print(data)
# 이벤트 루프 실행
asyncio.run(fetch_with_custom_user_agent())
웹 스크래핑에 최적의 사용자 에이전트를 찾아보세요!
쿠키 설정
HTTP 헤더와 마찬가지로 ClientSession()의 cookies 를 사용하여 커스텀 쿠키를 설정할 수 있습니다:
import aiohttp
import asyncio
async def fetch_with_custom_cookies():
# 쿠키를 사전(dictionary)으로 정의
cookies = {
"session_id": "9412d7hdsa16hbda4347dagb",
"user_preferences": "dark_mode=false"
}
async with aiohttp.ClientSession(cookies=cookies) as session:
# 사용자 정의 쿠키로 GET 요청 수행
async with session.get("https://httpbin.io/anything") as response:
data = await response.text()
# 응답 처리...
print(data)
# 이벤트 루프 실행
asyncio.run(fetch_with_custom_cookies())
쿠키는 웹 스크래핑 요청에 필요한 세션 데이터를 포함하는 데 도움이 됩니다.
ClientSession에서 설정된 쿠키는 해당 세션으로 수행되는 모든 요청에서 공유됩니다. 세션 쿠키에 접근하려면 ClientSession.cookie_jar를 참조하세요.
프록시 통합
AIOHTTP에서는 IP 차단 위험을 줄이기 위해 프록시 서버를 통해 요청을 라우팅할 수 있습니다. 세션의 HTTP 메서드 함수에서 proxy 인수를 사용하여 수행하세요:
import aiohttp
import asyncio
async def fetch_through_proxy():
# 프록시 서버 URL로 대체
proxy_url = "<YOUR_PROXY_URL>"
async with aiohttp.ClientSession() as session:
# 프록시 서버를 통해 GET 요청 수행
async with session.get("https://httpbin.io/anything", proxy=proxy_url) as response:
data = await response.text()
# 응답 처리...
print(data)
# 이벤트 루프 실행
asyncio.run(fetch_through_proxy())
AIOHTTP에서 프록시 사용 방법 가이드에서 프록시 인증 및 로테이션 수행 방법을 확인하세요.
오류 처리
기본적으로 AIOHTTP는 연결 또는 네트워크 문제에 대해서만 오류를 발생시킵니다. 4xx 및 5xx 상태 코드를 수신할 때 HTTP 응답에 대한 예외를 발생시키려면 다음 방법 중 하나를 사용할 수 있습니다:
-
ClientSession생성 시raise_for_status=True설정: 세션을 통해 수행된 모든 요청의 응답 상태가4xx또는5xx일경우 자동으로 예외 발생. - 개별요청 메서드(
session.get()또는session.post()등)에 직접raise_for_status=True전달: 다른 요청에 영향을 주지 않고 개별 요청 메서드에 대해 오류 발생을 활성화합니다. -
response.raise_for_status()를수동으로 호출: 예외 발생 시점을 완전히 제어하여 요청별로 결정할 수 있습니다.
옵션 #1 예시:
import aiohttp
import asyncio
async def fetch_with_session_error_handling():
async with aiohttp.ClientSession(raise_for_status=True) as session:
try:
async with session.get("https://httpbin.io/anything") as response:
# response.raise_for_status() 호출 불필요 (자동 처리됨)
data = await response.text()
print(data)
except aiohttp.ClientResponseError as e:
print(f"HTTP 오류 발생: {e.status} - {e.message}")
except aiohttp.ClientError as e:
print(f"요청 오류 발생: {e}")
# 이벤트 루프 실행
asyncio.run(fetch_with_session_error_handling())
세션 수준에서 raise_for_status=True가 설정되면, 해당 세션을 통해 수행된 모든 요청은 4xx 또는 5xx 응답에 대해 aiohttp.ClientResponseError를 발생시킵니다.
옵션 #2 예시:
import aiohttp
import asyncio
async def fetch_with_raise_for_status():
async with aiohttp.ClientSession() as session:
try:
async with session.get("https://httpbin.io/anything", raise_for_status=True) as response:
# response.raise_for_status()를 수동으로 호출할 필요 없음, 자동 처리됨
data = await response.text()
print(data)
except aiohttp.ClientResponseError as e:
print(f"HTTP 오류 발생: {e.status} - {e.message}")
except aiohttp.ClientError as e:
print(f"요청 오류 발생: {e}")
# 이벤트 루프 실행
asyncio.run(fetch_with_raise_for_status())
이 경우 raise_for_status=True 인자가 session.get() 호출에 직접 전달됩니다. 이는 4xx 또는 5xx 상태 코드에 대해 예외가 자동으로 발생하도록 보장합니다.
옵션 #3 예시:
import aiohttp
import asyncio
async def fetch_with_manual_error_handling():
async with aiohttp.ClientSession() as session:
try:
async with session.get("https://httpbin.io/anything") as response:
response.raise_for_status() # 4xx/5xx에 대해 수동으로 오류 발생
data = await response.text()
print(data)
except aiohttp.ClientResponseError as e:
print(f"HTTP 오류 발생: {e.status} - {e.message}")
except aiohttp.ClientError as e:
print(f"요청 오류 발생: {e}")
# 이벤트 루프 실행
asyncio.run(fetch_with_manual_error_handling())
개별 요청을 더 세밀하게 제어하려면 요청 후 response.raise_for_status()를 수동으로 호출할 수 있습니다. 이 방법을 사용하면 오류 처리 시점을 정확히 결정할 수 있습니다.
실패한 요청 재시도
AIOHTTP는 요청 재시도를 자동으로 지원하는 내장 기능을 제공하지 않습니다. 이를 구현하려면 사용자 정의 로직이나 aiohttp-retry 같은 타사 라이브러리를 사용해야 합니다. 이를 통해 실패한 요청에 대한 재시도 로직을 구성할 수 있어 일시적인 네트워크 문제, 시간 초과 또는 속도 제한을 처리하는 데 도움이 됩니다.
aiohttp-retry 설치 방법:
pip install aiohttp-retry
사용 방법은 다음과 같습니다:
import asyncio
from aiohttp_retry import RetryClient, ExponentialRetry
async def main():
retry_options = ExponentialRetry(attempts=1)
retry_client = RetryClient(raise_for_status=False, retry_options=retry_options)
async with retry_client.get("https://httpbin.io/anything") as response:
print(response.status)
await retry_client.close()
이 설정은 지수적 백오프 전략을 사용한 재시도 동작을 구성합니다. 자세한 내용은 공식 문서를 참조하세요.
웹 스크래핑을 위한 AIOHTTP vs Requests 비교
웹 스크래핑을 위한 AIOHTTP와 Requests 비교 요약표는 다음과 같습니다:
| 기능 | AIOHTTP | Requests |
|---|---|---|
| GitHub 스타 | 15.3k | 52.4k |
| 클라이언트 지원 | ✔️ | ✔️ |
| 동기화 지원 | ❌ | ✔️ |
| 비동기 지원 | ✔️ | ❌ |
| 서버 지원 | ✔️ | ❌ |
| 연결 풀링 | ✔️ | ✔️ |
| HTTP/2 지원 | ❌ | ❌ |
| 사용자 에이전트 사용자 지정 | ✔️ | ✔️ |
| 프록시 지원 | ✔️ | ✔️ |
| 쿠키 처리 | ✔️ | ✔️ |
| 재시도 메커니즘 | 타사 라이브러리를 통해서만 사용 가능 | HTTPAdapters를통해 사용 가능 |
| 성능 | 높음 | 중간 |
| 커뮤니티 지원 및 인기 | 중간 | 크다 |
전체 비교를 보려면 Requests vs HTTPX vs AIOHTTP에 대한 블로그 게시물을 확인하세요.
HTTPX로 웹사이트를 스크랩하는 방법을 알아보세요.
결론
이 글에서는 웹 스크래핑을 위해 aiohttp 라이브러리를 사용하는 방법을 배웠습니다. 이 라이브러리가 무엇인지, 제공하는 기능과 장점을 살펴보았습니다. AIOHTTP는 온라인 데이터를 수집할 때 HTTP 요청을 수행하는 빠르고 안정적인 선택으로 두각을 나타냅니다.
그러나 자동화된 HTTP 요청은 공개 IP 주소를 노출시킵니다. 이는 신원과 위치를 드러내어 개인 정보 보호를 위협할 수 있습니다. 보안과 개인 정보를 보호하기 위한 가장 효과적인 전략 중 하나는 프록시 서버를 사용하여 IP 주소를 숨기는 것입니다.
Bright Data는 포춘 500대 기업 및 20,000명 이상의 고객에게 서비스를 제공하는 세계 최고의 프록시 서버를 운영합니다. 다양한 프록시 유형을 제공합니다:
- 데이터센터 프록시 – 77만 개 이상의 데이터센터 IP.
- 주거용 프록시 – 195개국 이상에서 7,200만 개 이상의 주거용 IP.
- ISP 프록시 – 70만 개 이상의 ISP IP 주소.
Bright Data 무료 계정을 지금 생성하여 당사의 프록시 및 스크래핑 솔루션을 테스트해 보세요!