Profile picture

[Python] curl_cffi 사용법

JaehyoJJAng2024년 11월 18일

curl_cffi


curl_cffi는 파이썬에서 고성능 HTTP 요청을 수행하기 위해 설계된 라이브러리이다.

보통 파이썬으로 데이터를 스크래핑할 때 requestshttpx를 자주 사용하는데,

curl_cffi는 위 두 라이브러리와 달리 브라우저의 TLS 서명이나 JA3 지문을 모방할 수 있다고 한다.


curl_cffi의 특징은 다음과 같다.

  • JA3/TLS, http2 fingerprints 모방 지원
  • requests, httpx 보다 빠름
  • requests 라이브러리의 API들을 모방하여 기존 프로젝트에서 쉽게 전환가능

설치 방법

pip install curl_cffi

사용 방법

위에서 말했듯이 requests의 API들을 모방하였기에 requests를 쓰듯이 사용하면 되지만, 조금씩 다른 점은 있다.

from curl_cffi import requests as rq

url: str = "https://example.com/"

response = rq.get(url=url, impersonate="chrome110")
print(r.text)

  • impersonate: 특정 브라우저(chrome, firefox ..)와 동일한 방식으로 HTTP 요청을 보내는 기능
    • Chrome: chrome99, chrome100, ...chrome110
    • Firefox: firefox99, firefox100 ... firefox110

Proxy를 쓰려면 다음과 같이 작성하면 된다.

from curl_cffi import requests as rq

url: str = "https://example.com/"
proxies: list[str] = [
    "https": "http://localhost:4444"
]
response = rq.get(url=url, impersonate="chrome110")
print(r.text)

Session 객체를 생성할 수도 있다.

from curl_cffi import requests as rq

url: str = "https://example.com/"

s = rq.Session()
s.get(url=url)

print(s.cookies)
print(s.cookies.get_dict())

r = s.get(url=url)
print(r.json())

라이브러리 비교

스크래핑을 자주하는 사람들이라면 사이트에서 요청되는 headercookie 값을 추출하는 방법에 대해서 알고있을거다.

사이트에서 크롤링을 어떤 식으로 막느냐에 따라, 필요한 정보들이 달라지는데(User-Agent, referer, Cookie 값 등등)

문제는 서버를 속이기 위해 위 정보들을 감쪽같이 맞춰도 파훼가 되지 않는 경우가 있다는 것이다.


확실한 비교를 위해 클라우드 플레어의 HTML 정보를 가져오는 것을 목표로 코드를 작성해보자.

헤더 값은 해당 사이트를 들어갔을 때 요청되는 패킷을 그대로 복사 & 붙여넣기 한다.

from bs4 import BeautifulSoup as bs
import requests as rq


url: str = "https://www.cloudflare.com/ko-kr/lp/ppc/overview-x/"
headers = {
    "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7",
    "accept-encoding": "gzip, deflate, br, zstd",
    "accept-language": "ko,en;q=0.9,en-US;q=0.8",
    "cache-control": "max-age=0",
    "priority": "u=0, i",
    "referer": "https://velog.io/",
    "sec-ch-ua": '"Microsoft Edge";v="131", "Chromium";v="131", "Not_A Brand";v="24"',
    "sec-ch-ua-mobile": "?0",
    "sec-ch-ua-platform": '"Windows"',
    "sec-fetch-dest": "document",
    "sec-fetch-mode": "navigate",
    "sec-fetch-site": "cross-site",
    "sec-fetch-user": "?1",
    "upgrade-insecure-requests": "1",
    "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36 Edg/131.0.0.0",
}

response = rq.get(url=url, headers=headers)
soup = bs(response.text, "html.parser")

with open("test-requests.html", "w", encoding="utf-8") as fp:
    fp.write(soup.prettify())

image
뭔가 잘못된 것이 보이는가? 정상적으로 데이터 추출이 되지 않는 상황이다.


그렇다면, 지금 이 상황에서 requests가 아닌 curl_cffi를 사용해본다면 어떨까?

from bs4 import BeautifulSoup as bs
from curl_cffi import requests as rq


url: str = "https://www.cloudflare.com/ko-kr/lp/ppc/overview-x/"
headers = {
    "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7",
    "accept-encoding": "gzip, deflate, br, zstd",
    "accept-language": "ko,en;q=0.9,en-US;q=0.8",
    "cache-control": "max-age=0",
    "priority": "u=0, i",
    "referer": "https://velog.io/",
    "sec-ch-ua": '"Microsoft Edge";v="131", "Chromium";v="131", "Not_A Brand";v="24"',
    "sec-ch-ua-mobile": "?0",
    "sec-ch-ua-platform": '"Windows"',
    "sec-fetch-dest": "document",
    "sec-fetch-mode": "navigate",
    "sec-fetch-site": "cross-site",
    "sec-fetch-user": "?1",
    "upgrade-insecure-requests": "1",
    "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36 Edg/131.0.0.0",
}

response = rq.get(url=url, headers=headers)
soup = bs(response.text, "html.parser")

with open("test-requests.html", "w", encoding="utf-8") as fp:
    fp.write(soup.prettify())

image
뭐가 바뀌었는지 감이 오는가? 전체 HTML 파일을 정상적으로 추출한 것을 볼 수 있다.


마무리

사이트의 크롤링 차단 구조에 따라서 requests, httpx, curl_cffi를 적절하게 사용해보는 것이 좋을 것 같다.

그래도 안된다면 어쩔 수 없이 셀레니움 rr


Loading script...