2026년 Ferret을 활용한 선언적 웹 스크래핑

Ferret이 정적 및 동적 웹 페이지에서 설정부터 데이터 추출까지 선언적 웹 스크래핑을 Go로 어떻게 쉽게 만드는지 알아보세요.
1 분 읽기
Ferret web scraping blog image

이 가이드에서는 다음을 배울 수 있습니다:

  • Ferret이 선언적 웹 스크래핑 라이브러리로서 제공하는 기능
  • Go 환경에서 로컬 사용을 위한 구성 방법
  • – 정적 웹사이트에서 데이터를 수집하는 방법
  • – 동적 사이트 스크래핑 방법
  • Ferret의 주요 한계점과 이를 해결하는 방법

자, 시작해 보겠습니다!

웹 스크래핑을 위한 Ferret 소개

실제 사용을 보기 전에 Ferret이 무엇인지, 어떻게 작동하는지, 어떤 기능을 제공하는지, 그리고 언제 사용해야 하는지 살펴보겠습니다.

Ferret이란?

Ferret은 Go로 작성된 오픈소스 웹 스크래핑 라이브러리입니다. 선언적 접근 방식을 통해 웹 페이지에서 데이터 추출을 단순화하는 것이 목표입니다. 특히 자체 개발한 선언적 언어인 Ferret Query Language (FQL)를 사용하여 파싱 및 추출의 기술적 복잡성을 추상화합니다.

GitHub에서 거의 6천 개의 스타를 보유한 Ferret은 Go를 위한 가장 인기 있는 웹 스크래핑 라이브러리 중 하나입니다. 임베딩이 가능하며 정적 및 동적 웹 스크래핑을 모두 지원합니다.

FQL: 선언적 웹 스크래핑을 위한 페릿 쿼리 언어

Ferret 쿼리 언어(FQL)는 ArangoDB의 AQL에서 크게 영감을 받은 범용 쿼리 언어입니다. 더 많은 기능을 수행할 수 있지만, FQL은 주로 웹 페이지에서 데이터를 추출하는 데 사용됩니다.

FQL은 선언적 접근 방식을 따르므로, 데이터를 어떻게 추출 할지보다 어떤 데이터를 추출할지에 초점을 맞춥니다. AQL과 마찬가지로 SQL과 유사한 점이 있지만, AQL과 달리 FQL은 엄격히 읽기 전용입니다. 모든 형태의 데이터 조작은 특정 내장 함수를 사용해야 합니다.

FQL 구문, 키워드, 구문 구조 및 지원되는 데이터 유형에 대한 자세한 내용은 FQL 문서 페이지를 참조하십시오.

사용 사례

공식 GitHub 페이지에 강조된 바와 같이, Ferret의 주요 사용 사례는 다음과 같습니다:

  • UI 테스트: 브라우저 상호작용을 시뮬레이션하고 다양한 시나리오에서 페이지 요소가 올바르게 동작하고 렌더링되는지 검증하여 웹 애플리케이션 테스트를 자동화합니다.
  • 머신 러닝: 웹 페이지에서 구조화된 데이터를 추출하여 고품질 데이터 세트를 생성합니다. 이를 통해 머신 러닝 모델을 보다 효과적으로 훈련하거나 검증할 수 있습니다. 머신 러닝을 위한 웹 스크래핑 활용 방법을 확인하세요.
  • 분석: 가격, 리뷰, 사용자 활동 등 웹 데이터를 스크래핑하고 집계하여 인사이트 생성, 트렌드 추적 또는 대시보드 구동에 활용합니다.

동시에 웹 스크래핑의 잠재적 사용 사례는 이러한 예시를 훨씬 뛰어넘는다는 점을 명심하세요.

Ferret 시작하기

Ferret이 무엇인지 알게 되었으니, 이제 정적 및 동적 웹 페이지에서 실제로 작동하는 모습을 확인할 준비가 되었습니다. 두 가지의 차이점을 잘 모르신다면, 웹 스크래핑에서 정적 콘텐츠와 동적 콘텐츠의 차이점에 대한 가이드를 읽어보세요.

Ferret을 사용하여 웹 스크래핑을 할 환경을 설정해 보겠습니다!

필수 사항

로컬 머신에 다음이 설치되어 있는지 확인하세요:

  • Go
  • Docker

터미널에서 다음 명령어를 실행하여 Golang이 설치되고 준비되었는지 확인하세요:

go version

다음과 유사한 출력이 표시되어야 합니다:

go version go1.24.3 windows/amd64

오류가 발생하면 Golang을 설치하고 운영 체제에 맞게 구성하세요.

마찬가지로, Docker가 설치되어 있고 시스템에 맞게 제대로 구성되었는지 확인하세요.

Ferret 프로젝트 생성

이제 Ferret 웹 스크래핑 프로젝트용 폴더를 생성하고 해당 폴더로 이동하세요:

mkdir ferret-web-scraping
cd ferret-web-scraping

사용 중인 OS용 Ferret CLI를 다운로드하여 ferret-web-scraping/ 폴더에 직접 압축을 풀어주세요. 다음 명령어를 실행하여 정상 작동하는지 확인하세요:

./ferret help

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

사용법:
  ferret [플래그]
  ferret [명령어]

사용 가능한 명령어:
  browser     Ferret 브라우저 관리
  config      Ferret 구성 관리
  exec        FQL 스크립트 실행 또는 REPL 시작
  help        모든 명령어에 대한 도움말
  update
  version     CLI 버전 정보 표시

플래그:
  -h, --help               ferret 도움말
  -l, --log-level string   로깅 수준 설정 ("debug"|"info"|"warn"|"error"|"fatal") (기본값 "info")

명령어에 대한 자세한 내용은 "ferret [명령어] --help"를 사용하세요.

다음으로 Visual Studio Code와 같은 선호하는 IDE에서 프로젝트 폴더를 엽니다. 프로젝트 폴더 내부에 scraper.fql이라는 파일을 생성하세요:

ferret-web-scraping/
├── ferret
├── CHANGELOG.md
├── LICENSE
├── README.md
└── scraper.fql # <-- Ferret에서 웹 스크래핑을 위한 FQL 파일

scraper.fql 파일 에는 웹 스크래핑을 위한 선언적 FQL 로직이 포함됩니다.

Ferret Docker 설정 구성

Ferret의 모든 기능을 사용하려면 로컬에 Chrome 또는 Chromium이 설치되어 있거나 Docker 내에서 실행 중이어야 합니다. 공식 문서는 Docker 컨테이너에서 Chrome/Chromium을 실행할 것을 권장합니다.

모든 Chromium 기반 헤드리스 이미지를 사용할 수 있지만, montferret/chromium 이미지를 권장합니다. 다음 명령어로 가져옵니다:

docker pull montferret/chromium

그런 다음 다음 명령어로 해당 Docker 이미지를 실행하세요:

docker run -d -p 9222:9222 montferret/chromium

참고: FQL 스크립트 실행 중 브라우저에서 발생하는 상황을 확인하려면 호스트 머신에서 원격 디버깅을 활성화한 상태로 Chrome을 실행하세요:

chrome.exe --remote-debugging-port=9222

Ferret을 사용한 정적 사이트 스크래핑

Ferret을 사용하여 정적 웹사이트를 스크래핑하는 방법을 배우려면 아래 단계를 따르세요. 이 예시에서는 샌드박스 사이트 “Books to Scrape“를 대상으로 합니다:

The target site “Quotes to Scrape”

목표는 FQL을 통한 Ferret의 선언적 접근 방식으로 페이지의 각 도서에서 핵심 정보를 추출하는 것입니다.

1단계: 대상 사이트에 연결하기

scraper.fql에서 DOCUMENT 함수를 사용하여 대상 페이지에 연결합니다:

LET doc = DOCUMENT("https://books.toscrape.com/")

LET은 FQL에서 변수를 정의할 수 있게 합니다. 이 명령어 이후 doc에는 대상 페이지의 HTML이 포함됩니다.

단계 #2: 모든 책 요소 선택

먼저 브라우저에서 대상 웹 페이지를 방문하여 구조를 파악하세요. 구체적으로, 책 요소를 마우스 오른쪽 버튼으로 클릭하고 “검사” 옵션을 선택하여 개발자 도구를 엽니다:

Inspecting a book element

각 책 요소는 상위 <section> 내부의 <article> 노드임을 유의하세요. ELEMENTS() 함수로 모든 책 요소를 선택합니다:

LET book_elements = ELEMENTS(doc, "section article")

ELEMENTS() 는 두 번째 인자로 전달된 CSS 선택기를 문서에 적용합니다. 즉, 페이지에서 원하는 HTML 요소를 선택합니다.

선택된 요소 목록을 반복 처리하며 스크래핑 로직을 적용할 준비를 합니다:

FOR book_element IN book_elements
    // 책 스크래핑 로직...

대단하네요! 이제 각 book 요소를 반복 처리하며 데이터를 추출할 차례입니다.

단계 #3: 각 인용문에서 데이터 추출

이제 단일 HTML 책 요소를 살펴보겠습니다:

Inspecting an HTML book element in detail

다음과 같은 데이터를 스크래핑할 수 있습니다:

  • .image_container img 요소의 src 속성에서 이미지 URL.
  • h3 a 요소의 title 속성에서 책 제목.
  • h3 a 노드의 href 속성에서 책 페이지 URL.
  • .price_color의 텍스트에서 책 가격.
  • .instock의 텍스트에서 재고 정보.

다음과 같이 데이터 파싱 로직을 구현하세요:

LET image_element = ELEMENT(book_element, ".image_container img")
LET title_element = ELEMENT(book_element, "h3 a")
LET price_element = ELEMENT(book_element, ".price_color")
LET availability_element = ELEMENT(book_element, ".instock")

RETURN {
    image_url: base_url + image_element.attributes.src,
    title: base_url+ title_element.attributes.title,
    book_url: title_element.attributes.href,
    price: TRIM(INNER_TEXT(price_element)),
    availability: TRIM(INNER_TEXT(availability_element))
}

여기서 base_url은 for 루프 외부에서 정의된 변수입니다:

LET base_url = "https://books.toscrape.com/"

위 코드에서:

  • ELEMENT() 는 CSS 선택자를 사용하여 페이지에서 단일 요소를 선택할 수 있게 합니다.
  • attributes는 ELEMENT() 가 반환하는 모든 객체가 가진 특수 속성입니다. 현재 요소의 HTML 속성 값들을 포함합니다.
  • INNER_TEXT()는 현재 요소에 포함된 텍스트를 반환합니다.
  • TRIM()은 앞뒤 공백을 제거합니다.

훌륭합니다! 정적 스크래핑 로직 완료.

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

scraper.fql 파일은 다음과 같아야 합니다:

// 대상 사이트에 연결
LET doc = DOCUMENT("https://books.toscrape.com/")

// 책 HTML 요소 선택
LET book_elements = ELEMENTS(doc, "section article")

// 대상 사이트의 기본 URL
LET base_url = "https://books.toscrape.com/"

// 각 책 요소를 반복하며 스크래핑 로직 적용
FOR book_element IN book_elements
    // 모든 정보 요소 선택
    LET image_element = ELEMENT(book_element, ".image_container img")
    LET title_element = ELEMENT(book_element, "h3 a")
    LET price_element = ELEMENT(book_element, ".price_color")
    LET availability_element = ELEMENT(book_element, ".instock")

    // 필요한 데이터 스크래핑
    RETURN {
        image_url: base_url + image_element.attributes.src,
        title: base_url+ title_element.attributes.title,
        book_url: title_element.attributes.href,
        price: TRIM(INNER_TEXT(price_element)),
        availability: TRIM(INNER_TEXT(availability_element))
    }

보시다시피, 스크래핑 로직은 데이터를 어떻게 추출할 것인가보다 어떤 데이터를 추출할 것인가에 더 초점을 맞춥니다. 이것이 Ferret을 활용한 선언적 웹 스크래핑의 힘입니다!

5단계: FQL 스크립트 실행

Ferret 스크립트를 다음과 같이 실행하세요:

./ferret exec scraper.fql

터미널에는 다음과 같은 결과가 출력됩니다:

[{"availability":"In stock","book_url":"catalogue/a-light-in-the-attic_1000/index.html","image_url":"https://books.toscrape.com/media/cache/2c/da/2cdad67c44b002e7ead0cc35693c0e8b.jpg","price":"£51.77","title":"https://books.toscrape.com/A Light in the Attic"},{"재고 상태":"재고 있음","책 URL":"catalogue/tipping-the-velvet_999/index.html","image_url":"https://books.toscrape.com/media/cache/26/0c/260c6ae16bce31c8f8c95daddd9f4a1c.jpg","price":"£53.74","title":"벨벳을 넘어(https://books.toscrape.com/Tipping the Velvet)"},
// 생략...
,{"재고상태":"재고 있음","책_URL":"catalogue/its-only-the-himalayas_981/index.html","image_url":"https://books.toscrape.com/media/cache/27/a5/27a53d0bb95bdd88288eaf66c9230d7e.jpg","price":"£45.17","title":"https://books.toscrape.com/It의 '히말라야뿐이다'"}]

이것은 웹페이지에서 의도한 대로 수집된 모든 도서 데이터를 포함하는 JSON 문자열입니다. 선언적 접근 방식이 아닌 데이터 파싱 방법에 대해서는 Go를 사용한 웹 스크래핑 가이드를 참고하세요.

미션 완료!

Ferret으로 동적 사이트 스크래핑하기

Ferret은 자바스크립트 실행이 필요한 동적 웹사이트 스크래핑도 지원합니다. 본 가이드의 이 섹션에서는 “스크래핑할 인용문” 사이트의 자바스크립트 지연 버전을 대상으로 합니다:

Note how the quote elements are added to the page after a delay

이 페이지는 짧은 지연 후 JavaScript를 사용하여 DOM에 인용문 요소를 동적으로 삽입합니다. 이러한 시나리오에서는 JavaScript 실행이 필요하므로 브라우저에서 페이지를 렌더링해야 합니다. (이것이 이전에 Chromium Docker 컨테이너를 설정했던 이유이기도 합니다.)

Ferret을 사용하여 동적 웹 페이지를 처리하는 방법을 알아보려면 아래 단계를 따르세요!

단계 #1: 브라우저에서 대상 페이지에 연결하기

헤드리스 브라우저를 통해 대상 페이지에 연결하려면 다음 코드를 사용하세요:

LET doc = DOCUMENT("https://quotes.toscrape.com/js-delayed/?delay=2000", {
    driver: "cdp"
})

DOCUMENT() 함수에서 driver 필드의 사용에 유의하세요. 이 필드는 Ferret이 Docker를 통해 구성된 헤드리스 크로미움 인스턴스에서 페이지를 렌더링하도록 지시합니다.

단계 #2: 대상 요소가 페이지에 로드될 때까지 대기

브라우저에서 대상 페이지를 방문하고, 인용문 요소가 로드될 때까지 기다린 후 그중 하나를 검사하세요:

Inspecting a quote element

.quote CSS 선택자를 사용하여 인용문 요소를 선택할 수 있음을 확인하세요. 이 인용문 요소들은 짧은 지연 후 JavaScript를 통해 렌더링되므로 반드시 기다려야 합니다.

Ferret의 WAIT_ELEMENT() 함수를 사용하여 페이지에 인용문 요소가 나타날 때까지 대기합니다:

// 페이지에 인용문 요소가 표시될 때까지 최대 5초 대기
WAIT_ELEMENT(doc, ".quote", 5000)

이는 자바스크립트를 통해 콘텐츠를 렌더링하는 동적 웹 페이지를 스크래핑할 때 필수적인 구조입니다.

3단계: 스크래핑 로직 적용

이제 .quote 노드 내 정보 요소의 HTML 구조에 집중하세요:

Inspecting a .quote element in detail

다음 항목을 스크래핑할 수 있습니다:

  • .quote 내의 인용문 텍스트
  • .author 노드의 저자 정보

Ferret 웹 스크래핑 로직을 다음과 같이 구현합니다:

// quote HTML 요소 선택
LET quote_elements = ELEMENTS(doc, ".quote")

// 각 quote 요소 반복 처리 및 스크래핑 로직 적용
FOR quote_element IN quote_elements
    // 모든 info 요소 선택
    LET text_element = ELEMENT(quote_element, ".text")
    LET author_element = ELEMENT(quote_element, ".author")

    // 관심 데이터 스크래핑
    RETURN {
        quote: TRIM(INNER_TEXT(text_element)),
        author: TRIM(INNER_TEXT(author_element))
    } 

완벽합니다! 파싱 로직 완료.

4단계: 모든 것 조합하기

scraper.fql 파일에는 다음 내용이 포함되어야 합니다:

// 크로미움 헤드리스 인스턴스를 통해 대상 사이트에 연결
LET doc = DOCUMENT("https://quotes.toscrape.com/js-delayed/?delay=2000", {
    driver: "cdp"
})

// 페이지에 인용문 요소가 표시될 때까지 최대 5초 대기
WAIT_ELEMENT(doc, ".quote", 5000)

// 인용문 HTML 요소 선택
LET quote_elements = ELEMENTS(doc, ".quote")

// 각 인용문 요소를 반복 처리하며 스크래핑 로직 적용
FOR quote_element IN quote_elements
    // 모든 정보 요소 선택
    LET text_element = ELEMENT(quote_element, ".text")
    LET author_element = ELEMENT(quote_element, ".author")

    // 관심 데이터 스크래핑
    RETURN {
        quote: TRIM(INNER_TEXT(text_element)),
        author: TRIM(INNER_TEXT(author_element))
    }

보시다시피, 이는 정적 사이트용 스크립트와 크게 다르지 않습니다. 다시 말해, Ferret이 웹 스크래핑에 선언적 접근 방식을 사용하기 때문입니다.

단계 #5: FQL 코드 실행

Ferret 스크래핑 스크립트를 다음과 같이 실행하세요:

./ferret exec scraper.fql

이번에는 결과가 다음과 같습니다:

[{"author":"Albert Einstein","quote":"“우리가 창조한 세상은 우리의 사고 과정이다. 사고를 바꾸지 않고서는 세상을 바꿀 수 없다.”"},{"author":"J.K. Rowling","quote":"“해리, 우리의 진정한 모습을 드러내는 것은 능력보다 선택이다.”"},{"author":"Albert Einstein","quote":"“삶을 사는 방법은 두 가지뿐이다. 하나는 아무것도 기적처럼 여기지 않는 삶이고, 다른 하나는 모든 것을 기적처럼 여기는 삶이다.”"},{"author":"Jane Austen","quote":"“좋은 소설에서 즐거움을 느끼지 못하는 사람은, 신사든 숙녀든, 참을 수 없을 만큼 어리석은 사람일 것이다."},{"author":"Marilyn Monroe","quote":"“불완전함은 아름다움이고, 광기는 천재성이다. 그리고 완전히 지루한 것보다 완전히 우스꽝스러운 편이 낫다.”"},{"author":"Albert Einstein","quote":"“성공한 사람이 되려 하지 마라. 차라리 가치 있는 사람이 되라."},{"author":"앙드레 지드","quote":"“있는 그대로의 모습으로 미움받는 것이, 없는 모습으로 사랑받는 것보다 낫다.”"},{"author":"토마스 에디슨","quote":"“나는 실패한 것이 아니다. 단지 10,000가지의 실패한 방법을 발견했을 뿐이다.”"},{"author":"엘리너 루스벨트","quote":"“여자는 티백과 같다. 뜨거운 물에 담그기 전까지는 그 강도를 알 수 없다.”"},{"author":"스티브 마틴","quote":"“햇빛이 없는 날은, 알다시피, 밤과 같다.”"}]

자, 이제 보세요! 이것이 바로 자바스크립트로 렌더링된 페이지에서 추출한 구조화된 콘텐츠입니다.

페렛 선언적 웹 스크래핑 접근법의 한계

Ferret은 의심할 여지없이 강력한 도구이며, 웹 스크래핑에 선언적 접근 방식을 취하는 몇 안 되는 도구 중 하나입니다. 그러나 적어도 세 가지 주요 단점이 있습니다:

  • 부실한 문서화와 빈번하지 않은 업데이트: 공식 문서에는 유용한 내용이 포함되어 있지만, 포괄적인 API 참조가 부족합니다. 이로 인해 복잡한 스크립트 작성에 어려움을 겪습니다. 또한 프로젝트가 정기적으로 업데이트되지 않아 최신 스크래핑 기술에 뒤처질 수 있습니다.
  • 반스크래핑 우회 지원 부재: Ferret은 CAPTCHA, 속도 제한 또는 기타 고급 반스크래핑 방어 기법을 처리할 내장 메커니즘을 제공하지 않습니다. 이로 인해 보호 수준이 높은 사이트의 스크래핑에는 부적합합니다.
  • 표현력 제한: Ferret 쿼리 언어(FQ)는 아직 개발 중이며, Playwright나 Puppeteer 같은 최신 스크래핑 도구만큼의 유연성이나 제어력을 제공하지 않습니다.

이러한 한계는 단순한 통합으로 쉽게 해결될 수 없습니다. 또한 Ferret의 핵심 초점이 웹 데이터 수집에 있음을 잊지 마십시오. 따라서 더 강력한 대안을 고려하는 것이 해결책입니다.

Bright Data의 AI 인프라에는 신뢰할 수 있고 지능적인 웹 데이터 추출을 위해 맞춤화된 고급 서비스 제품군이 포함되어 있습니다. 이를 통해 모든 웹사이트에서 대규모로 데이터를 수집할 수 있습니다.

결론

이 튜토리얼에서는 Go에서 선언적 웹 스크래핑을 위해 Ferret을 사용하는 방법을 배웠습니다. 시연된 바와 같이, 이 라이브러리는 어떻게 추출할지보다 무엇을 추출할지에 집중함으로써 정적 및 동적 페이지 모두에서 데이터를 추출할 수 있게 합니다.

문제는 Ferret이 여러 제한 사항을 가지고 있어 최상의 솔루션이 아닐 수 있다는 점입니다. 웹 데이터를 더 효율적이고 확장성 있게 수집하는 방법을 찾고 있다면, 120개 이상의 인기 웹사이트에서 최신, 구조화되고 완전히 규정을 준수하는 웹 데이터를 추출하기 위한 전용 엔드포인트인 웹 스크레이퍼 API를도입하는 것을 고려해 보세요.

지금 바로 Bright Data 무료 계정에 가입하여 강력한 웹 스크래핑 인프라를 테스트해 보세요!