Python 단계별 가이드: 클라우드스크레이퍼

Python에서 Cloudscraper를 활용하여 Cloudflare 보호 기능을 우회하고, 오류를 처리하며, 봇 방지 시스템에 대한 대체 솔루션을 탐구하는 방법을 알아보세요.
4 분 읽기
Cloudscraper in Python blog image

이 튜토리얼에서는 클라우드스크레이퍼 Python 라이브러리를 사용하여 Cloudflare의 봇 탐지를 우회하고, 일반적인 오류를 처리하며, 가장 강력한 봇 방지 기능을 위한 대체 스크래핑 솔루션을 탐구하는 방법을 배웁니다.

Python에서 cloudscraper 사용 방법

이 튜토리얼에서는 Cloudflare로 보호되는 웹사이트에서 cloudscraper를 사용한 경우와 사용하지 않은 경우 모두 데이터를 스크래핑해 보겠습니다. 이를 위해 Beautiful Soup 및Requests패키지를 사용할 것입니다. 이 패키지들에 익숙하지 않다면, 자세한 내용은이 Python 웹 스크래핑 가이드를참고하세요.

시작하려면 다음 pip 명령을 실행하여 필요한 패키지를 설치하세요:

pip install tqdm==4.66.5 requests==2.32.3 beautifulsoup4==4.12.3

이 튜토리얼을 더 쉽게 따라할 수 있도록,ChannelsTV 웹사이트에 특정 날짜에 게시된 뉴스 기사의 메타데이터를 스크래핑하기 위한 웹 스크래퍼를 다음과 같이 만들었습니다:

import requests
from bs4 import BeautifulSoup
from datetime import datetime
from tqdm.auto import tqdm

def extract_article_data(article_source, headers):
    response = requests.get(article_source, headers=headers)
    if response.status_code != 200:
        return None

    soup = BeautifulSoup(response.content, 'html.parser')

    title = soup.find(class_="post-title display-3").text.strip()

    date = soup.find(class_="post-meta_time").text.strip()
    date_object = datetime.strptime(date, 'Updated %B %d, %Y').date()

    categories = [category.text.strip() for category in soup.find('nav', {"aria-label": "breadcrumb"}).find_all('li')]

    tags = [tag.text.strip() for tag in soup.find("div", class_="tags").find_all("a")]

    article_data = {
        'date': date_object,
        'title': title,
        'link': article_source,
        'tags': tags,
        'categories': categories
    }

    return article_data

def process_page(articles, headers):
    page_data = []
    for article in tqdm(articles):
        url = article.find('a', href=True).get('href')
        if "https://" not in url:
            continue
        article_data = extract_article_data(url, headers)
        if article_data:
            page_data.append(article_data)
    return page_data

def scrape_articles_per_day(base_url, headers):
    day_data = []
    page = 1

    while True:
        for article in day_data:
            article_data = extract_article_data(article_data)
            if article_data:
                page_data.append(article_data)
            return page_data

def process_page(articles, headers):
    page_data = []
    for article in tqdm(articles):
        url = article.find('a', href=True).get('href')
        if "https://" not in url:
            continue
        article_(article_data)
    return page_data

def scrape_articles_per_day(base_url, headers):
    day_data = []
    page = 1

    while True:
        page_url = f"{base_url}/page/{page}"
        response = requests.get(page_url, headers=headers)

        if not response or response.status_code != 200:
            break

        soup = BeautifulSoup(response.content, 'html.parser')
        articles = soup.find_all('article')

        if not articles:
            break
        page_data = process_page(articles, headers)
        day_data.extend(page_data)

        page += 1

    return day_data

headers = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36',
}

URL = "https://www.channelstv.com/2024/08/01/"

scraped_articles = scrape_articles_per_day(URL, headers)
print(f"{len(scraped_articles)}개의 기사가 스크랩되었습니다.")
print("샘플:")
print(scraped_articles[:2])

이 코드에서는 스크래핑 과정을 용이하게 하기 위해 세 개의 함수가 정의됩니다. 첫 번째 함수인 extract_article_data는 개별 기사에서 데이터를 불러와 발행일, 제목, 태그, 카테고리 등의 메타데이터를 파이썬 사전(dictionary)으로 추출한 후 반환합니다. 데이터 불러오기와 추출 단계는 Requests 및 Beautiful Soup 파이썬 라이브러리를 사용하여 구현됩니다.

두 번째 함수인 process_page는 특정 페이지의 모든 기사 링크를 가져와 extract_article_data 함수를 호출하여 데이터를 추출하고, 각 메타데이터 사전(dictionary)을 리스트에 저장한 후 반환합니다. 마지막 함수인 scrape_articles_per_day는 while 루프를 사용하여 페이지 번호를 증가시키고, 존재하지 않는 페이지에 도달할 때까지 각 페이지의 기사 데이터를 스크래핑합니다.

다음으로 스크랩할 URL을 정의하고, 필터링 날짜를 2024년 8월 1일로 지정하며, 샘플 사용자 에이전트가 포함된 headers 변수를 설정합니다. scrape_articles_per_day 함수를 호출하여 URL과 headers 변수를 전달합니다. 이후 스크랩된 기사 수와 첫 두 결과를 출력합니다.

이 스크레이퍼가 작동하기를 바라지만, ChannelsTV 웹사이트가 Cloudflare를 사용하여extract_article_datascrape_articles_per_day함수에 구현된 직접 요청을 통한 웹 페이지 콘텐츠 접근을 차단하기 때문에 작동하지 않습니다.

이 스크립트를 실행하면 다음과 같은 결과가 출력됩니다:

0개의 기사가 스크랩되었습니다.
샘플:
[]

CloudScraper 통합

앞서 특정 기사 메타데이터를 스크래핑하려 했을 때, Cloudflare의 보호 기능으로 인해 아무것도 반환되지 않았습니다. 이 섹션에서는 cloudscraper를 설치하고 사용해 이 문제를 우회할 것입니다.

시작하려면 다음 pip 명령을 실행하여 cloudscraper 라이브러리를 설치하세요:

pip install cloudscraper==1.2.71

그런 다음 패키지를 임포트하고 다음과 같이 fetch_html_content 함수를 정의하세요:

import cloudscraper

def fetch_html_content(url, headers):
    try:
        scraper = cloudscraper.create_scraper()
        response = scraper.get(url, headers=headers)

        if response.status_code == 200:
            return response
        else:
            print(f"URL {url} 가져오기 실패. 상태 코드: {response.status_code}")
            return None
    except Exception as e:
        print(f"URL {url} 가져오기 중 오류 발생: {str(e)}")
        return None

이 함수는 스크래핑할 URL과 요청 헤더를 입력 매개변수로 받아, 응답 객체 또는 None을 반환합니다. 함수 내부에서 try-except 블록을 정의합니다. try 블록에서는 cloudscraper.create_scraper 메서드를 사용하여 스크래퍼를 생성합니다. 다음으로 scraper.get 메서드를 호출하여 url과 headers 변수를 전달합니다. 응답 상태 코드가 200이면 응답을 반환합니다. 그렇지 않으면 오류 메시지를 출력하고 None을 반환합니다. 마찬가지로 try 블록에서 오류가 발생하면 except 블록이 실행되어 적절한 메시지를 출력하고 None을 반환합니다.

이후 스크립트 내 모든 requests.get 호출을 이 fetch_html_content 함수로 대체합니다. 먼저 extract_article_data 함수에서 다음과 같이 대체하세요:

def extract_article_data(article_source, headers):
    response = fetch_html_content(article_source, headers)

그런 다음 scrape_articles_per_day 함수 내 requests.get 호출을 다음과 같이 교체합니다:

def scrape_articles_per_day(base_url, headers):
    day_data = []
    page = 1

    while True:
        page_url = f"{base_url}/page/{page}" 
        response = fetch_html_content(page_url, headers)

이 함수를 정의함으로써 cloudscraper 라이브러리가 Cloudflare의 제한을 우회하는 데 도움을 줄 수 있습니다.

코드를 실행하면 다음과 같은 결과가 출력됩니다:

URL 가져오기 실패: https://www.channelstv.com/2024/08/01//page/5. 상태 코드: 404
55개의 기사가 스크래핑되었습니다.
샘플:
[{'date': datetime.date(2024, 8, 1),
  'title': 'Resilience, Tear Gas, Looting, Curfew As #EndBadGovernance Protests Hold',
  'link': 'https://www.channelstv.com/2024/08/01/tear-gas-resilience-looting-curfew-as-endbadgovernance-protests-hold/',
  'tags': ['Eagle Square', 'Hunger', 'Looting', 'MKO Abiola Park', 'violence'],
  'categories': ['헤드라인']},
 {'date': datetime.date(2024, 8, 1),
  'title': '러시아 예술가 어머니, 포로 교환으로 석방돼 딸 '포옹' 기다려',
  'link': 'https://www.channelstv.com/2024/08/01/mother-of-russian-artist-freed-in-prisoner-swap-waiting-to-hug-her/',
  'tags': ['Prisoner Swap', 'Russia'],
  'categories': ['World News']}]

추가 클라우드스크레이퍼 기능

보시다시피, 클라우드스크레이퍼는 클라우드플레어의 IUAM 보호 기능을 우회하는 데 도움이 될 뿐만 아니라, 강조할 만한 다른 기능들도 갖추고 있습니다.

프록시 사용

프록시는 사용자의 컴퓨터와 대상 사이트 사이의 중개 서버 역할을 하여 인터넷 탐색 시 익명성을 높여줍니다. 사용자의 요청은 프록시를 통해 전달되므로, 클라우드플레어로 보호되는 사이트와 같은 대상 웹사이트는 트래픽의 출처를 사용자의 기기가 아닌 프록시 서버로 인식합니다.

Cloudscraper를 사용하면 다음과 같이 프록시를 정의하고 이미 생성된 Cloudscraper 객체에 전달할 수 있습니다:

scraper = cloudscraper.create_scraper()

proxy = {
    'http': 'http://your-proxy-ip:port',
    'https': 'https://your-proxy-ip:port'
}

response = scraper.get(URL, proxies=proxy)

여기서 기본값을 가진 스크레이퍼 객체를 정의합니다. 그런 다음 httphttps 프록시를 포함한 프록시 사전(dictionary)을 정의합니다. 마지막으로 일반적인 request.get 메서드와 마찬가지로 프록시 사전 객체를 scraper.get 메서드에 전달합니다.

사용자 에이전트 및 자바스크립트 인터프리터 변경

이전 스크립트에서는 사용자 에이전트를 직접 지정했지만, cloudscraper 라이브러리는 사용자 에이전트를 자동 생성할 수도 있습니다. 이를 통해 스크립팅 시 필요한 수동 설정을 줄이고 다양한 브라우저 신원을 가진 실제 사용자를 모방할 수 있습니다. 이는 무작위로 수행되지만, cloudscraper.create_scraper 메서드에 browser 매개변수를 전달하여 샘플링할 사용자 에이전트 유형을 선택할 수도 있습니다. 이 browser 매개변수는 브라우저와 플랫폼에 대한 문자열 값, 데스크톱과 모바일에 대한 부울 값을 저장하는 사전(dictionary)을 포함합니다.

cloudscraper는 스크레이퍼와 함께 사용할 JavaScript 인터프리터 및 엔진도 지정할 수 있게 합니다. 기본값은 cloudscraper 팀이 만든 네이티브 솔버입니다. 다른 사용 가능한 옵션으로는 Node.js, Js2Py, ChakraCore, v8eval이 있습니다.

다음은 인터프리터와 브라우저 사양을 보여주는 샘플 코드 조각입니다:

scraper = cloudscraper.create_scraper(
    interpreter="nodejs",
    browser={
        "browser": "chrome",
        "platform": "ios",
        "desktop": False,
    }
)

여기서 인터프리터를 "nodejs" 로 설정하고 브라우저 매개변수에 사전(dictionary)을 전달합니다. 이 사전 내에서 브라우저는 Chrome으로, 플랫폼은 "ios"로 설정됩니다. 데스크톱 매개변수는 False로 설정되어 모바일에서 브라우저가 실행됨을 의미합니다(모바일 및 데스크톱 값은 기본적으로 True로 설정됨). 이 경우 Cloudflare는 Chrome 브라우저에서 실행되는 모바일 iOS 사용자 에이전트를 선택합니다.

CAPTCHA 처리

CAPTCHA는인간과 봇을 구분하기 위해 설계되었으며, 스크래핑 시 대상 웹 페이지 로딩을 차단하는 경우가 많습니다. Cloudscraper의 장점 중 하나는 reCAPTCHA, hCaptcha 등을 지원하는 일부 타사 CAPTCHA 해결 도구를 지원한다는 점입니다. 관심 있는 다른 타사 CAPTCHA 솔버가 있다면 GitHub 지원 티켓을 통해 cloudscraper 팀에 제안을 할 수 있습니다.

다음 코드 조각은 CAPTCHA 처리를 위해 스크레이퍼를 수정하는 방법을 보여줍니다:

scraper = cloudscraper.create_scraper(
  captcha={
    'provider': 'capsolver',
    'api_key': 'your_capsolver_api_key'
  }
)

이 코드에서는 CAPTCHA 제공자로 Capsolver를 지정하고 Capsolver API 키를 입력합니다. 두 값 모두 사전(dictionary)에 저장되어 cloudscraper.create_scraper 메서드의 CAPTCHA 매개변수로 전달됩니다.

일반적인 cloudscraper 오류

Cloudflare 제한을 우회하는 쉬운 방법인 cloudscraper를 사용하기 시작하면 몇 가지 오류가 발생할 수 있습니다. 다음은 가장 흔히 발생하는 오류(및 해결 방법)입니다.

모듈을 찾을 수 없음

모듈을 찾을 수 없음 오류는 Python 환경에 존재하지 않는 라이브러리를 임포트하거나 사용하려고 할 때 발생하는 Python의 일반적인 오류입니다.

Python 작업 시에는 특정 환경 내에서 실행되며, 해당 활성 환경에 설치된 라이브러리만 스크립트나 노트북에서 접근할 수 있습니다. 모듈을 찾을 수 없음 오류는 관련 (가상) 환경을 활성화하지 않았거나 환경에 패키지를 설치하지 않았음을 의미합니다.

Windows에서 가상 환경을 활성화하려면 다음 명령을 실행하세요:

<venv-name>Scriptsactivate.bat

Linux 또는 macOS에서 작업 중인 경우 다음 명령어를 사용할 수 있습니다:

source <venv-name>/bin/activate

패키지가 전혀 설치되지 않은 경우 다음 명령을 실행하여 설치하십시오:

pip install cloudscraper

cloudscraper가 최신 Cloudflare 버전을 우회할 수 없음

cloudscraper가 구버전 Cloudflare 우회용으로 설계된 버전을 사용할 때 'cloudscraper가 최신 Cloudflare 버전을 우회할 수 없 음’ 오류가 발생합니다. 이는 Python 라이브러리가 업데이트되기 전까지 최신 Cloudflare 버전의 변경 사항이 구버전 cloudscraper를 제한할 수 있기 때문입니다.

구버전 cloudscraper를 사용 중이라면 다음 명령어로 패키지를 업그레이드하는 것이 좋습니다:

pip install -U cloudscraper

이미 최신 버전의 cloudscraper를 사용 중인 경우, 업데이트를 기다리거나 작동하는 대체 솔루션을 찾아야 할 수 있습니다.

cloudscraper의 대안

여기서 배운 내용을 적용한 후에도 Cloudflare 보호 기능을 우회하는 데 여전히 문제가 있다면 Bright Data 사용을 고려해 보세요.

Bright Data는데이터 센터, ISP, 모바일, 주거용 프록시를 포함한 가장 광범위한 프록시 네트워크를 보유하고 있습니다. 이러한 프록시를 중개자로 활용하면 IP 차단 회피, 성능 향상, 지역 제한 우회, 개인정보 보호가 가능합니다.

Bright Data를 사용하여 Cloudflare 보호를 우회하려면 계정을 생성하고 설정하여 API 자격 증명을 획득하기만 하면 됩니다. 그런 다음 해당 자격 증명을 사용하여 다음과 같이 대상 URL의 데이터에 접근할 수 있습니다:

import requests

host = 'brd.superproxy.io'
port = 22225

username = 'brd-customer-<customer_id>-zone-<zone_name>'
password = '<zone_password>'

proxy_url = f'http://{username}:{password}@{host}:{port}'

proxies = {
    'http': proxy_url,
    'https': proxy_url
}

response = requests.get(URL, proxies=proxies)

여기서는 Python Requests 라이브러리를 사용해 GET 요청을 수행하고 proxies 매개변수를 통해 프록시를 전달합니다. 생성된 프록시는 Bright Data 사용자 이름, 비밀번호, 호스트, 포트 번호를 사용합니다. 특히 사용자 이름은 Bright Data 고객 ID와 존 이름을 기반으로 정의되며, 이 모든 정보는 계정에서 확인할 수 있습니다.

결론

이 튜토리얼에서는 Python의 cloudscraper 라이브러리를 사용하여 Cloudflare로 보호된 웹사이트를 스크래핑하는 방법을 배웠습니다. 또한 발생할 수 있는 일반적인 오류와 이를 우회하는 방법도 알아보았습니다. cloudscraper는 Cloudflare의 IUAM을 우회하는 훌륭한 솔루션이 될 수 있지만, 무료 기술인 만큼 한계가 있습니다. 그래서 인상적인Bright Data프록시 네트워크와Web Unlocker를사용하여 Cloudflare로 보호된 사이트에 접근하는 방법도 함께 배웠습니다.

Bright Data는 인터넷상의 데이터에 제한 없이 접근할 수 있는 자동화 도구를 제공합니다. 자동화가 목표가 아니라면, 대규모 프록시 네트워크를 활용해 실패한 요청 수를 줄일 수도 있습니다.

웹 스크래핑을 한 단계 업그레이드할 준비가 되셨나요? 저희 프리미엄 프록시와 전문 웹 데이터 수집 서비스가 가장 강력한 봇 차단도 손쉽게 우회하는 방법을 확인해 보세요. 지금 바로 무료 체험을 시작하세요!