cURL은 네트워크를 통해 데이터를 전송하는 다목적 오픈소스 명령줄 도구입니다. 거의 모든 요청을 처리할 수 있도록 다양한 매개변수를 제공합니다. 또한 cURL은 확장 가능하며 기본적으로 모든 현대 프로그래밍 언어에 인터페이스를 갖추고 있습니다.
프로그래밍 언어와 함께 cURL을 사용하면 많은 이점이 있습니다. 예를 들어 디버깅이나 웹 스크래핑 용도로 요청을 자동화할 수 있습니다.
이 글에서는 Python과 cURL을 함께 사용하여 GET, POST, PUT 요청을 자동화하고 파일 및 웹 페이지를 다운로드하는 방법을 배울 수 있습니다.
cURL이란?
cURL은 소프트웨어 프로젝트이지만, 그 이름은 두 가지 제품에도 사용됩니다: libcurl이라는 라이브러리와 curl이라는 명령줄 도구(libcurl을 사용함)입니다. 본 문서에서 curl을 언급할 때는 명령줄 도구를 가리킵니다.
curl은 다용도로 사용되지만 핵심 기능은 간단합니다: 다양한 네트워크 프로토콜을 통한 데이터 전송입니다. 현대 웹의 복잡성을 고려할 때, curl은 가장 복잡한 요청도 처리할 수 있는 방대한 옵션 목록을 제공합니다.
curl은 1996년 HttpGet으로 처음 출시되었으며, 이후 urlget으로 명명된 뒤 curl이 되었습니다. 최초 사용 사례는 IRC 채널에서 사용하기 위한 환율 조회였습니다. 현재 curl은 FTP(S), HTTP(S) (POST, GET, PUT), IMAP, POP3, MQTT, SMB 등 다양한 방식을 통한 데이터 전송을 지원합니다. 또한 쿠키와 SSL 인증서도 처리할 수 있습니다.
curl이 HTTPS를 통해 연결할 때, 원격 서버 인증서를 획득하고 자체 CA 인증서 저장소와 대조하여 해당 서버가 주장하는 서버인지 확인합니다. 예를 들어, 다음 요청은 Bright Data 웹사이트에 HTTPS 요청을 보내며 greeting이라는 쿠키를 hello 값으로 설정합니다:
curl --cookie "greeting=hello" https://www.brightdata.com
Python과 함께 curl을 사용하는 이유는?
curl이 다용도 도구임에도 불구하고 Python과 함께 사용해야 하는 주된 이유는 Python이 요청을 자동화할 수 있기 때문입니다. 다음은 이 조합이 유용한 세 가지 사용 사례입니다:
웹 스크래핑
웹 스크래핑은 하나 이상의 웹 페이지에서 (종종 대량의) 데이터를 수집하는 작업입니다. Python으로 데이터를 스크래핑할 때는 requests 라이브러리를 주로 사용합니다. 재귀적 스크래핑에는 wget을 사용할 수 있습니다. 그러나 복잡한 HTTP(S) 호출이 필요한 고급 스크래핑 용도에는 Python과 함께 curl을 사용하는 것이 이상적입니다.
단일 curl 명령어로 HTTP(S) 요청을 생성 및 처리하여 웹 페이지 데이터를 수집할 수 있지만, 재귀적 처리는 불가능합니다. Python 코드에 curl을 내장하면 요청 매개변수, 쿠키, 사용자 에이전트 등의 요소를 조작하여 웹사이트 내 탐색 경로를 시뮬레이션할 수 있습니다.
이 탐색 경로는 고정될 필요조차 없습니다. 스크래핑된 콘텐츠에 따라 달라지도록 설정함으로써, 각 새 요청은 완전히 동적으로 처리될 수 있습니다.
예를 들어 인기 뉴스 사이트의 댓글 섹션을 스크래핑할 때, 댓글에 혐오 표현 키워드가 포함된 경우에만 작성자 프로필 페이지를 추출하려면 스크랩된 댓글에 따라 조건문을 생성해 이 동적 필터를 쉽게 적용할 수 있습니다.
또한 많은 웹사이트에는 분산 서비스 거부(DDoS) 방지나 reCAPTCHA 프롬프트처럼 대량 페이지 스크래핑을 어렵게 만드는 안전 장치가 있습니다. 요청 사이에 특정 규칙과 일시 중지를 적용함으로써 탐지하기 어려운 인간 행동을 모방할 수 있습니다.
테스트 및 디버깅
자사 웹사이트에서 curl을 사용하는 것은 어리석어 보일 수 있지만, 테스트 및 디버깅 환경에서는 유용합니다. 애플리케이션의 하나 이상의 기능을 테스트하거나 디버깅하는 작업은 종종 번거롭습니다. 다양한 설정이나 매개변수로 반복적으로 테스트해야 하기 때문입니다. 기성 테스트 도구가 많지만, Python과 curl을 사용하면 간단한 테스트를 쉽게 구성할 수 있습니다.
예를 들어, 쿠키를 사용하고 리퍼러에 의존하며 브라우저(즉, 사용자 에이전트)별로 사소한 차이가 있고 결제 흐름의 모든 단계를 POST 요청 본문에 압축하는 (복잡한) 온라인 서비스의 새로운 결제 흐름을 출시하는 경우, 모든 변형을 수동으로 테스트하는 데는 오랜 시간이 걸릴 수 있습니다. Python에서는 전체 매개변수 세트를 포함하는 사전(dictionary)을 만들고 가능한 모든 조합에 대해 curl을 사용하여 요청을 보낼 수 있습니다.
워크플로 자동화
테스트, 디버깅, 웹 스크래핑 외에도 curl은 워크플로 자동화 시나리오에 활용될 수 있습니다. 예를 들어, 많은 데이터 통합 파이프라인은 CSV나 Apache Parquet 파일과 같은 데이터 내보내기 파일의 반복적 덤프 작업으로 시작합니다. (S)FTP 서버에서 새 파일을 주기적으로 확인하는 Python 애플리케이션을 통해 데이터 덤프 복사 작업을 완전히 자동화할 수 있습니다.
메일훅 설정도 고려해 보세요. 특정 쿼리가 포함된 이메일을 애플리케이션이 폴링할 수 있다면 얼마나 많은 일상 작업이 자동화될지 상상해 보십시오. POP3 또는 IMAP 프로토콜을 통해 새 메시지를 폴링함으로써, 메일박스에 특정 이메일이 수신될 때 Python 애플리케이션을 트리거할 수 있습니다.
Python에서 cURL 사용 방법
Python에서 curl을 사용해 요청을 수행하는 방법은 다양합니다. 본 문서에서는 두 가지 옵션을 다룹니다. 첫 번째는 os 및 subprocess Python 패키지를 통해 명령줄에서 curl 요청을 시뮬레이션하는 것입니다. 이 간단한 접근 방식은 운영체제의 명령줄 인터페이스에 프로그래밍 방식으로 명령을 전송합니다.
두 번째 방법은 PycURL 패키지를 사용하는 것입니다. Python으로 웹사이트를 스크래핑하는 다른 방법(curl 사용 제외)을 배우고 싶다면 Bright Data의 Python 스크래핑 가이드를 참고하세요.
필수 조건
이 튜토리얼을 시작하기 전에 curl을 다운로드하고 설치했는지 확인하세요. Windows를 사용하는 경우 curl 명령어를 간편하게 실행할 수 있도록 PATH 환경 변수에 curl을 추가해야 합니다.
운영체제와 Python 간 인터페이스를 구축하려면 다양한 패키지를 사용할 수 있습니다. 그러나 가장 널리 쓰이는 두 가지는 os와 subprocess입니다. 둘 다 설치하려면 다음 pip 명령어를 실행하세요:
pip install os subprocess
curl과 os를 사용한 요청 생성
os 패키지는 매우 간단한 패키지입니다. 응답을 처리하지 않고 curl 요청을 실행하는 데는 단 두 줄의 코드만 필요합니다. 이전 예제에서 설명한 쿠키를 전달하기만 하면 출력이 output.txt 파일에 기록됩니다:
import os
os.system('curl -o output.txt --cookie "greeting=hello" -k https://curl.se')
응답을 파일에 기록하지 않고 Python에서 처리하려면 다음 섹션에서 설명하는 subprocess 패키지를 사용해야 합니다.
다음 코드는 동일한 명령을 실행하지만, 응답을 파일에 기록하는 대신 stdout과 stderr을 튜플로 출력합니다. 이 출력은 Beautiful Soup 같은 다른 Python 패키지로 처리할 수 있습니다:
import shlex
import subprocess
shell_cmd = shlex.split('curl --cookie "greeting=hello" -k https://curl.se')
process = subprocess.Popen(shell_cmd,
stdout = subprocess.PIPE,
stderr = subprocess.PIPE,
text = True,
shell = True
)
std_out, std_err = process.communicate()
std_out.strip(), std_err
PycURL 사용
Python에서 터미널과 직접 인터페이스하는 대신 PycURL 패키지를 사용할 수 있습니다. Linux 사용자라면 pip를 통해 PycURL을 설치할 수 있어 편리합니다:
pip install pycurl
pip install certifi
HTTPS 프로토콜을 통해 인터페이스하려면 certifi도 설치해야 합니다. 문제가 발생하면Stack Overflow의 이 지침을 따르세요. PycURL로 POST 요청하기
PycURL은 Windows에서도 설치 가능하지만 매우 번거로운 작업입니다. pip로 설치 시도 시 다음과 같은 오류가 발생합니다:
--curl-dir=/path/to/built/libcurl 옵션을 지정해 주세요
따라서 소스에서 직접 설치해야 합니다. 이는 “수많은 종속성 가능성과 각 종속성이 고유한 디렉터리 구조, 구성 방식, 매개변수 및 특이점을 가지는 탓에 초보자에게는 권장되지 않습니다.”
이러한 이유로, Windows 머신에서 작업 중이라면 기본적인 네트워크 요청에는 requests 패키지를 사용하는 것이 권장됩니다.
PycURL로 요청하는 방법
이 글의 나머지 부분에서는 PycURL 패키지를 사용하여 다양한 유형의 요청을 생성하는 방법을 자세히 설명합니다.
PycURL로 GET 요청하기
PycURL로 만들 수 있는 가장 쉬운 요청은 GET 요청입니다. 이 요청은 기본적으로 이 섹션 전체에 걸쳐 다른 모든 요청의 템플릿 역할을 합니다.
다음 코드에서 다섯 단계를 확인할 수 있습니다:
- 필요한 모든 패키지를 임포트합니다.
- 두 개의 객체가 생성됩니다: curl 요청의 응답을 저장할 버퍼와 요청을 수행하는 데 사용되는 curl 객체입니다.
- 요청의 옵션이 지정됩니다: URL, 대상, SSL 검증.
- 요청 실행.
- 요청 결과 출력.
# 준비
import pycurl
import certifi
from io import BytesIO
# 버퍼 및 Curl 객체 설정
buffer = BytesIO()
c = pycurl.Curl()
# 요청 옵션 설정
## 요청 대상 설정
c.setopt(c.URL, 'http://pycurl.io/')
## 버퍼를 요청 응답의 대상으로 설정.
c.setopt(c.WRITEDATA, buffer)
## SSL 인증서 유효성 검사를 위해 설치된 인증 기관 번들을 참조.
c.setopt(c.CAINFO, certifi.where())PycURL로 POST 요청 수행
# 요청 실행 및 종료
c.perform()
c.close()
# 라틴1(iso-8859-1) 인코딩으로 버퍼 내용 출력
body = buffer.getvalue()
data = body.decode('iso-8859-1')
print(data)PycURL로 파일 다운로드
PycURL로 POST 요청하기
PycURL로 POST 요청을 만드는 것은 GET 요청을 만드는 것과 매우 유사합니다. 다만 요청에 POST 본문이 추가됩니다. 다음 코드 조각에서는 키-값을 설정하고 URL 인코딩하여 적절히 처리되도록 합니다:
# 준비
import pycurl
import certifi
from io import BytesIO
from urllib.parse import urlencode
# 버퍼 및 Curl 객체 설정
buffer = BytesIO()
c = pycurl.Curl()
# 요청 옵션 설정
## 요청 대상 설정
c.setopt(c.URL, 'http://pycurl.io/')
## 요청 본문을 설정합니다.
post_body = {'greeting': 'hello'}
postfields = urlencode(post_body)
c.setopt(c.POSTFIELDS, postfields)
## 버퍼를 요청 응답의 저장 위치로 설정.
c.setopt(c.WRITEDATA, buffer)
## SSL 인증서 유효성 검사를 위해 설치된 인증 기관 번들 참조.
c.setopt(c.CAINFO, certifi.where())
# 요청 실행 및 종료.
c.perform()
c.close()
# 버퍼 내용을 Latin1(iso-8859-1) 인코딩으로 출력합니다.
body = buffer.getvalue()
print(body.decode('iso-8859-1'))
PycURL을 사용한 PUT 요청 수행
이전 섹션에서 생성한 POST 요청은 PUT 요청으로도 전송할 수 있습니다. 요청 본문에 키-값을 전송하는 대신, UTF-8로 인코딩된 파일 표현으로 전송합니다. 이 방법은 파일 업로드에도 사용할 수 있습니다:
import pycurl
import certifi
from io import BytesIO
c = pycurl.Curl()
# 요청 옵션 설정.
## 요청 대상 설정.
c.setopt(c.URL, 'http://pycurl.io/')
## PUT 요청용 데이터 설정.
c.setopt(c.UPLOAD, 1)
data = '{"greeting": "hello"}'
buffer = BytesIO(data.encode('utf-8'))
c.setopt(c.READDATA, buffer)
## SSL 인증서 유효성 검사를 위해 설치된 인증 기관 번들을 참조합니다.
c.setopt(c.CAINFO, certifi.where())
# 요청을 실행하고 닫습니다.
c.perform()
c.close()
PycURL을 사용한 파일 다운로드
다음 코드 조각은 PycURL을 사용하여 파일을 다운로드하는 방법을 보여줍니다. 임의의 JPEG 이미지를 요청하고, some_image.jpg에 대한 쓰기 스트림을 열어 파일이 저장될 위치로 PycURL에 전달합니다:
import pycurl
import certifi
c = pycurl.Curl()
# 요청 대상 설정.
c.setopt(c.URL, 'http://pycurl.io/some_image.jpg')
# SSL 인증서 유효성 검증을 위해 설치된 인증 기관 번들을 참조합니다.
c.setopt(c.CAINFO, certifi.where())
# 요청을 실행하고 닫습니다.
with open('some_image.jpg', 'wb') as f:
c.setopt(c.WRITEFUNCTION, f.write)
c.perform()
c.close()
PycURL을 사용한 웹 페이지 다운로드 및 처리
많은 PycURL 사용 사례가 웹 스크래핑과 관련되어 있으므로, 다음 코드 조각은 HTML 파일 파싱에 널리 사용되는 Beautiful Soup 패키지를 사용하여 요청 응답을 처리하는 방법을 설명합니다.
먼저 pip를 사용하여 Beautiful Soup 4를 설치합니다:
pip install beautifulsoup4
둘째, GET 요청을 수행한 첫 번째 PycURL 코드 바로 뒤에 다음 코드 조각을 추가합니다. 이렇게 하면 Beautiful Soup이 응답 데이터를 처리하게 됩니다.
데모를 위해 find_all 메서드를 사용하여 모든 단락 요소를 찾고, 개별 단락의 내용을 출력합니다:
from bs4 import BeautifulSoup
# BeautifulSoup을 사용한 데이터 파싱
soup = BeautifulSoup(data, 'html.parser')
# 모든 단락 찾기
paragraphs = soup.find_all('p')
for p in paragraphs:
print(p.text)
PycURL에서 프록시 사용하기
대규모 웹 스크래핑은 프록시를 사용할 때 가장 효과적입니다. 이로 인해 스크래퍼가 봇으로 표시되거나 비정상적인 행동을 하는 것으로 인식되지 않으면서도 병렬로 브라우징 행동을 모방할 수 있습니다.
이 마지막 섹션에서는 프록시를 통해 PycURL로 요청을 생성하는 방법을 배웁니다. 이는 이전에 했던 것처럼 요청 옵션을 조정하여 달성됩니다. 이후 네 가지 설정이 설명되지만, 상황에 맞게 조정할 수 있습니다:
- 간편함을 위해 비보안 프록시가 활성화됩니다.
- 프록시 서버가 설정됩니다.
- 스크립트가 서버와 인증합니다.
HTTPS프록시 설정
# 비보안 프록시 활성화
c.setopt(c.PROXY_SSL_VERIFYHOST, 0)
c.setopt(c.PROXY_SSL_VERIFYPEER, 0)
# 프록시 서버 설정
c.setopt(pycurl.PROXY, <YOUR_HTTPS_PROXY_SERVER>)
# 프록시 서버 인증
c.setopt(pycurl.PROXYUSERPWD, f"{<YOUR_USERNAME>}:{<YOUR_PASSWORD>}")
# 프록시 유형을 https로 설정
c.setopt(pycurl.PROXYTYPE, 2)
이 옵션들은 앞서 설명한 코드 스니펫의 어느 위치에나 삽입하여 요청이 프록시 서버를 경유하도록 할 수 있습니다.
결론
본 문서에서는 웹 스크래핑 및 애플리케이션 테스트 용도로 복잡한 요청을 생성하기 위해 curl과 Python을 함께 사용하는 이유를 상세히 설명했습니다. 다양한 네트워크 요청 생성에 PycURL이 얼마나 유연하게 활용될 수 있는지 보여주기 위해 여러 예시를 제공했습니다.
대안으로, 개발자를 위해 모든 복잡한 작업을 처리하도록 특별히 설계된 Bright Data 프록시 네트워크와 웹 스크레이퍼 IDE를 활용할 수 있습니다. 이렇게 하면 스크래핑 방어 메커니즘을 우회하는 데 신경 쓰지 않고 스크래핑된 데이터 작업에 집중할 수 있습니다.