DuckDuckGo 검색 결과 페이지(SERP) 데이터 스크래핑 방법: 4가지 효과적인 접근법

Python 스크립트, DDGS 라이브러리, Bright Data의 SERP API 또는 AI 에이전트용 MCP를 통해 DuckDuckGo 검색 결과를 스크래핑하는 방법을 알아보세요.
1 분 읽기
How to Scrape DuckDuckGo

이 가이드에서는 다음을 확인하실 수 있습니다:

  • DuckDuckGo 스크래핑을 시작하는 데 필요한 모든 정보.
  • DuckDuckGo 웹 스크래핑에 가장 널리 쓰이고 효과적인 방법들.
  • 맞춤형 DuckDuckGo 스크레이퍼 구축 방법.
  • DDGS 라이브러리를 사용해 DuckDuckGo를 스크래핑하는 방법.
  • Bright Data SERP API를 통해 검색 엔진 결과 데이터를 가져오는 방법.
  • MCP를 통해 DuckDuckGo 검색 데이터를 AI 에이전트에 제공하는 방법.

자, 시작해 보겠습니다!

DuckDuckGo 스크래핑 시작하기

DuckDuckGo는 온라인 추적기 방지를 기본으로 제공하는 검색 엔진입니다. 검색 기록이나 브라우징 이력을 추적하지 않는 프라이버시 중심 정책으로 사용자들로부터 호평을 받고 있습니다. 이로 인해 주류 검색 플랫폼과 차별화되며, 수년간 꾸준히 사용량이 증가하고 있습니다.

DuckDuckGo 검색 엔진은 두 가지 버전으로 제공됩니다:

  • 동적 버전: 기본 버전으로, JavaScript가 필요하며 Google AI 개요의 대안인‘검색 지원(Search Assist)’과 같은 기능을 포함합니다.
  • 정적 버전: 자바스크립트 렌더링 없이도 작동하는 간소화된 버전입니다.

선택한 버전에 따라 스크래핑 접근 방식이 달라지며, 요약표에 설명된 바와 같습니다:

기능 동적 SERP 버전 정적 SERP 버전
JavaScript 필요 아니요
URL 형식 https://duckduckgo.com/?q=<검색어> https://html.duckduckgo.com/html/?q=<검색어>
동적 콘텐츠 예, AI 요약 및 상호작용 요소 등 아니요
페이지 매김 복잡함, “더 보기” 버튼 기반 단순, 페이지 재로딩이 수반되는 전통적인 “다음” 버튼을 통해
스크래핑 접근법 브라우저 자동화 도구 HTTP 클라이언트 + HTML 파서

두 가지 버전의 DuckDuckGo SERP(검색 엔진 결과 페이지)에 대한 스크래핑의 의미를 살펴볼 시간입니다!

DuckDuckGo: 동적 SERP 버전

기본적으로 DuckDuckGo는 JavaScript 렌더링이 필요한 동적 웹 페이지를 로드하며, URL은 다음과 같습니다:

https://duckduckgo.com/?q=<SEARCH_QUERY>
An example of the dynamic version of a DuckDuckGo SERP

이 버전에는 “추가 결과” 버튼과 같이 동적으로 다른 결과를 불러오는 복잡한 페이지 내 사용자 상호작용이 포함됩니다:

The “More Results" button

동적 DuckDuckGo SERP는 더 많은 기능과 풍부한 정보를 제공하지만, 스크래핑을 위해서는 브라우저 자동화 도구가 필요합니다. 그 이유는 자바스크립트에 의존하는 페이지는 오직 브라우저만이 렌더링할 수 있기 때문입니다.

문제는 브라우저 제어 시 추가적인 복잡성과 리소스 사용이 발생한다는 점입니다. 따라서 대부분의 스크래퍼는 사이트의 정적 버전을 활용합니다!

DuckDuckGo: 정적 SERP 버전

자바스크립트를 지원하지 않는 기기를 위해 DuckDuckGo는 SERP의 정적 버전도 제공합니다. 해당 페이지의 URL 형식은 다음과 같습니다:

https://html.duckduckgo.com/html/?q=<SEARCH_QUERY>
An example of the static version of a DuckDuckGo SERP

이 버전에는 AI 생성 요약과 같은 동적 콘텐츠가 포함되지 않습니다. 또한 페이지네이션은 “다음” 버튼을 통해 다음 페이지로 이동하는 전통적인 방식을 따릅니다:

The “Next” button for “traditional” pagination behavior

이 SERP는 정적이기 때문에 전통적인 HTTP 클라이언트 + HTML 파서 방식을 사용해 스크래핑할 수 있습니다. 이 방법은 더 빠르고 구현이 쉬우며 자원을 덜 소모합니다.

DuckDuckGo 스크래핑을 위한 가능한 접근법

본 문서에서 소개할 네 가지 DuckDuckGo 웹 스크래핑 접근법을 살펴보세요:

접근법 통합 복잡도 필요 사항 가격 차단 위험 확장성
사용자 지정 스크레이퍼 구축 중간/높음 파이썬 프로그래밍 기술 무료 (차단을 피하기 위해 프리미엄 프록시가 필요할 수 있음) 가능 제한적
DuckDuckGo 스크래핑 라이브러리에 의존 낮음 파이썬 기술 / CLI 사용 무료 (차단을 피하기 위해 프리미엄 프록시가 필요할 수 있음) 가능 제한적
Bright Data의 SERP API 사용 낮음 모든 HTTP 클라이언트 유료 없음 무제한
웹 MCP 서버 통합 낮음 MCP를 지원하는 AI 에이전트 프레임워크/솔루션 무료 이용 가능, 이후 유료 없음 무제한

이 튜토리얼을 진행하면서 각 항목에 대해 자세히 알아보게 될 것입니다.

어떤 접근 방식을 따르든, 이 블로그 게시물의 대상 검색 쿼리는 “agentic rag”입니다. 즉, 해당 쿼리에 대한 DuckDuckGo 검색 결과를 검색하는 방법을 보게 될 것입니다.

사용자가 이미 로컬에 Python을 설치하고 이에 익숙하다고 가정합니다.

접근 방식 #1: 맞춤형 스크레이퍼 구축

브라우저 자동화 도구 또는 HTTP 클라이언트와 HTML 파서를 결합하여 DuckDuckGo 웹 스크래핑 봇을 처음부터 구축합니다.

👍 장점:

  • 스크래핑 로직에 대한 완전한 제어권.
  • 필요한 정보를 정확히 추출하도록 맞춤 설정 가능.

👎 단점:

  • 설정 및 코딩이 필요합니다.
  • 대규모 스크래핑 시 IP 차단에 걸릴 수 있음.

접근법 #2: DuckDuckGo 스크래핑 라이브러리 활용

DDGS(Duck Distributed Global Search)와 같은 DuckDuckGo용 기존 스크래핑 라이브러리를 사용하면 코드 한 줄 작성 없이 필요한 모든 기능을 제공합니다.

👍 장점:

  • 설정이 거의 필요하지 않습니다.
  • Python 코드나 간단한 CLI 명령을 통해 검색 엔진 스크래핑 작업을 자동으로 처리합니다.

👎 단점:

  • 사용자 정의 스크레이퍼에 비해 유연성이 낮으며, 고급 사용 사례에 대한 제어 기능이 제한적입니다.
  • IP 차단 문제가 여전히 발생합니다.

접근법 #3: Bright Data의 SERP API 사용

프리미엄 Bright Data SERP API 엔드포인트를 활용하세요. 모든 HTTP 클라이언트에서 호출 가능하며, DuckDuckGo를 포함한 여러 검색 엔진을 지원합니다. 확장성 있는 대량 스크래핑을 제공하면서 모든 복잡한 작업을 처리해 줍니다.

👍 장점:

  • 무제한 확장성.
  • IP 차단 및 봇 방지 조치 회피.
  • 모든 프로그래밍 언어의 HTTP 클라이언트 또는 Postman과 같은 시각적 도구와도 통합됩니다.

👎 단점:

  • 유료 서비스.

접근법 #4: 웹 MCP 서버 통합

Bright Data Web MCP를 통해 Bright Data SERP API에 무료로 접근하여 AI 에이전트에 DuckDuckGo 스크래핑 기능을 제공합니다.

👍 장점:

  • 쉬운 AI 통합.
  • 무료 이용 가능.
  • AI 에이전트 및 워크플로 내에서 사용하기 쉬움.

👎 단점:

  • LLM을 완전히 제어할 수 없음.

방법 #1: Python으로 맞춤형 DuckDuckGo 스크래퍼 구축하기

Python으로 맞춤형 DuckDuckGo 스크래핑 스크립트를 구축하는 방법을 배우려면 아래 단계를 따르세요.

참고: 간편하고 빠른 데이터 파싱을 위해 DuckDuckGo의 정적 버전을 사용할 것입니다. AI 생성 “검색 지원”을 수집하는 데 관심이 있다면 , Google에서 AI 개요 결과를 스크래핑하는 방법에 대한 가이드를 읽어보세요. 이를 DuckDuckGo에 쉽게 적용할 수 있습니다.

단계 #1: 프로젝트 설정

터미널을 열고 DuckDuckGo 스크레이퍼 프로젝트용 새 폴더를 생성하세요:

mkdir duckduckgo-scraper

duckduckgo-scraper/ 폴더에 스크래핑 프로젝트를 보관합니다.

다음으로 프로젝트 디렉토리로 이동하여 내부에서 Python 가상 환경을 생성합니다:

cd duckduckgo-scraper
python -m venv .venv

이제 선호하는 Python IDE에서 프로젝트 폴더를 엽니다. Python 확장 프로그램이 설치된 Visual Studio Code 또는 PyCharm Community Edition을 권장합니다.

프로젝트 디렉토리 루트에 scraper.py라는 새 파일을 생성하세요. 프로젝트 구조는 다음과 같아야 합니다:

duckduckgo-scraper/
├── .venv/
└── agent.py

터미널에서 가상 환경을 활성화하세요. Linux 또는 macOS에서는 다음 명령어를 실행합니다:

source venv/bin/activate

Windows에서는 다음과 같이 실행하세요:

venv/Scripts/activate

가상 환경이 활성화된 상태에서 프로젝트 종속성을 설치합니다:

pip install requests beautifulsoup4

필요한 두 라이브러리는 다음과 같습니다:

좋습니다! 이제 DuckDuckGo 스크래핑 스크립트를 구축할 준비가 된 Python 개발 환경이 준비되었습니다.

2단계: 대상 페이지 연결

scraper.py에서 requests를 임포트하는 것으로 시작하세요:

import requests

다음으로 requests.get() 메서드를 사용하여 DuckDuckGo 정적 버전에 브라우저와 유사한 GET 요청을 수행합니다:

# DuckDuckGo 정적 버전의 기본 URL
base_url = "https://html.duckduckgo.com/html/"

# 예시 검색어
search_query = "agentic rag"

# 브라우저 요청 시뮬레이션 및 403 오류 방지
headers = {
  "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36"
}

# 대상 SERP 페이지에 연결
params = {
    "q": search_query
}
response = requests.get(base_url, params=params, headers=headers)

이 구문에 익숙하지 않다면, Python HTTP 요청 가이드를 참조하세요.

위 코드 조각은 다음 User-Agent 헤더와 함께 https://html.duckduckgo.com/html/?q=agentic+rag (본 튜토리얼의 대상 SERP)로 GET HTTP 요청을 보냅니다:

Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36

DuckDuckGo로부터 403 Forbidden 오류를 받지 않으려면 위와 같은 실제 사용자 에이전트를 설정해야 합니다. 웹 스크래핑에서 사용자 에이전트 헤더의 중요성에 대해 자세히 알아보세요.

서버는 GET 요청에 DuckDuckGo 정적 페이지의 HTML로 응답합니다. 다음으로 접근하세요:

html = response.text

출력하여 페이지 내용을 확인하세요:

print(html)

다음과 유사한 HTML이 표시됩니다:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
  <head>
    <meta http-equiv="content-type" content="text/html; charset=UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=3.0, user-scalable=1" />
    <meta name="referrer" content="origin" />
    <meta name="HandheldFriendly" content="true" />
    <meta name="robots" content="noindex, nofollow" />
    <title>DuckDuckGo의 에이전틱 래그</title>
    <!-- 간결함을 위해 생략... -->
  </head>
  <!-- 간결함을 위해 생략... -->
  <body>
    <div>
      <div class="serp__results">
        <div id="links" class="results">

          <div class="result results_links results_links_deep web-result">
            <div class="links_main links_deep result__body">
              <h2 class="result__title">
                <a rel="nofollow" class="result__a"
                  href="//duckduckgo.com/l/?uddg=https%3A%2F%2Fwww.geeksforgeeks.org%2Fartificial%2Dintelligence%2Fwhat%2Dis%2Dagentic%2Drag%2F&amp;rut=db125a181b0852a6be3a488cb8060da7f7359f97d50cdc2b70fd9cd4dd3d1df9">
                  What is Agentic RAG? - GeeksforGeeks
                </a>
              </h2>
              <!-- 간결함을 위해 생략... -->
            </div>
          </div>
          <!-- 기타 결과 ... -->
        </div>
      </div>
    </div>
  </body>
</html>

훌륭합니다! 이 HTML에는 스크래핑하려는 모든 SERP 링크가 포함되어 있습니다.

3단계: HTML 파싱하기

scraper.py에서 Beautiful Soup 가져오기:

from bs4 import BeautifulSoup

그런 다음, 이전에 가져온 HTML 문자열을 탐색 가능한 트리 구조로 파싱하기 위해 사용합니다:

soup = BeautifulSoup(html, "html.parser")

이 코드는 Python 내장 "html.parser"를 사용해 HTML을 파싱합니다. BeautifulSoup 웹 스크래핑 가이드에서 설명한 대로 lxml이나 html5lib 같은 다른 파서를 구성할 수도 있습니다.

잘하셨습니다! 이제 BeautifulSoup API를 사용하여 페이지의 HTML 요소를 선택하고 필요한 데이터를 추출할 수 있습니다.

4단계: 모든 SERP 결과 스크래핑 준비

스크래핑 로직을 살펴보기 전에 DuckDuckGo SERP의 구조를 숙지해야 합니다. 브라우저에서 시크릿 모드(깨끗한 세션 보장)로 다음 웹 페이지를 열어보세요:

https://html.duckduckgo.com/html/?q=agentic+rag

다음으로 SERP 결과 요소에서 마우스 오른쪽 버튼을 클릭하고 “검사” 옵션을 선택하여 브라우저 개발자 도구를 엽니다:

Inspecting the target DuckDuckGo page

HTML 구조를 살펴보세요. 각 SERP 요소는 result 클래스를 가지며 links ID로 식별되는 <div> 에 포함되어 있습니다. 이는 다음 CSS 선택자로 모든 검색 결과 요소를 선택할 수 있음을 의미합니다:

#links .result

Beautiful Soup의 select() 메서드로 파싱된 페이지에 이 선택기를 적용하세요:

result_elements = soup.select("#links .result") 

페이지에 여러 SERP 요소가 포함되어 있으므로, 스크랩된 데이터를 저장할 리스트가 필요합니다. 다음과 같이 초기화하세요:

serp_results = []

마지막으로 선택된 각 HTML 요소를 반복 처리합니다. DuckDuckGo 검색 결과를 추출하고 serp_results 리스트를 채우기 위한 스크래핑 로직을 적용할 준비를 합니다:

for result_element in result_elements:
    # 데이터 파싱 로직...

좋습니다! 이제 DuckDuckGo 스크래핑 목표를 달성하는 데 가까워졌습니다.

단계 #5: 결과 데이터 스크래핑

다시 한번 결과 페이지의 SERP 요소 HTML 구조를 살펴보세요:

Note the HTML structure of the SERP element

이번에는 중첩된 HTML 노드에 집중하세요. 보시다시피, 해당 요소들로부터 다음을 스크래핑할 수 있습니다:

  • .result__a 텍스트에서 결과 제목
  • .result__a href 속성에서 결과 URL
  • .result__url 텍스트에서 표시 URL
  • .result__snippet 텍스트에서 결과 스니펫/설명

특정 노드를 선택하려면 BeautifulSoup의 select_one() 메서드를 적용한 후, 텍스트 추출에는 .get_text() 를, HTML 속성 접근에는 [<attribute_name>]을 사용하세요.

스크래핑 로직 구현:

title_element = result_element.select_one(".result__a")
url = "https:" + title_element["href"]
title = title_element.get_text(strip=True)

url_element = result_element.select_one(".result__url")
display_url = url_element.get_text(strip=True)

snippet_element = result_element.select_one(".result__snippet")
snippet = snippet_element.get_text(strip=True)

참고: strip=True는 추출된 텍스트의 앞뒤 공백을 제거하므로 유용합니다.

서버에서 반환된 HTML이 브라우저에 렌더링된 것과 약간 다르기 때문에 title_element["href"]"https:"를 연결해야 하는지 궁금할 수 있습니다. 스크래퍼가 실제로 파싱하는 원시 HTML에는 다음과 같은 형식의 URL이 포함됩니다:

//duckduckgo.com/l/?uddg=https%3A%2F%2Fwww.geeksforgeeks.org%2Fartificial%2Dintelligence%2Fwhat%2Dis%2Dagentic%2Drag%2F&amp;rut=db125a181b0852a6be3a488cb8060da7f7359f97d50cdc2b70fd9cd4dd3d1df9

보시다시피, URL은 스킴(https://)을 포함하지 않고 //로 시작합니다. "https:"를 앞에 붙이면 URL이 더 사용하기 편리해집니다(해당 형식을 지원하는 브라우저 외부에서도 마찬가지입니다).

이 동작을 직접 확인해 보세요. 페이지에서 마우스 오른쪽 버튼을 클릭하고 “페이지 소스 보기” 옵션을 선택하세요. 그러면 서버에서 반환된 원시 HTML 문서(브라우저 렌더링이 적용되지 않은 상태)가 표시됩니다. SERP 링크가 다음과 같은 형식으로 표시될 것입니다:

Note the format of the SERP link

이제 스크랩한 데이터 필드를 사용하여 각 검색 결과에 대한 사전(dictionary)을 생성하고 이를 serp_results 리스트에 추가합니다:

serp_result = {
  "url": url,
  "title": title,
  "display_url": display_url,
  "snippet": snippet
}
serp_results.append(serp_result) 

완벽합니다! DuckDuckGo 웹 스크래핑 로직이 완성되었습니다. 이제 스크랩한 데이터를 내보내기만 하면 됩니다.

6단계: 스크랩한 데이터를 CSV로 내보내기

지금까지 DuckDuckGo 검색 결과를 Python 리스트에 저장했습니다. 다른 팀이나 도구가 이 데이터를 활용할 수 있도록 Python 내장 csv 라이브러리를 사용해 CSV 파일로 내보내세요:

with open("duckduckgo_results.csv", "w", newline="", encoding="utf-8") as f:
    headers = serp_results[0].keys()
    writer = csv.DictWriter(f, fieldnames=headers)

    # 헤더 작성
    writer.writeheader()

    # 모든 데이터 행 작성
    writer.writerows(serp_results)

csv를 임포트하는 것을 잊지 마세요:

import csv

이렇게 하면 DuckDuckGo 스크래퍼가 모든 스크랩된 결과를 CSV 형식으로 담은 duckduckgo_results.csv라는 출력 파일을 생성합니다. 미션 완료!

7단계: 모든 것을 통합하기

scraper.py 에 포함된 최종 코드는 다음과 같습니다:

import requests
from bs4 import BeautifulSoup
import csv

# DuckDuckGo 정적 버전의 기본 URL
base_url = "https://html.duckduckgo.com/html/"

# 예시 검색어
search_query = "agentic rag"

# 브라우저 요청 시뮬레이션 및 403 오류 방지
headers = {
  "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36"
}

# 대상 SERP 페이지에 연결
params = {
    "q": search_query
}
response = requests.get(base_url, params=params, headers=headers)
# 응답에서 HTML 콘텐츠 가져오기
html = response.text

# HTML 파싱
soup = BeautifulSoup(html, "html.parser")

# 모든 결과 컨테이너 찾기
result_elements = soup.select("#links .result")

# 스크랩된 데이터 저장 위치
serp_results = []

# 각 SERP 결과를 반복하며 데이터를 추출
for result_element in result_elements:
    # 데이터 파싱 로직
    title_element = result_element.select_one(".result__a")
    url = "https:" + title_element["href"]
    title = title_element.get_text(strip=True)

    url_element = result_element.select_one(".result__url")
    display_url = url_element.get_text(strip=True)

    snippet_element = result_element.select_one(".result__snippet")
    snippet = snippet_element.get_text(strip=True)

    # 새 SERP 결과 객체를 생성하고 목록에 추가
    serp_result = {
      "url": url,
      "title": title,
      "display_url": display_url,
      "snippet": snippet
    }
    serp_results.append(serp_result)

# 수집한 데이터를 CSV로 내보내기
with open("duckduckgo_results.csv", "w", newline="", encoding="utf-8") as f:
    headers = serp_results[0].keys()
    writer = csv.DictWriter(f, fieldnames=headers)

    # 헤더 작성
    writer.writeheader()

    # 모든 데이터 행 작성
    writer.writerows(serp_results)

와! 65줄도 안 되는 코드로 DuckDuckGo 데이터 스크래핑 스크립트를 만들었네요.

다음 명령어로 실행하세요:

python scraper.py

프로젝트 폴더에 duckduckgo_results.csv 파일이 생성됩니다. 파일을 열면 다음과 같은 스크래핑된 데이터를 확인할 수 있습니다:

The duckduckgo_results.csv produced by the script

자, 이제 DuckDuckGo 웹 페이지의 구조화되지 않은 검색 결과를 구조화된 CSV 파일로 변환했습니다.

[추가] 차단 회피를 위한 회전 프록시 통합

위 스크레이퍼는 소규모 작업에는 효과적이지만 확장성이 떨어집니다. 동일한 IP에서 과도한 트래픽이 감지되면 DuckDuckGo가 요청을 차단하기 때문입니다. 차단 시 서버는 다음과 같은 메시지가 포함된 403 Forbidden 오류 페이지를 반환합니다:

이 문제가 지속될 경우, <a href="mailto:[email protected]?subject=Error getting results">이메일로 문의해 주세요</a>.<br />
지원 이메일 주소에는 검색 맥락을 파악하는 데 도움이 되는 익명화된 오류 코드가 포함됩니다.

이는 서버가 귀하의 요청을 자동화된 것으로 식별하여 차단했음을 의미하며, 일반적으로 속도 제한 문제 때문입니다. 차단을 피하려면 IP 주소를 순환시켜야 합니다.

해결책은 회전 프록시를 통해 요청을 전송하는 것입니다. 이 메커니즘에 대해 자세히 알고 싶으시다면, IP 주소 회전 방법에 대한 저희 가이드를 참고해 주세요.

Bright Data는 1억 5천만 개 이상의 IP로 구성된 네트워크를 기반으로 회전 프록시를 제공합니다. DuckDuckGo 스크레이퍼에 이를 통합하여 차단되는 것을 방지하는 방법을 확인하세요!

공식 프록시 설정 가이드를 따라하면 다음과 같은 프록시 연결 문자열을 얻을 수 있습니다:

<BRIGHT_DATA_PROXY_USERNAME>:<BRIGHT_DATA_PROXY_PASSWORD>@brd.superproxy.io:33335
The residential rotating proxy configuration in the Bright Data dashboard

Requests에서 아래와 같이프록시를 설정하세요:

proxy_url = "http://<BRIGHT_DATA_PROXY_USERNAME>:<BRIGHT_DATA_PROXY_PASSWORD>@brd.superproxy.io:33335"
proxies = {
    "http": proxy_url,
    "https": proxy_url,
}

# 파라미터 및 헤더 정의...

response = requests.get(
    base_url,
    params=params,
    headers=headers,
    proxies=proxies,   # 회전 프록시를 통해 요청 라우팅
    verify=False,
)

참고: verify=False는 SSL 인증서 검증을 비활성화합니다. 이는 프록시 인증서 검증 관련 오류를 방지하지만, 보안상 취약합니다. 보다 안정적인 구현을 위해서는 당사 SSL 인증서 검증 문서 페이지를 참조하십시오.

이제 DuckDuckGo에 대한 GET 요청은 Bright Data의 1억 5천만 IP 주거용 프록시 네트워크를 통해 라우팅되어 매번 새로운 IP를 보장하며 IP 관련 차단 문제를 방지합니다.

접근법 #2: DDGS 같은 DuckDuckGo 스크래핑 라이브러리 활용

이 섹션에서는 DDGS 라이브러리 사용법을 배웁니다. GitHub에서 1,800개 이상의 스타를 보유한 이 오픈소스 프로젝트는 과거 DuckDuckGo 전용으로 개발되어 duckduckgo-search로 알려졌습니다. 최근 다른 검색 엔진도 지원하게 되면서 DDGS(Dux Distributed Global Search)로 리브랜딩되었습니다.

여기서는 명령줄에서 DuckDuckGo 검색 결과를 스크래핑하는 방법을 살펴보겠습니다!

1단계: DDGS 설치

ddgs PyPI 패키지를 통해 DDGS를 전역적으로 또는 가상 환경 내에서 설치하세요:

pip install -U ddgs

설치 후 ddgs 명령줄 도구로 접근할 수 있습니다. 다음 명령으로 설치 여부를 확인하세요:

ddgs --help

출력 결과는 다음과 같아야 합니다:

보시다시피, 이 라이브러리는 다양한 유형의 데이터(예: 텍스트, 이미지, 뉴스 등)를 스크래핑하기 위한 여러 명령어를 지원합니다. 이 경우 SERP의 검색 결과를 대상으로 하는 text 명령어를 사용하게 됩니다.

참고: 문서에 설명된 대로 Python 코드에서 DDGS API를 통해 해당 명령어를 호출할 수도 있습니다.

2단계: CLI를 통한 DuckDuckGo 웹 스크래핑에 DDGS 사용

먼저 다음 명령을 실행하여 text 명령어에 익숙해지세요:

ddgs text --help

그러면 지원되는 모든 플래그와 옵션이 표시됩니다:

The result of the text --help command

"agentic rag" 에 대한 DuckDuckGo 검색 결과를 스크래핑하여 CSV 파일로 내보내려면 다음을 실행하세요:

ddgs text -q "agentic rag" -b duckduckgo -o duckduckgo_results.csv

출력 결과는 duckduckgo_results.csv 파일입니다. 파일을 열면 다음과 같은 내용이 표시됩니다:

The resulting duckduckgo_results.csv file

대단합니다! 맞춤형 Python DuckDuckGo 스크레이퍼와 동일한 검색 결과를 단일 CLI 명령어로 얻었습니다.

[추가] 회전 프록시 통합

방금 목격했듯이 DDGS는 매우 강력한 SERP 검색 및 웹 스크래핑 도구입니다. 하지만 마법은 아닙니다. 대규모 스크래핑 프로젝트에서는 앞서 언급한 IP 차단 및 접근 제한 문제에 직면하게 됩니다.

이러한 문제를 피하려면 이전과 마찬가지로 로테이팅 프록시가 필요합니다. 당연히 DDGS는 -pr (또는 --proxy) 플래그를 통한 프록시 통합을 기본 지원합니다.

Bright Data 회전 프록시 URL을 가져와 ddgs CLI 명령에 다음과 같이 설정하세요:

ddgs text -q "agentic rag" -b duckduckgo -o duckduckgo_results.csv -pr <BRIGHT_DATA_PROXY_USERNAME>:<BRIGHT_DATA_PROXY_PASSWORD>@brd.superproxy.io:33335

완료! 이제 라이브러리가 수행하는 기본 웹 요청이 Bright Data 회전 프록시 네트워크를 통해 라우팅됩니다. 이를 통해 IP 관련 차단에 대한 걱정 없이 안전하게 스크래핑할 수 있습니다.

방법 #3: Bright Data의 SERP API 사용

이 장에서는 Bright Data의 올인원 SERP API를 사용하여 DuckDuckGo의 동적 버전에서 프로그래밍 방식으로 검색 결과를 가져오는 방법을 배웁니다. 시작하려면 아래 지침을 따르세요!

참고: 간소화되고 빠른 설정을 위해, 요청 라이브러리가 설치된 Python 프로젝트가 이미 준비되어 있다고 가정합니다.

1단계: Bright Data SERP API 영역 설정

먼저 Bright Data 계정을 생성하거나 기존 계정으로 로그인하세요. 아래에서 DuckDuckGo 스크래핑을 위한 SERP API 제품 설정 과정을 안내해 드립니다.

더 빠른 설정을 위해 공식 SERP API “빠른 시작” 가이드를 참조할 수도 있습니다. 그렇지 않으면 다음 단계를 계속 진행하세요.

로그인 후 Bright Data 계정으로 이동하여 “프록시 및 스크래핑(Proxies & Scraping)” 옵션을 클릭하면 다음 페이지로 이동합니다:

설정된 Bright Data 제품이 나열된 “My Zones” 테이블을 확인하세요. 활성 SERP API 존이 이미 존재한다면 바로 시작할 수 있습니다. 존 이름(이 경우serp_api)을 복사해 두세요. 나중에 필요할 것입니다.

존이 존재하지 않는 경우, “스크래핑 솔루션” 섹션으로 스크롤하여 “SERP API” 카드의 “존 생성” 버튼을 클릭하세요:

Creating a new “SERP API” zone

존에 이름을 지정하고(예: serp-api) “추가”를 누르세요:

Clicking the “Add” button

다음으로 해당 존의 제품 페이지로 이동하여 스위치를 “활성”으로 전환하여 활성화되었는지 확인하세요:

Making sure the zone is enabled

훌륭합니다! 이제 Bright Data의 SERP API 설정을 성공적으로 완료했습니다.

2단계: Bright Data API 키 가져오기

SERP API 요청 인증을 위한 권장 방법은 Bright Data API 키를 사용하는 것입니다. 아직 생성하지 않았다면 공식 가이드를 따라 생성하세요.

SERP API에 POST 요청을 할 때 인증을 위해 다음과 같이 Authorization 헤더에 API 키를 포함하세요:

"Authorization: Bearer <BRIGHT_DATA_API_KEY>"

훌륭합니다! 이제 Python 스크립트(또는 다른 HTTP 클라이언트)에서 Bright Data의 SERP API를 호출하는 데 필요한 모든 구성 요소를 갖추셨습니다.

3단계: SERP API 호출

이 모든 것을 결합하여 다음 Python 스니펫으로 “agentic rag” DuckDuckGo 검색 페이지에 대한 Bright Data SERP API를 호출하세요:

# pip install requests
import requests

# Bright Data 인증 정보 (TODO: 본인의 값으로 대체) 
bright_data_api_key = "<YOUR_BRIGHT_DATA_API_KEY>"
bright_data_serp_api_zone_name = "<YOUR_SERP_API_ZONE_NAME>"

# 대상 DuckDuckGo 검색 페이지
duckduckgo_page_url = "https://duckduckgo.com/?q=agentic+rag"

# Bright Data의 SERP API에 요청 수행
response = requests.post(
    "https://api.brightdata.com/request",
    headers={
        "Authorization": f"Bearer {bright_data_api_key}",
        "Content-Type": "application/json"
    },
    json={
        "zone": bright_data_serp_api_zone_name,
        "url": duckduckgo_page_url,
        "format": "raw"
    })


# DuckDuckGo의 동적 버전에서 렌더링된 HTML에 접근
html = response.text
# 파싱 로직...

더 완전한 예제는 GitHub의 “Bright Data SERP API Python 프로젝트”를 참조하세요.

이번에는 대상 URL이 동적 DuckDuckGo 버전(예: https://duckduckgo.com/?q=agentic+rag)일 수 있다는 점에 유의하세요. SERP API는 자바스크립트 렌더링을 처리하고, IP 로테이션을 위해 Bright Data 프록시 네트워크와 연동되며, 브라우저 지문 인식 및 CAPTCHA와 같은 기타 스크래핑 방지 조치를 관리합니다. 따라서 동적 SERP를 스크래핑할 때 문제가 발생하지 않습니다.

html 변수에는 DuckDuckGo 페이지의 완전히 렌더링된 HTML이 포함됩니다. 다음 명령어로 HTML을 출력하여 확인하세요:

print(html)

다음과 같은 결과가 출력됩니다:

<!DOCTYPE html>
<html lang="en-US">
  <head>
      <meta http-equiv="content-type" content="text/html; charset=utf-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <title>Agentic RAG at DuckDuckGo</title>
      <!-- 간결함을 위해 생략 ... -->
  </head>
  <body>
      <div class="site-wrapper" style="min-height: 825px;">
          <div id="content">
              <div id="duckassist-answer" class="answer-container">
                  <div class="answer-content-block">
                      <p class="answer-text">
                          <span class="highlight">에이전트형 RAG</span>은 검색 강화 생성(RAG)의 고급 버전으로, AI 에이전트가 정보를 검색하고 활용하는 방식을 동적으로 관리할 수 있게 하여 실시간 질의에 응답하고 변화하는 상황에 적응하는 능력을 향상시킵니다. 이 접근법은 추론과 데이터 검색을 통합함으로써 AI 시스템의 정확성과 의사결정 능력을 향상시킵니다.
                      </p>
                      <!-- 간결함을 위해 생략 ... -->
                  </div>
                  <!-- 간결함을 위해 생략 ... -->
              </div>

              <ul class="results-list">
                  <li class="result-item">
                      <article class="result-card">
                          <div <!-- 생략 ... -->

                          <div class="result-body">
                              <h2 class="result-title">
                                  <a href="https://www.geeksforgeeks.org/artificial-intelligence/what-is-agentic-rag/" rel="noopener" target="_blank" class="result-link">
                                      <span class="title-text">에이전틱 RAG란 무엇인가? - GeeksforGeeks</span>
                                  </a>
                              </h2>
                              <div class="result-snippet-container">
                                  <div class="result-snippet">
                                      <div>
                                          <span class="snippet-text">
                                              <span class="snippet-date">2026년 9월 8일</span>
                                              <span>
                                                  <b>에이전틱 RAG</b> 아키텍처는 자율 에이전트와 전문 도구 통합을 활용하여 적응성과 지능을 극대화하도록 설계되었습니다. 핵심적으로, 이 아키텍처는 의사 결정, 계획 수립 및 검색이 가능한 추론 에이전트들을 조정된 시스템으로 구성합니다. <b>에이전트 기반 RAG</b> 아키텍처의 주요 구성 요소를 살펴보겠습니다. 1.
                                              </span>
                                          </span>
                                      </div>
                                  </div>
                              </div>
                          </div>
                      </article>
                  </li>
                  <!-- 기타 검색 결과 ... -->
              </ul>
              <!-- 생략 ... -->
          </div>
          <!-- 생략 ... -->
      </div>
  </body>
</html>

참고: 동적 페이지 버전을 다루고 있으므로 출력 HTML에는 “검색 지원” AI 생성 요약이 포함될 수도 있습니다.

이제 첫 번째 접근 방식에 따라 이 HTML을 파싱하여 필요한 DuckDuckGo 데이터에 접근하세요!

접근법 #4: MCP를 통해 DuckDuckGo 스크래핑 도구를 AI 에이전트에 통합하기

SERP API 제품은 Bright Data 웹 MCP에서 제공되는 search_engine 도구로도 노출된다는 점을 기억하세요.

오픈소스 MCP 서버는 DuckDuckGo 스크래핑 기능을 포함한 Bright Data의 웹 데이터 검색 솔루션에 대한 AI 접근을 제공합니다. 자세히 설명하면, search_engine 도구는 Web MCP 무료 티어에서 이용 가능하므로 비용 없이 AI 에이전트나 워크플로에 통합할 수 있습니다.

Web MCP를 AI 솔루션에 통합하려면 일반적으로 로컬에 Node.js가 설치되어 있어야 하며 다음과 같은 구성 파일이 필요합니다:

{
  "mcpServers": {
    "Bright Data Web MCP": {
      "command": "npx",
      "args": ["-y", "@brightdata/mcp"],
      "env": {
        "API_TOKEN": "<YOUR_BRIGHT_DATA_API_KEY>"
      }
    }
  }
}

예를 들어, 이 설정은 Claude Code와 호환됩니다. 문서에서 다른 통합 기능을 확인하세요.

이 통합 덕분에 자연어로 SERP 데이터를 검색하고 AI 기반 워크플로우나 에이전트에서 활용할 수 있습니다.

결론

이 튜토리얼에서는 DuckDuckGo 스크래핑을 위한 네 가지 권장 방법을 살펴보았습니다:

  1. 사용자 정의 스크레이퍼를 통한 방법
  2. DDGS 사용
  3. DuckDuckGo 검색 API 활용
  4. Web MCP 제공

시연된 바와 같이, DuckDuckGo를 대규모로 크롤링하면서 차단되는 것을 피할 수 있는 유일한 신뢰할 수 있는 방법은 Bright Data와 같이 강력한 봇 우회 기술과 대규모 프록시 네트워크를 기반으로 하는 구조화된 크롤링 솔루션을 사용하는 것입니다.

무료 Bright Data 계정을 생성하고 스크래핑 솔루션을 탐색해 보세요!